第三章:数据获取与清洗——数据源选择、字段定义与预处理

做量化回测,第一步就是搞数据。这事儿听起来简单,但坑特别多。

我见过不少新手,策略写得漂漂亮亮,一跑回测发现结果不对。查来查去,最后发现是数据源的问题。嗯,数据质量直接决定你的回测是否可信。今天我们就来聊聊数据获取与清洗的那些事儿。

3.1 数据源选择:Tushare / Wind / 聚宽

选数据源,说白了就是选「食材」。食材不好,再好的厨艺也白搭。

Tushare:我个人比较喜欢它。免费版够用,Pro版数据更全。适合个人研究和小团队。缺点嘛,免费版有调用频率限制,我曾在项目中因为没注意限流,被ban了半小时。

Wind:机构标配。数据全、更新快、接口稳定。但价格不便宜,一年几万块。如果你在机构工作,直接用Wind就好。个人用户嘛,性价比不高。

聚宽:平台自带数据,省心。适合在聚宽平台上直接做研究。但如果你想把数据拉下来本地跑,就得走API,稍微麻烦点。

我的建议:个人研究用Tushare Pro,机构用Wind。聚宽适合不想折腾数据的人。

3.2 数据字段定义

拿到数据后,第一件事就是搞清楚每个字段的含义。别小看这一步,我见过有人把「复权因子」当「收盘价」用,回测结果直接翻车。

常用的字段有这些:

字段名 含义 注意事项
trade_date 交易日期 注意格式统一,别混用
open 开盘价 未复权,需自行处理
high 最高价 同上
low 最低价 同上
close 收盘价 回测中最常用的价格
volume 成交量 注意单位(股还是手)
adj_factor 复权因子 用于计算复权价格

你想想看,如果字段定义不清晰,后面所有计算都是错的。所以拿到数据后,先打印几行看看,确认字段名和含义。

3.3 缺失值处理

数据里总会有缺失值。停牌、节假日、数据源本身的问题,都可能导致某天没数据。

常见的处理方法:

  • 向前填充:用上一个交易日的数据填充。适合价格类数据。
  • 向后填充:用下一个交易日的数据填充。适合某些指标。
  • 直接删除:如果缺失值不多,直接删掉那行。
  • 插值法:用前后数据做线性插值。适合连续型数据。

小技巧:我个人习惯先看缺失比例。如果某只股票缺失超过20%,直接放弃。别在垃圾数据上浪费时间。

代码示例:

import pandas as pd

# 假设df是DataFrame,包含股票数据
# 向前填充缺失值
df['close'].fillna(method='ffill', inplace=True)

# 如果还是缺失,用向后填充
df['close'].fillna(method='bfill', inplace=True)

# 检查是否还有缺失
print(df.isnull().sum())

3.4 复权处理

复权是个大坑。我刚开始做回测时,直接用原始价格算收益率,结果发现分红除权后,价格突然跳空,收益率算出来完全不对。

为什么会这样?因为股票分红、送股后,价格会调整。如果不复权,你的回测会看到「假跌」或「假涨」。

复权有两种方式:

  • 前复权:调整历史价格,让价格连续。适合看历史走势。
  • 后复权:调整当前价格,适合计算真实收益率。

我个人习惯用前复权。因为回测时,我们关心的是「如果我在过去买入,现在卖出能赚多少」。前复权能更直观地反映价格变化。

注意:不同数据源的复权方式可能不同。Tushare的复权因子是乘数,Wind的复权因子是除数。千万别搞混。

代码示例:

# 假设df包含close和adj_factor字段
# 前复权价格 = 原始价格 * 复权因子
df['adj_close'] = df['close'] * df['adj_factor']

# 计算收益率
df['return'] = df['adj_close'].pct_change()

3.5 异常值检测

数据里总会有「脏数据」。比如某天价格突然涨了100倍,或者成交量突然为0。这些异常值会严重干扰你的回测。

常用的检测方法:

  • 3σ原则:超过均值±3倍标准差的数据,视为异常。
  • IQR方法:四分位距法,适合非正态分布的数据。
  • 业务规则:比如涨跌幅超过20%,或者成交量突然为0。

我曾经在项目中遇到过一个奇葩情况:某只股票某天的收盘价是前一天收盘价的10倍。查了半天,发现是数据源录入错误。嗯,这种异常值如果不处理,回测结果会非常离谱。

代码示例:

# 使用3σ原则检测异常值
mean = df['return'].mean()
std = df['return'].std()
df['is_outlier'] = (df['return'] > mean + 3*std) | (df['return'] < mean - 3*std)

# 删除异常值
df_clean = df[~df['is_outlier']]

3.6 知识体系图

下面这张图展示了本章的核心逻辑:

数据获取与清洗 数据源选择 Tushare Wind 聚宽 字段定义 日期/开盘/收盘/成交量 缺失值处理 向前填充/删除/插值 复权处理 前复权/后复权 异常值检测 3σ/IQR/业务规则

3.7 总结

数据获取与清洗,说白了就是「把食材洗干净、切好」。这一步做扎实了,后面的回测才能放心跑。

我个人习惯是:先选好数据源,然后定义字段,接着处理缺失值和复权,最后检测异常值。每一步都写个函数,方便复用。

记住一句话:垃圾数据进,垃圾结果出。别偷懒,数据清洗值得你花时间。