4、数据获取:获取期货行情数据(Tushare/JoinQuant)、获取现货指数数据、数据对齐与清洗。
数据获取,说白了就是给策略找「食材」。
你策略再牛,模型再漂亮,拿到的数据是脏的、错的、缺的,那结果就是垃圾进垃圾出。我见过太多人花90%时间调参数,却不愿意花10%时间检查数据源。嗯,这里要敲黑板——数据质量决定了你的策略天花板。
4.1 期货行情数据怎么拿?
我个人习惯用Tushare和JoinQuant两个平台。为什么?因为稳定,而且接口设计得比较人性化。
Tushare获取期货日线数据
先注册账号,拿到token。然后装库、调接口。代码长这样:
import tushare as ts
# 设置token
ts.set_token('你的token')
pro = ts.pro_api()
# 获取螺纹钢主力连续合约日线
df_futures = pro.fut_daily(
ts_code='RB9999.XSGE', # 螺纹钢主力连续
start_date='20240101',
end_date='20241231',
fields='trade_date,open,high,low,close,vol,amount'
)
print(df_futures.head())
这里有个坑——主力连续合约不是真实合约。它是把不同月份的主力合约拼接起来的。你直接拿它算基差,会出问题。我曾经在回测里用主力连续算套利收益,结果发现换月那天基差跳了2%,差点把策略搞崩。
JoinQuant获取数据
如果你在JoinQuant平台上跑策略,可以直接用它的API:
from jqdata import *
# 获取螺纹钢2010合约日线
df_jq = get_price(
'RB2010.XSGE',
start_date='2024-01-01',
end_date='2024-12-31',
frequency='daily',
fields=['open', 'close', 'high', 'low', 'volume', 'money']
)
JoinQuant的好处是数据已经做过复权处理,但坏处是——它默认后复权。做套利时,后复权会把分红、配股等因素算进去,反而会扭曲基差。我个人建议用不复权数据,自己手动处理。
4.2 现货指数数据怎么拿?
现货数据比期货难拿。为什么?因为现货没有统一的交易所,数据分散在各个平台。
常见的现货数据源:
- Wind/Choice:最全,但收费。如果你在机构,直接拉API就行。
- Tushare指数数据:免费,但更新有延迟。
- 中证指数公司官网:可以手动下载CSV,但没法自动化。
- 聚宽/米筐:平台自带指数数据,方便回测。
拿沪深300指数举个例子:
# Tushare获取沪深300指数
df_index = pro.index_daily(
ts_code='000300.SH',
start_date='20240101',
end_date='20241231',
fields='trade_date,open,high,low,close,vol'
)
这里要注意——指数点位和期货价格的单位不一样。指数是点数,期货是元/吨(商品)或指数点(金融)。你直接拿它们相减算基差,数值会很大。我一般会做归一化处理,或者直接用百分比基差。
4.3 数据对齐与清洗——最磨人的一步
数据对齐,说白了就是让期货和现货的日期对上。
期货有夜盘,现货没有。所以期货的交易日和现货的交易日不完全重合。比如周五晚上期货还在交易,但现货已经收盘了。你拿周五夜盘的期货价格去对周五白天的现货价格,时间戳就不对。
对齐规则:
- 期货日线数据,取15:00收盘价(日盘收盘),不要用夜盘收盘。
- 如果某天现货休市(比如节假日),期货即使有交易,也要剔除。
- 如果期货某天没有交易(比如涨停跌停),用前一天的收盘价填充?不,我建议直接剔除。
代码实现:
import pandas as pd
# 假设df_futures和df_index都已经有了
# 合并两个DataFrame,以现货日期为准
df_merged = pd.merge(
df_futures[['trade_date', 'close']].rename(columns={'close': 'futures_close'}),
df_index[['trade_date', 'close']].rename(columns={'close': 'index_close'}),
on='trade_date',
how='inner' # 只保留两边都有的日期
)
# 计算基差
df_merged['basis'] = df_merged['futures_close'] - df_merged['index_close']
df_merged['basis_pct'] = df_merged['basis'] / df_merged['index_close'] * 100
数据清洗的常见问题:
| 问题类型 | 表现 | 处理方法 |
|---|---|---|
| 缺失值 | 某天期货或现货数据为空 | 直接删除该行,不要填充 |
| 异常值 | 价格突然跳涨/跳跌超过5% | 检查是否除权除息,否则剔除 |
| 重复值 | 同一天出现多条记录 | 保留第一条,删除重复 |
| 时间戳错位 | 期货日期和现货日期不匹配 | 用merge的inner模式强制对齐 |
4.4 本章知识体系
下面这张图,是我自己画的数据获取流程。你看一遍,基本就知道每一步该做什么了。
你看,整个流程其实不复杂。但每一步都有细节。我个人建议你先把数据拿到手,然后花一天时间做清洗和对齐。别急着跑策略——数据干净了,策略才有意义。
嗯,数据获取这块就讲这么多。记住一句话:数据质量决定策略上限。你花在清洗上的每一分钟,都会在回测结果里回报你。