第四章:协整性检验实战

协整性检验,说白了就是判断两个资产之间有没有「长期绑定」的关系。

我刚开始做配对交易时,总觉得只要两个股票走势长得像就能配对。结果呢?回测漂亮,实盘亏钱。后来才明白——没有协整关系的配对,就是空中楼阁

4.1 协整性的核心思想

先问个问题:两个非平稳的时间序列,它们的线性组合能不能变成平稳的?

如果能,那它们就是协整的。举个例子:

  • 茅台和五粮液,各自价格都是随机游走(非平稳)
  • 但它们的价差(比如 1 股茅台 - 5 股五粮液)可能是平稳的
  • 这个价差就是配对交易的「核心信号」

我个人习惯把协整关系想象成「拴在一根绳子上的两只狗」。每只狗自己乱跑(非平稳),但绳子长度基本固定(价差平稳)。

重要提醒:相关性 ≠ 协整性。两个股票可以高度相关,但完全没有协整关系。我见过太多人把相关系数 0.9 的股票拿来配对,结果亏得怀疑人生。

4.2 平稳性检验基础

在讲协整检验之前,得先搞定平稳性检验。这是所有检验的「地基」。

4.2.1 ADF 检验(Augmented Dickey-Fuller)

ADF 是最常用的单位根检验。它的原假设是「序列存在单位根(非平稳)」。

嗯,这里要注意:p 值小于 0.05 才能拒绝原假设,也就是序列平稳。

from statsmodels.tsa.stattools import adfuller

# 模拟一个随机游走序列
import numpy as np
np.random.seed(42)
random_walk = np.cumsum(np.random.randn(100))

# ADF 检验
result = adfuller(random_walk)
print(f'ADF 统计量: {result[0]:.4f}')
print(f'p 值: {result[1]:.4f}')
print(f'临界值 (1%): {result[4]["1%"]:.4f}')

# 输出示例:
# ADF 统计量: -1.2345
# p 值: 0.6543
# 临界值 (1%): -3.4987
# 结论:p 值 > 0.05,不能拒绝原假设,序列非平稳

我在项目中遇到过一个问题:ADF 检验对滞后阶数很敏感。默认的 AIC 自动选择有时候会选错。我的建议是——手动尝试 3-5 个不同的滞后阶数,看看结论是否一致。

4.2.2 PP 检验(Phillips-Perron)

PP 检验和 ADF 检验的目标一样,都是检验单位根。但 PP 检验对异方差更鲁棒。

from statsmodels.tsa.stattools import pp_test

result_pp = pp_test(random_walk)
print(f'PP 统计量: {result_pp[0]:.4f}')
print(f'p 值: {result_pp[1]:.4f}')

说实话,我平时更常用 ADF。但遇到金融数据(尤其是高频数据)有明显波动率聚集时,PP 检验会更可靠。

4.2.3 KPSS 检验

KPSS 检验和 ADF/PP 正好相反——它的原假设是序列平稳

from statsmodels.tsa.stattools import kpss

result_kpss = kpss(random_walk)
print(f'KPSS 统计量: {result_kpss[0]:.4f}')
print(f'p 值: {result_kpss[1]:.4f}')
我的小技巧:同时使用 ADF 和 KPSS 做「双重检验」。如果 ADF 说非平稳、KPSS 说平稳,那基本就是平稳。如果两个都说非平稳,那铁定非平稳。这种交叉验证能避免很多误判。

4.3 Engle-Granger 两步法

这是最经典的协整检验方法。说白了就是两步走:

  1. 第一步:用 OLS 回归估计协整关系
  2. 第二步:对残差做 ADF 检验
import statsmodels.api as sm

# 模拟两个协整序列
np.random.seed(123)
x = np.cumsum(np.random.randn(200))
y = 0.8 * x + np.random.randn(200) * 0.5  # 协整关系

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

# 第二步:对残差做 ADF 检验
result_eg = adfuller(residuals, maxlag=1)
print(f'残差 ADF p 值: {result_eg[1]:.4f}')

if result_eg[1] < 0.05:
    print('✅ 两个序列存在协整关系')
else:
    print('❌ 没有协整关系')
避坑指南:我曾经在 Engle-Granger 检验上栽过跟头——残差的 ADF 检验临界值不能用标准表!因为残差是估计出来的,自由度损失了。正确的做法是用 MacKinnon 临界值表。statsmodels 的 adfuller 默认已经处理了这个问题,但如果你自己手写代码,一定要小心。

4.4 Johansen 检验

Engle-Granger 只能检验一对资产。那如果你有 3 个、5 个资产呢?

这时候就得用 Johansen 检验。它可以同时检验多个时间序列之间是否存在协整关系,还能告诉你存在几个协整向量。

from statsmodels.tsa.vector_ar.vecm import coint_johansen

# 模拟三个协整序列
z = 0.5 * x + 0.3 * y + np.random.randn(200) * 0.3
data = np.column_stack([x, y, z])

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

print('特征值:', result_joh.eig)
print('\n迹统计量:', result_joh.lr1)
print('95% 临界值:', result_joh.cvt[:, 1])
print('\n最大特征值统计量:', result_joh.lr2)
print('95% 临界值:', result_joh.cvm[:, 1])

# 判断协整秩
trace_stat = result_joh.lr1
crit_val = result_joh.cvt[:, 1]
r = sum(trace_stat > crit_val)
print(f'\n协整秩(协整向量数量): {r}')

Johansen 检验有两个统计量:迹统计量和最大特征值统计量。我个人习惯两个都看,如果结论一致就放心了。如果不一致...嗯,那就得结合业务逻辑判断了。

4.5 四种检验方法对比

检验方法 适用场景 原假设 优点 缺点
ADF 单个序列平稳性 存在单位根 最常用,简单直观 对滞后阶数敏感
PP 单个序列平稳性 存在单位根 对异方差鲁棒 小样本下表现一般
KPSS 单个序列平稳性 序列平稳 与 ADF 互补使用 对趋势设定敏感
Engle-Granger 两个序列协整 无协整关系 简单易实现 只能检验一对
Johansen 多个序列协整 协整秩 ≤ r 可检验多个序列 计算复杂,参数多

4.6 实战流程总结

你想想看,在实际项目中,我一般按这个流程走:

  1. 第一步:对每个资产单独做 ADF + KPSS 双重检验,确认都是 I(1) 序列
  2. 第二步:如果是两个资产,用 Engle-Granger 两步法
  3. 第三步:如果是多个资产,用 Johansen 检验确定协整秩
  4. 第四步:交叉验证——用不同的滞后阶数、不同的样本期再跑一遍
核心原则:协整检验不是「一次定终身」。市场结构会变,协整关系也会变。我建议每 3-6 个月重新检验一次,或者在重大市场事件后重新跑一遍。

好了,协整性检验这块就讲到这里。记住一句话:没有协整,就没有配对交易。这是所有策略的基石,值得你花时间彻底搞懂。

协整性检验知识体系 协整性检验 平稳性检验(基础) ADF 检验 PP 检验 KPSS 检验 协整检验方法 Engle-Granger (两资产) Johansen 检验 (多资产) 核心原则:先单序列平稳性检验,再双序列/多序列协整检验

蓝海数据掘金营,专注资料整理