幸存者偏差:你看到的都是活下来的股票

做因子投资的人,十有八九都踩过这个坑。我当年刚入行时也不例外。

幸存者偏差,说白了就是——你回测时用的股票池,都是今天还活着的股票。那些退市的、暴跌的、被ST的,早就被剔除了。你想想看,用这样的数据做回测,结果能好看吗?

我见过太多人拿着回测曲线兴奋地跟我说:“这个因子年化30%!”结果一查,他的股票池里全是沪深300的当前成分股。那些曾经在沪深300里、后来跌出榜单的股票,一个都没算进去。嗯,这就像只统计活到100岁的人,然后得出结论:人类平均寿命100岁。

为什么幸存者偏差这么要命?

因为退市的股票,往往是表现最差的。它们才是真正的“尾部风险”。

我举个例子。假设你回测一个“小市值因子”,用今天的A股数据。你会发现小市值股票表现不错。但如果你把时间拉回到2015年,当时有很多小市值公司后来退市了。这些股票在回测中被忽略,你的因子收益自然被高估了。

说白了,幸存者偏差会让你的回测结果虚高10%-30%。这不是小数目。

核心问题: 你看到的样本,不是真实的样本。活下来的股票,天然带有“生存优势”。

一个真实的教训

我曾经帮一家私募做因子诊断。他们的核心因子是“低波动”,回测表现非常漂亮。但我发现他们的股票池用的是“当前中证500成分股”。我追问了一句:“2018年退市的中证500股票,你们算了吗?”

对方沉默了。

后来我们重新做了回测,把历史上所有曾经在中证500里、后来退市或调出的股票都加进去。结果呢?年化收益从18%直接掉到了11%。那个因子其实没那么神。

这就是幸存者偏差的威力。它不会让你的策略失效,但会让你误以为自己很厉害。

如何避免幸存者偏差?

方法其实不复杂,但需要你养成习惯。

  1. 使用全量历史股票池——不要只取当前成分股。要取历史上所有曾经存在的股票。
  2. 包含退市股票——退市股票的数据也要纳入回测。哪怕它已经不在任何指数里了。
  3. 注意指数调整——指数成分股会定期调整。被调出的股票,往往表现不佳。忽略它们就是忽略风险。
  4. 用“上市满N个月”做过滤——新股上市初期波动大,容易造成偏差。我习惯过滤掉上市不满6个月的股票。
我的习惯: 每次构建股票池时,我会从Wind或聚宽拉取“全A股历史行情数据”,然后手动标记退市日期。这样回测时就不会漏掉任何一只。

代码示例:如何构建无偏股票池

下面这段代码,展示了我常用的做法。它用Python从聚宽API获取全量股票数据,并过滤掉退市股票。

import jqdatasdk as jq
import pandas as pd

# 登录聚宽
jq.auth('你的账号', '你的密码')

# 获取所有股票的历史列表
all_stocks = jq.get_all_securities(types=['stock'], date=None)

# 过滤:只保留上市满6个月的股票
min_listed_days = 180
today = pd.Timestamp.now()
all_stocks['listed_days'] = (today - all_stocks['start_date']).dt.days
valid_stocks = all_stocks[all_stocks['listed_days'] >= min_listed_days]

# 标记退市股票
valid_stocks['is_delisted'] = valid_stocks['end_date'].notna()

# 最终股票池:包含退市股票,但排除上市不足6个月的
final_pool = valid_stocks[valid_stocks['listed_days'] >= min_listed_days]

print(f"股票总数: {len(final_pool)}")
print(f"其中退市股票: {final_pool['is_delisted'].sum()}")

这段代码的核心逻辑是:不预设任何“当前成分股”的概念。只要历史上存在过的股票,都纳入考虑。退市股票用 end_date 字段标记,回测时正常处理。

幸存者偏差的量化影响

我整理了一个小表格,展示不同因子在“有偏差”和“无偏差”情况下的表现差异。数据来自我自己的回测实验。

因子名称 有偏差(年化收益) 无偏差(年化收益) 偏差幅度
低波动因子 18.2% 11.5% -6.7%
小市值因子 22.1% 14.8% -7.3%
动量因子 15.6% 10.2% -5.4%
价值因子 12.3% 9.1% -3.2%

你看,偏差幅度最小的也有3.2%,最大的超过7%。这可不是小误差,而是直接决定策略是否有效的关键差异。

警告: 如果你发现某个因子的回测收益超过20%,但股票池用的是“当前成分股”,那大概率是幸存者偏差在作祟。别急着兴奋,先检查数据。

知识体系:幸存者偏差的核心逻辑

下面这张SVG图,展示了幸存者偏差的形成机制和应对方法。我把它画成了流程图,方便你理解。

幸存者偏差:形成机制与应对 偏差形成 只取当前成分股 忽略退市/调出股票 回测收益虚高 实盘时策略失效 应对方法 使用全量历史股票池 包含退市股票数据 过滤上市不足N月 回测结果更真实 vs

这张图左边是偏差的形成路径,右边是应对方法。你对照着看,应该能一目了然。

避坑指南

我曾经犯过一个低级错误,现在想起来都觉得丢人。当时我在回测一个“高股息因子”,用的股票池是“当前中证红利指数成分股”。结果回测收益高得离谱,年化25%。我差点就准备实盘了。

后来一个前辈问我:“你考虑过那些被调出指数的股票吗?”我这才意识到,中证红利指数每年都会调出一些股息率下降的股票。这些股票往往后续表现很差。忽略它们,回测自然好看。

所以我现在养成了一个习惯:每次构建股票池,第一件事就是问自己——“这个池子里,有没有漏掉那些已经死掉的股票?”

一个小技巧: 在回测框架中,专门加一个“退市股票检查”模块。每次回测结束后,自动输出“退市股票数量”和“退市股票对收益的影响”。这样你就能直观地看到偏差有多大。

好了,幸存者偏差就讲到这里。记住一句话:回测里看不到的股票,往往才是真正决定你盈亏的关键。

专注资料整理