3. 回测陷阱与生存偏差:前视偏差、生存偏差、过拟合回测的识别与修正
做量化策略,最怕什么?
不是亏钱,而是你明明觉得策略很稳,一上实盘就崩。我见过太多人,回测曲线漂亮得像教科书,实盘却像过山车。说白了,问题往往出在回测本身——你踩了陷阱,自己还不知道。
这一章,咱们就聊聊三个最常见的回测陷阱:前视偏差、生存偏差、过拟合。我会结合自己的踩坑经历,告诉你如何识别,怎么修正。
3.1 前视偏差:你看到了未来
前视偏差,也叫未来函数。简单说,就是你用未来的信息去决策今天的交易。这在回测里特别隐蔽。
我遇到过的一个典型例子:
有个团队做因子回测,用当天的收盘价计算信号,然后当天买入。你想想看,收盘价出来的时候,交易时间已经结束了。这等于你提前知道了收盘价,再去下单——这不就是作弊吗?
核心判断标准:
回测中,任何用到未来数据的操作,都是前视偏差。
常见的几种前视偏差:
- 价格类未来函数:用当日最高价、最低价、收盘价计算信号,然后当日交易。
- 财务数据未来函数:用季报发布后的数据,但回测中假设数据在季报截止日就能用。实际上,财报发布有延迟。
- 事件类未来函数:比如用“涨停”作为信号,但涨停后你根本买不进去。
怎么修正?
我个人习惯,在回测框架里强制加入一个“数据延迟”机制。比如,用当天的数据,只能生成明天的信号。代码里可以这样写:
# 错误示例:前视偏差
df['signal'] = (df['close'] > df['close'].shift(1)) # 用当天收盘价
df['position'] = df['signal'].shift(0) # 当天开仓
# 正确示例:避免前视偏差
df['signal'] = (df['close'].shift(1) > df['close'].shift(2)) # 用昨天数据
df['position'] = df['signal'].shift(1) # 今天开仓
小技巧:写回测时,养成一个习惯——所有用到价格的地方,都先 .shift(1)。这样能挡住90%的前视偏差。
3.2 生存偏差:活下来的都是英雄
生存偏差,指的是你的回测数据里,只包含了现在还活着的股票。那些退市、被ST、暴跌的股票,早就被剔除了。
为什么这是个陷阱?
你想想看,如果只拿活着的股票做回测,策略表现当然好。因为那些烂股票已经被历史淘汰了。但实盘时,你可能会买到一只即将退市的股票——回测里根本没它的数据。
我曾经踩过的坑:
几年前,我做一个小市值策略。回测年化收益30%,最大回撤不到10%。我兴奋得不行。结果实盘跑了三个月,亏了15%。后来一查,回测数据里少了大量退市的小盘股。那些股票在回测期间暴跌,但数据里没有它们——这就是生存偏差。
怎么识别?
- 检查数据源:是否包含退市、ST、暂停上市的股票?
- 对比全样本和存活样本的收益分布。如果存活样本收益明显更高,那就有问题。
修正方法:
我建议,回测时一定要用“全样本数据”,包括已经退市的。如果数据源不完整,至少要在回测中模拟退市风险。比如,随机剔除一部分股票,看看策略是否还能赚钱。
警告:很多免费数据源默认只提供当前存活的股票。如果你不做处理,生存偏差会严重高估策略表现。
3.3 过拟合回测:曲线漂亮,实盘拉胯
过拟合,是量化策略的头号杀手。你不断调整参数,直到回测曲线完美。但实盘时,市场一变,策略就失效。
过拟合的典型特征:
- 回测收益极高,夏普比率超过3甚至5。
- 参数稍微一改,收益就大幅下降。
- 策略在样本外测试中表现很差。
我见过最夸张的例子:
有个朋友,把50个参数优化到极致,回测年化收益80%。我问他:“你参数怎么选的?”他说:“一个一个试,找到最好的组合。” 嗯,这其实就是过拟合。后来实盘,三个月亏了40%。
怎么识别过拟合?
我个人习惯用“夏普比率衰减测试”。具体做法:
- 把数据分成训练集和测试集(比如前70%训练,后30%测试)。
- 在训练集上优化参数。
- 在测试集上验证。如果测试集夏普比率比训练集低很多(比如低50%以上),那大概率过拟合了。
修正方法:
- 减少参数数量:参数越少,过拟合风险越低。我一般控制在3-5个以内。
- 交叉验证:用滚动窗口的方式,反复训练和测试。
- 正则化:在优化目标中加入惩罚项,防止参数过大。
# 简单的交叉验证示例
from sklearn.model_selection import TimeSeriesSplit
tscv = TimeSeriesSplit(n_splits=5)
for train_index, test_index in tscv.split(data):
train_data = data.iloc[train_index]
test_data = data.iloc[test_index]
# 在train_data上优化,在test_data上验证
核心原则:回测曲线越漂亮,你越要怀疑。真正稳健的策略,回测曲线往往没那么惊艳。
3.4 知识体系总览
为了让你更直观地理解这三个陷阱的关系,我画了一张图。它展示了回测陷阱的识别与修正流程。
这张图的核心逻辑很简单:回测策略出来后,先检查有没有前视偏差、生存偏差、过拟合。如果有,就用对应的修正方法处理。最后,才能得到一个相对稳健的结果。
3.5 避坑指南
最后,分享几个我自己的避坑经验:
- 我曾经因为数据源没更新退市股票,白高兴了一个月。现在,我每次回测前都会检查数据是否包含退市样本。
- 我曾经为了追求高夏普,把参数优化到极致。结果实盘一塌糊涂。现在,我宁愿要一个夏普1.5但稳健的策略,也不要夏普3但脆弱的策略。
- 我建议,每次回测后,都做一次“压力测试”:随机改变一些参数,看看策略是否还能赚钱。如果稍微一变就亏,那说明过拟合了。
一句话总结:回测不是用来证明策略有多好,而是用来发现策略有多烂。你越早发现陷阱,实盘就越安全。