Alpha-Online
踩坑复盘

回测实盘路径分叉

回测、模拟盘、实盘的信号 / 风控 / 因子筛选存在三套独立实现,导致结果不可复现。#31 事故档案,One Codebase 不变量的直接驱动。

层级 全局(回测 ↔ 执行) 严重度 致命  关联 #31 · commit 226fef2 · engine.py signal_generator.py run_daily.py

每条路径单独验证均无问题,但三条路径合在一起时,回测与实盘产生系统性不一致。

现象

同一个策略,回测净值漂亮,模拟盘却跑不出来——而且每次想复现都对不上。换个入口跑,信号、持仓、绩效全都不一样。系统对外宣称是一套策略,内部其实是三套。

定位

把回测、模拟盘、实盘三条路径并排摊开,发现它们各自实现了一份:

  • 信号生成——截面排序、Top N 选择的代码各写了一遍;
  • 风控——单票上限 / 总仓上限 / 止损的判定逻辑不一致;
  • 因子筛选——IC/ICIR 的口径与去冗余规则有出入。

三份实现随各自迭代逐渐漂移,一个 shift 或一个边界条件的差异即可导致截然不同的结果。

根因

违反了系统的第二条不变量——One Codebase。只要产生交易决策的逻辑存在第二份拷贝,回测就不再能有效预测实盘表现。「所见即所得」原则一旦被破坏,所有回测指标均丧失参考价值。

修复

强制收敛到唯一真相源

  • 回测与实盘都调用同一个 SignalGenerator 产生目标持仓;
  • 同一个 RiskManager 施加风控;
  • 回测 _shift_signals 在截面维度对每个 symbol 做 shift(1),保证 T 日只用 T-1 信息——实盘则天然在收盘后才跑,时间隔离对齐。

此后「回测 → 模拟 → 实盘」是同一段代码喂不同数据,而不是三段代码假装一致。

教训:回测的正确做法是让生产代码处理历史数据,而非另写一套模拟器。每多一份决策逻辑的拷贝,就多一个回测与实盘分叉的风险点。可复现性需要通过架构约束来保证,而非仅靠测试验证。

关联

On this page