3、平稳性检验:ADF检验原理、KPSS检验、实战中如何判断价差是否平稳
做统计套利,最核心的一步是什么?
我个人觉得,就是判断价差是否平稳。
你想想看,如果价差本身就不平稳,那它今天涨到天上去,明天跌到地板上,你凭什么认为它会回归?说白了,没有平稳性,就没有均值回归,也就没有套利机会。
这一节,我们就来彻底搞懂平稳性检验。我会把ADF和KPSS这两个最常用的检验讲透,再结合我踩过的坑,告诉你实战中到底该怎么用。
3.1 什么是平稳性?为什么它这么重要?
平稳性,用大白话说就是:一个时间序列的统计性质不随时间变化。
具体到价差上,我们希望它满足三个条件:
- 均值恒定:价差围绕一个固定值上下波动,不会越飘越远
- 方差恒定:波动的幅度大致稳定,不会突然放大或缩小
- 协方差只与时间间隔有关:过去和未来的关系只取决于相隔多久,不取决于具体在哪个时间点
我在项目中遇到过这样的情况:两个品种看起来走势很相似,价差图也挺漂亮,但一检验发现是非平稳的。结果呢?实盘跑了两个月,价差一路向北,亏得我头皮发麻。嗯,从那以后,我再也不敢跳过平稳性检验了。
核心结论:只有平稳的价差序列,才能用统计套利模型。非平稳的价差,你做的不是套利,是趋势交易。
3.2 ADF检验:最常用的单位根检验
ADF检验,全称是Augmented Dickey-Fuller检验。名字挺长,但原理其实不复杂。
3.2.1 原理一句话说清楚
ADF检验的核心,就是检验序列是否存在单位根。
什么是单位根?你可以把它理解成序列的"记忆性"。如果存在单位根,说明过去的冲击会永久性地影响未来——价差回不去了。
ADF的原假设是:序列存在单位根(非平稳)。
备择假设是:序列不存在单位根(平稳)。
所以,我们想要的是p值小于显著性水平(比如0.05),拒绝原假设,认为价差平稳。
3.2.2 实战中的代码实现
我习惯用statsmodels库来做ADF检验。代码很简单,但有几个参数需要注意。
import statsmodels.tsa.stattools as ts
# 假设价差序列存在spread这个变量里
result = ts.adfuller(spread,
maxlag=None, # 自动选择滞后阶数
regression='c', # 只包含常数项,最常用
autolag='AIC') # 用AIC准则选最优滞后
# 提取关键结果
adf_statistic = result[0]
p_value = result[1]
critical_values = result[4]
print(f'ADF统计量: {adf_statistic:.4f}')
print(f'p值: {p_value:.6f}')
print(f'临界值: {critical_values}')
if p_value < 0.05:
print('✅ 拒绝原假设,价差平稳')
else:
print('❌ 无法拒绝原假设,价差非平稳')
我的经验:regression参数有三个选项——'c'(常数项)、'ct'(常数项+趋势项)、'ctt'(常数项+趋势项+二次趋势)。对于价差序列,我一般用'c'就够了。如果你发现价差有明显的趋势,可以试试'ct',但这种情况通常说明你的配对有问题。
3.2.3 一个容易踩的坑
我曾经犯过一个错误:直接用原始价差做ADF检验,结果p值0.03,欢天喜地以为找到好配对。结果实盘一跑,价差根本不回归。
后来才发现,问题出在滞后阶数的选择上。如果数据有自相关性,默认的滞后阶数可能不够,导致检验结果不可靠。
我的建议是:
- 用autolag='AIC'让程序自动选最优滞后
- 或者手动设置maxlag为int(np.sqrt(len(spread)))——这是经验法则
- 两种方法都试试,结果一致才放心
3.3 KPSS检验:ADF的互补工具
光用ADF够吗?不够。
ADF有个弱点:它的检验功效在样本量小的时候不太行。而且,ADF的原假设是非平稳,有时候我们想反过来验证——假设序列是平稳的,看能不能被拒绝。
这时候,KPSS检验就派上用场了。
3.3.1 KPSS的原理
KPSS检验和ADF正好相反:
- 原假设:序列是平稳的(趋势平稳)
- 备择假设:序列存在单位根(非平稳)
所以,我们想要的是KPSS的p值大于0.05,不能拒绝原假设,认为序列平稳。
3.3.2 代码实现
from statsmodels.tsa.stattools import kpss
# KPSS检验
kpss_stat, p_value, lags, critical_values = kpss(spread,
regression='c',
nlags='auto')
print(f'KPSS统计量: {kpss_stat:.4f}')
print(f'p值: {p_value:.6f}')
if p_value > 0.05:
print('✅ 不能拒绝原假设,价差平稳')
else:
print('❌ 拒绝原假设,价差非平稳')
注意:KPSS的regression参数和ADF类似。但要注意,如果序列有确定性趋势(比如缓慢上升),用'ct'会更合适。我一般先用'c',如果结果不理想再试'ct'。
3.4 实战中如何判断价差是否平稳?我的完整流程
说了这么多理论,实战中到底怎么操作?我总结了一套流程,用了好几年,基本没出过问题。
3.4.1 第一步:肉眼观察
别急着跑代码。先画个图看看。
价差图如果看起来像白噪声,围绕零轴上下波动,那大概率是平稳的。如果价差有明显的趋势、漂移、或者波动幅度越来越大,那基本没戏。
3.4.2 第二步:ADF + KPSS双检验
我习惯两个检验都做,然后看组合结果:
| ADF结果 | KPSS结果 | 结论 |
|---|---|---|
| 平稳(p<0.05) | 平稳(p>0.05) | ✅ 强平稳,放心用 |
| 平稳(p<0.05) | 非平稳(p<0.05) | ⚠️ 可能是趋势平稳,需进一步分析 |
| 非平稳(p>0.05) | 平稳(p>0.05) | ⚠️ 数据量可能不够,建议增加样本 |
| 非平稳(p>0.05) | 非平稳(p<0.05) | ❌ 非平稳,放弃这个配对 |
你看,两个检验一起用,能避免很多误判。我曾经就遇到过ADF说平稳、KPSS说非平稳的情况,后来发现是数据里有个结构性断点。这种情况,要么换数据区间,要么用更复杂的检验(比如Zivot-Andrews检验)。
3.4.3 第三步:看自相关图
检验通过后,我还会看一眼自相关图(ACF)。
平稳序列的自相关应该快速衰减到零附近。如果自相关衰减很慢,或者有明显的周期性,那说明序列可能还有问题。
import matplotlib.pyplot as plt
from statsmodels.graphics.tsaplots import plot_acf
plot_acf(spread, lags=40)
plt.show()
3.4.4 第四步:滚动统计量验证
这是我自己加的一道保险。
我会计算滚动窗口(比如60个交易日)的均值和标准差,看看它们是否稳定。如果滚动均值一直在漂移,或者滚动标准差在扩大,那即使检验通过了,我也不会用这个配对。
rolling_mean = spread.rolling(window=60).mean()
rolling_std = spread.rolling(window=60).std()
plt.plot(rolling_mean, label='滚动均值')
plt.plot(rolling_std, label='滚动标准差')
plt.legend()
plt.show()
一个小技巧:如果价差通过了所有检验,但滚动均值偶尔有大的偏移,我会考虑用"去中心化"的价差——也就是减去滚动均值。这样能消除局部的不平稳,让模型更稳健。
3.5 本章知识体系
下面这张图,是我梳理的平稳性检验完整流程。你可以把它当成一个检查清单。
嗯,这套流程走下来,基本能过滤掉90%以上的伪配对。剩下的10%,要么是数据质量有问题,要么是市场结构发生了变化——这些就不是检验能解决的了。
记住一句话:检验是工具,不是信仰。再漂亮的检验结果,也比不上你对市场和品种的深入理解。
公众号:蓝海资料掘金营,微信deep3321