数据陷阱:幸存者偏差——为什么你的回测总是那么完美?

做量化的人,谁没做过几个「完美」的回测?

曲线漂亮得像教科书上的范例,夏普比率高得让人心跳加速。我当时刚入行那会儿,也兴奋地拿着这样的结果去找老板汇报。结果他看了一眼,只问了一句:「你的样本里,退市的股票去哪了?」

嗯,这一问,直接把我问懵了。

这就是我们今天要聊的——幸存者偏差。它可能是量化投资里最隐蔽、也最致命的陷阱。

什么是幸存者偏差?

说白了,就是你只看到了活下来的,忽略了已经死掉的。

在回测里,如果你只用了当前还在交易的股票,那些因为业绩差、财务造假、甚至退市的股票,全被你自动过滤掉了。结果呢?你的策略看起来永远在买「好股票」,因为「坏股票」已经被市场清理出局了。

我见过一个团队,回测了某个小市值因子,年化收益超过30%。他们差点就要实盘了。后来我建议他们拉一下历史全样本——结果发现,那些退市的、被ST的股票,恰恰是小市值因子最偏爱的。回测里的收益,有一大半是「幸存者」贡献的幻觉。

核心问题: 幸存者偏差会让你的回测结果系统性偏高。你看到的不是真实的因子收益,而是「活下来的人」的平均收益。

为什么回测总是那么完美?

原因其实很简单。我们来看一个例子。

假设你在2010年选了100只小市值股票。到2020年,这100只里可能有20只退市了,30只被ST过,只有50只还活得好好的。如果你只拿这50只做回测,那你的收益曲线当然漂亮——因为那些亏掉90%甚至归零的股票,已经被你「不小心」删掉了。

我个人习惯,每次拿到一个新因子,第一件事就是检查样本的完整性。我会问自己三个问题:

  • 这个数据集是否包含了所有曾经存在的股票?
  • 退市、暂停上市、ST的股票是否被剔除了?
  • 如果剔除了,我的回测结果会偏多少?

你想想看,如果连分母都不完整,分子算得再准又有什么意义?

一个真实的案例

我记得有一次,帮一个朋友复现一篇论文里的因子。论文里说这个因子在A股市场年化超额收益有12%。我拿Wind的全量数据一跑,结果只有5%。

为什么会这样?

因为论文用的数据源,默认只保留了当前上市的公司。那些在样本期内退市的、被借壳的、重组的,全被过滤掉了。而恰恰是这些股票,在退市前往往有极差的收益表现。去掉它们,因子的收益自然被高估了。

这就是幸存者偏差的典型表现——你看到的「优秀」,只是因为「差生」被开除了

如何识别和避免?

嗯,这里有几个实操方法,是我这些年踩坑踩出来的。

1. 使用全量历史数据

不要只下载当前交易日的股票列表。一定要拉取包含退市、暂停上市、ST在内的全量历史数据。国内常用的数据源,比如Wind、聚宽、Tushare,都提供「是否退市」的标记字段。

2. 做「生存率」分析

在回测之前,先统计一下你的样本池里,每年有多少股票退市。如果某个因子偏好小市值、低流动性股票,那它的生存率通常更低。这时候你要格外小心。

3. 对比「幸存样本」和「全量样本」的结果

这是一个很好的 sanity check。分别用两种数据跑一遍回测,看看收益差距有多大。如果差距超过20%,那你的因子很可能被幸存者偏差污染了。

我的一个小技巧: 在构建因子时,我会强制保留所有退市股票直到它们实际退市的那一天。这样虽然会让回测曲线「丑」一点,但更接近真实世界。

代码示例:如何检查幸存者偏差

下面是一个简单的Python示例,演示如何对比「幸存样本」和「全量样本」的收益差异。

import pandas as pd
import numpy as np

# 假设 df 包含所有股票的历史收益
# 其中 'delisted' 列标记是否退市

# 幸存样本:只保留从未退市的股票
survivor_df = df[df['delisted'] == 0]

# 全量样本:保留所有股票
full_df = df.copy()

# 计算两组样本的累计收益
survivor_cum = (1 + survivor_df.groupby('date')['return'].mean()).cumprod()
full_cum = (1 + full_df.groupby('date')['return'].mean()).cumprod()

# 计算偏差
bias = (survivor_cum.iloc[-1] / full_cum.iloc[-1] - 1) * 100
print(f"幸存者偏差导致收益高估: {bias:.2f}%")

这段代码虽然简单,但能帮你快速定位问题。如果 bias 超过10%,我建议你重新审视因子构建逻辑。

幸存者偏差的SVG结构图

下面这张图,展示了幸存者偏差如何影响你的回测结果。

幸存者偏差:回测中的隐形杀手 真实世界(全量样本) 好股票(存活) 坏股票(退市) 好股票(存活) 坏股票(退市) 好股票(存活) 坏股票(退市) 收益:-5%(包含亏损和退市损失) 数据过滤 回测世界(幸存样本) 好股票(存活) 好股票(存活) 好股票(存活) 好股票(存活) 好股票(存活) 好股票(存活) 收益:+15%(只看到好股票) 结论:回测收益被系统性高估了20%!

避坑指南

我曾经犯过一个低级错误,现在想起来都觉得脸红。当时我在构建一个动量因子,回测结果好得离谱。后来发现,我用的数据集默认剔除了所有ST股票。而动量因子在A股市场,恰恰容易在ST股票上亏大钱。去掉它们,回测自然漂亮。

从那以后,我给自己定了几条铁律:

  • 数据源必须包含退市标记,没有这个字段的数据集,我坚决不用。
  • 回测前先跑一个「生存率曲线」,看看样本池的退市比例是否随时间变化。
  • 对比「包含退市」和「剔除退市」两组结果,如果差异显著,说明因子对幸存者偏差敏感。
警告: 幸存者偏差不仅影响回测,还会影响因子分析。如果你用幸存样本计算因子收益率,那IC值、多空组合收益都会偏高。这会导致你高估因子的预测能力。

总结一下

幸存者偏差,说白了就是「只看活人,不看死人」。在量化投资里,它让无数人掉进「完美回测」的陷阱。

我个人习惯,每次看到一份漂亮的回测报告,第一反应不是兴奋,而是怀疑。我会问:数据完整吗?退市的股票去哪了?如果这两个问题答不上来,那这份报告的价值就要打折扣。

记住一句话:回测越完美,越要警惕。真实世界里,没有只赚不赔的策略,只有被数据过滤掉的亏损。

好了,今天就聊到这里。希望下次你看到自己的回测曲线时,能多留一个心眼。


专注资料整理