第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')

核心原则:数据对齐时,永远用「未来数据」填充「过去数据」?不对!应该用「过去数据」填充「未来数据」。说白了,就是只能用已知信息,不能用未来信息。这个错误我见过太多人犯了。

知识体系总览

下面这张图,是我梳理的行情数据获取与清洗的完整流程。你可以把它当成一个检查清单,每次做数据准备时对照着来:

行情数据获取与清洗 · 知识体系 数据获取 股票(tushare/akshare) | 期货(ctpbee/API) | 加密货币(ccxt) 数据清洗 缺失值处理 | 异常值检测 | 重复值删除 | 格式统一 复权处理 前复权(回测用) | 后复权(收益率分析) | 加密货币特殊处理 数据对齐 时间对齐 | 频率对齐 | 品种对齐 | 避免未来信息 质量检查 交叉验证 | 统计检验 | 可视化检查 | 业务规则校验 ✅ 干净可用的行情数据

嗯,到这里,行情数据获取与清洗的核心内容就讲完了。记住:数据质量决定了策略的天花板。花时间把数据搞干净,后面写策略的时候你会感谢自己的。

专注资料整理