🚀 AI 一键生成 joinquant 策略代码
立即体验
👉 AI 生成代码 👈

为什么JoinQuant中回测与get_backtest获取的变量值不一样?

joinquant | 发布时间: 2026/6/1
以下内容由 EasyQuant 生成。

问题描述

变量在回测和get_backtest_data下值不一样

非常困扰的一个问题,同样的一个变量在回测和get_backtest_data模式下,在参数相同,日期也相同的情况下,日志打印后得到的变量值却不一样,值在回测模式下是ok的,但是在get_backtest_data下一直是0,不知道怎么回事?

解决方案

在JoinQuant(聚宽)平台上,遇到回测环境与研究环境(如通过 get_backtest 获取数据)中同一个变量值不一致,甚至在研究环境中为 0 的情况,通常是由环境上下文差异数据获取机制引起的。以下是常见的几个原因及排查建议:

1. record() 函数的调用时机问题

如果你是在回测中通过 record(var=xxx) 记录变量,然后在研究环境中通过 get_backtest(id).get_records() 获取数据,发现值为 0,这通常是因为 记录时机不对

  • 原因:如果在 before_trading_start(开盘前)调用了 record(),此时当天的交易尚未发生,很多基于盘中交易或持仓计算的变量(如当日收益、动态持仓价值等)可能尚未更新,初始值即为 0
  • 解决办法:将 record() 函数的调用移动到 handle_data(盘中)或 after_trading_end(收盘后)中,确保变量已经完成了当天的计算更新。

2. API 默认日期的差异

回测环境和研究环境在调用数据API时,如果不显式传入日期参数,默认行为是不同的:

  • 回测环境:默认日期通常是 context.current_dt(回测的当前逻辑时间)或其前一个交易日(如 get_fundamentals 默认取前一天以避免未来函数)。
  • 研究环境:默认日期通常是今天或数据库中最新的交易日。
  • 解决办法:在研究环境中调用数据API(如 get_price, get_fundamentals)时,务必显式传入 dateend_date 参数,确保与回测时的逻辑时间完全对齐。

3. 动态复权(真实价格)模式的影响

在回测中,我们通常会在 initialize 中设置 set_option('use_real_price', True)。这会导致回测中获取的历史价格是基于回测当前日期的前复权价格

  • 原因:研究环境中获取历史数据时,如果不指定复权基准日(fq_ref_date),默认是基于当天的前复权价格。如果回测日期和研究环境当前日期之间发生了分红派息,复权因子不同,计算出的均线、指标等变量就会产生差异。
  • 解决办法:在研究环境中获取数据时,注意复权参数 fq 的设置,必要时使用 get_bars 并显式指定 fq_ref_date 为回测当天的日期。

4. 回测专用 API 的限制

某些 API 和对象是回测/模拟盘专用的,例如 get_current_data()context.portfolio 等。如果在研究环境中尝试复现回测的逻辑,直接调用这些对象会失败或返回空值/0。

  • 解决办法:研究环境中无法直接复现 context 对象的动态变化。必须依赖 get_backtest(id).get_positions()get_backtest(id).get_portfolio() 等专用方法来提取回测的历史切片状态。

5. 停牌数据的处理差异

  • 原因:回测中,historyattribute_history 默认不跳过停牌日(使用前一交易日数据填充)。如果在研究环境中使用了不同的参数(如 skip_paused=True),会导致计算周期内的数据长度或数值不同,进而导致计算结果(如移动平均线)不一致。
  • 解决办法:检查研究环境和回测环境中获取数据时的 skip_pausedfill_paused 参数是否保持一致。

💡 终极排查建议

  1. 打印时间戳:在回测中打印该变量计算时的 context.current_dt,然后在研究环境中严格传入该时间戳获取底层数据,对比底层数据是否一致。
  2. 检查除零逻辑:如果变量是通过除法计算得出的(如收益率、占比),检查分母在研究环境中是否因为上述原因变成了 0NaN,导致最终结果被异常处理为 0