第1章:数据获取与清洗——量化建模的第一步
做量化投资,数据就是你的原材料。没有干净的数据,再牛的模型也是白搭。我刚开始做量化那会儿,就吃过这个亏——拿到的数据直接跑回测,结果收益曲线漂亮得不行,实盘一跑就崩。后来才发现,原来是数据里藏着未来信息。
这一章,咱们就聊聊数据获取与清洗的那些事儿。说白了,就是怎么把股票数据搞到手,再把它收拾得服服帖帖。
核心要点:数据质量决定模型上限。花80%的时间做数据清洗,一点都不夸张。
1.1 用pandas-datareader获取股票数据
我个人习惯用 pandas-datareader 来拉数据。这个库封装了多个数据源接口,用起来很方便。你想想看,一行代码就能拿到股票的历史行情,多省事。
import pandas as pd
import pandas_datareader.data as web
import datetime
# 设置时间范围
start = datetime.datetime(2020, 1, 1)
end = datetime.datetime(2023, 12, 31)
# 获取贵州茅台股票数据
df = web.DataReader('600519.SS', 'yahoo', start, end)
print(df.head())
这里要注意,A股代码在雅虎财经后面要加后缀:上海交易所加 .SS,深圳交易所加 .SZ。我在项目中遇到过,有人忘了加后缀,结果拉了一堆空数据回来,排查了半天。
| 交易所 | 代码后缀 | 示例 |
|---|---|---|
| 上海证券交易所 | .SS | 600519.SS |
| 深圳证券交易所 | .SZ | 000001.SZ |
小技巧:如果雅虎数据源不稳定,可以试试 stooq 或 alphavantage。我一般会备两个数据源,一个挂了马上切另一个。
1.2 数据缺失值处理
拿到的数据,十有八九是有缺失的。为什么?停牌、节假日、数据源本身的问题,都会导致缺失。嗯,这里要注意,缺失值处理不当,会直接影响后续计算。
常见的处理方法有几种:
- 直接删除:缺失比例小的时候可以用,但别删太多,否则样本量不够。
- 向前填充:用上一个交易日的数据填充,适合价格类数据。我个人比较推荐这个。
- 插值法:线性插值或时间插值,适合成交量这类波动较大的数据。
# 检查缺失值
print(df.isnull().sum())
# 向前填充(最常用)
df.fillna(method='ffill', inplace=True)
# 或者用插值法
df.interpolate(method='linear', inplace=True)
避坑指南:我曾经犯过一个错误——对收益率序列用了向前填充。结果呢?收益率被重复复制,回测结果严重失真。记住,收益率数据不能用填充,最好直接删除缺失行。
1.3 异常值检测与处理
异常值,说白了就是那些离谱的数据点。比如某只股票一天涨了1000%,这明显是数据录入错误。你想想看,A股有涨跌停限制,怎么可能一天翻十倍?
常用的检测方法:
- 3σ原则:超出均值±3倍标准差的数据,视为异常。
- 箱线图法:用四分位数来识别异常值,更稳健。
- 业务规则:比如涨跌幅超过10%的,直接标记。
# 用3σ原则检测异常值
mean = df['Close'].mean()
std = df['Close'].std()
outliers = df[(df['Close'] > mean + 3*std) | (df['Close'] < mean - 3*std)]
# 处理方式:用中位数替换
median = df['Close'].median()
df.loc[outliers.index, 'Close'] = median
我在项目中遇到过,某只小盘股因为数据源问题,连续几天出现价格跳空。用3σ检测出来之后,我选择了用前后两天的均值替换。效果还不错。
1.4 数据对齐与重采样
做多因子模型时,经常要把不同频率的数据对齐。比如日线数据和周线数据,怎么对齐?重采样就是干这个的。
数据对齐的核心思想:以最细粒度的数据为基准,其他数据向它对齐。
# 重采样:日线转周线
weekly = df.resample('W').agg({
'Open': 'first',
'High': 'max',
'Low': 'min',
'Close': 'last',
'Volume': 'sum'
})
# 多只股票数据对齐
# 假设我们有两只股票的数据
stock1 = web.DataReader('600519.SS', 'yahoo', start, end)['Close']
stock2 = web.DataReader('000001.SZ', 'yahoo', start, end)['Close']
# 合并并自动对齐
combined = pd.concat([stock1, stock2], axis=1, keys=['茅台', '平安'])
combined.dropna(inplace=True) # 删除对齐后仍有缺失的行
经验之谈:重采样时,开盘价用第一个值,收盘价用最后一个值,最高价取最大值,最低价取最小值,成交量求和。这个规则我用了好多年,没出过问题。
知识体系总览
下面这张图,把本章的核心逻辑串起来了。你可以看到,数据获取是起点,清洗是中间环节,最终得到干净、对齐的数据,才能喂给模型。
数据清洗这块,我建议你养成一个习惯:每做一步清洗,都保存一个中间结果。这样万一后面发现数据有问题,还能回溯到上一步。我曾经因为没保存中间结果,重新拉了一遍数据,浪费了大半天时间。
本章小结:数据获取用pandas-datareader,缺失值处理优先向前填充,异常值检测结合统计方法和业务规则,数据对齐和重采样是建模前的最后一步。把这四步走扎实了,后面的建模工作才能顺利开展。