问题描述
非常困扰的一个问题,同样的一个变量在回测和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)时,务必显式传入date或end_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. 停牌数据的处理差异
- 原因:回测中,
history和attribute_history默认不跳过停牌日(使用前一交易日数据填充)。如果在研究环境中使用了不同的参数(如skip_paused=True),会导致计算周期内的数据长度或数值不同,进而导致计算结果(如移动平均线)不一致。 - 解决办法:检查研究环境和回测环境中获取数据时的
skip_paused和fill_paused参数是否保持一致。
💡 终极排查建议
- 打印时间戳:在回测中打印该变量计算时的
context.current_dt,然后在研究环境中严格传入该时间戳获取底层数据,对比底层数据是否一致。 - 检查除零逻辑:如果变量是通过除法计算得出的(如收益率、占比),检查分母在研究环境中是否因为上述原因变成了
0或NaN,导致最终结果被异常处理为0。