第1章:时间序列基础——平稳性、自相关与差分操作
各位同学,咱们今天聊聊时间序列分析里最基础、也最要命的一块内容。说实话,我入行高频交易那会儿,第一个坑就栽在平稳性上。那时候我拿了一堆价格数据直接建模,结果模型跑出来漂亮得很,一上线就亏钱。后来才明白——数据不平稳,模型就是个笑话。
所以这一章,咱们把地基打牢。我会带着大家把平稳性检验、自相关函数、偏自相关函数、滞后算子和差分操作这几个核心概念,一个一个掰开揉碎了讲清楚。
1.1 平稳性:时间序列的“定海神针”
什么叫平稳性?说白了,就是一个时间序列的统计性质不随时间变化。均值不变、方差不变、自相关结构也不变。你想想看,如果一条序列的均值在漂移,你用历史数据训练的模型,能预测未来吗?显然不能。
平稳性分两种:
- 严平稳:联合分布不随时间平移而变化。这个条件太强,实际中几乎用不到。
- 弱平稳(也叫宽平稳):均值恒定、方差恒定、自协方差只依赖于时间间隔。咱们平时说的平稳,基本都指这个。
核心要点:高频交易中,价格序列几乎都是非平稳的,但收益率序列通常是弱平稳的。所以别拿价格直接建模,先算收益率。
1.2 ADF检验:给平稳性“把脉”
ADF检验(Augmented Dickey-Fuller Test)是检验平稳性的标准工具。它的原假设是“序列存在单位根”,也就是非平稳。如果p值小于0.05,就拒绝原假设,认为序列平稳。
我在项目中遇到过一件事:有一次用ADF检验某只股票的5分钟收益率,p值0.03,我以为是平稳的。结果回测时发现模型在特定时间段失效。后来仔细一看,原来是那个时间段有分红除权,导致均值发生了跳跃。所以啊,ADF检验不是万能的,它只能检测特定类型的非平稳性。
# Python代码示例:ADF检验
from statsmodels.tsa.stattools import adfuller
import numpy as np
# 生成一个随机游走序列(非平稳)
np.random.seed(42)
n = 1000
random_walk = np.cumsum(np.random.randn(n))
# ADF检验
result = adfuller(random_walk)
print(f'ADF统计量: {result[0]:.4f}')
print(f'p值: {result[1]:.4f}')
print(f'临界值: {result[4]}')
# 对差分后的序列做ADF检验
diff_series = np.diff(random_walk)
result_diff = adfuller(diff_series)
print(f'\n差分后ADF统计量: {result_diff[0]:.4f}')
print(f'差分后p值: {result_diff[1]:.4f}')
避坑指南:我曾经在检验时忘了加常数项和趋势项,结果把平稳序列误判为非平稳。ADF检验有三个版本:无常数项、有常数项、有常数项+趋势项。高频数据一般用有常数项的版本就够了。
1.3 自相关与偏自相关:序列的“记忆”有多长?
自相关函数(ACF)衡量的是序列与其滞后值之间的相关性。偏自相关函数(PACF)则是在剔除了中间滞后项的影响后,衡量两个时间点之间的直接相关性。
这两个函数有什么用?嗯,它们能帮你判断ARIMA模型的阶数。ACF拖尾、PACF截尾——适合AR模型;ACF截尾、PACF拖尾——适合MA模型;两者都拖尾——适合ARMA模型。
我记得刚学那会儿,总搞不清截尾和拖尾的区别。后来自己画了几百张图,才真正看明白。截尾就是突然掉到置信区间内,拖尾就是慢慢衰减。
# Python代码示例:ACF和PACF
from statsmodels.graphics.tsaplots import plot_acf, plot_pacf
import matplotlib.pyplot as plt
# 生成AR(1)过程
phi = 0.7
n = 500
ar1 = np.zeros(n)
for t in range(1, n):
ar1[t] = phi * ar1[t-1] + np.random.randn()
# 画图
fig, (ax1, ax2) = plt.subplots(2, 1, figsize=(10, 8))
plot_acf(ar1, lags=30, ax=ax1)
plot_pacf(ar1, lags=30, ax=ax2)
plt.show()
注意:高频数据通常有微观结构噪声,ACF/PACF在滞后1期或2期会出现异常值。我建议对原始数据做预白化处理,或者使用稳健估计方法。
1.4 滞后算子:时间序列的“代数工具”
滞后算子L,也叫后移算子,定义是:L y_t = y_{t-1}。就这么简单。但它的威力在于,可以把差分、季节调整等操作写成简洁的代数形式。
比如一阶差分:Δ y_t = y_t - y_{t-1} = (1 - L) y_t
季节差分:Δ_s y_t = y_t - y_{t-s} = (1 - L^s) y_t
你想想看,有了滞后算子,ARMA模型就可以写成:Φ(L) y_t = Θ(L) ε_t。这种表示法在理论推导中非常方便。
个人经验:我在写高频交易策略时,经常用滞后算子来构造特征。比如用(1-L)(1-L^5)来捕捉日内的周度模式。这种组合差分操作,用滞后算子写起来特别清晰。
1.5 差分操作:让非平稳变平稳的“手术刀”
差分是处理非平稳序列最常用的方法。一阶差分可以消除线性趋势,二阶差分可以消除二次趋势。对于高频数据,通常一阶差分就够了。
但要注意:差分会损失信息。每做一次差分,就少一个观测值。而且过度差分会导致序列变得“太干净”,丢失了有用的低频成分。
| 差分阶数 | 适用场景 | 注意事项 |
|---|---|---|
| 一阶差分 | 价格→收益率 | 最常用,适用于大多数金融时间序列 |
| 二阶差分 | 加速度或变化率的变化 | 高频数据中很少用,噪声放大严重 |
| 季节差分 | 消除周期性模式 | 高频数据中注意周期长度选择 |
# Python代码示例:差分操作
import pandas as pd
# 模拟价格序列
dates = pd.date_range('2024-01-01', periods=1000, freq='5min')
price = 100 + np.cumsum(np.random.randn(1000) * 0.1)
# 创建DataFrame
df = pd.DataFrame({'price': price}, index=dates)
# 一阶差分(收益率)
df['return'] = df['price'].diff()
# 季节差分(假设日周期,252个5分钟bar)
df['seasonal_diff'] = df['price'].diff(252)
# 组合差分
df['combined'] = df['price'].diff().diff(252)
print(df.head(10))
实战技巧:我建议在做差分之前,先做ADF检验确定差分阶数。另外,高频数据中我常用对数收益率(log return)而不是简单收益率,因为对数收益率在统计性质上更接近正态分布。
1.6 本章知识体系
下面这张图总结了本章的核心逻辑,我画了很久,希望能帮大家理清思路:
这张图把本章的四个核心概念串起来了。你从中心出发,先理解平稳性,然后学会用ACF/PACF分析序列结构,再用滞后算子做代数表达,最后用差分操作实现平稳化。每一步都是下一步的基础。
1.7 本章小结
好了,这一章的内容就到这里。咱们回顾一下:
- 平稳性是时间序列建模的前提,ADF检验是判断平稳性的标准工具
- ACF和PACF能帮你识别序列的依赖结构,是模型定阶的关键
- 滞后算子让复杂的差分和模型表达变得简洁
- 差分操作是把非平稳序列变平稳的实用方法
说实话,这些内容看起来简单,但真正用好需要大量实践。我建议你找一段真实的高频数据,自己动手跑一遍ADF检验,画一画ACF/PACF图,再做几次差分操作。只有亲手做过,才能真正理解。
一句话总结:不平稳的数据,再漂亮的模型也是空中楼阁。先把地基打牢,后面的事情自然水到渠成。