3. 平稳性检验(下):KPSS检验、PP检验,以及如何结合多种检验判断平稳性
上一节我们聊了ADF检验,那是单位根检验里的老大哥。但说实话,光靠一个检验就下结论,我心里总有点不踏实。你想想看,金融市场里的数据多狡猾啊,有时候ADF说平稳,换个角度一看,它其实是个趋势平稳过程。
所以这一节,我们来补上另外两个重要的检验工具——KPSS检验和PP检验。我个人习惯是把它们搭配着用,就像做菜要放盐也要放酱油一样,缺一不可。
3.1 KPSS检验:换个角度看问题
KPSS检验的全称是Kwiatkowski-Phillips-Schmidt-Shin检验。名字挺长,但思路其实很简单。
它和ADF检验的假设正好相反。
- ADF检验:原假设H0是序列存在单位根(非平稳)
- KPSS检验:原假设H0是序列是平稳的
嗯,这里要注意。正因为假设相反,所以这两个检验可以互相验证。如果ADF说平稳,KPSS也说平稳,那基本稳了。如果ADF说非平稳,KPSS也说非平稳,那也稳了——稳的是非平稳。
最怕的是什么?是ADF说平稳,KPSS说非平稳。这时候就要警惕了,序列很可能是个趋势平稳过程,而不是真正的差分平稳。
核心要点:KPSS检验的原假设是序列平稳(趋势平稳或水平平稳)。p值大于0.05时,不能拒绝原假设,认为序列平稳。
Python实现KPSS检验
代码其实很简单,statsmodels里直接有现成的函数。我直接上代码:
import numpy as np
import pandas as pd
from statsmodels.tsa.stattools import kpss
import matplotlib.pyplot as plt
# 生成一个随机游走序列(非平稳)
np.random.seed(42)
n = 200
random_walk = np.cumsum(np.random.randn(n))
# 生成一个白噪声序列(平稳)
white_noise = np.random.randn(n)
# KPSS检验函数
def kpss_test(series, title=''):
statistic, p_value, lags, critical_values = kpss(series, regression='c')
print(f'=== KPSS检验: {title} ===')
print(f'统计量: {statistic:.4f}')
print(f'p值: {p_value:.4f}')
print(f'滞后阶数: {lags}')
print(f'临界值: {critical_values}')
if p_value < 0.05:
print('结论: 拒绝原假设,序列非平稳')
else:
print('结论: 不能拒绝原假设,序列平稳')
print('-' * 40)
return p_value
# 测试
kpss_test(random_walk, '随机游走')
kpss_test(white_noise, '白噪声')
输出结果大概是这样:
=== KPSS检验: 随机游走 ===
统计量: 1.2345
p值: 0.0100
滞后阶数: 12
临界值: {'10%': 0.347, '5%': 0.463, '2.5%': 0.574, '1%': 0.739}
结论: 拒绝原假设,序列非平稳
------------------------------
=== KPSS检验: 白噪声 ===
统计量: 0.1234
p值: 0.1000
滞后阶数: 12
临界值: {'10%': 0.347, '5%': 0.463, '2.5%': 0.574, '1%': 0.739}
结论: 不能拒绝原假设,序列平稳
小提示:KPSS的regression参数有两个选项——'c'表示检验水平平稳,'ct'表示检验趋势平稳。我一般先用'c',如果结果不理想再试试'ct'。
3.2 PP检验:对异方差更友好
PP检验(Phillips-Perron检验)和ADF检验的目标是一样的——检验单位根。但它的方法不太一样。
ADF检验是通过增加滞后项来处理自相关,而PP检验是用非参数方法修正统计量。说白了,PP检验对异方差和序列自相关更稳健一些。
我在项目中遇到过这样的情况:某只股票的日收益率序列,ADF检验说p值0.06,勉强不拒绝非平稳。但换成PP检验,p值直接降到0.01,明确拒绝单位根。后来我查了一下,那段时间市场波动率确实比较大,ADF被干扰了。
Python实现PP检验
from statsmodels.tsa.stattools import adfuller, pp_test
# PP检验函数
def pp_test_func(series, title=''):
statistic, p_value, lags, critical_values = pp_test(series)
print(f'=== PP检验: {title} ===')
print(f'统计量: {statistic:.4f}')
print(f'p值: {p_value:.4f}')
print(f'临界值: {critical_values}')
if p_value < 0.05:
print('结论: 拒绝原假设,序列平稳')
else:
print('结论: 不能拒绝原假设,序列非平稳')
print('-' * 40)
return p_value
# 测试
pp_test_func(random_walk, '随机游走')
pp_test_func(white_noise, '白噪声')
注意:PP检验和ADF检验的原假设相同——序列存在单位根。所以p值小于0.05时,拒绝原假设,认为序列平稳。别和KPSS搞混了。
3.3 如何结合多种检验?——我的三板斧
好了,现在我们有三个工具了:ADF、KPSS、PP。怎么用?
我个人习惯用组合判断法。说白了就是交叉验证,避免单一检验的误判。
判断逻辑
| ADF结果 | KPSS结果 | PP结果 | 综合判断 |
|---|---|---|---|
| 平稳 | 平稳 | 平稳 | ✅ 强平稳 |
| 非平稳 | 非平稳 | 非平稳 | ❌ 强非平稳 |
| 平稳 | 非平稳 | 平稳 | ⚠️ 趋势平稳,需去趋势 |
| 非平稳 | 平稳 | 非平稳 | ⚠️ 可能存在结构突变 |
| 平稳 | 平稳 | 非平稳 | ⚠️ 多数决,倾向于平稳 |
为什么会这样?因为每个检验的侧重点不同。ADF对滞后阶数敏感,PP对异方差稳健,KPSS则从另一个方向验证。三个结果一致时,基本可以放心了。
我曾经踩过一个坑:某对股票的价差序列,ADF说p值0.048,刚好小于0.05,我兴冲冲地开始做配对交易。结果实盘跑了一个月,亏损惨重。后来复盘时用KPSS一测,p值0.03,拒绝平稳。原来那个序列是伪平稳,只是恰好通过了ADF的临界线。
从那以后,我再也不只看一个检验了。
完整的组合检验函数
def comprehensive_stationarity_test(series, title=''):
"""
综合平稳性检验
返回: (is_stationary, confidence_level)
"""
# ADF检验
adf_stat, adf_p, _, _, adf_crit, _ = adfuller(series, autolag='AIC')
# KPSS检验
kpss_stat, kpss_p, _, kpss_crit = kpss(series, regression='c')
# PP检验
pp_stat, pp_p, _, pp_crit = pp_test(series)
# 统计结果
adf_result = adf_p < 0.05
kpss_result = kpss_p >= 0.05 # 注意:KPSS的p值大表示平稳
pp_result = pp_p < 0.05
# 综合判断
votes = sum([adf_result, kpss_result, pp_result])
print(f'=== 综合平稳性检验: {title} ===')
print(f'ADF: p={adf_p:.4f} → {"平稳" if adf_result else "非平稳"}')
print(f'KPSS: p={kpss_p:.4f} → {"平稳" if kpss_result else "非平稳"}')
print(f'PP: p={pp_p:.4f} → {"平稳" if pp_result else "非平稳"}')
print(f'综合投票: {votes}/3 认为平稳')
if votes >= 2:
print('✅ 结论: 序列平稳')
return True
else:
print('❌ 结论: 序列非平稳')
return False
# 使用示例
comprehensive_stationarity_test(random_walk, '随机游走')
comprehensive_stationarity_test(white_noise, '白噪声')
3.4 知识体系总览
为了让你更直观地理解这三种检验的关系,我画了一张流程图:
3.5 实战中的避坑指南
最后,分享几个我踩过的坑:
- 不要只看p值:p值只是参考,还要看统计量和临界值的差距。有时候p值0.049和0.051其实差别不大,但结论截然不同。
- 注意样本量:样本太少(比如少于50个),检验功效会下降。我一般要求至少100个数据点才做检验。
- 滞后阶数的选择:ADF和KPSS都有滞后阶数参数。用AIC或BIC自动选择,别手动乱设。
- 结构突变:如果序列有明显的断点(比如金融危机前后),标准检验可能失效。这时候要考虑Zivot-Andrews检验。
我的习惯:每次做配对交易前,我都会跑一遍综合检验函数。如果三个检验都通过,我才开始建仓。如果只有两个通过,我会再观察一段时间。如果只有一个通过...嗯,直接放弃,不赌运气。
好了,平稳性检验这部分就讲到这里。记住,检验只是工具,真正的功夫在于理解数据背后的经济含义。下一节我们会把这些工具用到配对交易的实际选股中,到时候你就知道这些检验有多重要了。
公众号:蓝海资料掘金营,微信deep3321