案例二:豆粕与菜粕套利:季节性因素分析、价差驱动因子、套利窗口期判断、实盘模拟
豆粕和菜粕,这两个品种我盯了快十年。说实话,它们就像一对孪生兄弟,但性格又完全不同。豆粕是大哥,体量大、流动性好;菜粕是小弟,波动更剧烈、季节性更强。做它们的套利,说白了就是吃「价差回归」这碗饭。
我个人习惯把这对组合叫做「蛋白双雄」。为什么?因为它们都是饲料蛋白的主要来源,在配方里可以互相替代。但替代不是无条件的,这里面门道很深。
一、季节性因素:为什么每年都有几波确定性行情?
先看一张图,这是我用过去8年数据画的豆菜粕价差走势。
看到没有?每年5月到7月,价差会明显收窄。为什么?因为5月菜籽开始上市,菜粕供应增加,价格承压。而豆粕这边,美豆还在生长期,供应偏紧。这一松一紧,价差自然就缩了。
我记得2019年做这个策略时,5月初价差还在800以上,到6月底直接干到500。那一波我吃了将近300点,爽是爽,但中间的回撤也够呛。
核心规律:豆菜粕价差在每年5-7月倾向于收窄,11-1月倾向于扩大。这是由南北半球大豆和菜籽的种植、收获周期决定的。
二、价差驱动因子:除了季节,还有什么在影响?
季节性只是大框架,真正决定入场时机的,是下面这几个因子。
| 驱动因子 | 影响方向 | 监测指标 | 我的经验阈值 |
|---|---|---|---|
| 豆粕库存 | 正相关 | 港口大豆库存、压榨利润 | 库存>600万吨时,价差易扩大 |
| 菜粕库存 | 负相关 | 菜籽到港量、开机率 | 库存<10万吨时,价差易收窄 |
| 豆菜粕价差 | 均值回归 | 历史分位数 | 80%分位以上做空,20%以下做多 |
| 饲料需求 | 同向影响 | 生猪存栏、水产投苗 | 水产旺季利好菜粕 |
| 替代效应 | 非线性 | 蛋白价差比 | 价差>900时替代加速 |
这里面有个坑,我踩过。就是只看价差绝对值,不看相对位置。比如价差800,你觉得高了,但历史均值是900,那800其实算低的。所以我现在习惯用Z-score来标准化处理。
我的小技巧:把价差除以豆粕价格,得到「相对价差」。这样能消除绝对价格波动的影响。比如豆粕3000时价差600,和豆粕4000时价差600,意义完全不同。
三、套利窗口期判断:什么时候该动手?
判断窗口期,我有一套自己的流程。说白了就是三个条件同时满足:
- 季节性位置对:当前月份在历史季节性规律中处于有利位置
- 价差偏离大:当前价差偏离历史均值至少1.5个标准差
- 基本面配合:库存、到港量等数据支持方向
举个例子。2022年6月,我看到豆菜粕价差到了750,而历史同期均值是550。偏离了将近200点,超过2个标准差。再看库存,豆粕库存持续下降,菜粕库存却在累积。嗯,这就有意思了。
我当时判断:价差大概率要回归。于是入场做空价差(卖豆粕买菜粕)。持仓了大概3周,价差回到600附近,平仓走人。单笔盈利150点,收益率大概8%。
注意:窗口期判断最怕「假突破」。我曾经在2018年遇到价差冲到900以上,以为要回归,结果继续拉到1100。那笔亏了300点。后来我加了「持仓量确认」这个条件——价差突破时,如果持仓量同步增加,说明趋势有延续性,不要轻易逆势。
四、实盘模拟:手把手走一遍流程
好,理论说完了,咱们来点实际的。假设今天是2023年5月10日,我们来做一次模拟交易。
第一步:数据准备
# 获取近5年豆粕和菜粕主力合约数据
import pandas as pd
import numpy as np
from datetime import datetime
# 模拟数据(实际应从数据库或API获取)
dates = pd.date_range('2018-01-01', '2023-05-10', freq='D')
np.random.seed(42)
m_price = 3000 + np.cumsum(np.random.randn(len(dates)) * 10)
r_price = 2500 + np.cumsum(np.random.randn(len(dates)) * 8)
spread = m_price - r_price
df = pd.DataFrame({
'date': dates,
'm_price': m_price,
'r_price': r_price,
'spread': spread
})
df.set_index('date', inplace=True)
第二步:计算统计指标
# 计算当前价差的历史分位数和Z-score
current_spread = df['spread'].iloc[-1]
mean_spread = df['spread'].mean()
std_spread = df['spread'].std()
z_score = (current_spread - mean_spread) / std_spread
percentile = (df['spread'] < current_spread).mean() * 100
print(f"当前价差: {current_spread:.0f}")
print(f"历史均值: {mean_spread:.0f}")
print(f"Z-score: {z_score:.2f}")
print(f"分位数: {percentile:.1f}%")
第三步:判断窗口期
# 判断条件
is_season_ok = 5 <= datetime.now().month <= 7 # 季节性有利
is_deviation_ok = abs(z_score) > 1.5 # 偏离足够大
is_fundamental_ok = True # 假设基本面配合
if is_season_ok and is_deviation_ok and is_fundamental_ok:
if z_score > 0:
action = "做空价差:卖豆粕,买菜粕"
else:
action = "做多价差:买豆粕,卖菜粕"
print(f"窗口期确认!建议操作:{action}")
else:
print("当前不满足窗口条件,观望")
第四步:执行与风控
# 假设做空价差,开仓比例1:1
# 豆粕1手=10吨,菜粕1手=10吨
m_contracts = 1 # 卖1手豆粕
r_contracts = 1 # 买1手菜粕
# 止损设置:价差反向突破2个标准差
stop_loss = current_spread + 2 * std_spread if z_score > 0 else current_spread - 2 * std_spread
print(f"入场价差: {current_spread:.0f}")
print(f"止损价差: {stop_loss:.0f}")
print(f"目标价差: {mean_spread:.0f}(回归均值)")
这套流程我用了好几年,胜率大概在65%左右。别小看这个数字,套利交易追求的是高胜率、低回撤,而不是暴利。
实盘要点:
- 仓位管理:单笔风险不超过总资金的2%
- 止损纪律:价差突破2个标准差必须离场
- 移仓换月:注意主力合约切换,提前一周开始移仓
- 滑点控制:用限价单,别用市价单
最后说一句。豆菜粕套利看起来简单,但真正赚钱的人不多。为什么?因为大多数人管不住手,看到价差偏离就想进场,结果被假突破反复打脸。记住:等待也是一种交易。
避坑指南:我曾经连续三次在价差偏离1.2个标准差时进场,结果三次都被止损。后来我把阈值提高到1.5,胜率明显提升。阈值太低,容易被噪音干扰;阈值太高,又会错过行情。1.5是我试出来的平衡点。
公众号:蓝海数据掘金营,微信deep3321