数据清洗的坑:异常值、幸存者偏差、前视偏差与对齐错误
做量化交易的朋友,尤其是搞另类数据的,一定听过这句话:「垃圾进,垃圾出。」
数据清洗,说白了就是给原始数据「排雷」。我见过太多团队,模型跑得飞起,回测曲线漂亮得不像话,结果一上实盘就崩。为什么?数据里有鬼。
今天咱们就聊聊四个最常见的坑:异常值、幸存者偏差、前视偏差、数据对齐错误。每个我都踩过,每个都让我亏过钱。
核心观点:数据清洗不是体力活,是决定策略生死的关键步骤。80%的量化失败,根源都在数据质量。
1. 异常值处理:别把信号当噪声
异常值,就是那些「离谱」的数据点。比如某只股票一天涨了1000%,或者某个月成交量突然变成平时的100倍。
很多人第一反应是:删掉。或者用均值填充。嗯,这其实很危险。
为什么?
- 有些异常值是真实的极端事件(比如财报暴雷、政策突变)
- 有些异常值是数据采集错误(比如传感器故障、API返回乱码)
- 还有些是数据源本身的问题(比如复权计算错误)
我在项目中遇到过一件事:某次回测一个事件驱动策略,收益高得离谱。查了半天,发现是某只股票在除权日的数据没处理好,价格瞬间「暴跌」90%,策略自动买入,第二天又涨回来。这哪是策略赚钱?这是数据在骗人。
我的建议:先分类,再处理。
- 明显的数据错误(如负数价格、日期乱码)→ 直接剔除
- 极端但合理的值(如涨跌幅超过20%)→ 用Winsorize截尾处理
- 不确定的 → 标记出来,单独分析
# 一个简单的异常值处理示例
import numpy as np
def winsorize_series(series, limits=[0.01, 0.01]):
"""
对序列进行截尾处理
limits[0]:下尾截尾比例
limits[1]:上尾截尾比例
"""
lower = series.quantile(limits[0])
upper = series.quantile(1 - limits[1])
return series.clip(lower, upper)
# 我个人习惯先看分布,再决定截尾比例
# 比如用IQR方法先标记异常值,再人工复核
2. 幸存者偏差:你看到的都是活下来的
幸存者偏差,是量化回测里最隐蔽的杀手。
什么意思?你拿今天还在交易的股票做回测,那些退市的、被收购的、破产的,全被你忽略了。结果就是:回测收益被高估,风险被低估。
我记得有一次帮朋友复盘一个多因子策略。他用的数据是「当前沪深300成分股」。我说你回测区间是2015-2020,那时候的成分股和现在一样吗?他愣住了。
2015年的沪深300,到2020年至少有30%的股票被替换掉了。那些被踢出去的,很多都是表现差的。你只拿活下来的做回测,能不赚钱吗?
避坑指南:我曾经犯过这个错,后来学乖了。
- 必须使用「历史时点」的股票池,不能用当前数据回溯
- 退市、ST、暂停上市的股票,都要保留在回测中
- 做指数增强策略时,要使用指数当时的成分股列表
你想想看,如果只统计活到今天的公司,那「买入并持有」策略的回测收益率会高得离谱。因为那些倒闭的公司,数据根本不会出现在你的数据库里。
3. 前视偏差:未来信息穿越了
前视偏差,也叫未来函数。就是你在回测时,不小心用到了「未来才知道」的信息。
举个最简单的例子:你用当天的收盘价计算指标,然后判断当天是否买入。但收盘价是在收盘后才确定的,你不可能在盘中就知道收盘价。
更隐蔽的情况呢?
- 用财报数据时,没考虑财报发布日期(你以为Q1数据在4月底可用,其实可能5月才披露)
- 用新闻情绪数据时,没考虑新闻发布时间(你以为开盘前就能用,其实新闻是盘中才发的)
- 用因子数据时,用了未来信息做标准化(比如用全样本的均值和标准差)
关键原则:回测时,任何数据都必须使用「当时能获取到的」版本。做不到这一点,回测就是自欺欺人。
# 前视偏差的典型例子
# 错误做法:用未来数据做标准化
def wrong_standardize(df):
mean = df['factor'].mean() # 用了全样本均值
std = df['factor'].std() # 用了全样本标准差
return (df['factor'] - mean) / std
# 正确做法:用滚动窗口
def correct_standardize(df, window=252):
rolling_mean = df['factor'].rolling(window).mean()
rolling_std = df['factor'].rolling(window).std()
return (df['factor'] - rolling_mean) / rolling_std
我个人习惯在回测框架里加一个「时间戳检查」:每个数据点都带上它的「可用时间」,回测引擎只允许使用可用时间之前的数据。这样能杜绝99%的前视偏差。
4. 数据对齐错误:时间不同步
这个坑,说实话,比前三个都难发现。
数据对齐错误,就是不同数据源的时间戳没对上。比如股票数据用的是北京时间,新闻数据用的是UTC时间,差8个小时。或者日线数据有的用收盘时间,有的用开盘时间。
我在做另类数据时遇到过:卫星图像数据的时间戳是UTC,A股交易数据是北京时间。我一开始没注意,结果回测里「提前」看到了下午的卫星图,用来预测上午的行情。这能不赚钱吗?
我的检查清单:
- 统一时区:全部转成同一个时区(建议用UTC+0)
- 统一频率:日线、分钟线、Tick数据,对齐到同一时间粒度
- 检查边界:比如周五的夜盘数据,算哪一天?
- 做交叉验证:用两个独立数据源对比,看时间戳是否一致
| 数据源 | 原始时间戳 | 对齐后时间戳 | 常见问题 |
|---|---|---|---|
| A股日线 | 2024-01-15 15:00:00 CST | 2024-01-15 07:00:00 UTC | 注意夏令时 |
| 新闻API | 2024-01-15 06:30:00 UTC | 2024-01-15 06:30:00 UTC | 通常没问题 |
| 卫星数据 | 2024-01-15 06:45:00 Z | 2024-01-15 06:45:00 UTC | Z就是UTC |
数据对齐错误,最坑的地方在于:它不会让你的回测直接崩溃,而是让收益「看起来」更好。因为未来信息偷偷混进来了。你查代码查三天都查不出来。
知识体系总览
下面这张图,是我自己总结的数据清洗核心逻辑。四个坑,每个都有对应的处理思路。
嗯,这四个坑,每一个都能让你的回测结果失真。我个人的经验是:数据清洗花的时间,至少占整个项目时间的40%。别嫌多,这钱花得值。
最后提醒一句:不要相信任何未经清洗的回测结果。哪怕数据源再权威,也要自己过一遍。我在这个上面吃过太多亏了。
公众号:蓝海资料掘金营,微信deep3321