第二章 价差计算与数据获取:主力合约识别、近远月价差计算、数据源选择(Wind/天勤)、数据清洗
做跨期套利,说白了就是吃两个合约之间的价差波动。但有个前提——你得先把价差算对,把数据搞干净。这一步要是糊弄过去,后面策略再牛也是白搭。我见过太多人,策略回测曲线漂亮得像假的一样,一上实盘就崩,十有八九是数据源或者价差计算出了问题。
2.1 主力合约识别——别搞错交易对象
跨期套利的第一步,是搞清楚哪个合约是主力。你想想看,如果拿一个流动性枯竭的合约去跟主力合约配对,滑点都能把你吃破产。
主力合约的定义:通常我们取持仓量最大的那个合约。成交量也可以,但我个人习惯用持仓量——它更能反映资金沉淀的深度。
核心逻辑:每天收盘后扫描所有同品种合约,找出持仓量最大的那个,标记为主力。次主力同理。
我在项目中遇到过一个问题:换月期间,主力合约会从近月跳到远月。如果你不做平滑处理,价差序列会出现一个巨大的跳空缺口。嗯,这里要注意——换月日前后,价差数据要特别处理,要么对齐到同一个合约对,要么做滚动调整。
# 伪代码:主力合约识别逻辑
def find_main_contract(data):
# data 是某品种所有合约的持仓量DataFrame
main = data.loc[data.groupby('date')['open_interest'].idxmax()]
return main[['date', 'contract', 'close']]
2.2 近远月价差计算——公式很简单,坑不少
价差 = 近月合约价格 - 远月合约价格。就这么简单?嗯,表面上是这样。但实际做的时候,有几个细节你绕不开。
| 价差类型 | 计算公式 | 适用场景 |
|---|---|---|
| 简单价差 | P_near - P_far | 同品种、同交易所 |
| 比例价差 | (P_near - P_far) / P_near | 价格水平差异大时 |
| 对数价差 | ln(P_near) - ln(P_far) | 统计建模时常用 |
我个人习惯用简单价差,直观好理解。但如果你做的是股指期货或者国债期货,比例价差可能更合适——因为价格本身会随时间漂移。
避坑指南:我曾经在计算螺纹钢的价差时,没注意近月合约和远月合约的乘数不同(10吨/手 vs 10吨/手,其实一样),但有些品种比如焦炭,不同合约的乘数可能不同。一定要确认合约单位一致!
2.3 数据源选择——Wind还是天勤?
做量化交易,数据源就是你的弹药库。选错了,后面全白干。
- Wind:老牌金融终端,数据全、质量高。但贵,一年几万块。适合机构用户。
- 天勤:新兴的量化数据平台,API友好,Python直接调。免费版够用,付费版也不贵。适合个人和中小团队。
我自己的经验是:如果做高频或者日内套利,Wind的Tick数据更靠谱。如果做日线级别的跨期套利,天勤完全够用,而且代码写起来舒服很多。
# 天勤获取主力合约数据示例
from tqsdk import TqApi
api = TqApi()
# 获取螺纹钢主力连续合约
klines = api.get_kline_serial("SHFE.rb0000", 86400, data_length=500)
print(klines[['close', 'open_interest']])
小技巧:天勤的"rb0000"就是螺纹钢主力连续合约,它会自动帮你做换月处理。但注意——这个连续合约的价格是拼接出来的,不能直接用于计算真实价差。真实价差必须用具体合约(比如rb2401和rb2405)来算。
2.4 数据清洗——脏数据是策略的隐形杀手
数据清洗听着枯燥,但它是整个策略的基石。我见过有人用没清洗的数据跑回测,年化收益30%,实盘直接亏到怀疑人生。为什么?因为数据里有跳空、有缺失、有异常值,回测时这些都被当成了交易机会。
清洗步骤:
- 缺失值处理:如果某天某个合约没有交易,用前一天的收盘价填充,或者直接删除该天的价差数据。
- 异常值检测:价差突然偏离3个标准差以上,大概率是数据错误。我习惯用滚动窗口的Z-score来检测。
- 换月跳空处理:主力合约切换时,价差会有一个不自然的跳变。需要做后复权或者直接分段处理。
# 数据清洗示例:滚动Z-score检测异常价差
import pandas as pd
import numpy as np
def clean_spread(spread_series, window=20, threshold=3):
rolling_mean = spread_series.rolling(window).mean()
rolling_std = spread_series.rolling(window).std()
z_scores = (spread_series - rolling_mean) / rolling_std
# 标记异常值
spread_series[z_scores.abs() > threshold] = np.nan
# 向前填充
spread_series.fillna(method='ffill', inplace=True)
return spread_series
我曾经踩过的坑:有一次做豆粕的跨期套利,数据里突然出现一个-500的价差,我以为是交易机会,结果发现是数据源把近月和远月的价格搞反了。从那以后,我每次拿到数据都会先画个价差曲线图,肉眼扫一遍——这比任何算法都管用。
2.5 知识体系总览
下面这张图,是我做跨期套利数据准备阶段的核心流程。你照着这个走,基本不会漏掉关键步骤。
数据准备这一步,说白了就是打地基。地基不稳,楼盖得再高也得塌。我建议你每次拿到新数据,先花10分钟做一遍可视化检查——画个价差曲线,看看有没有异常跳点,有没有缺失区间。这10分钟,能帮你省下后面10小时的debug时间。
我的习惯:我会把清洗后的价差数据存成两个版本——一个是原始未清洗的(留作审计用),一个是清洗后的(用于策略计算)。这样万一策略出问题,我可以回头对比,看看是不是数据清洗环节出了岔子。