第4章:行情数据获取与清洗
做量化策略,说白了就是跟数据打交道。我见过太多人策略写得天花乱坠,结果一跑实盘就崩——为什么?数据源就有问题。今天咱们就把行情数据这块硬骨头啃下来。
核心观点:行情数据是量化策略的「原材料」。原材料不干净,再好的策略也是白搭。我个人习惯把数据获取和清洗的时间,占到整个策略研发周期的30%以上。
4.1 日线数据获取:从哪儿拿?怎么拿?
日线数据是最基础的。股票、期货、加密货币,获取方式各有不同。
股票日线:我个人最常用的是tushare和akshare。tushare需要注册拿token,akshare更轻量。举个例子:
import akshare as ak
# 获取A股日线
stock_daily = ak.stock_zh_a_hist(
symbol="000001",
period="daily",
start_date="20200101",
end_date="20231231",
adjust="qfq"
)
print(stock_daily.head())
期货日线:期货数据我建议用ctpbee或者直接对接交易所的API。注意期货有主力合约换月的问题,这个后面会讲。
加密货币日线:币安、OKX都有公开API。我习惯用ccxt这个库,一个接口搞定所有交易所:
import ccxt
exchange = ccxt.binance()
ohlcv = exchange.fetch_ohlcv(
symbol='BTC/USDT',
timeframe='1d',
limit=1000
)
# 返回格式:[[timestamp, open, high, low, close, volume], ...]
我的经验:数据源不要只依赖一个。我曾在项目中同时用tushare和东方财富的数据做交叉验证,发现过好几次数据不一致的情况。多一个数据源,多一份安心。
4.2 分钟线数据获取:细节决定成败
分钟线数据比日线复杂得多。为什么?数据量大、精度要求高、容易有缺失。
股票分钟线:A股分钟线数据,tushare和akshare都能拿,但要注意交易时间。A股只有上午9:30-11:30和下午13:00-15:00有数据。别把集合竞价的数据搞混了。
# 获取5分钟线
stock_min = ak.stock_zh_a_hist_min_em(
symbol="000001",
period="5",
start_date="2023-01-01",
end_date="2023-12-31"
)
期货分钟线:期货有夜盘,这个坑我踩过。有些品种夜盘到凌晨1点,有些到凌晨3点。你想想看,如果策略没考虑夜盘,第二天开盘直接跳空,那损失...
加密货币分钟线:7×24小时交易,数据量最大。我建议按时间分段拉取,一次别拉太多,容易被交易所限流。
避坑指南:我曾经在拉取加密货币分钟线时,没注意交易所的时间戳是UTC还是本地时间。结果策略在晚上8点后全部错位。后来我统一把所有时间戳转成UTC+8,再也没出过问题。
4.3 复权处理:别让分红影响你的策略
复权处理,说白了就是消除分红、送股、配股对价格的影响。不做复权,你的策略会被这些「噪音」干扰。
前复权 vs 后复权:
| 类型 | 说明 | 适用场景 |
|---|---|---|
| 前复权 | 调整历史价格,保持当前价格不变 | 回测时常用,方便看历史走势 |
| 后复权 | 调整当前价格,保持历史价格不变 | 分析真实收益率时用 |
我个人习惯用前复权做回测。为什么?因为前复权后的价格曲线是连续的,不会出现跳空。但要注意:前复权后的价格可能变成负数(极端情况),这时候需要做特殊处理。
# tushare获取复权数据
import tushare as ts
pro = ts.pro_api('your_token')
df = pro.daily(
ts_code='000001.SZ',
start_date='20200101',
end_date='20231231',
adj='qfq' # 前复权
)
注意:加密货币没有分红概念,所以不需要复权。但要注意币种拆分(比如BTC的硬分叉),这跟股票送股类似,需要手动处理。
4.4 缺失值处理:数据不完整怎么办?
数据缺失是常态。非交易日、数据源故障、网络问题,都可能导致缺失。我见过有人直接删掉缺失行,结果策略回测曲线漂亮得不行,一实盘就完蛋——因为缺失的数据里藏着重要信息。
常见处理方法:
- 向前填充:用上一个有效值填充。适合非交易日缺失的情况。
- 线性插值:用前后两个值做线性插值。适合短时间缺失。
- 删除:如果缺失比例很小(比如<1%),可以直接删除。
- 标记:保留缺失值,在策略中特殊处理。
import pandas as pd
# 向前填充
df['close'].fillna(method='ffill', inplace=True)
# 线性插值
df['close'].interpolate(method='linear', inplace=True)
我的建议:别一上来就填充。先搞清楚缺失的原因。如果是非交易日,向前填充没问题。如果是数据源故障,我建议用其他数据源补全。我曾经用三个数据源互相补全,效果很好。
4.5 异常值检测与处理:揪出那些「离谱」的数据
异常值,就是那些明显不合理的数据。比如股票价格突然涨了100倍,或者成交量突然变成0。这些数据会严重干扰策略。
检测方法:
- 3σ原则:超过均值±3倍标准差的数据,视为异常。
- IQR方法:超过Q1-1.5×IQR或Q3+1.5×IQR的数据,视为异常。
- 业务规则:比如涨跌幅超过10%(A股),或者成交量突然为0。
# IQR方法检测异常值
Q1 = df['close'].quantile(0.25)
Q3 = df['close'].quantile(0.75)
IQR = Q3 - Q1
lower_bound = Q1 - 1.5 * IQR
upper_bound = Q3 + 1.5 * IQR
anomalies = df[(df['close'] < lower_bound) | (df['close'] > upper_bound)]
print(f"发现 {len(anomalies)} 个异常值")
避坑指南:我曾经在检测加密货币数据时,用3σ原则把比特币的暴涨暴跌都当成了异常值。结果呢?策略错过了2021年的大牛市。后来我改用业务规则+人工审核,才解决了这个问题。记住:异常值不一定是错误数据,可能是市场真实波动。
4.6 数据对齐:让不同数据「步调一致」
数据对齐,就是把不同频率、不同时间戳的数据,对齐到同一个时间轴上。这是量化策略中最容易被忽视的环节。
对齐什么?
- 时间对齐:股票、期货、加密货币的交易时间不同,需要统一时间轴。
- 频率对齐:日线、分钟线、Tick数据,需要对齐到同一频率。
- 品种对齐:多品种策略中,需要对齐所有品种的数据。
# 将分钟线对齐到日线
daily_index = pd.date_range(start='2023-01-01', end='2023-12-31', freq='D')
minute_data = minute_data.reindex(daily_index, method='ffill')
核心原则:数据对齐时,永远用「未来数据」填充「过去数据」?不对!应该用「过去数据」填充「未来数据」。说白了,就是只能用已知信息,不能用未来信息。这个错误我见过太多人犯了。
知识体系总览
下面这张图,是我梳理的行情数据获取与清洗的完整流程。你可以把它当成一个检查清单,每次做数据准备时对照着来:
嗯,到这里,行情数据获取与清洗的核心内容就讲完了。记住:数据质量决定了策略的天花板。花时间把数据搞干净,后面写策略的时候你会感谢自己的。