3、协整关系检验:平稳性检验(ADF)、协整回归(OLS)、协整检验(Engle-Granger)
好,咱们接着往下走。前面我们已经找到了潜在的配对,也计算好了价差。但问题来了——这个价差序列,它到底靠不靠谱?会不会今天看着挺稳,明天就一去不回头了?
说白了,我们做价差回归交易,赌的就是价差会围绕某个均值来回摆动。如果价差本身是个随机游走,那它飘走了就再也回不来了,你的仓位就等着爆仓吧。所以,在动手交易之前,我们必须做一套严格的「体检」——这就是协整关系检验。
我个人习惯把这套流程拆成三步:先测平稳性,再做回归,最后验证残差。每一步都有坑,我一个个说。
3.1 平稳性检验(ADF检验)
先问个问题:为什么非要检验平稳性?
你想想看,如果两个价格序列都是随机游走的,比如茅台和五粮液,它们各自乱跑,但长期看却步调一致——这就是协整。但如果其中一个序列本身就不平稳,那回归出来的结果全是假的,这叫「伪回归」。
ADF检验,全称Augmented Dickey-Fuller检验,就是用来判断一个时间序列是否平稳的。它的原假设是「序列存在单位根」,也就是不平稳。如果p值小于0.05,我们就拒绝原假设,认为序列是平稳的。
嗯,这里要注意:我们通常先对两个价格序列分别做ADF检验。如果它们本身都是平稳的,那就不用做协整了,直接做均值回归就行。但现实中,价格序列几乎都是非平稳的,所以我们需要看它们的线性组合是否平稳。
代码实现很简单,用statsmodels库就行:
from statsmodels.tsa.stattools import adfuller
# 假设 price_a 和 price_b 是两个价格序列
result_a = adfuller(price_a)
result_b = adfuller(price_b)
print(f"A的ADF统计量: {result_a[0]:.4f}, p值: {result_a[1]:.4f}")
print(f"B的ADF统计量: {result_b[0]:.4f}, p值: {result_b[1]:.4f}")
# 通常p值大于0.05,说明序列非平稳
if result_a[1] > 0.05 and result_b[1] > 0.05:
print("两个序列都是非平稳的,可以尝试做协整检验")
我在项目中遇到过一种情况:某个股票对在2018年之前协整关系很好,但2019年之后突然失效了。后来一查,是因为公司基本面发生了重大变化。所以,ADF检验不是做一次就完事了,我建议每隔一段时间重新跑一遍。
3.2 协整回归(OLS)
确认两个序列都是非平稳的后,下一步就是找出它们之间的线性关系。说白了,就是算一个回归方程:
price_a = β * price_b + ε
这里的β就是对冲比率,ε就是价差。我们用普通最小二乘法(OLS)来估计这个β。
为什么用OLS?因为它简单、直观,而且在小样本下表现不错。但要注意,这里的回归是「伪回归」——因为两个变量都是非平稳的,所以传统的t统计量、R方都是不可靠的。我们关心的不是回归系数是否显著,而是残差是否平稳。
import statsmodels.api as sm
# 添加常数项
X = sm.add_constant(price_b)
model = sm.OLS(price_a, X).fit()
beta = model.params[1] # 对冲比率
alpha = model.params[0] # 截距项
spread = price_a - beta * price_b - alpha # 价差
print(f"对冲比率 β: {beta:.4f}")
print(f"截距项 α: {alpha:.4f}")
这里有个坑,我踩过好几次:OLS回归对异常值非常敏感。如果数据里有一天出现了极端行情,比如某只股票突然涨停,那回归出来的β会被严重扭曲。我的做法是:先对数据做1%的缩尾处理,或者用稳健回归(如Huber回归)代替OLS。
3.3 协整检验(Engle-Granger方法)
好,现在我们有价差序列了。最后一步,就是检验这个价差是否平稳。这就是Engle-Granger两步法的精髓:
- 第一步: 用OLS估计协整关系(刚才做的)
- 第二步: 对残差(价差)做ADF检验
注意,这里的ADF检验和之前的不太一样。因为残差是从回归中得到的,所以临界值需要调整。statsmodels里专门有个函数处理这个:
from statsmodels.tsa.stattools import coint
# 直接做Engle-Granger协整检验
coint_t, p_value, critical_values = coint(price_a, price_b)
print(f"协整检验统计量: {coint_t:.4f}")
print(f"p值: {p_value:.4f}")
print(f"1%临界值: {critical_values[0]:.4f}")
print(f"5%临界值: {critical_values[1]:.4f}")
if p_value < 0.05:
print("存在协整关系,可以继续做价差回归交易")
else:
print("不存在协整关系,换个配对试试")
这个coint函数把两步合在一起了,非常方便。它内部会自动做OLS回归,然后对残差做ADF检验,并且使用正确的临界值。
我个人习惯是,除了看p值,还会看协整检验统计量是否小于5%的临界值。如果统计量比临界值还小,说明协整关系非常强。
- p值 < 0.05 → 存在协整关系 ✓
- p值 ≥ 0.05 → 不存在协整关系 ✗
- 统计量 < 5%临界值 → 协整关系较强
3.4 完整流程与可视化
把上面三步串起来,就是一个完整的协整检验流程。我画了张图,帮你理清思路:
这张图把整个流程串起来了。你从左边开始,先做ADF检验,确认两个序列都是非平稳的。然后做OLS回归,得到对冲比率。最后对价差做ADF检验,看是否平稳。如果通过了,恭喜你,找到了一个可以交易的配对。
如果没通过呢?别灰心,换个配对重新来。我刚开始做的时候,经常要试几十个配对才能找到一两个靠谱的。这很正常,协整关系本身就不是随处可见的。
3.5 常见问题与避坑
| 问题 | 原因 | 解决方法 |
|---|---|---|
| ADF检验p值刚好在0.05附近 | 样本量不足或存在结构突变 | 增加数据长度,或用KPSS交叉验证 |
| OLS回归的β不稳定 | 数据中存在异常值 | 做缩尾处理,或用稳健回归 |
| 协整检验通过,但实盘失效 | 样本内过拟合,或市场结构变化 | 做样本外验证,定期重新检验 |
| 多个配对都通过检验 | 可能都是伪协整 | 用更严格的临界值(1%),或做滚动检验 |
最后说一句:协整检验不是一劳永逸的。市场在变,公司的基本面在变,协整关系也会变。我个人的习惯是,每个月重新跑一遍协整检验,如果发现某个配对的p值开始变大,就提前减仓或者平仓。
好了,协整检验这部分就讲到这里。下一节我们会聊如何构建交易信号和仓位管理,那才是真正赚钱的地方。