2. 单变量时序分析基础:时间序列分解、平稳性检验、ACF/PACF

各位同学,欢迎来到多因子时序预测实战训练营的第二讲。

今天咱们聊点基础但极其重要的东西——单变量时序分析。你可能会问:“老师,我们不是要做多因子模型吗?为什么还要花一整章讲单变量?”

嗯,这个问题问得好。我个人的经验是:多因子模型的地基,就是单变量分析。如果你连一个时间序列的“脾气”都摸不透,堆再多因子也是白搭。我在做CTA策略的时候,就吃过这个亏——上来就搞复杂模型,结果回测漂亮,实盘一塌糊涂。后来老老实实回去做单变量诊断,才发现问题出在数据本身。

好,咱们正式开始。

2.1 时间序列分解:把数据拆开看

一个时间序列,说白了就是三个成分的叠加:趋势(Trend)季节性(Seasonal)残差(Residual)。用数学公式表达就是:

Y(t) = T(t) + S(t) + R(t)   (加法模型)
Y(t) = T(t) * S(t) * R(t)   (乘法模型)

加法模型适用于季节性波动幅度不随时间变化的情况。乘法模型则适用于波动幅度随趋势增长而放大的情况——比如股票价格,牛市里波动大,熊市里波动小,这就是乘法模型的典型特征。

我个人习惯先用加法模型试试,如果残差有明显的异方差性,再换乘法模型。

2.1.1 如何分解?

Python里最常用的工具是 statsmodels.tsa.seasonal_decompose。来看一段代码:

import pandas as pd
import numpy as np
import matplotlib.pyplot as plt
from statsmodels.tsa.seasonal import seasonal_decompose

# 生成模拟数据:趋势 + 季节 + 噪声
np.random.seed(42)
t = np.arange(0, 365)
trend = 0.05 * t
seasonal = 10 * np.sin(2 * np.pi * t / 30)
noise = np.random.normal(0, 2, 365)
data = trend + seasonal + noise

# 分解
result = seasonal_decompose(data, model='additive', period=30)
result.plot()
plt.show()

这段代码会输出四张图:原始序列、趋势成分、季节成分、残差成分。你一眼就能看出数据里有没有明显的趋势和周期。

我的小技巧: 分解之后,一定要看残差图。如果残差里还有明显的模式,说明你的周期参数选错了,或者数据本身有更复杂的结构。我曾经在分析某只股票的日收益率时,发现残差里还有周度效应——后来才知道,那是期权到期日的影响。

2.2 平稳性检验:数据“稳”了才能建模

平稳性,是时间序列建模的第一道门槛。什么叫平稳?简单说就是:统计性质不随时间变化。具体来说,均值、方差、自协方差都是常数。

为什么这么重要?因为大多数模型(ARIMA、GARCH等)都假设数据是平稳的。如果数据不平稳,模型会学到虚假的规律——比如把趋势当成预测信号,结果一预测就崩。

我见过最惨的案例:有人用非平稳的股价直接跑LSTM,训练集上拟合得完美,测试集上直接飞了。原因就是模型学到了趋势,但趋势的方向变了。

2.2.1 ADF检验(单位根检验)

ADF检验的原假设是:序列存在单位根(非平稳)。如果p值小于0.05,拒绝原假设,认为序列平稳。

from statsmodels.tsa.stattools import adfuller

result = adfuller(data)
print(f'ADF Statistic: {result[0]}')
print(f'p-value: {result[1]}')
print(f'Critical Values: {result[4]}')

# 输出示例:
# ADF Statistic: -3.45
# p-value: 0.009
# Critical Values: {'1%': -3.45, '5%': -2.87, '10%': -2.57}

p值0.009 < 0.05,说明序列平稳。但注意:ADF检验对滞后阶数敏感。我建议用AIC自动选择滞后阶数,而不是随便设一个固定值。

2.2.2 KPSS检验(互补检验)

KPSS检验的原假设正好相反:序列是平稳的。如果p值小于0.05,拒绝原假设,认为序列非平稳。

from statsmodels.tsa.stattools import kpss

result = kpss(data, regression='c')
print(f'KPSS Statistic: {result[0]}')
print(f'p-value: {result[1]}')
避坑指南: 我建议同时做ADF和KPSS两个检验。为什么?因为有些序列是“趋势平稳”的——去掉趋势后是平稳的,但ADF可能误判为不平稳。两个检验交叉验证,结果更可靠。

四种可能的结果:

ADF结果 KPSS结果 结论
平稳 平稳 序列平稳
不平稳 不平稳 序列非平稳(差分处理)
平稳 不平稳 可能存在趋势(去趋势处理)
不平稳 平稳 可能存在结构突变

2.3 自相关与偏自相关图:找“记忆”的长度

ACF(自相关函数)和PACF(偏自相关函数)是判断ARIMA模型阶数的核心工具。说白了,它们告诉你:过去的数据对现在有多大影响?影响能持续多久?

2.3.1 ACF图

ACF衡量的是 Y(t)Y(t-k) 之间的相关性,不考虑中间变量的影响。如果ACF在滞后k处显著不为0,说明k期前的数据对当前有直接影响。

from statsmodels.graphics.tsaplots import plot_acf
import matplotlib.pyplot as plt

plot_acf(data, lags=40)
plt.show()

2.3.2 PACF图

PACF则是在剔除了中间滞后项的影响后,计算 Y(t)Y(t-k) 的偏相关。它更“干净”,能直接告诉你AR模型的阶数。

from statsmodels.graphics.tsaplots import plot_pacf

plot_pacf(data, lags=40)
plt.show()
注意: ACF和PACF的解读需要结合置信区间(蓝色区域)。如果柱子超出蓝色区域,才认为显著。我曾经见过有人把刚好碰到边界的也当成显著,结果模型过拟合得一塌糊涂。

2.3.3 如何用ACF/PACF定阶?

这是经典套路:

  • AR(p)模型:PACF在p阶后截尾(突然降到0),ACF拖尾(缓慢衰减)
  • MA(q)模型:ACF在q阶后截尾,PACF拖尾
  • ARMA(p,q)模型:两者都拖尾

举个例子:如果PACF在滞后2之后突然掉到置信区间内,而ACF缓慢衰减,那大概率是AR(2)模型。

但说实话,实际数据很少这么完美。我的做法是:先用ACF/PACF初步判断,然后用AIC/BIC在候选阶数里选最优。别死磕“截尾”这个标准,数据不会那么听话。

2.4 本章知识体系总览

下面这张图,是我自己总结的单变量时序分析流程。每次拿到新数据,我都会走一遍这个流程:

单变量时序分析流程 原始时间序列 时间序列分解(趋势/季节/残差) 平稳性检验(ADF + KPSS) 差分/去趋势处理 进入ACF/PACF分析

这张图的核心逻辑是:先分解看结构,再检验看平稳性,最后用ACF/PACF定阶。每一步都有明确的输出,每一步都在为下一步做准备。

2.5 本章小结

好,咱们来捋一捋今天的内容:

  • 时间序列分解:把数据拆成趋势、季节、残差,看清数据的底层结构
  • 平稳性检验:ADF和KPSS双剑合璧,判断数据是否“稳”得住
  • ACF/PACF:找数据的“记忆长度”,为ARIMA定阶做准备

这些工具,说白了就是帮你读懂数据。我做了这么多年量化,最大的体会就是:模型再复杂,也救不了对数据的一无所知。每次拿到新数据,我都会老老实实走一遍这个流程——哪怕只是看一眼ACF图,心里也有个底。

下一章,咱们会把这些工具串起来,正式进入ARIMA模型的实战。到时候你会发现,今天打下的基础,全都能用上。


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