NOTE · 技术笔记

回测时间轴上的三个静默错误:日历、可用时点与执行时点

回测里最贵的一类错误是"静默错误":不报错、不崩溃,结果只是悄悄偏了一点点。今天这篇文章不讲抽象原则,而是复现我系统里真实发生过的三个时间轴错误——每一个都有具体日期、具体数字、具体的修复代码。读完你可以直接对照自己的数据管道检查。

错误一:日历里混进了"伪交易日"

现场:10 月初一个增量取数任务跑完,返回"0 行新数据"。当天明明是交易日,理论上应该拉回当天的行情。

排查:任务本身没报错——它按"本地交易日历"请求数据,请求区间内没有"交易日",自然没有数据。打印日历一查:日历里躺着一个伪交易日 10-07(国庆休市)。

根因:我的交易日历是本地推导的——"周一至周五 = 交易日,法定节假日按手工清单剔除"。推导式日历迟早会错:调休、临时休市、以及清单更新滞后。而 10-07 恰是国庆假期里的工作日,被推导逻辑当成了交易日。

修复(可复现):

1. 增量窗口的日历改为从数据源同步(我用的是 baostock 的 query_trade_dates),不再依赖本地推导:

# before: 本地推导日历(会含伪交易日)
days = [d for d in workdays(start, end) if d not in holidays]
# after: 权威日历同步(源侧为准)
days = query_trade_dates(start, end)  # 只信源头

2. 同步时同时做两件事:补上本地缺失的权威交易日;剔除本地伪日(已剔除 10-07);

3. 加一条可断言的校验:每次取数完成后断言"请求窗口内的权威交易日 = 日历表内交易日"。

验收标准:对同一区间,本地日历与权威日历的对称差必须为空;不为空就告警而不是继续跑。

错误二:用了"盘中未收盘"的当日数据

现场:盯盘程序在 14:59:46 抓到某标的的快照价 62.40;收盘后我从数据源重新取官方收盘价,是 62.43——差 3 分钱。

根因:14:59:46 还在收盘集合竞价阶段,快照价是竞价期间的价格,不是收盘价。如果我把这个"当日快照"当作收盘价写进日线库,那么所有依赖"当日收盘"的计算(收盘信号、当日收益)都带着这 3 分钱的错误——在低价股上就是百分之零点几的日收益误差。

修复(可复现):

1. 硬门禁:15:00 之前禁止写入当日任何"收盘类"字段——盘中价格只进 provisional(临时)层,结构上不与正式层互通;

2. 新增"收盘定格"窗口:15:00–15:02 之间再取一次快照,这一次取到的才是可入库的收盘价(实测 15:51 重新取值即为真收盘价);

3. 取证记录里把"取值时刻"当一等字段:snapshot_time=14:59:46 / market_open=1 和 snapshot_time=15:52 / market_open=0 是两条语义完全不同的记录,不得混用。

验收标准:库里任何标记为"收盘"的值,其取值时刻必须 ≥ 15:00;用 SQL 随机抽查即可断言。

错误三:信号日和执行日没有分离

现场:一个"当日收盘后生成信号、按当日收盘价回测成交"的初始版本。把成交价改成"次日可交易价"后,同一策略的收益直接掉了一大截(换手越高、掉得越狠)。

根因:收盘价是收盘那一刻才知道的信息。用当天收盘价成交,等于假设你在收盘价形成的同一瞬间就完成了决策+下单+成交——现实中不存在这个动作。

修复(可复现):

1. 回测产物里把执行时点写成显式多列(这是我现在 trade 表的结构):

signal_date(信号日,只能使用此日收盘及之前的数据)
buy_date(执行日,T+1)
planned_sell_date / sell_date(退出时点,T+2)
buy_price / sell_price(执行日成交价,禁止回退为信号日价格)

2. 引擎里加断言:buy_date > signal_date,违反直接拒绝计算;

3. 检查清单:任何一次回测跑完,抽样核对"信号日列的值是否当天就可见"——如果某因子在信号日当天才形成(如收盘后才计算的涨跌幅),它就不允许出现在信号判据里。

验收标准:把执行价临时改回"信号日收盘价"跑一遍对照——两组收益差异越大,说明你的策略对执行时点越敏感,越应该重视这条纪律。

总结:三条可复制的纪律

1. 交易日历只信数据源,本地推导只做校验对照;

2. 未收盘的数据永远进不了正式层,取值时刻是必填字段;

3. 信号与执行必须分列,并在引擎层断言先后顺序。

这三个错误有两个共同特征:出问题时没有异常抛出,只有一个"稍微不对"的数字。所以检验它们的方法也不是看日志,而是对账:和权威源对、和常识对(差 3 分钱也是对不上)、和历史版本对。


本站内容为个人研究记录与方法分享,模拟,不构成投资建议;据此操作风险自负。


Joyous 的技术笔记 · 沪ICP备2026053770号


隐私最小化:不设 cookie、无表单、无第三方脚本;访问统计仅取服务端日志(≤30 天,仅聚合),不导出 IP。