第4章 Johansen协整检验:多变量协整检验的原理、迹检验与最大特征值检验
好,咱们继续深入。上一章我们聊了EG两步法,那个方法简单粗暴,适合两个品种。但现实中的套利组合,往往不止两个品种。比如我做过一个跨品种策略,同时涉及螺纹钢、热卷和铁矿石,三个品种之间互相影响。这时候再用EG两步法,就有点力不从心了。
为什么?因为EG两步法一次只能检验一对关系。三个品种,两两检验?那结果可能互相矛盾。而且它默认一个变量是因,另一个是果,这在多变量场景下太武断了。
所以,我们需要一个更强大的工具——Johansen协整检验。它一次性就能搞定多个变量,告诉你到底存在几组协整关系。
4.1 为什么需要Johansen检验?
说白了,EG两步法有个硬伤:它假设协整关系只有一个。但多个时间序列之间,可能存在多个稳定的线性组合。你想想看,三个品种,可能有两组不同的套利关系在同时起作用。
我举个例子。假设我们有三个指数:沪深300、中证500和上证50。它们之间可能存在一个长期均衡关系,但也可能存在另一个独立的均衡关系。EG两步法只能找到其中一个,另一个就被忽略了。
Johansen检验的核心思想,就是基于向量自回归模型(VAR),通过检验矩阵的秩,来确定协整关系的个数。嗯,这里要注意,它不预设谁是因谁是果,而是把所有变量都当作内生变量来处理。
4.2 Johansen检验的数学原理
咱们不搞太复杂的数学推导,但核心逻辑得讲清楚。
假设我们有k个时间序列,构成一个k维向量Y_t。我们建立一个VAR(p)模型:
Y_t = Π₁Y_{t-1} + Π₂Y_{t-2} + ... + Π_pY_{t-p} + ε_t
然后,我们把它改写成误差修正形式:
ΔY_t = ΠY_{t-1} + Γ₁ΔY_{t-1} + ... + Γ_{p-1}ΔY_{t-p+1} + ε_t
这里的Π矩阵是关键。它的秩(rank)决定了协整关系的个数。
- 如果秩为0,说明没有协整关系
- 如果秩为r(0 < r < k),说明存在r个协整关系
- 如果秩为k,说明所有序列都是平稳的,不需要协整
Johansen检验就是通过两种统计量来推断这个秩r:迹检验和最大特征值检验。
4.3 迹检验(Trace Test)
迹检验的原假设是:协整关系的个数 ≤ r。备择假设是:协整关系的个数 > r。
它的统计量长这样:
λ_trace(r) = -T * Σ ln(1 - λ_i) (i = r+1 到 k)
其中λ_i是Π矩阵的特征值,按从大到小排列。T是样本量。
我个人习惯先做迹检验。因为它能一次性告诉你,最多可能存在多少组协整关系。比如检验结果说r=2,那就意味着至少存在两组稳定的线性组合。
我在项目中遇到过这样的情况:迹检验显示r=2,但最大特征值检验显示r=1。这时候怎么办?嗯,我一般会结合经济意义来判断。如果两个组合都有明确的套利逻辑,我就接受r=2;如果其中一个组合看起来像是统计上的巧合,我就保守一点选r=1。
4.4 最大特征值检验(Max-Eigenvalue Test)
最大特征值检验的原假设是:协整关系的个数 = r。备择假设是:协整关系的个数 = r+1。
它的统计量是:
λ_max(r, r+1) = -T * ln(1 - λ_{r+1})
说白了,它是一次只检验一个特征值是否显著。从最大的特征值开始,一个一个往下检验。
这两种检验方法,就像两个不同的侦探。迹检验是广撒网,看看整体有多少线索;最大特征值检验是逐个击破,确认每个线索是否真的成立。
重要提示:两种检验的结果可能不一致。这时候,我建议你参考以下原则:
- 如果样本量较大(>500),优先相信迹检验
- 如果样本量较小,最大特征值检验更稳健
- 如果两者冲突,取较小的r值,避免过度拟合
4.5 Python实战:用statsmodels做Johansen检验
理论讲完了,咱们直接上代码。我用的是Python的statsmodels库,它内置了Johansen检验函数。
import numpy as np
import pandas as pd
import statsmodels.tsa.vector_ar.vecm as vecm
import yfinance as yf
# 下载三个ETF数据
tickers = ['SPY', 'QQQ', 'IWM'] # 标普500、纳斯达克、罗素2000
data = yf.download(tickers, start='2020-01-01', end='2023-12-31')['Adj Close']
# 取对数价格
log_prices = np.log(data)
# 进行Johansen检验
# det_order = 0 表示没有确定性趋势,1表示有常数项,2表示有常数项和趋势项
# k_ar_diff 是差分项的滞后阶数
johansen_result = vecm.coint_johansen(log_prices, det_order=1, k_ar_diff=1)
# 输出结果
print("特征值:", johansen_result.eig)
print("迹统计量:", johansen_result.lr1)
print("迹统计量临界值(90%, 95%, 99%):", johansen_result.cvt)
print("最大特征值统计量:", johansen_result.lr2)
print("最大特征值统计量临界值(90%, 95%, 99%):", johansen_result.cvm)
运行这段代码,你会得到类似这样的输出:
特征值: [0.0234, 0.0089, 0.0012]
迹统计量: [45.67, 12.34, 1.56]
迹统计量临界值(90%, 95%, 99%): [[27.07, 29.80, 35.46],
[13.43, 15.49, 19.94],
[2.71, 3.84, 6.63]]
最大特征值统计量: [33.33, 10.78, 1.56]
最大特征值统计量临界值(90%, 95%, 99%): [[18.89, 21.13, 25.86],
[12.30, 14.26, 18.52],
[2.71, 3.84, 6.63]]
怎么解读?
- 迹检验:第一个统计量45.67 > 35.46(99%临界值),拒绝r=0。第二个统计量12.34 < 13.43(90%临界值),不能拒绝r≤1。所以迹检验认为存在1个协整关系。
- 最大特征值检验:第一个统计量33.33 > 25.86(99%临界值),拒绝r=0。第二个统计量10.78 < 12.30(90%临界值),不能拒绝r=1。所以最大特征值检验也认为存在1个协整关系。
两者一致,结论很明确:这三个ETF之间存在1个协整关系。
避坑指南:我曾经在实盘回测时,发现Johansen检验的结果非常不稳定。后来排查发现,是滞后阶数选错了。建议用AIC或BIC准则来选择最优滞后阶数,不要随便设一个值。
4.6 如何选择滞后阶数?
滞后阶数p的选择,直接影响检验结果。选小了,残差可能有自相关;选大了,自由度损失太多。
我一般用statsmodels的select_order函数:
from statsmodels.tsa.vector_ar.var_model import VAR
model = VAR(log_prices)
lag_order = model.select_order(maxlags=10)
print(lag_order.summary())
它会输出AIC、BIC、HQIC等准则的建议值。我个人习惯用BIC,因为它对复杂模型惩罚更重,不容易过拟合。
4.7 知识体系总览
为了让你更直观地理解本章的知识结构,我画了一张图:
4.8 实战中的注意事项
最后,分享几个我在实战中踩过的坑:
- 数据频率要一致:别把日线数据和周线数据混在一起检验,结果会乱套。
- 样本量要足够:Johansen检验对样本量敏感,至少要有100个以上的观测值。
- 确定性趋势要选对:
det_order参数很关键。如果数据有明显趋势,选2;如果没有趋势,选0或1。我一般先画个图看看趋势。 - 不要过度解读:统计上显著,不代表经济上有意义。我曾经发现一个协整关系,但实际套利时滑点就把利润吃掉了。
警告:Johansen检验对异常值非常敏感。如果数据中有明显的跳空或极端值,建议先做预处理。我习惯用3倍标准差法剔除异常点,或者用中位数平滑。
好了,这一章的内容就到这里。Johansen检验是跨品种套利的核心工具,掌握了它,你就能处理更复杂的多品种组合。下一章我们会讲如何利用协整关系构建实际的套利策略,包括如何确定权重、设置阈值、管理风险等。
公众号:蓝海资料掘金营,微信deep3321