第二章:数据准备与清洗——量化分析的“地基工程”
做量化研究这么多年,我有个很深的体会:策略模型再漂亮,数据不行全白搭。你想想看,地基没打好,上面盖的楼再花哨,早晚得出事。这一章,我们就来聊聊数据准备与清洗的那些事儿。
核心观点:数据清洗占量化研究60%以上的工作量,但这是最值得投入的60%。
2.1 数据源选择:选对工具,事半功倍
国内主流的数据源,我基本都用过。每个都有脾气,得摸透了才行。
| 数据源 | 优点 | 缺点 | 适合场景 |
|---|---|---|---|
| Wind | 数据最全,机构标配 | 贵,个人用户不友好 | 机构研究、专业回测 |
| Tushare | 免费,社区活跃 | 频率限制,数据质量参差 | 个人学习、小规模研究 |
| 聚宽 | 一站式平台,回测方便 | 本地化程度一般 | 策略快速验证 |
| AKShare | 开源,更新快 | 文档略乱 | 爬虫爱好者、定制需求 |
我个人习惯是:机构用Wind,个人用Tushare+AKShare组合。Wind的API虽然贵,但数据质量确实稳。Tushare嘛,免费的东西你得容忍它偶尔抽风——我遇到过某天突然返回空数据,排查了半天才发现是接口限流了。
小技巧:别把所有鸡蛋放一个篮子里。我建议至少准备两个数据源,一个主力,一个备用。关键时刻能救命。
2.2 数据获取与存储:别让数据“裸奔”
数据拿到手,第一件事不是分析,而是存好它。我见过太多人,每次跑策略都重新拉数据,结果某天数据源挂了,策略直接瘫痪。
存储方案我推荐分层:
- 原始层(Raw): 存原始数据,不做任何修改。格式用Parquet,压缩比高,读取快。
- 清洗层(Clean): 存清洗后的数据,标准化、去极值后的结果。
- 因子层(Factor): 存计算好的因子值,直接用于回测。
代码示例(Python + Tushare):
import tushare as ts
import pandas as pd
# 设置token
ts.set_token('your_token_here')
pro = ts.pro_api()
# 获取日线数据
df = pro.daily(ts_code='000001.SZ', start_date='20200101', end_date='20231231')
# 存储为Parquet
df.to_parquet('raw_data/000001_daily.parquet', index=False)
print(f"获取到 {len(df)} 条数据")
注意:千万别用CSV存大量数据!我吃过亏——某次存了3年5分钟线数据,CSV文件打开直接卡死。Parquet或HDF5才是正经选择。
2.3 数据清洗:脏数据是量化的大敌
数据清洗说白了就三件事:去极值、补缺失、标准化。顺序别搞反,我一般先处理极值,再补缺失,最后标准化。
2.3.1 去极值:别让异常值带偏你
股票数据里经常出现“妖股”,一天涨跌20%+。这种极端值如果不处理,相关性计算直接崩。我常用的方法是MAD法(中位数绝对偏差),比3σ法更稳健。
def winsorize_mad(series, n=5):
"""
MAD法去极值
n: 阈值倍数,默认5倍MAD
"""
median = series.median()
mad = (series - median).abs().median()
upper = median + n * mad
lower = median - n * mad
return series.clip(lower, upper)
# 应用
df['pct_chg_winsor'] = winsorize_mad(df['pct_chg'])
我曾经用3σ法处理一个因子,结果发现数据本身就有偏态分布,3σ砍掉太多有效信息。换成MAD法后,效果好了不少。
2.3.2 缺失值处理:别乱填,先看原因
缺失值处理,我有个原则:先搞清楚为什么缺失。
- 停牌导致缺失: 用前向填充(ffill),假设价格不变
- 数据源漏了: 用行业均值填充
- 刚上市的新股: 直接删除,历史数据太少没意义
# 前向填充停牌数据
df['close'] = df['close'].fillna(method='ffill')
# 行业均值填充
df['pe_ttm'] = df.groupby('industry')['pe_ttm'].transform(
lambda x: x.fillna(x.median())
)
# 删除上市不足60天的股票
df = df[df['list_days'] >= 60]
避坑指南:我曾经直接对所有缺失值用均值填充,结果回测表现特别好——后来发现是因为填充后的数据恰好跟未来收益率有虚假相关性。嗯,从那以后我再也不敢乱填了。
2.3.3 标准化:让因子站在同一起跑线
不同因子的量纲差太多,比如市盈率可能是几十,换手率是百分之几。不标准化的话,模型会天然偏向数值大的因子。
我常用的两种方法:
- Z-score标准化: (值 - 均值) / 标准差。适合正态分布的数据。
- Rank标准化: 排序后映射到[0,1]。适合偏态分布,不受极值影响。
from scipy.stats import rankdata
def zscore_standardize(series):
return (series - series.mean()) / series.std()
def rank_standardize(series):
ranks = rankdata(series)
return (ranks - 1) / (len(ranks) - 1)
# 应用
df['factor_z'] = zscore_standardize(df['factor'])
df['factor_rank'] = rank_standardize(df['factor'])
2.4 数据对齐与频率转换:时间轴必须对齐
这是最容易出bug的地方。不同数据源的频率不一样:日线、分钟线、Tick级。你想想看,如果用日线数据去预测分钟级别的交易,那结果能对吗?
对齐的核心原则:用低频率对齐高频率。比如你要做日频策略,就把所有数据都对齐到日线。
# 将5分钟线重采样为日线
df_daily = df_5min.resample('D').agg({
'open': 'first',
'high': 'max',
'low': 'min',
'close': 'last',
'volume': 'sum'
})
# 对齐不同股票的交易日期
all_dates = pd.date_range('2020-01-01', '2023-12-31', freq='B')
df_aligned = df.reindex(all_dates)
注意:不同交易所的节假日不一样!A股和港股休市时间不同,如果你做跨市场策略,一定要手动对齐交易日历。我吃过这个亏,回测结果漂亮得不行,实盘直接打脸。
2.5 本章知识体系总览
下面这张图,是我自己总结的数据准备流程。每次做新项目,我都会先过一遍这个流程,确保没漏掉什么。
这张图里,每个环节都环环相扣。你跳过任何一步,后面都会出问题。我刚开始做量化时,觉得数据清洗太麻烦,直接拿原始数据跑回测,结果策略表现时好时坏——后来才发现是数据没对齐导致的。
总结一句话:数据准备不是苦力活,而是技术活。花80%的时间在数据上,剩下20%的时间做策略,你会发现回测结果靠谱得多。
公众号:蓝海数据掘金营,微信deep3321