3、数据获取与预处理:获取期货行情数据、数据清洗、数据对齐、构建价差序列
做跨期套利,第一步就是搞数据。
很多人觉得策略牛逼就行,数据随便搞搞。我告诉你,这是大错特错。我在项目里见过太多人,策略逻辑写得天花乱坠,结果数据一塌糊涂,最后亏得底裤都不剩。数据预处理,说白了就是地基。地基不稳,楼盖得再高也得塌。
3.1 数据获取:从哪里拿数据?
国内做期货量化,常用的数据源就那么几个。我个人习惯用Tushare和JoinQuant,各有各的好。
- Tushare:免费版够用,但有限流。我建议注册后申请Pro版权限,数据质量不错。
- JoinQuant:聚宽的数据接口很稳定,尤其是分钟级数据,做高频套利时我常用它。
- 其他:Wind、Choice这些收费的当然更好,但个人玩家没必要。
举个例子,用Tushare拿螺纹钢主力连续合约的数据,代码长这样:
import tushare as ts
pro = ts.pro_api('你的token')
df = pro.fut_daily(
ts_code='RB.SHF',
start_date='20200101',
end_date='20231231',
fields='trade_date,close,vol,oi'
)
print(df.head())
嗯,这里要注意。主力连续合约是拼接出来的,不是真实存在的合约。你拿到的其实是每个月换月后的数据。做跨期套利时,我建议你直接拿具体合约,比如RB2401和RB2405,这样更干净。
3.2 数据清洗:脏数据是魔鬼
数据拿到手,第一件事不是跑策略,而是清洗。我踩过最大的坑,就是没处理缺失值,结果策略回测曲线漂亮得像假的一样,实盘直接崩。
3.2.1 缺失值处理
缺失值怎么搞?三种思路:
- 直接删除:如果缺失比例小于5%,删掉最省事。
- 向前填充:用上一个交易日的数据填充。期货市场连续性强,这个方法我常用。
- 插值法:线性插值或者多项式插值。适合数据量少但精度要求高的情况。
我曾经在橡胶合约上遇到过一个奇葩问题:某天所有合约都缺失了数据,原因是交易所系统故障。那天我用了向前填充,结果价差序列出现了一个巨大的跳空,差点让我误判为套利机会。后来我加了一个异常检测逻辑,专门处理这种连续缺失。
# 缺失值处理示例
df.fillna(method='ffill', inplace=True) # 向前填充
# 或者
df.interpolate(method='linear', inplace=True) # 线性插值
3.2.2 异常值处理
异常值怎么判断?我一般用两种方法:
- 3σ原则:超过均值±3倍标准差的数据,视为异常。
- IQR方法:四分位距法,更稳健,不受极端值影响。
你想想看,如果价差突然从50跳到500,这正常吗?大概率是数据错误。我建议用IQR方法,因为期货数据本身就有厚尾特征,3σ原则容易误杀。
# IQR异常值检测
Q1 = df['close'].quantile(0.25)
Q3 = df['close'].quantile(0.75)
IQR = Q3 - Q1
lower = Q1 - 1.5 * IQR
upper = Q3 + 1.5 * IQR
df = df[(df['close'] >= lower) & (df['close'] <= upper)]
3.3 数据对齐:不同合约的日期对齐
做跨期套利,你手里至少有两个合约。问题是,这两个合约的交易日期可能不一样。比如,近月合约交易到第15天,远月合约还在交易。怎么对齐?
说白了,就是取两个合约的交易日期的交集。我习惯用pandas的merge函数,内连接搞定。
# 数据对齐示例
df1 = pro.fut_daily(ts_code='RB2401.SHF', ...)
df2 = pro.fut_daily(ts_code='RB2405.SHF', ...)
# 内连接,只保留共同交易日
df_merged = pd.merge(df1, df2, on='trade_date', how='inner', suffixes=('_near', '_far'))
这里有个细节:不同交易所的节假日安排不同。比如上期所和大商所,节假日可能差一天。我建议你直接用交易日历数据,把非共同交易日剔除掉。Tushare有交易日历接口,可以拿来用。
3.4 构建价差序列:核心步骤
数据对齐好了,接下来就是构建价差序列。价差 = 近月合约价格 - 远月合约价格。就这么简单?嗯,但细节决定成败。
我见过有人直接用收盘价相减,结果发现价差序列有趋势性,根本没法做统计套利。为什么?因为两个合约的价格水平不同,直接相减会受绝对价格影响。我建议用对数价差:
# 构建价差序列
df_merged['spread'] = df_merged['close_near'] - df_merged['close_far']
# 或者用对数价差
df_merged['log_spread'] = np.log(df_merged['close_near']) - np.log(df_merged['close_far'])
对数价差的好处是,它把价格尺度归一化了,更容易满足平稳性要求。我在做螺纹钢套利时,发现对数价差的平稳性明显优于普通价差。
最后,把价差序列可视化一下,看看有没有明显的趋势或季节性。我习惯用matplotlib画个图,一眼就能看出问题。
import matplotlib.pyplot as plt
plt.plot(df_merged['trade_date'], df_merged['spread'])
plt.title('螺纹钢跨期价差序列')
plt.xticks(rotation=45)
plt.show()
知识体系总览
下面这张图,把数据获取到价差构建的整个流程串起来了。你跟着这个流程走,基本不会出错。
数据预处理这块,说白了就是「垃圾进,垃圾出」。你花80%的时间在数据上,剩下20%的时间写策略,这才是正确的节奏。我见过太多人反过来,结果就是回测漂亮,实盘打脸。
好了,数据搞定了,价差序列也构建好了。接下来就可以开始真正的统计套利分析了。