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两步法。名字听着高大上,其实逻辑很朴素。我在项目中用过不下上百次,步骤就两步:

  1. 第一步:估计长期均衡关系。用OLS回归 Y_t = α + βX_t + ε_t,得到残差序列 e_t。
  2. 第二步:检验残差的平稳性。对 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 配对交易在基差中的应用:从理论到实战

好了,理论讲完了,咱们看看怎么用。配对交易在基差中的应用,说白了就是三步:

  1. 找配对:找到协整关系显著的品种对。比如螺纹钢和热卷、豆粕和菜粕、IF和IC。
  2. 算价差:用协整系数构建价差序列 Z_t = Y_t - βX_t。
  3. 设阈值:当价差偏离到一定程度时开仓,回归时平仓。

阈值怎么设?我一般用价差序列的均值和标准差。比如:

  • 当 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,再到配对交易,是一条完整的技术链路。

协整关系与配对交易知识体系 输入:两个 I(1) 价格序列 Engle-Granger 两步法 Step 1: OLS回归 → 残差序列 Step 2: ADF检验残差平稳性 ✅ 协整成立 残差平稳 → 长期均衡关系存在 ❌ 协整不成立 残差非平稳 → 放弃该配对 误差修正模型 (ECM) 配对交易策略:价差均值回归 关键参数 • 协整系数 β • 调整速度 γ • 开仓阈值 (2σ) • 止损阈值 (4σ)

嗯,这张图把整个流程串起来了。从两个价格序列开始,先做EG两步法检验协整关系。如果协整不成立,直接放弃这个配对。如果成立,就进入ECM建模,估计调整速度。最后,基于价差的均值回归特性,设计配对交易策略。

记住,协整是配对交易的「入场券」。没有协整关系的配对,就是赌博。有了协整关系,你做的才是统计套利。

最后一个小建议:别贪多。刚开始做配对交易,盯住一两个品种对就够了。把螺纹钢和热卷吃透,比同时看十个品种强得多。我见过太多人,策略还没跑稳就急着上杠杆,结果一个协整断裂就爆仓了。


公众号:蓝海资料掘金营,微信deep3321