第二章 数据获取与清洗:数据源选择、字段说明与清洗流程
做量化回测,第一步就是搞数据。
数据搞不定,后面全是白搭。我见过太多人,策略写得花里胡哨,结果数据源选错了,或者复权没处理好,回测曲线漂亮得像假的一样。嗯,今天我们就来聊聊数据获取与清洗这档子事。
2.1 数据源选择:tushare / akshare / baostock
国内常用的免费数据源,我主要用过这三个:tushare、akshare、baostock。各有各的脾气。
| 数据源 | 特点 | 适合场景 | 我的评价 |
|---|---|---|---|
| tushare | 数据全,但高权限需要积分 | 专业回测、因子研究 | 老牌,稳定,但门槛渐高 |
| akshare | 免费,接口丰富,更新快 | 快速原型、小规模回测 | 我最近用得最多 |
| baostock | 免费,无需注册,数据规范 | 入门学习、简单回测 | 省心,但数据量有限 |
我个人习惯是:做研究用 baostock,做策略开发用 akshare,做正式回测用 tushare。为什么?因为 tushare 的复权数据最干净,我踩过坑才知道。
注意: tushare 的 pro 接口需要 token,而且高频调用会被限流。我曾经一次性拉5年数据,结果被 ban 了半小时。
2.2 数据字段说明
不管用哪个数据源,核心字段就那么几个。我列个表,你对照着看。
| 字段名 | 含义 | 类型 | 备注 |
|---|---|---|---|
| trade_date | 交易日期 | datetime | 注意格式统一 |
| open | 开盘价 | float | 复权后价格 |
| high | 最高价 | float | 同上 |
| low | 最低价 | float | 同上 |
| close | 收盘价 | float | 核心字段 |
| volume | 成交量 | int | 单位:股 |
| amount | 成交额 | float | 单位:元 |
你想想看,如果拿到的数据里 open 比 close 还高,或者 volume 是负数,那肯定有问题。我遇到过某数据源某天的 volume 字段全是 0,查了半天才发现是接口 bug。
2.3 数据清洗流程
数据清洗,说白了就是「去伪存真」。我的标准流程分四步:
- 格式统一:日期转 datetime,价格转 float,成交量转 int
- 排序去重:按日期升序,删除重复行
- 异常值检测:价格不能为负,成交量不能为 0(停牌除外)
- 缺失值处理:见下一节
代码示例,我一般这么写:
import pandas as pd
def clean_stock_data(df):
# 1. 格式统一
df['trade_date'] = pd.to_datetime(df['trade_date'])
df = df.sort_values('trade_date')
# 2. 去重
df = df.drop_duplicates(subset=['trade_date'])
# 3. 异常值过滤
df = df[(df['close'] > 0) & (df['volume'] >= 0)]
# 4. 缺失值处理(前向填充)
df = df.fillna(method='ffill')
return df
小技巧: 我习惯在清洗后加一个「数据完整性检查」,比如看看日期是否连续,有没有跳空。曾经有个策略因为数据跳了三天,回测结果完全失真。
2.4 缺失值处理
缺失值怎么处理?分情况。
- 停牌导致的缺失:用前一天的收盘价填充,或者直接删除该行
- 数据源本身缺失:比如某天没有数据,我建议用前向填充(ffill)
- 节假日缺失:正常现象,不用处理
我个人习惯是:回测时用 ffill,实盘时用插值。为什么?因为回测是事后看,用前一天的价格更合理;实盘是实时,插值能平滑一些。
避坑指南: 我曾经用均值填充缺失值,结果把一只连续跌停的股票填成了上涨,回测曲线漂亮得不行,实盘直接亏哭。从那以后,我再也不敢乱填缺失值了。
2.5 复权数据处理
复权,这是个大坑。
说白了,股票分红送股后,价格会跳空。如果不复权,你的回测会失真。比如一只股票从 100 块跌到 50 块,你以为亏了 50%,其实是 10 送 10,实际没亏。
复权有两种:
- 前复权:调整历史价格,让价格连续。适合看趋势
- 后复权:调整当前价格,适合算真实收益
我建议回测时用 前复权。为什么?因为前复权后的价格更接近真实交易成本。后复权算出来的收益虽然准确,但价格太高,看着不直观。
代码示例,用 tushare 获取前复权数据:
import tushare as ts
pro = ts.pro_api('你的token')
df = pro.daily(ts_code='000001.SZ',
start_date='20200101',
end_date='20231231',
adj='qfq') # qfq 表示前复权
我的经验: 如果你用 akshare,复权参数是 adjust="qfq"。baostock 默认返回的就是前复权数据,省心。但记得检查一下,我遇到过 baostock 某只股票复权数据有误的情况。
2.6 本章知识体系图
下面这张图,是我自己总结的数据获取与清洗流程。你照着走,基本不会出大问题。
核心要点:
- 数据源选对,后面省一半功夫
- 清洗流程要标准化,别偷懒
- 缺失值处理要谨慎,别乱填
- 复权必须做,前复权是首选
好了,数据获取与清洗就聊这么多。记住一句话:数据质量决定回测质量。你花 80% 的时间在数据上,都不为过。