平稳性检验:单位根检验(ADF检验)的原理与Python实现
各位同学,咱们今天聊一个在跨品种套利中绕不开的话题——平稳性检验。说白了,就是判断你的时间序列数据是不是“靠谱”的。
我刚开始做量化那会儿,拿到数据就往上怼模型,结果回测曲线漂亮得不行,实盘一跑就崩。后来才发现,问题出在数据本身就不平稳。嗯,这里要敲黑板了——平稳性是协整关系的前提,没有它,后面的一切都是空中楼阁。
什么是平稳性?
平稳性,简单理解就是:时间序列的统计性质不随时间变化。具体来说,均值、方差、自协方差这些指标,在时间平移下保持不变。
你想想看,如果两个品种的价格序列都不平稳,它们的均值一直在漂移,方差忽大忽小,你怎么能指望它们之间存在稳定的线性关系?
我个人习惯把平稳性分成两类:
- 严平稳:要求联合分布完全不变。这个太严格了,现实中几乎不存在。
- 弱平稳:只要求一阶矩(均值)和二阶矩(方差、自协方差)平稳。我们实际用的就是这种。
核心要点:在跨品种套利中,我们关心的是价差序列的平稳性。如果价差平稳,说明两个品种长期保持均衡关系,偏离了就会回归。
单位根检验的原理
单位根检验,最常用的就是ADF检验(Augmented Dickey-Fuller Test)。它的核心思想其实很朴素——检验序列是否存在单位根。
为什么会叫“单位根”这个名字?这得从自回归模型说起。
假设一个简单的一阶自回归模型:
y_t = ρ * y_{t-1} + ε_t
如果 ρ = 1,就说明存在单位根。这时候序列就是随机游走,非平稳的。如果 |ρ| < 1,序列就是平稳的。
ADF检验的原假设 H₀ 就是:序列存在单位根(非平稳)。备择假设 H₁:序列不存在单位根(平稳)。
检验统计量跟常规的t统计量不太一样,它的临界值是通过蒙特卡洛模拟得到的。我记得第一次看这个表的时候,觉得挺奇怪的——为什么临界值比常规的t分布要更负?后来才明白,这是因为单位根过程的分布是“有偏”的。
避坑指南:我曾经犯过一个低级错误——直接用p值大于0.05就认为序列平稳。实际上,ADF检验的p值如果小于0.05,才拒绝原假设,认为序列平稳。千万别搞反了!
ADF检验的Python实现
好了,理论说完了,咱们直接上代码。Python里实现ADF检验非常方便,用statsmodels库就行。
import numpy as np
import pandas as pd
import matplotlib.pyplot as plt
from statsmodels.tsa.stattools import adfuller
# 生成一个随机游走序列(非平稳)
np.random.seed(42)
n = 200
random_walk = np.cumsum(np.random.randn(n))
# 生成一个白噪声序列(平稳)
white_noise = np.random.randn(n)
# 定义ADF检验函数
def adf_test(series, title=''):
result = adfuller(series, autolag='AIC')
print(f'=== {title} ===')
print(f'ADF统计量: {result[0]:.4f}')
print(f'p值: {result[1]:.4f}')
print(f'临界值:')
for key, value in result[4].items():
print(f' {key}: {value:.4f}')
if result[1] < 0.05:
print('结论: 拒绝原假设,序列平稳')
else:
print('结论: 无法拒绝原假设,序列非平稳')
print()
# 检验随机游走
adf_test(random_walk, '随机游走序列')
# 检验白噪声
adf_test(white_noise, '白噪声序列')
运行结果大概是这样:
=== 随机游走序列 ===
ADF统计量: -1.2345
p值: 0.6543
临界值:
1%: -3.4582
5%: -2.8735
10%: -2.5731
结论: 无法拒绝原假设,序列非平稳
=== 白噪声序列 ===
ADF统计量: -10.2345
p值: 0.0000
临界值:
1%: -3.4582
5%: -2.8735
10%: -2.5731
结论: 拒绝原假设,序列平稳
你看,随机游走的p值很大(0.65),说明非平稳;白噪声的p值几乎为0,说明平稳。这个逻辑很清晰吧?
ADF检验的参数选择
实际使用中,ADF检验有几个参数需要关注:
| 参数 | 说明 | 我的建议 |
|---|---|---|
| maxlag | 最大滞后阶数 | 默认用AIC自动选择,一般够用 |
| autolag | 滞后阶数选择方法 | 'AIC' 或 'BIC',我习惯用AIC |
| regression | 回归形式 | 'c'(常数项)、'ct'(常数+趋势)、'n'(无) |
关于regression参数,我多说两句。如果你检验的序列有明显的趋势(比如股价),建议用'ct';如果序列围绕某个均值波动,用'c';如果序列均值明显为0,用'n'。我在项目中遇到过,选错回归形式会导致检验结果完全相反。
重要提醒:ADF检验对滞后阶数比较敏感。阶数太少,可能无法消除自相关;阶数太多,又会降低检验功效。我个人建议先用AIC自动选择,然后手动检查残差是否白噪声。
实战中的注意事项
说了这么多,最后总结几条我在实战中踩过的坑:
- 数据频率要匹配:日线数据用日线ADF,分钟数据用分钟ADF。不同频率的检验结果可能不同。
- 样本量要足够:ADF检验需要至少50个样本点,少于30个基本不可靠。我一般要求至少100个。
- 结构性突变:如果序列在某个时间点发生了结构性变化(比如政策变化),ADF检验可能失效。这时候可以考虑Zivot-Andrews检验。
- 不要过度依赖p值:p值只是参考,还要结合ADF统计量与临界值的比较。有时候p值刚好在0.05附近,我会多看几个检验方法。
嗯,平稳性检验就聊到这儿。说白了,它就是协整分析的前置检查——数据不平稳,后面的一切都是白搭。但要注意,平稳不等于没有套利机会,平稳只是说明价差会回归均值,至于回归的速度和幅度,那是另一回事了。