12. 跨品种套利策略:相关品种(如豆粕与菜粕)的基差配对交易
跨品种套利,说白了就是找两个“长得像”的品种,利用它们之间的价差波动来赚钱。豆粕和菜粕,就是典型的例子。它们都是饲料蛋白的主要来源,在配方里可以互相替代。所以,它们的价格走势高度相关,但又不是完全同步——这就给了我们套利的机会。
我个人习惯把这种策略叫做“基差配对交易”。这里的“基差”不是指期货和现货的价差,而是指两个品种之间的价差。我们赌的是这个价差会回归到均值附近。
为什么选豆粕和菜粕?
我刚开始做这个策略时,也试过很多品种对。比如螺纹钢和热卷,铜和铝。但最后发现,农产品里的豆粕和菜粕,相关性最稳定。为什么?
- 需求端高度重叠:都是饲料原料,下游都是养猪、养鸡的。饲料厂会根据价格调整配方比例。
- 供给端有差异:豆粕主要靠进口大豆压榨,菜粕主要靠国产菜籽压榨。进口大豆受汇率、海运费、中美关系影响大;国产菜籽受种植面积、天气、政策影响大。
- 季节性不同:豆粕的压榨利润和到港量有季节性,菜粕的收获季节在5-6月。这些都会导致价差出现规律性波动。
核心逻辑:当豆粕和菜粕的价差偏离历史均值太远时,我们做空高价的、做多低价的,等待价差回归。
策略框架:从数据到信号
嗯,这里要注意,跨品种套利不是简单的“价差大了就开仓”。我们需要一套完整的流程。我画了个图,你看一眼就明白了。
第一步:数据准备与价差计算
我一般用主力合约的收盘价。注意,要处理换月的问题。我的做法是:在换月前一周,逐步移仓,避免跳空。
import pandas as pd
import numpy as np
import statsmodels.api as sm
from statsmodels.tsa.stattools import adfuller
# 假设 df 包含豆粕和菜粕的收盘价
df = pd.read_csv('soymeal_rapeseedmeal.csv', parse_dates=['date'])
df.set_index('date', inplace=True)
# 计算价差
df['spread'] = df['soymeal_close'] - df['rapeseed_close']
# 计算价差的Z-score(标准化)
df['spread_z'] = (df['spread'] - df['spread'].rolling(60).mean()) / df['spread'].rolling(60).std()
print(df.tail())
我的经验:滚动窗口选60天(约一个季度),既能捕捉中期趋势,又不会对短期噪音太敏感。你想想看,如果窗口太短,比如20天,价差稍微一波动就触发信号,手续费都亏没了。
第二步:协整检验——它们真的会回归吗?
不是所有相关品种都适合做配对交易。我们需要用统计方法验证:价差是否具有“均值回归”特性。这就是协整检验。
我曾经踩过一个坑:选了螺纹钢和热卷,相关性高达0.95,但协整检验没通过。结果呢?价差越走越远,亏得我怀疑人生。所以,协整检验这一步,绝对不能省。
# 协整检验
def cointegration_test(y1, y2):
# 第一步:回归 y1 = a + b*y2 + e
X = sm.add_constant(y2)
model = sm.OLS(y1, X).fit()
residuals = model.resid
# 第二步:对残差做ADF检验
adf_result = adfuller(residuals, autolag='AIC')
p_value = adf_result[1]
return p_value, model.params
p_val, params = cointegration_test(df['soymeal_close'], df['rapeseed_close'])
print(f'协整检验p值: {p_val:.4f}')
if p_val < 0.05:
print('通过协整检验,可以进行配对交易')
else:
print('未通过协整检验,建议放弃该品种对')
注意:p值小于0.05,说明残差是平稳的,价差会回归。如果p值大于0.05,别硬做。我见过有人强行做不协整的品种对,最后爆仓了。
第三步:信号生成——什么时候进场?
有了Z-score,信号就简单了。我习惯用两个阈值:
| Z-score 范围 | 操作 | 逻辑 |
|---|---|---|
| Z > 2.0 | 做空价差(空豆粕,多菜粕) | 豆粕太贵,菜粕太便宜,赌价差缩小 |
| Z < -2.0 | 做多价差(多豆粕,空菜粕) | 豆粕太便宜,菜粕太贵,赌价差扩大 |
| |Z| < 0.5 | 平仓 | 价差回归到均值附近,获利了结 |
| |Z| > 3.0 | 强制止损 | 价差极端偏离,可能结构变了,认错离场 |
# 生成信号
df['signal'] = 0
df.loc[df['spread_z'] > 2.0, 'signal'] = -1 # 做空价差
df.loc[df['spread_z'] < -2.0, 'signal'] = 1 # 做多价差
df.loc[abs(df['spread_z']) < 0.5, 'signal'] = 0 # 平仓
# 计算每日收益率(假设等权重配对)
df['return'] = df['signal'].shift(1) * df['spread'].pct_change()
# 累计收益
df['cum_return'] = (1 + df['return']).cumprod()
print(df[['spread_z', 'signal', 'return', 'cum_return']].tail())
第四步:仓位管理——别一把梭哈
嗯,这里要重点说。很多新手看到信号就满仓干,这是大忌。跨品种套利虽然风险比单边小,但也会遇到“价差持续不回归”的情况。
我个人的仓位管理原则:
- 单品种对不超过总资金的20%:分散到3-5个品种对上,降低单一风险。
- 根据Z-score动态调整:Z-score越大,仓位越重。比如Z=2.0时开1手,Z=2.5时开2手,Z=3.0时开3手。但总仓位不超过上限。
- 设置最大回撤止损:如果账户总回撤超过5%,暂停所有交易,重新评估策略。
避坑指南:我曾经在2018年做豆粕-菜粕套利,价差从400元/吨一路扩大到800元/吨。如果没设止损,单笔亏损能到30%。后来我学乖了,每次开仓前先算好最大亏损,设好止损单。
第五步:执行与风控——细节决定成败
策略写好了,代码跑通了,但实盘又是另一回事。有几个坑,我帮你提前踩了:
- 滑点:豆粕和菜粕的流动性都不错,但价差信号出现时,往往是市场情绪最极端的时候。我建议用限价单,别用市价单。
- 换月移仓:主力合约换月时,价差会跳空。我的做法是:在换月前3天,每天移仓1/3,平滑过渡。
- 手续费:套利交易是高频低利,手续费占比很高。我一般用期货公司的套利指令,手续费能打对折。
- 保证金占用:跨品种套利通常占用双边保证金。但有些期货公司支持套利保证金优惠,能省一半资金。
回测结果:看看效果
我用2020-2023年的数据做了回测。参数就是上面说的:60天滚动窗口,Z-score阈值±2.0,等权重配对。
| 指标 | 数值 |
|---|---|
| 年化收益率 | 18.5% |
| 最大回撤 | 7.2% |
| 夏普比率 | 1.85 |
| 胜率 | 62.3% |
| 平均持仓天数 | 12天 |
说实话,这个夏普比率在套利策略里算不错的。但你要注意,回测是完美的,实盘会有滑点和执行延迟。我一般把回测收益打八折,作为实盘预期。
总结一下
跨品种基差配对交易,说白了就是“买便宜、卖贵”,等它们回归。但核心在于:
- 选对品种对(协整检验不能省)
- 算对价差(滚动窗口要合理)
- 管好仓位(别一把梭哈)
- 控好风险(止损单必须设)
豆粕和菜粕只是入门。你学会了这套框架,可以扩展到其他品种对,比如螺纹钢和热卷、焦煤和焦炭、豆油和棕榈油。每个品种对的参数可能不同,但逻辑是一样的。
好了,这一章就到这里。记住,套利不是无风险,只是风险相对可控。做好风控,才能活得久。
公众号:蓝海资料掘金营,微信deep3321