8. 协整关系与配对交易:协整检验(Engle-Granger方法)、误差修正模型(ECM)、配对交易在基差中的应用
各位同学,欢迎来到第八章。前面我们聊了基差的基本形态和统计特征,今天要讲的内容,才是真正把基差从「观察指标」变成「交易信号」的核心武器——协整关系与配对交易。
说实话,我刚入行那会儿,看到两个价格序列一起涨一起跌,第一反应就是「它们相关」。后来被市场狠狠教育了几次才发现,相关性是骗人的鬼,协整才是真朋友。为什么?因为相关性只能告诉你「它们过去一起动」,协整却能告诉你「它们未来会回到一起」。
8.1 协整关系的本质:不是相关,是「长期绑定」
先问一个问题:两个完全不相关的随机游走序列,它们的相关系数也可能很高。你信不信?我做过实验,用Python生成两个独立的随机游走,相关系数经常跑到0.8以上。这就是所谓的「伪回归」。
协整要解决的就是这个问题。它不关心两个序列是否同向运动,它关心的是:这两个序列的线性组合,是不是一个平稳序列。
举个具体的例子。螺纹钢和热卷,一个是建筑用钢,一个是工业用钢。它们的价格走势长期看是绑定的,因为原材料成本、冶炼工艺都差不多。但短期可能因为需求节奏不同而偏离。这种「偏离后必回归」的特性,就是协整。
核心定义:如果两个非平稳序列 X_t 和 Y_t,存在一个线性组合 Z_t = Y_t - βX_t 是平稳的,那么 X_t 和 Y_t 就是协整的。β 被称为协整系数。
这里有个关键点:两个序列必须都是同阶单整的。说白了,就是它们的不平稳程度要一样。一般都是 I(1) 序列——一阶差分后平稳。
8.2 Engle-Granger两步法:最经典的协整检验
Engle-Granger方法,简称EG两步法。名字听着高大上,其实逻辑很朴素。我在项目中用过不下上百次,步骤就两步:
- 第一步:估计长期均衡关系。用OLS回归 Y_t = α + βX_t + ε_t,得到残差序列 e_t。
- 第二步:检验残差的平稳性。对 e_t 做ADF检验。如果残差是平稳的,就说明存在协整关系。
代码实现其实很简单:
import statsmodels.api as sm
import pandas as pd
import numpy as np
from statsmodels.tsa.stattools import adfuller
# 假设 df 包含两列:'price_a' 和 'price_b'
def engle_granger_test(df, y_col='price_a', x_col='price_b'):
# 第一步:OLS回归
X = sm.add_constant(df[x_col])
model = sm.OLS(df[y_col], X).fit()
residuals = model.resid
# 第二步:ADF检验残差
adf_result = adfuller(residuals, autolag='AIC')
p_value = adf_result[1]
# 输出协整系数
beta = model.params[x_col]
alpha = model.params['const']
return {
'alpha': alpha,
'beta': beta,
'adf_stat': adf_result[0],
'p_value': p_value,
'is_cointegrated': p_value < 0.05
}
# 使用示例
result = engle_granger_test(df)
print(f"协整系数 β: {result['beta']:.4f}")
print(f"ADF统计量: {result['adf_stat']:.4f}")
print(f"p值: {result['p_value']:.4f}")
print(f"是否协整: {result['is_cointegrated']}")
个人经验:EG两步法有个小坑——第一步的OLS回归假设残差是独立同分布的,但实际中残差往往存在自相关。我建议在第二步之前,先看看残差的ACF图。如果自相关严重,可以考虑用动态OLS(DOLS)替代普通OLS。
另外,EG两步法只能检验一对一的协整关系。如果你手里有三四个品种想一起看,那就得用Johansen检验了。不过对于基差交易,两个品种的配对已经够用。
8.3 误差修正模型(ECM):把偏离拉回来
协整检验告诉你「它们会回归」,但没告诉你「怎么回归」。误差修正模型(ECM)就是干这个的。
ECM的核心思想是:短期偏离会被长期均衡关系「拉回来」。拉回来的速度有多快?这就是误差修正项前面的系数——调整速度。
模型形式长这样:
ΔY_t = γ * (Y_{t-1} - α - βX_{t-1}) + δ * ΔX_t + ε_t
其中 (Y_{t-1} - α - βX_{t-1}) 就是上一期的偏离程度,也就是误差修正项。γ 是调整系数,通常为负值——偏离越大,往回拉的力度越大。
用Python实现:
from statsmodels.tsa.api import VAR
import statsmodels.api as sm
def estimate_ecm(df, y_col='price_a', x_col='price_b', lags=1):
# 先做EG检验得到残差
eg_result = engle_granger_test(df, y_col, x_col)
residuals = df[y_col] - eg_result['alpha'] - eg_result['beta'] * df[x_col]
# 构建差分序列
dy = df[y_col].diff().dropna()
dx = df[x_col].diff().dropna()
# 误差修正项(滞后一期)
ecm_term = residuals.shift(1).dropna()
# 对齐数据
data = pd.DataFrame({
'dy': dy,
'dx': dx,
'ecm': ecm_term
}).dropna()
# 估计ECM
X = sm.add_constant(data[['dx', 'ecm']])
model = sm.OLS(data['dy'], X).fit()
return {
'gamma': model.params['ecm'],
'delta': model.params['dx'],
'const': model.params['const'],
'r_squared': model.rsquared,
'model': model
}
ecm_result = estimate_ecm(df)
print(f"调整速度 γ: {ecm_result['gamma']:.4f}")
print(f"短期影响 δ: {ecm_result['delta']:.4f}")
print(f"R²: {ecm_result['r_squared']:.4f}")
避坑指南:我曾经在股指期货的期现套利中,估计出来的γ只有-0.02。这意味着偏离后每天只能修复2%,太慢了。后来发现是因为数据频率太低(日线),换成5分钟数据后,γ变成了-0.15。所以,数据频率直接影响调整速度的估计。做基差交易,建议至少用15分钟以上的高频数据。
8.4 配对交易在基差中的应用:从理论到实战
好了,理论讲完了,咱们看看怎么用。配对交易在基差中的应用,说白了就是三步:
- 找配对:找到协整关系显著的品种对。比如螺纹钢和热卷、豆粕和菜粕、IF和IC。
- 算价差:用协整系数构建价差序列 Z_t = Y_t - βX_t。
- 设阈值:当价差偏离到一定程度时开仓,回归时平仓。
阈值怎么设?我一般用价差序列的均值和标准差。比如:
- 当 Z_t > μ + 2σ 时,做空价差(卖Y买X)
- 当 Z_t < μ - 2σ 时,做多价差(买Y卖X)
- 当 Z_t 回归到 μ 附近时,平仓
这里有个细节:2σ 只是经验值。我建议用滚动窗口动态计算,比如过去60个交易日的均值和标准差。市场在变,阈值也得跟着变。
代码实现一个简单的配对交易策略:
def pair_trading_signals(df, y_col='price_a', x_col='price_b', window=60, z_threshold=2):
# 滚动估计协整系数
df = df.copy()
df['beta'] = np.nan
df['spread'] = np.nan
for i in range(window, len(df)):
window_data = df.iloc[i-window:i]
eg = engle_granger_test(window_data, y_col, x_col)
df.loc[df.index[i], 'beta'] = eg['beta']
df.loc[df.index[i], 'spread'] = (
df.loc[df.index[i], y_col] -
eg['alpha'] -
eg['beta'] * df.loc[df.index[i], x_col]
)
# 计算滚动均值和标准差
df['spread_ma'] = df['spread'].rolling(window=window).mean()
df['spread_std'] = df['spread'].rolling(window=window).std()
# 生成信号
df['z_score'] = (df['spread'] - df['spread_ma']) / df['spread_std']
df['signal'] = 0
df.loc[df['z_score'] > z_threshold, 'signal'] = -1 # 做空价差
df.loc[df['z_score'] < -z_threshold, 'signal'] = 1 # 做多价差
return df.dropna()
signals = pair_trading_signals(df)
print(f"开仓次数: {(signals['signal'] != 0).sum()}")
print(f"做空信号占比: {(signals['signal'] == -1).mean():.2%}")
实战要点:配对交易不是稳赚不赔的。我踩过最大的坑是「协整关系断裂」。2020年疫情刚爆发时,很多原本协整的品种对突然脱钩了。所以,一定要设置止损。我的经验是:当价差偏离超过4σ时,不管有没有回归,先平仓再说。活着比什么都重要。
8.5 本章知识体系
下面这张图,把本章的核心逻辑串起来了。从协整检验到ECM,再到配对交易,是一条完整的技术链路。
嗯,这张图把整个流程串起来了。从两个价格序列开始,先做EG两步法检验协整关系。如果协整不成立,直接放弃这个配对。如果成立,就进入ECM建模,估计调整速度。最后,基于价差的均值回归特性,设计配对交易策略。
记住,协整是配对交易的「入场券」。没有协整关系的配对,就是赌博。有了协整关系,你做的才是统计套利。
最后一个小建议:别贪多。刚开始做配对交易,盯住一两个品种对就够了。把螺纹钢和热卷吃透,比同时看十个品种强得多。我见过太多人,策略还没跑稳就急着上杠杆,结果一个协整断裂就爆仓了。
公众号:蓝海资料掘金营,微信deep3321