回测程序没有报错,并不等于逻辑没有“偷看未来”。数据泄漏往往不会触发异常,反而会让净值更平滑、胜率更高,因此比普通代码错误更难发现。判断它的核心标准只有一个:在生成信号的那个时刻,这项信息是否已经真实可用?
先看常见的时间穿越。程序用第t日收盘价计算均线突破,同时把买入价写成第t日收盘价。可均线信号只有收盘数据完整后才能确认,订单无法回到收盘前成交。更合理的实现,是把信号延迟到下一交易时点,并使用下一根K线能够实现的成交价格。
第二类问题藏在特征工程中。若先用全样本计算均值、波动率、分位数,再切分训练集和测试集,测试期信息已经进入了模型参数。正确顺序应当是先按时间划分,再只用训练窗口拟合变换器,并把同一组参数应用到后续区间。滚动训练时,每次也只能看到当时已经结束的窗口。
第三类来自数据版本。财务指标应按公告日期进入策略,不能按报表所属期直接使用;指数成分、行业分类和股票池要保留历史版本。只拿当前仍交易的证券回看过去,会遗漏退市或失去流动性的样本,形成幸存者偏差。
工程上可以设置几道闸门。第一,为每条记录同时保存业务日期和可见时间;第二,在数据读取层禁止访问决策时点之后的字段;第三,给信号、委托和成交建立严格递增的时间戳;第四,采用走步验证,让模型在过去训练、在未来测试;第五,对费用、滑点、停牌、涨跌停和成交量约束做统一建模。
测试也应针对时间逻辑。可以故意把特征延迟一期,比较结果是否合理;随机截取某个历史日期,只加载该日以前的数据,验证程序能否得到相同信号;还可记录每笔交易的证据链,逐项检查数据何时发布。
回测曲线越惊艳,越应该先审查时间边界。可信的研究不追求最漂亮的历史答案,而追求在不知道未来的条件下,仍能重复得到同样决策过程。
量化回测的隐形作弊:前视偏差是怎样混进代码的