4. 协整关系:协整定义、Engle-Granger两步法、Johansen检验、协整对与协整篮子
各位同学,咱们今天聊协整。说实话,这是统计套利里最核心的概念之一。没有协整,你那些价差序列就是随机游走,做进去跟赌博没区别。
协整这个词,听起来挺唬人。我当年刚接触时也懵,心想这跟普通的相关性有啥区别?后来踩了坑才明白——相关性看的是价格走势像不像,协整看的是价差会不会回归。这是本质区别。
4.1 协整的定义
先给个严谨定义:如果两个或多个非平稳时间序列的线性组合是平稳的,那它们就存在协整关系。
用大白话说:单个序列像醉汉走路,东倒西歪;但它们的某种组合却像牵着绳子,走远了会被拉回来。
举个例子。我做过螺纹钢和热卷的套利。这两个品种单独看,价格都是非平稳的,但它们的价差(螺纹钢 - 热卷×系数)却经常围绕某个均值波动。这就是协整。
协整的三个关键点:
- 每个序列本身是非平稳的(通常I(1))
- 存在一个线性组合是平稳的(I(0))
- 这个组合的系数就是套利比例
你可能会问:为什么非要非平稳?两个平稳序列不行吗?嗯,平稳序列的价差天然就是平稳的,但那种套利空间太小,而且容易过拟合。真正赚钱的机会,往往藏在非平稳序列的协整关系里。
4.2 Engle-Granger两步法
这是最经典的协整检验方法,1987年提出的。我个人习惯叫它EG两步法,简单粗暴,适合新手入门。
第一步:估计协整系数
说白了,就是跑一个OLS回归。假设我们要检验A和B是否协整:
import statsmodels.api as sm
import numpy as np
# 假设price_a, price_b是价格序列
X = sm.add_constant(price_b)
model = sm.OLS(price_a, X).fit()
beta = model.params[1] # 协整系数
residuals = model.resid # 残差序列
这里的beta就是套利比例。比如beta=1.5,意味着1份A对应1.5份B。
第二步:检验残差的平稳性
对残差做ADF检验。如果残差是平稳的,就说明存在协整关系。
from statsmodels.tsa.stattools import adfuller
adf_result = adfuller(residuals, maxlag=1)
p_value = adf_result[1]
if p_value < 0.05:
print("残差平稳,存在协整关系")
else:
print("残差不平稳,没有协整关系")
避坑指南:我曾经犯过一个错误——直接用原始价格跑回归,没检查数据频率是否对齐。结果协整系数算出来是错的,回测看起来很美,实盘一跑就亏。记住:数据频率必须一致,时间戳要对齐。
EG两步法有个缺点:它只能检验一对变量。如果你有三个品种想一起做,就得用Johansen检验了。
4.3 Johansen检验
Johansen检验可以同时检验多个变量之间的协整关系。它基于VAR模型,能告诉你存在几个协整向量。
我刚开始用Johansen检验时,被输出结果搞晕过。其实核心就两个统计量:迹统计量和最大特征值统计量。
from statsmodels.tsa.vector_ar.vecm import coint_johansen
# 假设data是多个品种的价格矩阵,每列一个品种
result = coint_johansen(data, det_order=0, k_ar_diff=1)
# 迹统计量检验
trace_stat = result.lr1
trace_crit = result.cvt # 临界值
# 最大特征值检验
max_eigen_stat = result.lr2
max_eigen_crit = result.cvm
print("迹统计量:", trace_stat)
print("临界值(95%):", trace_crit[:, 1])
输出结果怎么看?从r=0开始,如果统计量大于临界值,就拒绝原假设,说明至少存在一个协整关系。然后看r=1,以此类推。
我的经验:实际做多品种套利时,Johansen检验的滞后阶数选择很关键。我一般用AIC或BIC准则选阶,但别选太大,否则参数太多容易过拟合。通常1-4阶就够了。
4.4 协整对与协整篮子
这两个概念,说白了就是套利对象的组织形式。
协整对:两个品种之间的协整关系。比如螺纹钢和热卷、豆粕和菜粕。这是最基础的套利形式,也是我最早做的。
协整篮子:三个及以上品种的协整组合。比如做多A、做空B和C,按一定比例组合。篮子里的品种可以来自不同板块,只要它们有共同的经济驱动因素。
我举个例子。有段时间我发现铜、铝、锌三个品种存在协整关系。单独看两两之间都不太稳,但三个一起做,价差篮子就非常平稳。这就是篮子的优势——分散风险,提高稳定性。
怎么构建篮子?用Johansen检验得到协整向量,那就是你的持仓比例。
# 获取协整向量(假设存在一个协整关系)
coint_vector = result.evec[:, 0] # 第一个协整向量
print("持仓比例:", coint_vector)
# 构建价差篮子
spread = data @ coint_vector # 矩阵乘法
重要提醒:协整关系不是一成不变的。我见过太多人找到一个协整对就死抱着不放。市场结构会变,协整关系也会失效。建议每3-6个月重新检验一次,或者用滚动窗口动态更新。
最后说一句:协整检验只是工具,不是圣杯。我见过有人用几十个品种跑Johansen,硬是找出一个协整篮子,结果实盘一塌糊涂。为什么?统计显著不等于经济显著。你选的品种之间必须有逻辑关系,比如产业链上下游、替代品、同板块等。否则就是数据挖掘,迟早要还的。