第三章:数据获取与清洗——数据源选择、清洗流程与复权处理
做量化投资,有一句话我特别认同:「垃圾进,垃圾出」。你策略再牛,模型再花哨,数据源选错了,或者数据没洗干净,结果就是白忙活。这一章,咱们就聊聊数据获取与清洗的那些事儿。
核心观点:数据质量决定了策略的天花板。80%的量化时间其实都花在数据清洗上,而不是写策略。
3.1 数据源选择:Wind、Tushare、聚宽,怎么选?
我个人习惯,先看需求再选数据源。别一上来就追求「全」,够用就行。
| 数据源 | 特点 | 适用场景 | 成本 |
|---|---|---|---|
| Wind | 机构级,数据全,接口稳定 | 公募、私募、券商自营 | 高(年费几万起) |
| Tushare | 开源免费,社区活跃,数据较全 | 个人研究、小团队回测 | 低(积分制,部分免费) |
| 聚宽 | 平台化,自带回测环境 | 策略开发、快速验证 | 免费/付费 |
| 其他(东方财富、Yahoo Finance等) | 免费但质量参差不齐 | 快速原型、海外数据 | 免费 |
我在项目中遇到过一件事:用Tushare拉A股日线数据,结果某天突然发现某只股票的前复权价格是负数。嗯,这就是数据源的问题。后来我加了一层校验逻辑,才避免了这个坑。
我的建议:个人研究用Tushare或聚宽就够了。如果是实盘,至少用Wind。别省那点钱,数据出问题,亏的是真金白银。
3.2 数据清洗流程:从原始数据到可用数据
数据清洗,说白了就是「去伪存真」。我一般按这个流程走:
- 格式统一:日期格式、股票代码格式、字段命名统一
- 缺失值处理:空值、NaN、停牌数据
- 异常值处理:价格突变、成交量异常、财务数据错误
- 复权处理:前复权、后复权、不复权
- 去重与排序:按时间排序,去除重复行
你想想看,如果第一步格式都没统一,后面所有分析都是错的。我曾经见过一个团队,因为日期格式不统一(有的用YYYY-MM-DD,有的用YYYYMMDD),导致回测结果完全对不上,查了三天才发现问题。
3.3 缺失值处理:别直接drop,先想想
缺失值处理,很多人上来就dropna(),这其实很危险。为什么?因为缺失值可能包含信息。
- 停牌数据:停牌期间没有交易数据,但复牌后会有补涨/补跌。直接删除会丢失信息。
- 财务数据:季报发布有延迟,某些字段可能为空。这时候用前向填充(ffill)更合理。
- 新股上市:上市前几天的数据可能缺失,需要特殊处理。
# 我的常用处理方式
import pandas as pd
# 前向填充:用上一个有效值填充
df['close'].fillna(method='ffill', inplace=True)
# 插值法:适用于连续数据
df['volume'].interpolate(method='linear', inplace=True)
# 标记缺失:保留缺失信息
df['is_missing'] = df['close'].isna().astype(int)
避坑指南:我曾经用ffill填充了连续5天的缺失值,结果发现那5天其实是停牌。复牌后价格跳空,我的策略完全没反应过来。后来我加了一个「连续缺失天数」的阈值,超过3天就标记为停牌,单独处理。
3.4 异常值处理:别一刀切,要分情况
异常值,说白了就是「看起来不对劲」的数据。比如某只股票突然涨了1000%,或者成交量突然放大100倍。这些可能是数据错误,也可能是真实事件(比如借壳上市)。
我一般用两种方法:
- 3σ原则:超出均值±3倍标准差的数据,视为异常
- IQR方法:超出Q1-1.5*IQR或Q3+1.5*IQR的数据,视为异常
# 3σ原则处理
def remove_outliers_3sigma(df, col):
mean = df[col].mean()
std = df[col].std()
return df[(df[col] >= mean - 3*std) & (df[col] <= mean + 3*std)]
# IQR方法处理
def remove_outliers_iqr(df, col):
Q1 = df[col].quantile(0.25)
Q3 = df[col].quantile(0.75)
IQR = Q3 - Q1
return df[(df[col] >= Q1 - 1.5*IQR) & (df[col] <= Q3 + 1.5*IQR)]
我的经验:别直接删除异常值。先标记,然后人工核查。如果是数据错误,修正;如果是真实事件,保留但单独标注。我见过有人把茅台2015年股灾时的暴跌当成异常值删掉了,结果回测结果异常漂亮——但实盘完全不是那么回事。
3.5 复权处理:前复权 vs 后复权
复权处理,是数据清洗里最容易出错的地方。说白了,就是处理分红、送股、配股这些事件对价格的影响。
| 复权方式 | 原理 | 适用场景 |
|---|---|---|
| 前复权 | 调整历史价格,使当前价格不变 | 回测、技术分析 |
| 后复权 | 调整当前价格,使历史价格不变 | 长期趋势分析 |
| 不复权 | 保持原始价格 | 事件研究、分红套利 |
我个人习惯用前复权做回测。为什么?因为前复权保证了当前价格是真实的,回测时买入卖出价格更接近实际。但要注意:前复权可能导致历史价格变成负数(比如某只股票分红特别多)。
# 聚宽平台的前复权数据获取
from jqdatasdk import *
# 获取前复权数据
df = get_price('000001.XSHE', start_date='2020-01-01', end_date='2023-12-31',
frequency='daily', fields=['close', 'volume'], fq='pre')
# 手动复权计算(以Tushare为例)
import tushare as ts
# 获取复权因子
df_factor = ts.pro_bar(ts_code='000001.SZ', adj='qfq',
start_date='20200101', end_date='20231231')
# 前复权价格 = 原始价格 * 复权因子
df_factor['adj_close'] = df_factor['close'] * df_factor['adj_factor']
避坑指南:我曾经用后复权数据做回测,结果发现策略收益高得离谱。后来一查,后复权价格包含了分红再投资的收益,而我的策略根本没考虑分红。从那以后,我回测只用前复权,分红单独处理。
3.6 数据清洗的完整流程(SVG流程图)
下面这张图,是我自己总结的数据清洗流程。每次做新项目,我都会先过一遍这个流程。
3.7 总结:数据清洗的「黄金法则」
做了这么多年量化,我总结了几条数据清洗的「黄金法则」:
- 不要信任任何数据源:即使是Wind,也可能有错误。一定要交叉验证。
- 保留原始数据:永远不要修改原始数据。清洗后的数据另存一份。
- 记录每一步操作:清洗流程要可复现。我习惯用Jupyter Notebook记录每一步。
- 自动化清洗流程:写一个清洗函数,每次新数据直接调用。别手动操作。
最后说一句:数据清洗很枯燥,但它是量化投资的基石。你花在清洗上的每一分钟,都会在回测和实盘中得到回报。别嫌麻烦,认真做。