4. 协整检验实战:Engle-Granger两步法、Johansen检验在Python中的实现

协整检验,说白了就是帮我们判断两个或多个时间序列之间,是否存在一种长期稳定的线性关系。在波动率套利里,我们经常需要找到那些价格走势虽然短期会偏离,但长期一定会回归的品种对。嗯,这就是协整的核心价值。

我个人习惯把协整关系想象成「拴在一根绳子上的两只狗」。一只狗跑快了,绳子会把它拉回来;另一只跑慢了,绳子也会拽它一把。它们可能各自乱窜,但距离始终被绳子约束着。这个「绳子」,就是协整向量。

核心要点: 协整 ≠ 相关。两个完全不相关的序列也可能协整,两个高度相关的序列也可能不协整。关键看残差是否平稳。

4.1 Engle-Granger两步法:简单粗暴,但够用

这个方法我用了很多年,尤其是在做股票配对交易的时候。它的思路非常直观:先回归,再检验残差。

第一步:建立回归模型

假设我们有两支股票的价格序列 X 和 Y。我们先跑一个OLS回归:

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

# 模拟数据:假设 X 和 Y 是协整的
np.random.seed(42)
n = 500
X = np.cumsum(np.random.randn(n)) + 100
Y = X * 0.8 + np.random.randn(n) * 0.5 + 50

# 第一步:OLS回归
X_const = sm.add_constant(X)
model = sm.OLS(Y, X_const).fit()
residuals = model.resid

print(f"回归系数:beta = {model.params[1]:.4f}")
print(f"截距项:alpha = {model.params[0]:.4f}")

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

这里用的是ADF检验。如果残差是平稳的,就说明 X 和 Y 存在协整关系。

# 第二步: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("残差平稳 → X和Y存在协整关系")
else:
    print("残差不平稳 → 没有协整关系")
我的经验: 我曾经在测试一组能源ETF时,发现ADF的p值刚好在0.05附近徘徊。这时候别急着下结论。我建议你多换几个滞后阶数试试,或者用BIC准则重新选参。有时候数据频率也会影响结果——日线数据协整,换成小时线可能就不协整了。

4.2 Johansen检验:多品种协整的利器

Engle-Granger只能处理两个变量,而且它假设协整关系是唯一的。但现实中,我们经常要同时分析三四个品种。比如在波动率套利里,我可能同时看 VIX、VX期货、以及某个ETF的隐含波动率。这时候Johansen检验就派上用场了。

Johansen检验的核心思想,是通过向量自回归模型(VAR)来检验多个时间序列之间是否存在协整关系。它给出两个统计量:迹统计量和最大特征值统计量。

from statsmodels.tsa.vector_ar.vecm import coint_johansen

# 模拟三个协整序列
np.random.seed(123)
T = 500
z = np.cumsum(np.random.randn(T))
X1 = z + np.random.randn(T) * 0.3
X2 = z * 0.5 + np.random.randn(T) * 0.2 + 10
X3 = z * 1.2 + np.random.randn(T) * 0.4 - 5

data = np.column_stack([X1, X2, X3])

# Johansen检验
johansen_result = coint_johansen(data, det_order=0, k_ar_diff=1)

print("迹统计量:", johansen_result.lr1)
print("迹统计量临界值(95%):", johansen_result.cvt[:, 1])
print("最大特征值统计量:", johansen_result.lr2)
print("最大特征值临界值(95%):", johansen_result.cvm[:, 1])

# 判断协整秩
r = 0
for i in range(len(johansen_result.lr1)):
    if johansen_result.lr1[i] > johansen_result.cvt[i, 1]:
        r += 1
print(f"协整秩(协整关系个数):{r}")
避坑指南: 我曾经在分析一组利率互换数据时,Johansen检验给出了3个协整关系,但回测时发现策略根本不赚钱。后来才发现,我忘了处理数据中的结构性断点。2008年金融危机前后的数据,协整结构完全变了。所以,做检验前一定要先看看数据的时间段是否稳定。

4.3 两种方法的对比与选择

对比维度 Engle-Granger两步法 Johansen检验
变量数量 仅限两个变量 两个及以上变量
协整关系个数 最多一个 可检测多个
实现难度 简单,两步搞定 稍复杂,需理解VAR
小样本表现 偏差较大 相对稳健
适用场景 快速筛选配对 多品种组合分析

我个人建议:如果你只是做简单的配对交易,Engle-Granger完全够用。但如果你要构建一个多品种的波动率套利组合,比如同时交易3个ETF和2个期货,那Johansen是更好的选择。

4.4 实战中的注意事项

嗯,这里我要多说几句。理论归理论,实战中坑不少。

  • 数据频率要一致: 别把日线数据和小时线数据混在一起检验。我见过有人把5分钟数据和日线数据放一起跑Johansen,结果出来一堆伪协整。
  • 滞后阶数的选择: AIC和BIC准则都可以用,但BIC更保守一些。我一般先用AIC选一个范围,再手动调一调。
  • 协整关系会变: 去年协整的品种,今年可能就不协整了。我建议每3个月重新跑一次检验。
  • 不要过度拟合: 如果你在1000个品种里找协整对,大概率能找到几个「伪协整」的。多重比较校正很重要。
一个小技巧: 我习惯在跑完协整检验后,再做一个滚动窗口的残差平稳性检验。如果残差在大部分时间窗口内都是平稳的,那这个协整关系才真正可靠。

4.5 知识体系总览

下面这张图,是我自己整理的一个协整检验实战流程。你可以把它当作一个检查清单。

协整检验实战流程 第一步:数据准备 价格序列 → 对数收益率 第二步:平稳性检验 ADF / KPSS 检验 选择方法 2变量 or 多变量? Engle-Granger两步法 OLS回归 → 残差ADF检验 适用于2个变量 Johansen检验 VAR模型 → 迹/最大特征值 适用于2个及以上变量 第四步:结果验证 滚动窗口检验 + 回测验证 第五步:构建套利策略

你看,整个流程其实不复杂。关键是要理解每一步在做什么,以及为什么这么做。我刚开始做协整检验的时候,也踩过不少坑。但只要你按照这个流程走一遍,再结合自己的经验调整,很快就能上手。

最后提醒一句:协整检验只是工具,不是圣杯。它帮你找到统计上的关系,但最终能不能赚钱,还得看市场买不买账。回测、实盘、风控,一个都不能少。

专注资料整理