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 异常值处理

异常值怎么判断?我一般用两种方法:

  1. 3σ原则:超过均值±3倍标准差的数据,视为异常。
  2. 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'])

对数价差的好处是,它把价格尺度归一化了,更容易满足平稳性要求。我在做螺纹钢套利时,发现对数价差的平稳性明显优于普通价差。

重点: 价差序列构建后,一定要做平稳性检验(ADF检验)。如果价差不平稳,你的统计套利模型就是空中楼阁。

最后,把价差序列可视化一下,看看有没有明显的趋势或季节性。我习惯用matplotlib画个图,一眼就能看出问题。

import matplotlib.pyplot as plt
plt.plot(df_merged['trade_date'], df_merged['spread'])
plt.title('螺纹钢跨期价差序列')
plt.xticks(rotation=45)
plt.show()

知识体系总览

下面这张图,把数据获取到价差构建的整个流程串起来了。你跟着这个流程走,基本不会出错。

数据获取与预处理流程 数据获取 Tushare/JoinQuant 数据清洗 缺失值 + 异常值 数据对齐 日期交集内连接 构建价差 价差/对数价差 主力连续 vs 具体合约 注意限流与数据质量 向前填充 / 插值 3σ / IQR异常检测 内连接避免缺失 交易日历对齐 普通价差 / 对数价差 ADF平稳性检验 最终输出:干净、对齐、平稳的价差序列

数据预处理这块,说白了就是「垃圾进,垃圾出」。你花80%的时间在数据上,剩下20%的时间写策略,这才是正确的节奏。我见过太多人反过来,结果就是回测漂亮,实盘打脸。

好了,数据搞定了,价差序列也构建好了。接下来就可以开始真正的统计套利分析了。

专注资料整理