6. 协整关系检验:Engle-Granger两步法、Johansen检验,判断期货与现货的长期均衡

好,咱们接着聊。前面我们找到了价差,也做了平稳性检验。但有个问题——如果两个价格序列本身是非平稳的,但它们的线性组合却是平稳的,这该怎么处理?

这就是协整关系要解决的问题。说白了,协整就是两个“疯跑”的序列,它们之间有一条看不见的绳子牵着。期货和现货,就是典型的例子。

6.1 为什么需要协整检验?

我个人习惯,在开始任何配对交易之前,先问自己一个问题:这两个品种真的会“回归”吗?

如果只是单纯看相关性,很容易掉坑里。我记得有一次,我拿螺纹钢期货和热卷期货做分析,相关系数高达0.95。但一跑协整检验,发现根本不协整。后来行情一波动,价差越拉越大,亏得我直拍大腿。

所以,协整检验的核心目的就一个:验证期货和现货之间是否存在长期均衡关系。如果存在,价差就会围绕均值来回摆动,我们就能在偏离时入场,回归时离场。

核心观点: 相关性高 ≠ 协整。协整才是套利交易的真正基石。

6.2 Engle-Granger两步法

这个方法很直观,我最早做量化时用的就是它。说白了,就两步。

第一步:建立回归方程

把现货价格(S)对期货价格(F)做回归:

S_t = α + β * F_t + ε_t

这里的β就是套保比率,ε_t就是残差,也就是我们常说的“价差”。

嗯,这里要注意:回归时一定要用普通最小二乘法(OLS)。我见过有人用带趋势项的回归,结果把协整关系都搞没了。

第二步:检验残差的平稳性

对残差序列ε_t做ADF检验。如果残差是平稳的,就说明S和F存在协整关系。

from statsmodels.tsa.stattools import adfuller
import numpy as np
import statsmodels.api as sm

# 假设我们有期货价格futures和现货价格spot
# 第一步:OLS回归
futures_with_const = sm.add_constant(futures)
model = sm.OLS(spot, futures_with_const).fit()
residuals = model.resid

# 第二步:对残差做ADF检验
adf_result = adfuller(residuals, autolag='AIC')
print(f'ADF统计量: {adf_result[0]:.4f}')
print(f'p值: {adf_result[1]:.4f}')

if adf_result[1] < 0.05:
    print('残差平稳,存在协整关系')
else:
    print('残差不平稳,不存在协整关系')

避坑指南: 我曾经犯过一个低级错误——直接用原始价格做回归,没检查数据对齐。结果期货和现货的交易日不一致,残差里全是噪声。记住,数据对齐是第一步

6.3 Johansen检验

Engle-Granger两步法虽然简单,但有个硬伤——它只能处理一对变量。如果你手里有多个品种,比如同时看螺纹钢、热卷、铁矿石,那就得用Johansen检验了。

Johansen检验的核心思想,是通过向量自回归(VAR)模型,检验多个时间序列之间是否存在共同的随机趋势。说白了,就是看它们是不是被同一根绳子牵着。

6.3.1 检验原理

Johansen检验有两种统计量:

  • 迹统计量(Trace Statistic):检验协整秩(即协整关系的个数)是否小于等于r
  • 最大特征值统计量(Max Eigenvalue Statistic):检验协整秩是否等于r

我个人习惯,两个统计量都看。如果它们结论一致,那基本就稳了。如果矛盾,我会倾向于相信迹统计量,因为它对样本量更鲁棒。

6.3.2 Python实现

from statsmodels.tsa.vector_ar.vecm import coint_johansen

# 假设我们有多个价格序列,存在data DataFrame中
# data的每一列是一个品种的价格
def johansen_test(data, det_order=0, k_ar_diff=1):
    """
    det_order: 确定性趋势的阶数
        -1: 无确定性趋势
        0: 常数项
        1: 线性趋势
    k_ar_diff: VAR模型的滞后阶数(差分形式)
    """
    result = coint_johansen(data, det_order, k_ar_diff)
    
    print('迹统计量:')
    print(result.lr1)
    print('迹统计量临界值(90%, 95%, 99%):')
    print(result.cvt)
    
    print('\n最大特征值统计量:')
    print(result.lr2)
    print('最大特征值临界值(90%, 95%, 99%):')
    print(result.cvm)
    
    # 判断协整秩
    r = 0
    for i in range(len(result.lr1)):
        if result.lr1[i] > result.cvt[i, 1]:  # 使用95%置信水平
            r += 1
        else:
            break
    
    print(f'\n协整秩(协整关系个数): {r}')
    return r

# 使用示例
# johansen_test(data[['futures', 'spot', 'another_asset']])

重要提醒: Johansen检验对滞后阶数非常敏感。我建议用AIC或BIC准则来选择最优滞后阶数,别拍脑袋定。另外,样本量最好在100以上,否则检验功效会大打折扣。

6.4 两种方法的对比与选择

对比维度 Engle-Granger两步法 Johansen检验
适用场景 两个品种(如期货vs现货) 多个品种(如期货、现货、相关商品)
计算复杂度 低,两步搞定 高,需要估计VAR模型
对样本量的要求 较低,30个样本以上即可 较高,建议100个样本以上
能否处理多个协整关系 不能 能,给出协整秩
对变量顺序的敏感性 敏感(谁做因变量有影响) 不敏感

你想想看,如果你只是做最简单的期现套利,Engle-Granger完全够用。但如果你要做多品种的统计套利,比如同时交易螺纹钢、热卷、铁矿石,那Johansen就是必须的了。

6.5 实战中的注意事项

嗯,这里有几个坑,我替你们踩过了:

  1. 数据频率要一致:别拿日线期货和分钟线现货做协整,那是在自欺欺人。
  2. 注意结构性突变:2015年股灾、2020年疫情,这些事件会打破原有的协整关系。我建议用滚动窗口重新检验。
  3. 协整不等于因果关系:两个品种协整,不代表一个能预测另一个。它只说明它们有共同的长期趋势。
  4. 别忘了经济含义:如果两个品种理论上就不该有关系,那协整检验结果再漂亮也别信。我见过有人把比特币和猪肉价格做协整,结果还通过了检验——纯属数据挖掘。

我的经验: 在实际项目中,我会先用Engle-Granger快速筛选候选对,再用Johansen做精细化分析。这样既快又准。

6.6 本章知识体系

下面这张图,是我自己总结的协整检验全流程。每次做新策略前,我都会过一遍:

协整关系检验全流程 数据准备 价格序列对齐、频率统一 平稳性检验 ADF检验、PP检验 选择方法 看变量个数 两个变量 多个变量 Engle-Granger两步法 ① OLS回归求残差 ② 残差ADF检验 Johansen检验 ① 建立VAR模型 ② 迹/最大特征值检验 结果判断 是否存在协整关系? ✅ 存在 → 构建套利策略 ❌ 不存在 → 放弃该组合

这张图把整个流程串起来了。从数据准备开始,到平稳性检验,再到方法选择,最后到结果判断。每一步都不能跳过。

好了,协整检验就讲到这里。记住一句话:没有协整,就没有套利。这是整个基差交易模型的根基。