回测结果太好,可能是回测本身在骗你
年化 80%、最大回撤 5% 的回测曲线第一眼很美,第二眼该起疑了。本文梳理四类最常见的回测自欺机制,并给出一份上线前的自检清单。
一句话版本:回测收益 = 策略真实收益 + 回测偏差。后者的量级经常比前者大,而且它永远是正的——这正是回测曲线总是很好看的原因。
为什么「太好」本身就是警报
一份合规的、无偏的回测,其样本外表现通常低于样本内。如果你的回测满足下面任意一条,请优先怀疑回测,而不是庆祝策略:
- 年化收益高得像印钞机,回撤却小得像货币基金;
- 参数稍微改动 5%,绩效指标剧烈波动;
- 用全市场历史数据选出来的「最优参数」,只在这一段历史里最优。
下面四类机制,覆盖了我见过 80% 的「假回测」。
陷阱一:未来函数(前视偏差)
定义:在时刻 T 的决策里,用到了 T 时刻尚不可知的信息。
最常见的三种形态:
# ❌ 1. 用当天收盘价决策、按当天收盘价成交
signal = data.close[t] > ma(t) # 收盘后才知道
buy(price=data.close[t]) # 却按收盘价成交
# ❌ 2. 用了"全样本"计算的指标
z = (x - x.mean()) / x.std() # mean/std 含未来数据
# ❌ 3. 财报数据没有对齐公告日
use(report_data=quarterly_revenue) # 4 月才公告的一季报,1 月就用上了
修法:决策输入只用 t-1 及之前的数据;所有统计量做滚动窗口;财务数据按 point-in-time(公告日)对齐,而不是按报告期对齐。
陷阱二:幸存者偏差
定义:样本里只有「活到最后」的标的,退市、停牌、被剔除的都消失了。
用今天的成分股名单去回测过去十年,等于用后视镜选股——你提前知道了谁会活下来。
修法:
- 使用历史时点的成分股名单(point-in-time universe),而不是今天的名单;
- 把退市标的保留在数据集里,用退市前的真实价格结算;
- 如果数据源不提供历史名单,至少在文中明确声明这是已知偏差,并估计它的量级。
陷阱三:参数挖掘与隐性过拟合
当你在 500 组参数里挑出最好的一组时,你做的不是优化,是多重检验——哪怕全是随机数据,500 组里也必然有一组看起来像圣杯。
一个快速自检:
| 现象 | 可能病因 |
|---|---|
| 最优参数在网格边缘 | 搜索空间没边界感,多半在拟合噪声 |
| 参数改动 ±10%,绩效腰斩 | 适应度曲面太陡,样本内幻觉 |
| 换一段历史,最优参数就变 | 你找到的不是规律,是巧合 |
修法:
- walk-forward 滚动验证:只用「当时可用」的数据选参数,滚动到下一段评估;
- 参数平原优先于参数尖峰:选一大片「都还行」的区域,不选孤峰;
- Deflated Sharpe Ratio / 多重检验校正:把「试了多少组」计入显著性,试得越多,要求越严。
陷阱四:样本内幻觉
全样本调参 + 全样本报告绩效,等于考试前拿到了答案再考试。
最低标准:切出一段从未参与任何决策的时间段(含参数选择、规则修改、指标筛选)作为最终测试集。碰过它一次,它就不再是测试集。
上线前自检清单
把下面这份清单固化成脚本,跑通全绿再看收益曲线:
- 决策输入是否只含 T 时刻已知信息?(滚动统计、滞后信号)
- 财务/基本面数据是否按公告日对齐?(point-in-time)
- 股票池是否保留了历史时点的退市/剔除记录?
- 参数选择过程是否与最终评估数据隔离?
- 是否报告了「尝试过的参数组数」并做多重检验校正?
- 样本外绩效是否单独列示(而不是和样本内混在一起)?
- 交易成本、滑点、涨跌停无法成交是否建模?
写在最后
回测的作用是证伪,不是证明。一条好看的回测曲线只能说明「这个策略在过去的数据上没有被击毙」,不能说明它未来可用。
先怀疑回测,再怀疑市场,最后才轮到怀疑策略——这个顺序,能帮你省下很多真金白银的学费。
本文讨论的是回测工程方法,不构成投资建议。任何策略的实盘表现都可能与回测存在显著差异。