第四讲:Engle-Granger两步法(上)——第一步,协整回归
好,咱们今天进入协整建模的核心环节。Engle-Granger两步法,说白了就是把一个复杂问题拆成两个简单步骤。第一步做什么?用OLS估计长期均衡关系。听起来是不是挺直接的?嗯,但这里面的门道可不少。
我个人习惯把EG两步法比作「先搭骨架,再填血肉」。第一步就是搭骨架——找到变量之间那个稳定的长期关系。你想想看,如果骨架都歪了,后面填再多的血肉也是白搭。
4.1 为什么要先做协整回归?
先问个问题:两个非平稳序列,直接做回归,不怕伪回归吗?
怕,当然怕。但这里有个关键点——如果它们真的存在协整关系,那么OLS估计出来的参数就是「超级一致」的。什么意思?就是说,即使变量是非平稳的,只要它们协整,OLS估计量的收敛速度比平稳序列还快。这是Stock在1987年证明的结论,我当年读到这篇论文时,说实话,挺震撼的。
所以,第一步协整回归的目的很明确:
- 估计长期均衡系数 β
- 提取残差序列,用于第二步检验
- 为后续的误差修正模型提供基准
核心要点:协整回归不是普通的回归,它是在「非平稳」的土壤里寻找「平稳」的关系。OLS在这里扮演的角色,更像是一个探测器。
4.2 协整回归的数学表达
假设我们有两个时间序列 y_t 和 x_t,都是 I(1) 过程。那么长期均衡关系可以写成:
y_t = β₀ + β₁·x_t + ε_t
如果 y_t 和 x_t 真的协整,那么残差 ε_t 应该是平稳的,即 I(0)。
这里有个细节我特别想强调:回归方程中要不要加截距项? 我在项目中遇到过好几次,有人为了图省事直接跑不带截距的回归,结果残差序列怎么检验都不平稳。后来发现,是截距项缺失导致估计偏误。
我的建议是:默认加截距项。除非你有非常强的理论依据说长期均衡必须过原点,否则别省这个截距。
4.3 Python实现:从数据到回归
好,咱们直接上代码。我用的是 statsmodels 和 numpy,这两个库做协整回归足够了。
import numpy as np
import pandas as pd
import statsmodels.api as sm
import matplotlib.pyplot as plt
# 生成模拟数据:两个协整的 I(1) 序列
np.random.seed(42)
T = 200
# 共同随机趋势
common_trend = np.cumsum(np.random.normal(0, 1, T))
# y 和 x 围绕共同趋势波动
x = common_trend + np.random.normal(0, 0.5, T)
y = 2 + 0.8 * x + np.random.normal(0, 0.3, T)
# 第一步:协整回归
X = sm.add_constant(x) # 加截距项
model = sm.OLS(y, X)
results = model.fit()
print(results.summary())
# 提取残差
residuals = results.resid
跑完这段代码,你会看到回归结果。重点关注几个指标:
- 系数估计值:β₀ 和 β₁ 是否接近真实值(2 和 0.8)
- R²:协整回归的 R² 通常很高,但这不代表模型好——因为变量本身有趋势
- Durbin-Watson 统计量:如果接近 2,说明残差自相关不严重,这是个好信号
个人经验:我一般不会太纠结协整回归的 R² 值。有一次我帮客户做汇率分析,R² 高达 0.98,客户特别兴奋。我说别急,等第二步残差检验出来再说。结果残差非平稳,那个 0.98 就是个美丽的陷阱。
4.4 残差提取:关键中的关键
协整回归做完,最重要的产出不是系数,而是残差序列。为什么?因为第二步要用这个残差来检验协整关系是否存在。
残差的计算很简单:
# 计算残差
residuals = y - results.predict(X)
# 或者直接用 results.resid
# 可视化残差
plt.figure(figsize=(10, 4))
plt.plot(residuals, label='Residuals', color='navy')
plt.axhline(y=0, color='red', linestyle='--', alpha=0.5)
plt.title('协整回归残差序列')
plt.legend()
plt.show()
嗯,这里要注意一点:残差序列的均值应该接近 0。因为 OLS 回归保证了残差均值为 0,这是最小二乘法的性质。如果发现残差均值明显偏离 0,那说明回归可能有问题——比如遗漏了重要变量。
避坑指南:我曾经犯过一个错误——直接用原始数据做回归,忘了检查变量是否同阶单整。结果残差看起来挺平稳,但实际上是两个不同阶数的变量硬凑出来的假象。所以,在做协整回归之前,一定先确认所有变量都是 I(1) 的。
4.5 协整回归的SVG流程图
下面这张图,把第一步的整个逻辑串起来了。我建议你保存下来,做项目时对照着看。
4.6 实际应用中的几个坑
做协整回归这么多年,我踩过的坑还真不少。挑几个典型的说说:
- 变量顺序问题:EG两步法对变量顺序敏感。你把 y 和 x 互换,估计出来的系数可能完全不同。我的建议是:根据经济理论确定哪个是因变量,哪个是自变量。
- 样本量太小:协整检验对样本量有要求。少于50个观测值,结果基本不可信。我一般要求至少100个观测值才敢做。
- 结构突变:如果样本期内发生了政策变化或金融危机,协整关系可能断裂。这时候需要分段检验,或者用 Gregory-Hansen 方法处理结构突变。
一句话总结:协整回归不是终点,而是起点。它的价值在于为第二步的残差检验提供「干净」的输入。残差质量直接决定协整检验的成败。
4.7 代码封装:写一个协整回归函数
为了方便复用,我习惯把协整回归封装成一个函数。这样每次做新项目时,直接调用就行。
def cointegrating_regression(y, x, add_constant=True):
"""
执行Engle-Granger两步法的第一步:协整回归
参数:
y : array-like, 因变量
x : array-like, 自变量
add_constant : bool, 是否添加截距项
返回:
results : OLS回归结果对象
residuals : 残差序列
"""
if add_constant:
X = sm.add_constant(x)
else:
X = x
model = sm.OLS(y, X)
results = model.fit()
residuals = results.resid
print("=" * 50)
print("协整回归结果")
print("=" * 50)
print(f"样本量: {len(y)}")
print(f"R²: {results.rsquared:.4f}")
print(f"调整R²: {results.rsquared_adj:.4f}")
print(f"DW统计量: {sm.stats.durbin_watson(residuals):.4f}")
print("\n系数估计:")
print(results.params)
return results, residuals
# 使用示例
results, resid = cointegrating_regression(y, x)
这个函数我用了好几年,从外汇市场到股票配对交易,从宏观经济到能源价格分析,基本没出过问题。当然,前提是数据质量过关。
好了,第一步的内容就到这里。记住:协整回归的核心不是追求高 R²,而是得到可靠的残差序列。残差好不好,直接决定了第二步检验的成败。做项目时,多花点时间在第一步上,后面会省很多麻烦。