协整理论入门:平稳性与单位根检验
做配对交易这么多年,我最大的体会就是——千万别把两个不相关的序列硬凑在一起。你想想看,如果两个股票价格走势完全是各走各的,那所谓的“价差回归”就是空中楼阁。所以,在谈配对之前,我们必须先搞懂一个核心概念:协整。
说白了,协整就是描述两个或多个非平稳时间序列之间,存在一种长期稳定的线性关系。嗯,这里要注意,单个序列可以是不平稳的,但它们的某种组合必须是平稳的。这就是协整的精髓。
核心思想:协整关系 ≠ 相关性。两个股票可能高度相关(比如同行业),但未必协整。协整要求的是价差具有均值回归特性,这才是配对交易能赚钱的根本。
1. 平稳性:时间序列的“定海神针”
先聊聊平稳性。我刚开始做量化那会儿,经常被这个概念绕晕。后来我总结了一句话:平稳序列,就是统计性质不随时间变化的序列。
具体来说,一个平稳时间序列需要满足三个条件:
- 均值恒定:序列围绕一个固定值波动,没有长期上升或下降趋势
- 方差恒定:波动幅度不随时间变化,不会出现“前期平稳、后期剧烈震荡”
- 协方差只与时间间隔有关:两个时间点的相关性只取决于它们相隔多远,与具体时刻无关
举个例子,白噪声就是最典型的平稳序列。而股票价格,你想想看,它长期来看是上涨的,均值在变,所以它不平稳。
我的经验:在实际项目中,我很少见到严格平稳的金融数据。但没关系,我们只需要“弱平稳”就够了——均值和方差稳定,协方差只与滞后阶数有关。别被教科书上的严格定义吓到。
2. 单位根检验:判断平稳性的“试金石”
怎么判断一个序列是否平稳?最常用的方法就是单位根检验。我个人习惯用ADF检验(Augmented Dickey-Fuller Test),它也是业界标准。
ADF检验的原假设是:序列存在单位根,即非平稳。如果p值小于0.05,我们就拒绝原假设,认为序列是平稳的。
来看一段Python代码,这是我常用的写法:
from statsmodels.tsa.stattools import adfuller
def check_stationarity(series, name=''):
result = adfuller(series.dropna())
p_value = result[1]
print(f'{name} ADF检验p值: {p_value:.6f}')
if p_value < 0.05:
print(f'→ {name} 是平稳序列')
else:
print(f'→ {name} 非平稳,需要差分')
return p_value < 0.05
# 对股票价格做检验
check_stationarity(stock_a['close'], '股票A')
check_stationarity(stock_b['close'], '股票B')
我曾经踩过一个坑:直接用原始价格做ADF检验,发现p值很大,就以为序列非平稳。这没错,但问题是——很多金融序列确实非平稳,这恰恰是我们要的。因为协整的前提就是单个序列非平稳,但组合后平稳。
避坑指南:我曾经遇到一个案例,两个序列的ADF检验p值都接近0.5,明显非平稳。但它们的价差序列做ADF检验时,p值竟然也大于0.05。这说明什么?说明它们根本不协整!别盲目配对,先检验再说。
3. 协整关系的数学定义
好,现在进入正题。协整的数学定义其实不复杂:
如果两个时间序列 \(X_t\) 和 \(Y_t\) 都是 \(d\) 阶单整(记为 \(I(d)\)),且存在一个非零向量 \(\beta\),使得线性组合 \(Z_t = Y_t - \beta X_t\) 是 \(I(d-b)\) 阶单整(其中 \(b > 0\)),那么 \(X_t\) 和 \(Y_t\) 存在协整关系。
翻译成人话:两个非平稳序列,它们的线性组合变成了平稳序列。
对于配对交易,我们通常关注 \(d=1\) 的情况:
- \(X_t\) 和 \(Y_t\) 都是 \(I(1)\)(一阶单整,即一阶差分后平稳)
- 存在 \(\beta\) 使得 \(Y_t - \beta X_t\) 是 \(I(0)\)(平稳)
这个 \(\beta\) 就是对冲比率,也就是我们常说的“配对系数”。
关键点:协整关系中的 \(\beta\) 是唯一的(在标准化条件下)。这意味着两个序列之间的长期均衡关系是确定的,不会出现“今天用这个系数,明天用那个系数”的情况。这也是配对交易能稳定盈利的理论基础。
4. Engle-Granger两步法:最经典的协整检验方法
说到协整检验,Engle-Granger两步法是我用得最多的方法。它简单、直观,而且效果不错。具体分两步走:
第一步:估计协整回归
用普通最小二乘法(OLS)估计长期均衡关系:
\[ Y_t = \alpha + \beta X_t + \varepsilon_t \]
这里 \(\beta\) 就是我们要的对冲比率。残差 \(\varepsilon_t\) 代表价差。
import statsmodels.api as sm
# 第一步:OLS回归
X = sm.add_constant(stock_a['close'])
model = sm.OLS(stock_b['close'], X).fit()
beta = model.params['close'] # 对冲比率
spread = stock_b['close'] - beta * stock_a['close'] # 价差序列
print(f'对冲比率 β = {beta:.4f}')
print(f'回归R² = {model.rsquared:.4f}')
第二步:检验残差的平稳性
对残差序列 \(\varepsilon_t\) 做ADF检验。如果残差是平稳的,就说明 \(X_t\) 和 \(Y_t\) 存在协整关系。
# 第二步:检验残差平稳性
is_stationary = check_stationarity(spread, '价差序列')
if is_stationary:
print('✅ 存在协整关系,可以构建配对交易策略')
else:
print('❌ 不存在协整关系,换个配对试试')
我的建议:Engle-Granger两步法虽然简单,但有个小问题——它假设 \(\beta\) 在样本期内是常数。实际上,这个系数可能会随时间缓慢变化。我一般会滚动窗口估计,比如每60个交易日重新估计一次 \(\beta\),这样更稳健。
5. 一个完整的协整检验流程
把上面的步骤串起来,我通常这样操作:
- 数据准备:获取两只股票的历史价格,确保时间对齐
- 单整阶数检验:分别对两个序列做ADF检验,确认它们都是 \(I(1)\)
- 协整回归:用OLS估计 \(\beta\),计算价差序列
- 残差检验:对价差做ADF检验,确认平稳性
- 结论判断:如果价差平稳,则存在协整关系;否则放弃
下面这张图是我自己画的协整检验流程图,你可以对照着看:
总结一下:协整理论是配对交易的基石。没有协整,所谓的“价差回归”就是自欺欺人。我见过太多人拿着两个相关性高的股票就做配对,结果亏得一塌糊涂。记住:相关性 ≠ 协整,协整才是硬道理。
好了,这一章的内容就到这里。下一章我们会深入讨论如何用协整关系构建实际的交易信号,包括阈值设定、仓位管理这些实战细节。到时候我会分享一些我在实盘中踩过的坑,保证让你少走弯路。