3、协整关系检验:单位根检验(ADF)、Engle-Granger两步法、Johansen检验
做配对交易,最核心的一步是什么?
我个人觉得,就是确认两个品种之间有没有「长期稳定的关系」。说白了,就是它们会不会走散。
你想想看,如果两个股票的价格序列根本就是各走各的,那你还谈什么配对?谈什么回归?
所以,我们需要一个数学工具来验证这种关系——这就是协整检验。
3.1 什么是协整?先理解「伪回归」
我记得刚入行那会儿,犯过一个低级错误。我把两只完全不相关的股票价格拉进回归模型,结果R²高达0.85,看起来完美极了。但实盘一跑,亏得我怀疑人生。
为什么会这样?因为两个非平稳的时间序列,即使毫无关系,回归结果也可能「看起来」很显著。这就是经典的伪回归问题。
协整的概念,就是为了解决这个问题而生的。
简单说:如果两个序列各自是非平稳的(比如随机游走),但它们的某个线性组合是平稳的,那我们就说它们之间存在协整关系。
核心理解: 协整 ≈ 两个醉汉互相搀扶走路。单个看都东倒西歪,但合在一起却能走稳。
3.2 单位根检验(ADF)—— 先看单个序列稳不稳
做协整检验之前,我们得先确认一件事:每个序列本身是不是非平稳的。
如果两个序列本身就是平稳的,那直接做回归就行,根本不需要协整这套工具。
ADF检验,就是用来判断一个时间序列是否存在单位根(即非平稳)的。
原假设 H₀: 序列存在单位根(非平稳)
备择假设 H₁: 序列不存在单位根(平稳)
嗯,这里要注意:如果p值小于0.05,我们就拒绝原假设,认为序列是平稳的。
# Python 示例:ADF检验
from statsmodels.tsa.stattools import adfuller
# 假设 price_A 和 price_B 是两个价格序列
result_A = adfuller(price_A)
result_B = adfuller(price_B)
print(f"品种A的ADF统计量: {result_A[0]:.4f}, p值: {result_A[1]:.4f}")
print(f"品种B的ADF统计量: {result_B[0]:.4f}, p值: {result_B[1]:.4f}")
# 如果p值 > 0.05,说明序列非平稳,符合协整的前提条件
我的经验: 实际项目中,价格序列几乎都是非平稳的。但如果你遇到p值刚好在0.05附近,我建议你多取几个不同时间段的数据再测一次。有时候数据长度不同,结论会变。
3.3 Engle-Granger两步法 —— 最经典的协整检验
这个方法,说白了就是两步走:
- 第一步: 用OLS回归估计对冲比率(也就是β)
- 第二步: 对残差序列做ADF检验,看残差是否平稳
如果残差是平稳的,那就说明两个序列之间存在协整关系。
# Python 示例:Engle-Granger两步法
import statsmodels.api as sm
# 第一步:回归
X = sm.add_constant(price_B) # 自变量
model = sm.OLS(price_A, X).fit()
hedge_ratio = model.params[1] # 这就是对冲比率β
residuals = model.resid # 残差序列
# 第二步:对残差做ADF检验
residual_test = adfuller(residuals)
print(f"残差ADF p值: {residual_test[1]:.4f}")
if residual_test[1] < 0.05:
print("残差平稳 → 存在协整关系,可以配对交易")
else:
print("残差不平稳 → 不存在协整关系,放弃这对")
避坑指南: 我曾经在测试中发现,EG两步法有个明显的缺陷——它假设协整关系是唯一的。如果两个变量之间可能存在多个协整关系,EG法就抓瞎了。这时候,你需要用Johansen检验。
3.4 Johansen检验 —— 处理多个协整关系
Johansen检验比EG法更强大。它基于VAR模型,可以同时检验多个协整向量。
我个人习惯在以下场景使用Johansen:
- 配对交易涉及3个或更多品种时
- 不确定协整关系是否唯一时
- 需要更稳健的检验结果时
Johansen检验输出两个统计量:迹统计量(Trace) 和 最大特征值统计量(Max-Eigen)。
# Python 示例:Johansen检验
from statsmodels.tsa.vector_ar.vecm import coint_johansen
# 将两个价格序列合并成矩阵
data = pd.DataFrame({'A': price_A, 'B': price_B})
# 进行Johansen检验
# det_order = 0 表示不含确定性趋势,1表示含常数项
# k_ar_diff = 1 表示滞后阶数(通常用AIC/BIC选择)
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个协整向量」的原假设。比如r=0被拒绝,说明至少存在1个协整关系。r=1如果也被拒绝,说明至少存在2个。
3.5 三种方法怎么选?一张表说清楚
| 方法 | 适用场景 | 优点 | 缺点 |
|---|---|---|---|
| ADF检验 | 判断单个序列是否平稳 | 简单快速,是协整检验的前置步骤 | 只能测单个序列,不能直接判断配对关系 |
| Engle-Granger两步法 | 两个品种的配对交易 | 直观易懂,直接给出对冲比率 | 只能处理一个协整关系,小样本下偏差大 |
| Johansen检验 | 多品种、需要稳健结果 | 可检验多个协整关系,统计性质更优 | 计算复杂,对滞后阶数敏感 |
3.6 知识体系总览
下面这张图,是我自己整理的一个流程框架。每次做配对交易前,我都会按这个思路走一遍:
3.7 实战中的几点提醒
- 数据频率要一致: 我见过有人用日线数据和小时数据混在一起做检验,结果一塌糊涂。频率必须统一。
- 滞后阶数别乱选: Johansen检验对滞后阶数很敏感。我一般用AIC或BIC准则自动选择,而不是拍脑袋定。
- 滚动检验: 协整关系不是一成不变的。我建议每3-6个月重新检验一次,防止关系失效了你还在傻傻交易。
- 不要过度依赖p值: 0.049和0.051其实差别不大。我有时候会结合残差图肉眼判断,比单纯看p值更靠谱。
一句话总结: ADF看单序列是否非平稳,EG两步法给对冲比率,Johansen处理多协整。三者配合使用,才能把配对交易的底裤看清楚。
公众号:蓝海资料掘金营,微信deep3321