第四章:数据获取与清洗——量化分析的“地基工程”
做量化投资,有个道理我越做越觉得深刻:数据质量决定策略上限。你模型再漂亮,因子再新颖,数据是脏的,结果就是垃圾进、垃圾出。这一章,咱们就聊聊数据获取与清洗那些事儿。
核心观点:数据清洗占量化研究60%以上的时间,别嫌烦,这是必修课。
4.1 数据源选择:三大主流平台怎么选?
国内做量化,数据源基本就三个选项:Tushare、Wind、聚宽。我三个都用过,说说我的真实感受。
| 数据源 | 优点 | 缺点 | 适合场景 |
|---|---|---|---|
| Tushare | 免费、数据全、社区活跃 | 有调用频率限制、需积分 | 个人研究、小规模回测 |
| Wind | 数据质量高、接口稳定 | 贵(年费几万起) | 机构、专业团队 |
| 聚宽 | 一站式平台、数据+回测+交易 | 本地化不够灵活 | 快速原型、策略验证 |
我个人习惯是:研究阶段用Tushare,免费且够用;实盘阶段用Wind,数据可靠性高。聚宽呢?适合不想折腾环境的朋友,开箱即用。
小技巧:如果你刚开始学,建议从Tushare入手。注册个账号,拿token就能用。代码示例:
import tushare as ts
# 设置token(去官网注册获取)
ts.set_token('your_token_here')
pro = ts.pro_api()
# 获取日线数据
df = pro.daily(ts_code='000001.SZ', start_date='20230101', end_date='20231231')
print(df.head())
嗯,这里要注意:Tushare的积分制度。新用户只有100分,很多接口需要200分以上。我当时为了攒积分,每天签到、发帖,折腾了半个月才够用。如果你不想等,可以直接买积分,也不贵。
4.2 数据清洗流程:从原始数据到可用数据
数据拿到手,别急着跑模型。先走一遍清洗流程。我总结了一个四步法:
- 格式统一——日期格式、列名、数据类型
- 缺失值处理——该补的补,该删的删
- 异常值处理——识别并处理极端值
- 一致性检查——前后逻辑、跨表校验
为什么会这样排序?因为格式不统一,后面所有操作都会报错。我刚开始做的时候,就因为日期格式没统一,卡了整整一个下午。
下面这张图,是我自己画的数据清洗流程,你看一眼就明白了:
4.3 缺失值处理:别让“空”坑了你
缺失值,说白了就是数据里有些格子是空的。原因很多:停牌、数据源没更新、节假日等等。怎么处理?三种主流方法:
- 删除法——直接删掉有缺失的行或列。简单粗暴,但会损失信息。
- 填充法——用均值、中位数、前向/后向填充。我常用前向填充(ffill),因为金融数据有连续性。
- 插值法——用线性插值、多项式插值。适合时间序列数据。
我曾经在做一个因子回测时,因为没处理停牌日的缺失值,结果策略夏普比率虚高。后来发现,停牌日的数据全是NaN,回测引擎自动跳过了,相当于“只选好日子交易”。你说这能真实吗?
避坑指南:千万不要对所有缺失值一删了之。先搞清楚缺失原因。如果是停牌,建议用前向填充;如果是数据源问题,建议重新获取。
import pandas as pd
import numpy as np
# 示例数据
df = pd.DataFrame({
'date': pd.date_range('2023-01-01', periods=10, freq='D'),
'close': [10, 11, np.nan, 12, 13, np.nan, 14, 15, 16, 17]
})
# 方法1:前向填充(推荐)
df['close_ffill'] = df['close'].ffill()
# 方法2:线性插值
df['close_interp'] = df['close'].interpolate(method='linear')
# 方法3:删除缺失值
df_drop = df.dropna(subset=['close'])
print(df)
4.4 异常值处理:识别“离谱”的数据点
异常值,就是那些明显偏离正常范围的数据。比如某只股票一天涨了1000%,或者成交量突然变成0。这些数据会严重干扰你的模型。
识别异常值,我常用两种方法:
- 3σ原则——超出均值±3倍标准差的数据,视为异常。适合正态分布的数据。
- IQR方法——超出Q1-1.5*IQR或Q3+1.5*IQR的数据,视为异常。适合偏态分布。
你想想看,金融数据大多不是正态分布,所以IQR方法更实用。我个人的经验是:先用IQR筛一遍,再结合业务逻辑人工判断。
实战技巧:处理异常值,不要直接删除。先标记出来,看看是不是数据错误。如果是数据错误,修正;如果是真实极端事件(比如股灾),保留但做缩尾处理。
def detect_outliers_iqr(df, column):
"""用IQR方法检测异常值"""
Q1 = df[column].quantile(0.25)
Q3 = df[column].quantile(0.75)
IQR = Q3 - Q1
lower_bound = Q1 - 1.5 * IQR
upper_bound = Q3 + 1.5 * IQR
outliers = df[(df[column] < lower_bound) | (df[column] > upper_bound)]
return outliers, lower_bound, upper_bound
# 示例
df = pd.DataFrame({'return': [0.01, 0.02, -0.01, 0.03, 0.5, -0.02, 0.015, 0.025]})
outliers, lb, ub = detect_outliers_iqr(df, 'return')
print(f"异常值:{outliers['return'].values}")
print(f"正常范围:{lb:.3f} ~ {ub:.3f}")
嗯,这里要提醒一下:异常值处理没有银弹。同一个数据,在不同策略里可能一个算异常、一个不算。比如高频交易中,0.5%的收益率可能算异常;但在日频策略里,这很正常。所以,一定要结合你的策略逻辑来判断。
总结一句话:数据清洗不是一次性工作,而是贯穿整个量化研究过程的持续任务。每次新增数据、每次修改策略,都要重新检查数据质量。养成这个习惯,你的策略会少踩很多坑。