3. 数据获取:使用Python获取期货行情数据(Tushare/JoinQuant)
做跨期套利,第一步就是搞定数据。没有数据,策略就是空中楼阁。
我个人习惯把数据获取分成两类:一类是历史数据回测用的,另一类是实盘实时行情用的。今天咱们重点讲前者——怎么用Python把期货的历史日线数据稳稳地拿到手。
3.1 数据源选型:Tushare vs JoinQuant
国内能拿期货数据的平台不少,但用得最多的就两个:Tushare和JoinQuant(聚宽)。
我当年刚入行时,踩过不少坑。有的平台数据不全,有的接口动不动就限流。后来我固定下来两套方案:
| 对比维度 | Tushare Pro | JoinQuant |
|---|---|---|
| 数据覆盖 | 全品种,含主力合约、指数 | 全品种,含分钟线 |
| 获取方式 | HTTP API + Token | SDK + 本地数据 |
| 免费额度 | 200次/分钟,够用 | 本地回测免费 |
| 历史数据长度 | 2010年至今 | 2010年至今 |
| 我的推荐场景 | 快速原型、研究分析 | 策略回测、本地部署 |
3.2 Tushare实战:一行代码拿数据
先讲Tushare。注册账号、拿到token这些基础操作我就不啰嗦了,直接上代码。
import tushare as ts
# 设置token(建议写到配置文件里)
ts.set_token('你的token_here')
pro = ts.pro_api()
# 获取螺纹钢主力连续合约日线
df = pro.fut_daily(
ts_code='RB8888.XDCE', # 螺纹钢主力连续
start_date='20230101',
end_date='20231231',
fields='trade_date,open,high,low,close,vol,amount'
)
print(df.head())
这里有个关键点——RB8888.XDCE是啥?
这是Tushare对主力连续合约的编码规则。8888代表主力连续,9999代表指数。我在项目中吃过亏,一开始用了具体合约代码(比如RB2310),结果换月后数据就断了。你想想看,跨期套利要的就是两个合约之间的价差,如果用具体合约,换月那天价差直接跳空,策略就废了。
3.3 JoinQuant实战:本地数据更稳
再说JoinQuant。它的优势在于数据直接下载到本地,回测时不用反复请求网络。
from jqdatasdk import *
# 登录(需要聚宽账号)
auth('你的账号', '你的密码')
# 获取螺纹钢主力连续合约日线
df = get_price(
'RB8888.XDCE', # 聚宽的主力连续编码
start_date='2023-01-01',
end_date='2023-12-31',
frequency='daily',
fields=['open', 'high', 'low', 'close', 'volume', 'money']
)
print(df.head())
嗯,这里要注意:JoinQuant的编码和Tushare略有不同。聚宽的主力连续也是8888结尾,但交易所代码是.XDCE(大商所)、.XSGE(上期所)、.XZCE(郑商所)。
3.4 核心逻辑:如何构建价差数据
数据拿到手了,下一步就是构建价差。跨期套利的核心就一句话:近月合约价格 - 远月合约价格。
但这里有个坑——两个合约的交易日可能不完全对齐。比如近月合约在交割月前会提前退市,远月合约还在交易。这时候价差序列就会出现缺失值。
我的处理方式是:
- 分别获取近月和远月合约的日线数据
- 按日期做inner join,只保留两个合约都有数据的日期
- 计算价差 = 近月收盘价 - 远月收盘价
import pandas as pd
# 假设已经获取了两个合约的数据
near = df_near[['trade_date', 'close']].rename(columns={'close': 'near_close'})
far = df_far[['trade_date', 'close']].rename(columns={'close': 'far_close'})
# 合并
spread = pd.merge(near, far, on='trade_date', how='inner')
spread['spread'] = spread['near_close'] - spread['far_close']
# 看一眼价差序列
print(spread.head())
3.5 数据存储:别每次都重新下载
说实话,每次回测都去API拉数据,效率太低了。我的习惯是把数据存到本地,用的时候直接读。
# 保存到CSV
df.to_csv('RB_spread_data.csv', index=False)
# 或者存到HDF5(大数据量时推荐)
df.to_hdf('futures_data.h5', key='RB_spread', mode='a')
我个人更推荐HDF5格式。为什么?因为期货数据动辄几年、几十个品种,CSV文件多了管理起来很头疼。HDF5一个文件就能搞定,读取速度还快。
3.6 本章知识体系
下面这张图,是我自己总结的数据获取流程。你看一眼,基本就全明白了。
说白了,数据获取这件事,技术难度不高,但细节决定成败。你只要记住三点:
- 用连续合约,别用具体月份
- 日期对齐,别让缺失值坑了你
- 本地存储,别每次都去API拉
做到这三点,你的数据基础就稳了。后面讲策略回测时,你会发现数据干净了,回测结果才有说服力。