数据获取与清洗:数据源选择、清洗流程与复权处理

做量化回测,第一步就是搞数据。这一步要是没做好,后面所有策略都是空中楼阁。我见过太多人,策略写得花里胡哨,结果一跑回测,发现数据里藏着各种坑——停牌没处理、复权搞错了、甚至日期对不上。嗯,今天我们就来聊聊数据获取与清洗这件事。

数据源选择:三大主流工具对比

国内做量化,常用的数据源就那么几个。我个人习惯用 tushare 和 akshare 搭配着来。为什么?因为没有一个数据源是完美的,你想想看,免费的东西总得有点取舍。

数据源 特点 适合场景 我踩过的坑
tushare 数据全面,但需要积分 专业回测、研究 积分不够时,日频数据都拿不全
akshare 免费、接口丰富 快速验证、小规模策略 偶尔数据更新延迟,得加重试机制
baostock 稳定、无需注册 入门学习、简单回测 数据维度少,没有财务指标
我的建议: 如果你刚开始做回测,先用 baostock 练手。等策略成型了,再切换到 tushare 拿更精细的数据。别一上来就追求完美,先跑起来再说。

数据清洗流程:从原始数据到可用数据

数据拿到手,第一件事不是分析,而是清洗。我曾经有一次,从 tushare 拉了5年的日线数据,直接丢进回测引擎,结果收益率曲线漂亮得不像话。后来一查,原来是数据里混了退市整理期的股票,收益率被虚高了。从那以后,我养成了固定的清洗流程。

清洗流程说白了就三步:

  1. 格式统一:把日期转成 datetime 类型,把价格列转成 float,把代码统一成 6 位字符串。
  2. 异常值过滤:剔除涨跌幅超过 20% 的(A股有涨跌停限制),剔除成交量小于 0 的。
  3. 缺失值处理:这个我们单独说。

核心原则: 宁可数据少,不要数据脏。一条错误数据可能让你的回测结果完全失真。

缺失值处理:别让空值毁了你的策略

为什么会缺失?停牌、新股上市、退市、数据源本身就没提供。处理方式有几种,我按推荐程度排个序:

  • 向前填充(ffill):用上一个交易日的值填充。适合价格数据,因为停牌期间价格不变。
  • 直接删除:如果缺失比例小于 5%,直接删掉那几行。简单粗暴,但有效。
  • 插值法:用前后值做线性插值。适合成交量这种连续数据。
  • 千万别用均值填充:我见过有人用整个月的均价填充缺失的日价格,结果回测出来策略在停牌日疯狂交易——这明显是错的。
# 我常用的缺失值处理代码
import pandas as pd

def clean_missing(df):
    # 先看缺失比例
    missing_ratio = df.isnull().sum() / len(df)
    print(f"缺失比例:{missing_ratio}")
    
    # 价格列用向前填充
    price_cols = ['open', 'high', 'low', 'close']
    df[price_cols] = df[price_cols].ffill()
    
    # 成交量用插值
    df['volume'] = df['volume'].interpolate(method='linear')
    
    # 如果还有缺失,直接删除
    df = df.dropna()
    return df
注意: 向前填充只适用于停牌日。如果是数据源本身就没提供某一天的数据,向前填充会引入未来信息。我建议你每次填充前,先检查一下数据源的完整性。

复权数据处理:前复权还是后复权?

这个问题,我当年纠结了很久。说白了,复权就是为了消除分红送股对价格的影响。你想想看,一只股票今天10块钱,明天10送10,价格变成5块。如果不复权,你的回测会以为股价腰斩了,但实际上你的资产没变。

两种复权方式的区别:

复权方式 原理 适合场景
前复权 调整历史价格,让价格连续 技术分析、看K线形态
后复权 调整当前价格,保持历史真实 计算真实收益率

我个人习惯用后复权做回测。为什么?因为后复权保留了历史价格的真实值,计算收益率时更准确。前复权虽然看起来K线连续,但会改变历史价格,导致你算出来的收益率和实际有偏差。

一个小技巧: 如果你用 tushare,可以直接拿复权因子自己算。公式很简单:
后复权价格 = 实际价格 × 复权因子
前复权价格 = 实际价格 / 当前复权因子 × 历史复权因子
# 用 tushare 获取复权数据的示例
import tushare as ts

# 获取复权因子
df_factor = ts.adj_factor(ts_code='000001.SZ')

# 获取日线数据(未复权)
df_daily = ts.daily(ts_code='000001.SZ', start_date='20230101', end_date='20231231')

# 合并并计算后复权价格
df = df_daily.merge(df_factor, on='trade_date')
df['close_adj'] = df['close'] * df['adj_factor']

知识体系总览

下面这张图,是我自己总结的数据获取与清洗的完整流程。你可以把它当成一个检查清单,每次做回测前过一遍。

数据获取与清洗流程 数据源选择 tushare | akshare | baostock 数据清洗 格式统一 → 异常值过滤 → 缺失值处理 ffill | 删除 | 插值 复权处理 前复权(K线分析) | 后复权(收益率计算) 复权因子计算 可用数据 回测引擎输入 | 策略分析 每一步都要验证 宁可慢,不要错 数据决定上限

嗯,数据获取与清洗这部分,说白了就是「磨刀不误砍柴工」。我见过太多人急着跑策略,结果被数据坑得怀疑人生。你花1小时把数据弄干净,后面能省10小时排查问题的时间。

最后提醒一句:每次拿到新数据,先画个图看看。价格曲线有没有断点?成交量有没有异常?一眼就能看出来。别问我怎么知道的——我曾经用了一周时间调试一个策略,最后发现是数据里多了一行空值。

专注资料整理