第1章:数据源与数据清洗
做因子挖掘这么多年,我最大的感触就是——数据决定上限,模型只是逼近这个上限。说白了,你数据是垃圾,再牛的因子也白搭。今天咱们就从最基础的数据源和数据清洗聊起。
1.1 常见数据源
我个人习惯把数据源分成三类:行情数据、财务数据、另类数据。这三类各有各的脾气,处理方式也完全不同。
行情数据
这是最基础的数据。包括日线、分钟线、Tick级数据。我刚开始做量化时,以为行情数据就是K线那几列,后来发现坑多着呢。
- 日线数据:开高低收、成交量、成交额。注意复权问题,前复权和后复权差别很大。
- 分钟数据:5分钟、15分钟、30分钟。我遇到过分钟数据时间戳不对齐的问题,后来发现是交易所撮合机制导致的。
- Tick数据:逐笔成交。这个数据量巨大,一天下来几个G很正常。
我的经验:行情数据最好从Wind、聚宽、Tushare这些平台拿。别自己爬,容易被封IP不说,数据质量也没保证。
财务数据
财务数据是因子挖掘的富矿。但这里有个大坑——财务数据发布有滞后性。比如一季报要在4月底前发布,但你3月份做因子时,用的还是去年的数据。
- 资产负债表:总资产、净资产、负债率
- 利润表:营收、净利润、毛利率
- 现金流量表:经营现金流、投资现金流
注意:财务数据一定要做「对齐处理」。比如2024年4月的数据,实际对应的是2023年年报。我曾经因为没对齐,回测结果漂亮得不行,实盘直接崩了。
另类数据
这个就五花八门了。新闻舆情、电商数据、卫星图像、甚至信用卡消费数据。我有个朋友专门做「上市公司食堂外卖订单量」的因子,据说效果还不错。
- 舆情数据:新闻情感分析、社交媒体热度
- 产业链数据:上下游价格、库存数据
- 宏观数据:GDP、CPI、PMI
1.2 数据缺失值处理
数据缺失是家常便饭。你想想看,A股有停牌、有新股上市、有退市,数据怎么可能完整?
缺失值类型
| 类型 | 原因 | 处理方式 |
|---|---|---|
| 完全随机缺失 | 数据采集故障 | 直接删除或插值 |
| 随机缺失 | 停牌、节假日 | 前向填充 |
| 非随机缺失 | 退市、ST | 需谨慎处理 |
处理策略
我个人习惯用这几种方法:
- 删除法:缺失比例超过50%的字段,直接删掉。别心疼,留着也是噪音。
- 前向填充:用上一个有效值填充。适合行情数据,比如停牌期间用停牌前价格。
- 插值法:线性插值或多项式插值。适合财务数据,比如季度数据之间的月份。
- 模型预测:用其他字段预测缺失值。这个比较重,一般不用。
# 前向填充示例
import pandas as pd
df['close'].fillna(method='ffill', inplace=True)
# 线性插值
df['revenue'].interpolate(method='linear', inplace=True)
避坑指南:我曾经在回测时用了前向填充,结果停牌复牌后直接跳空,因子值突变,回测曲线漂亮得不像话。后来发现是填充逻辑没处理好复牌当天的数据。
1.3 异常值检测与处理
异常值这东西,说白了就是数据里的「刺头」。可能是数据录入错误,也可能是真实的极端行情。
检测方法
- 3σ原则:超过均值±3倍标准差的值视为异常。适合正态分布的数据。
- 箱线图法:超过1.5倍IQR的值视为异常。这个更稳健,不受极端值影响。
- MAD法:中位数绝对偏差。我比较喜欢这个,对异常值不敏感。
# MAD法检测异常值
import numpy as np
def detect_outliers_mad(data, threshold=3.5):
median = np.median(data)
mad = np.median(np.abs(data - median))
modified_z_scores = 0.6745 * (data - median) / mad
return np.abs(modified_z_scores) > threshold
处理策略
检测到异常值后怎么办?我的建议是:
- 先分析原因:是数据错误还是真实事件?比如2015年股灾时的极端波动,那是真实事件,不能删。
- 截尾处理:将异常值替换为上下限值。比如用99%分位数替换。
- 分箱处理:将连续值离散化,减少极端值影响。
注意:千万别一刀切地删除所有异常值。我记得有一次做波动率因子,把2015年股灾的数据全删了,结果因子在正常行情下表现很好,但一遇到波动就失效。
1.4 数据对齐与重采样
数据对齐是特征工程里最容易被忽视的环节。你想想看,日线数据是每天一条,财务数据是每季度一条,舆情数据可能每分钟一条。怎么对齐?
时间对齐
我一般用「向后对齐」原则。比如财务数据发布日,对齐到发布日之后的第一个交易日。这样能避免未来信息泄露。
# 财务数据对齐到交易日
financial_data.index = financial_data.index + pd.offsets.BDay(1)
频率对齐
不同频率的数据要统一。我的习惯是:
- 高频转低频:用聚合函数,比如分钟数据转日线用收盘价。
- 低频转高频:用插值或前向填充。比如季度财务数据转月度,用前向填充。
重采样技巧
Pandas的resample函数很强大,但要注意边界问题。
# 日线转周线
weekly_data = daily_data.resample('W').agg({
'open': 'first',
'high': 'max',
'low': 'min',
'close': 'last',
'volume': 'sum'
})
我的习惯:重采样时一定要检查边界。比如周线是从周一开始还是周日开始?不同交易所的交易日历不一样,A股和港股就有差异。
知识体系总览
下面这张图是我自己整理的,把本章的核心逻辑串起来了。你看一眼就能明白数据从哪来、怎么洗、怎么对齐。
嗯,数据清洗这块就这些。别小看这些基础工作,我见过太多人因子做得花里胡哨,结果数据源都没搞干净,回测和实盘完全是两码事。记住一句话:数据干净了,因子就成功了一半。