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 知识体系总览

为了让你更直观地理解这三种检验的关系,我画了一张流程图:

平稳性检验知识体系 平稳性检验 ADF检验 KPSS检验 PP检验 特点 • 原假设:存在单位根 • 对滞后阶数敏感 • 最常用,但需谨慎 特点 • 原假设:序列平稳 • 与ADF互补验证 • 可检测趋势平稳 特点 • 原假设:存在单位根 • 对异方差稳健 • 非参数方法修正 组合判断:三票两胜制 三个检验结果一致 → 高置信度 结果不一致 → 需进一步分析(去趋势、结构突变检验等)

3.5 实战中的避坑指南

最后,分享几个我踩过的坑:

  • 不要只看p值:p值只是参考,还要看统计量和临界值的差距。有时候p值0.049和0.051其实差别不大,但结论截然不同。
  • 注意样本量:样本太少(比如少于50个),检验功效会下降。我一般要求至少100个数据点才做检验。
  • 滞后阶数的选择:ADF和KPSS都有滞后阶数参数。用AIC或BIC自动选择,别手动乱设。
  • 结构突变:如果序列有明显的断点(比如金融危机前后),标准检验可能失效。这时候要考虑Zivot-Andrews检验。

我的习惯:每次做配对交易前,我都会跑一遍综合检验函数。如果三个检验都通过,我才开始建仓。如果只有两个通过,我会再观察一段时间。如果只有一个通过...嗯,直接放弃,不赌运气。

好了,平稳性检验这部分就讲到这里。记住,检验只是工具,真正的功夫在于理解数据背后的经济含义。下一节我们会把这些工具用到配对交易的实际选股中,到时候你就知道这些检验有多重要了。


公众号:蓝海资料掘金营,微信deep3321