4. 平稳性检验

各位好,欢迎来到第四章。

前面我们聊了协整和配对交易的基本概念,也看了价差序列的样子。但有个问题一直悬着:凭什么两个不平稳的序列,它们的线性组合就能变得平稳?

说白了,平稳性就是整个统计套利大厦的地基。地基不稳,后面建的模型再漂亮也是空中楼阁。我早年做CTA策略时,就吃过这个亏——用非平稳数据跑回归,结果回测曲线漂亮得像假的一样,实盘直接崩了。

4.1 为什么需要平稳性?

先问个问题:你拿一把尺子去量一个不断变长的东西,能量准吗?

当然不能。平稳性要解决的就是这个事——它保证时间序列的统计性质不随时间漂移。

平稳序列的核心特征:

  • 均值恒定:序列围绕一个固定水平波动,不会越走越远
  • 方差有限且稳定:波动幅度不会无限膨胀
  • 自协方差只与时间间隔有关:两点的相关性只取决于它们相隔多远,与具体时刻无关

在配对交易里,我们为什么死磕平稳性?因为只有价差序列是平稳的,我们才能用均值回归的逻辑去交易——价差偏离均值后,我们有信心它会回来。如果价差是非平稳的,它可能一去不返,你的仓位就变成无底洞。

我的经验:我习惯在拿到任何一对股票后,先画价差图,肉眼扫一眼。如果价差明显有趋势或漂移,基本可以判断非平稳,后面就不用浪费时间了。这招虽然粗糙,但能帮你快速过滤掉80%的无效配对。

4.2 平稳性的两种类型

教科书上把平稳性分成两种,但实战中我们主要关心一种。

类型 定义 实战意义
严平稳 序列的联合分布不随时间平移而变化 太严格,实战中几乎无法验证
弱平稳(宽平稳) 均值、方差恒定,自协方差只与滞后阶数有关 我们实际用的就是它,够用

嗯,这里要注意:我们平时说的「平稳性检验」,检验的都是弱平稳。别被严平稳吓到,那玩意在金融数据里基本不存在。

4.3 ADF检验原理

ADF检验,全称Augmented Dickey-Fuller检验。名字挺长,但核心思想很简单:检验序列是否存在单位根。

什么是单位根?我尽量不绕弯子。

假设一个最简单的模型:y_t = ρ * y_{t-1} + ε_t

  • 如果 |ρ| < 1,序列是平稳的——冲击会逐渐衰减
  • 如果 ρ = 1,序列就带单位根——冲击的影响永久存在,序列随机游走
  • 如果 |ρ| > 1,序列爆炸性增长——金融数据里很少见

ADF检验的原假设就是:H0: ρ = 1(存在单位根,非平稳)。备择假设是 ρ < 1(平稳)。

检验统计量是t统计量,但它的分布不是标准t分布,得查专门的临界值表。如果计算出的统计量小于临界值,就拒绝原假设,认为序列平稳。

避坑指南:我曾经犯过一个低级错误——直接用OLS的t检验临界值去判断ADF结果。结果发现怎么测都是平稳的,差点把非平稳序列当宝贝。后来才意识到,ADF的临界值比标准t分布更靠左,必须用专用临界值表。Python的statsmodels会自动处理,但如果你手算,千万注意。

ADF检验的扩展版本加入了滞后项,用来处理序列中的自相关。公式长这样:

Δy_t = α + β*t + γ*y_{t-1} + δ₁*Δy_{t-1} + ... + δₚ*Δy_{t-p} + ε_t

其中γ就是我们要检验的核心——如果γ显著为负,说明序列有均值回归倾向,即平稳。

4.4 KPSS检验原理

ADF检验有个问题:它的原假设是非平稳。这就意味着,如果数据不够多,它可能「无法拒绝」非平稳,但也不代表数据就是平稳的。说白了,ADF倾向于认为数据非平稳。

KPSS检验正好反过来。它的原假设是:H0: 序列是平稳的。

KPSS检验的思路是把序列分解成确定性趋势、随机游走和平稳误差三部分。如果随机游走部分的方差为零,序列就是平稳的。

检验统计量基于残差的累积和。如果统计量超过临界值,就拒绝平稳的原假设。

检验方法 原假设 备择假设 倾向
ADF 非平稳(有单位根) 平稳 倾向于认为非平稳
KPSS 平稳 非平稳 倾向于认为平稳

我的建议:两个检验一起用,交叉验证。如果ADF说平稳,KPSS也说平稳,那基本稳了。如果ADF说非平稳,KPSS也说非平稳,那肯定非平稳。最麻烦的是两者矛盾——这时候我一般倾向于相信数据量更大的那个检验,或者直接画图判断。

4.5 实战:用Python做平稳性检验

光说不练假把式。我们直接用代码跑一遍。

import numpy as np
import pandas as pd
import yfinance as yf
from statsmodels.tsa.stattools import adfuller, kpss

# 下载数据
tickers = ['XOM', 'CVX']
data = yf.download(tickers, start='2020-01-01', end='2023-12-31')['Adj Close']

# 计算价差(假设协整系数为1,实际需要先估计)
spread = data['XOM'] - data['CVX']

# ADF检验
adf_result = adfuller(spread, autolag='AIC')
print(f'ADF统计量: {adf_result[0]:.4f}')
print(f'p值: {adf_result[1]:.4f}')
print(f'临界值:')
for key, value in adf_result[4].items():
    print(f'  {key}: {value:.4f}')

# KPSS检验
kpss_result = kpss(spread, regression='c', nlags='auto')
print(f'\nKPSS统计量: {kpss_result[0]:.4f}')
print(f'p值: {kpss_result[1]:.4f}')
print(f'临界值:')
for key, value in kpss_result[3].items():
    print(f'  {key}: {value:.4f}')

输出解读:

  • ADF的p值小于0.05 → 拒绝非平稳原假设 → 价差平稳
  • KPSS的p值大于0.05 → 不能拒绝平稳原假设 → 价差平稳
  • 两者一致,说明这对价差适合做配对交易

关键点:ADF的p值要小于显著性水平(通常0.05)才说明平稳;KPSS的p值要大于显著性水平才说明平稳。别搞反了,我见过有人把KPSS的p值小于0.05当成平稳,结果模型全错了。

4.6 知识体系总览

下面这张图把本章的核心逻辑串起来了。你可以把它当作一个决策流程图。

平稳性检验知识体系 平稳性检验知识体系 为什么需要平稳性? 平稳序列核心特征 均值恒定 | 方差有限 | 自协方差只与间隔有关 ADF检验 KPSS检验 H₀: 存在单位根(非平稳) H₀: 序列平稳 p值 < 0.05 → 拒绝H₀ → 平稳 p值 ≥ 0.05 → 无法拒绝 → 非平稳 p值 ≥ 0.05 → 无法拒绝 → 平稳 p值 < 0.05 → 拒绝H₀ → 非平稳 交叉验证 → 最终判断

这张图从左到右展示了:为什么需要平稳性 → 平稳序列的特征 → 两种检验方法 → 各自的假设和判断逻辑 → 最终交叉验证得出结论。你可以把它当作一个检查清单,每次做配对交易前过一遍。

最后说一句:平稳性检验不是万能的。有些序列在某个时间段平稳,换段时间就不平稳了。我习惯用滚动窗口做平稳性检验——比如每60个交易日测一次,看平稳性能不能持续。如果经常在平稳和非平稳之间切换,这对股票可能不太适合做长期配对。