第四章:协整性检验实战
协整性检验,说白了就是判断两个资产之间有没有「长期绑定」的关系。
我刚开始做配对交易时,总觉得只要两个股票走势长得像就能配对。结果呢?回测漂亮,实盘亏钱。后来才明白——没有协整关系的配对,就是空中楼阁。
4.1 协整性的核心思想
先问个问题:两个非平稳的时间序列,它们的线性组合能不能变成平稳的?
如果能,那它们就是协整的。举个例子:
- 茅台和五粮液,各自价格都是随机游走(非平稳)
- 但它们的价差(比如 1 股茅台 - 5 股五粮液)可能是平稳的
- 这个价差就是配对交易的「核心信号」
我个人习惯把协整关系想象成「拴在一根绳子上的两只狗」。每只狗自己乱跑(非平稳),但绳子长度基本固定(价差平稳)。
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}')
4.3 Engle-Granger 两步法
这是最经典的协整检验方法。说白了就是两步走:
- 第一步:用 OLS 回归估计协整关系
- 第二步:对残差做 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('❌ 没有协整关系')
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 实战流程总结
你想想看,在实际项目中,我一般按这个流程走:
- 第一步:对每个资产单独做 ADF + KPSS 双重检验,确认都是 I(1) 序列
- 第二步:如果是两个资产,用 Engle-Granger 两步法
- 第三步:如果是多个资产,用 Johansen 检验确定协整秩
- 第四步:交叉验证——用不同的滞后阶数、不同的样本期再跑一遍
好了,协整性检验这块就讲到这里。记住一句话:没有协整,就没有配对交易。这是所有策略的基石,值得你花时间彻底搞懂。