4. 多资产协整:如何从多个资产中寻找协整关系
协整这个概念,说白了就是回答一个问题:几个看起来随机游走的资产,它们之间有没有一条看不见的绳子牵着?
我在做多资产策略的时候,经常遇到这种情况:单看两只股票,价格走势都跟醉汉走路似的,但把它们放在一起,嘿,居然发现它们之间保持着某种长期平衡。这就是协整的核心思想。
4.1 从两资产到多资产:维度升级
两资产协整我们之前聊过,就是找一对资产,让它们的线性组合变成平稳序列。多资产协整呢?其实是一个道理,只是从「配对」变成了「组合」。
数学上,如果有一组时间序列 y₁, y₂, ..., yₖ,每个都是 I(1)(一阶单整),那么存在一个向量 β,使得:
β₁y₁ + β₂y₂ + ... + βₖyₖ ~ I(0)
这个 β 就是协整向量。说白了,就是给每个资产分配一个权重,让它们的加权和变成平稳的。
核心要点:多资产协整不是简单的两两配对,而是寻找一个线性组合,使得组合后的序列均值回归。
4.2 协整的检验方法
我个人习惯用两种方法:Johansen 检验和Engle-Granger 两步法。前者适合多资产,后者适合两资产。
4.2.1 Johansen 检验
Johansen 检验是我在项目中用得最多的方法。它基于向量自回归模型(VAR),通过特征值来判断协整关系的个数。
具体步骤是这样的:
- 先对每个资产做 ADF 检验,确认都是 I(1)
- 建立 VAR 模型,确定滞后阶数
- 计算迹统计量或最大特征值统计量
- 与临界值比较,判断协整秩
我曾经在做一个商品期货组合时,用 Johansen 检验发现了 3 个协整关系。当时我选了秩为 2 的模型,结果回测效果出奇的好。嗯,这里要注意:协整秩的选择不是越大越好,要根据经济意义和回测结果综合判断。
4.2.2 代码实现
下面我用 Python 的 statsmodels 库演示一下 Johansen 检验:
import numpy as np
import pandas as pd
from statsmodels.tsa.vector_ar.vecm import coint_johansen
# 假设我们有 4 个资产的价格数据
# data 是一个 DataFrame,列名为 asset1, asset2, asset3, asset4
def johansen_test(data, det_order=0, k_ar_diff=1):
"""
执行 Johansen 协整检验
参数:
- data: 价格数据,每列一个资产
- det_order: 确定性趋势项,0=无,1=常数,2=趋势
- k_ar_diff: 差分后的滞后阶数
返回:
- 迹统计量和临界值
"""
result = coint_johansen(data, det_order, k_ar_diff)
# 输出迹统计量
print("=== 迹统计量 ===")
for i in range(len(result.lr1)):
print(f"秩 r <= {i}: 统计量 = {result.lr1[i]:.4f}, "
f"95% 临界值 = {result.cvt[i, 1]:.4f}")
# 输出最大特征值统计量
print("\n=== 最大特征值统计量 ===")
for i in range(len(result.lr2)):
print(f"秩 r <= {i}: 统计量 = {result.lr2[i]:.4f}, "
f"95% 临界值 = {result.cvm[i, 1]:.4f}")
return result
# 示例数据(实际使用时替换为真实数据)
np.random.seed(42)
dates = pd.date_range('2020-01-01', periods=500, freq='D')
data = pd.DataFrame({
'asset1': np.cumsum(np.random.randn(500)) + 100,
'asset2': np.cumsum(np.random.randn(500)) + 50,
'asset3': np.cumsum(np.random.randn(500)) + 200,
'asset4': np.cumsum(np.random.randn(500)) + 150
}, index=dates)
# 执行检验
result = johansen_test(data, det_order=0, k_ar_diff=1)
小技巧:在实际项目中,我通常会把 det_order 设为 1(包含常数项),因为大多数金融时间序列都有漂移项。如果你不确定,可以跑三个模型对比一下。
4.3 协整向量的估计
找到协整关系后,下一步就是估计协整向量。Johansen 检验会直接给出特征向量,这些特征向量就是协整向量的估计。
我们来看一下如何提取协整向量:
# 提取协整向量(假设秩为 2)
def get_coint_vectors(result, rank=2):
"""
提取协整向量
参数:
- result: coint_johansen 的结果对象
- rank: 协整秩
返回:
- 协整向量矩阵,每列一个协整向量
"""
# 特征向量存储在 result.evec 中
# 前 rank 列就是协整向量
coint_vectors = result.evec[:, :rank]
# 通常我们会归一化,让第一个资产系数为 1
for i in range(rank):
coint_vectors[:, i] = coint_vectors[:, i] / coint_vectors[0, i]
return coint_vectors
coint_vecs = get_coint_vectors(result, rank=2)
print("协整向量(归一化后):")
print(coint_vecs)
注意:协整向量不是唯一的!任何线性变换后的向量仍然是协整向量。所以我们在实际应用中,通常会选择经济意义最合理的那个。我曾经见过有人直接拿第一个特征向量就用,结果回测一塌糊涂——因为那个向量对应的组合方差太大。
4.4 多资产协整组合的构建
有了协整向量,我们就可以构建交易组合了。核心思路是:用协整向量作为权重,构建一个均值回归的价差序列。
具体步骤:
- 选择协整秩
r,提取对应的r个协整向量 - 对每个协整向量,计算价差序列:
spread = Σ βᵢ * priceᵢ - 对价差序列做标准化,设置开仓阈值(比如 ±1.5 倍标准差)
- 当价差偏离阈值时开仓,回归均值时平仓
我建议你用一个协整向量就够了。为什么?因为多个协整向量之间可能存在相关性,组合起来反而增加复杂度。我在一个 6 资产组合中试过用 3 个协整向量,结果管理起来特别麻烦,最后还不如只用 1 个效果好。
4.5 实战中的坑与避坑指南
做多资产协整,有几个坑我踩过,分享给你:
- 伪回归风险:我曾经用 10 个完全不相关的随机游走序列做 Johansen 检验,结果居然发现了一个「显著」的协整关系。这就是典型的伪回归。所以一定要做稳健性检验,比如滚动窗口检验。
- 滞后阶数选择:滞后阶数太大会损失自由度,太小又无法捕捉动态关系。我一般用 AIC 或 BIC 准则,但最终会结合经济直觉判断。
- 协整关系的不稳定性:协整关系不是一成不变的。市场结构变了,协整关系可能就断了。我建议每 3-6 个月重新估计一次协整向量。
避坑指南:我曾经在 2020 年 3 月(疫情爆发期)用历史数据训练的协整模型做交易,结果价差直接崩了。后来我加入了滚动窗口和结构断点检测,才解决了这个问题。记住:协整是样本内的统计关系,不代表未来一定成立。
4.6 知识体系图
下面我用一张 SVG 图来总结本章的核心逻辑:
4.7 小结
多资产协整的核心,就是找到一组权重,让组合后的序列变得平稳。Johansen 检验是主力工具,但要注意:统计显著不等于经济有效。我建议你在实际应用中,多结合基本面分析,别只看统计结果。
嗯,这一章就到这里。记住:协整是工具,不是目的。我们的目的是找到稳定可交易的价差关系。
公众号:蓝海资料掘金营,微信deep3321