第二章:金融时间序列特性
各位同学,今天我们来聊聊金融时间序列的那些“脾气”。
做量化交易,说白了就是跟数据打交道。但金融数据跟物理实验数据不一样——它有自己的性格,甚至有点“任性”。我刚开始做策略回测时,就吃过不少亏。明明策略在历史数据上跑得漂亮,一上实盘就崩。后来才发现,是我没摸透时间序列的特性。
这一章,我们就来把这些特性一个一个拆开看。
核心要点:金融时间序列的四大特性——平稳性、自相关性、随机性、波动聚集性。搞懂它们,你就能避开80%的坑。
2.1 平稳性与非平稳性
先问一个问题:股价是平稳的吗?
答案很明确——不是。股价会涨会跌,长期趋势明显,均值也在变。这就是典型的非平稳序列。
那什么是平稳序列?我个人的理解很简单:统计性质不随时间变化。比如白噪声,均值恒定,方差稳定,没有趋势。
为什么我们要关心这个?因为大多数统计模型(比如ARIMA、GARCH)都要求数据是平稳的。你用非平稳数据去建模,结果就是伪回归——看着相关,其实毫无意义。
我在项目中遇到过一位同事,用股价直接跑回归,R²高达0.9,兴奋得不行。结果我一测残差,有明显的单位根。嗯,那模型基本废了。
如何判断平稳性?
两种常用方法:
- 肉眼观察法:看时序图。有趋势、有季节性波动,基本就是非平稳。
- 统计检验法:ADF检验(Augmented Dickey-Fuller Test)。
ADF检验的原假设是“存在单位根”(即非平稳)。p值小于0.05,就拒绝原假设,认为序列平稳。
import pandas as pd
import numpy as np
from statsmodels.tsa.stattools import adfuller
# 模拟一个随机游走(非平稳)
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}')
# p值大概率 > 0.05,说明非平稳
# 一阶差分后
diff_series = np.diff(random_walk)
result_diff = adfuller(diff_series)
print(f'差分后p值: {result_diff[1]:.4f}')
# 差分后p值 < 0.05,变成平稳序列
个人经验:我习惯对收益率做分析,而不是价格。收益率通常接近平稳,而价格几乎永远非平稳。你想想看,如果两个不相关的股票价格都上涨,它们的相关系数可能很高——但这只是伪相关。
2.2 自相关与偏自相关
自相关,说白了就是“今天的价格跟昨天的价格有没有关系”。
金融数据里,短期自相关通常很弱。但如果你做高频交易,你会发现订单流有很强的自相关——大单后面往往跟着更多大单。
偏自相关呢?它是在剔除了中间滞后项的影响后,两个时间点之间的直接相关性。比如,今天和前天之间的偏自相关,就是去掉“今天和昨天”、“昨天和前天”这些间接关系后的纯相关。
这两个指标有什么用?
- ACF(自相关函数):判断MA模型的阶数
- PACF(偏自相关函数):判断AR模型的阶数
我曾经用ACF/PACF图来识别一个股指期货的日内模式。发现滞后1阶的ACF显著不为0,说明存在一阶自回归结构。后来用AR(1)模型做预测,效果还不错。
from statsmodels.graphics.tsaplots import plot_acf, plot_pacf
import matplotlib.pyplot as plt
# 生成一个AR(1)过程
np.random.seed(42)
ar1 = np.zeros(500)
for t in range(1, 500):
ar1[t] = 0.7 * ar1[t-1] + np.random.randn()
fig, (ax1, ax2) = plt.subplots(2, 1, figsize=(10, 6))
plot_acf(ar1, lags=20, ax=ax1)
plot_pacf(ar1, lags=20, ax=ax2)
plt.show()
避坑指南:我曾经在分析日收益率时,看到ACF在滞后5阶处显著,以为发现了什么周期规律。后来才发现是数据里有一个异常值——某天市场暴跌5%。去掉这个点后,自相关就消失了。所以,做自相关分析前,一定要先处理异常值。
2.3 白噪声与随机游走
白噪声,是时间序列的“纯净版”。每个时间点的值独立同分布,均值为0,方差恒定。说白了,就是完全随机,没有任何可预测性。
随机游走呢?它是白噪声的累积和。今天的值 = 昨天的值 + 白噪声。股价走势就非常接近随机游走。
为什么说“接近”?因为实际股价有微小的时间相关性、波动聚集性,不是完美的随机游走。但有效市场假说认为,价格已经反映了所有信息,未来变化不可预测——这本质上就是随机游走。
我个人觉得,理解随机游走是量化交易的第一课。如果你相信市场是随机游走,那任何技术分析都是徒劳。但如果你能找到偏离随机游走的模式——比如短期动量、均值回复——那就是你的alpha来源。
# 模拟随机游走 vs 白噪声
np.random.seed(42)
white_noise = np.random.randn(500)
random_walk = np.cumsum(white_noise)
# 检验白噪声:Ljung-Box检验
from statsmodels.stats.diagnostic import acorr_ljungbox
lb_test = acorr_ljungbox(white_noise, lags=[10])
print(f'白噪声的Ljung-Box p值: {lb_test["lb_pvalue"].values[0]:.4f}')
# p值 > 0.05,不能拒绝白噪声假设
lb_test_rw = acorr_ljungbox(random_walk, lags=[10])
print(f'随机游走的Ljung-Box p值: {lb_test_rw["lb_pvalue"].values[0]:.4f}')
# p值 < 0.05,拒绝白噪声假设(因为随机游走有强自相关)
关键区别:白噪声是“无记忆”的,随机游走是“有记忆”的——它记得自己走过的所有路径。这就是为什么随机游走是非平稳的,而白噪声是平稳的。
2.4 ARCH效应检验
最后,也是最关键的一个特性——波动聚集性。
你有没有发现,股市大跌之后往往接着大跌?大涨之后也容易继续大涨?这不是价格在聚集,而是波动率在聚集。高波动时期和低波动时期会交替出现。
这就是ARCH效应(自回归条件异方差)。简单说:波动率本身是自相关的。
为什么重要?因为很多模型假设方差恒定,但金融数据的方差明显在变。你用恒定方差的模型去预测,置信区间会严重失真。
检验ARCH效应的方法:
- 对收益率序列拟合一个均值模型(比如ARMA)
- 提取残差
- 对残差的平方做Ljung-Box检验或LM检验
如果残差平方存在自相关,就说明有ARCH效应。
from arch import arch_model
# 模拟一个ARCH(1)过程
np.random.seed(42)
n = 1000
omega = 0.1
alpha = 0.7
epsilon = np.random.randn(n)
sigma2 = np.zeros(n)
returns = np.zeros(n)
sigma2[0] = omega / (1 - alpha)
returns[0] = np.sqrt(sigma2[0]) * epsilon[0]
for t in range(1, n):
sigma2[t] = omega + alpha * returns[t-1]**2
returns[t] = np.sqrt(sigma2[t]) * epsilon[t]
# ARCH效应检验:使用arch包的LM检验
am = arch_model(returns, vol='ARCH', p=1)
res = am.fit(disp='off')
print(res.summary())
# 也可以手动检验残差平方的自相关
residuals = returns - np.mean(returns)
squared_resid = residuals**2
lb_test_arch = acorr_ljungbox(squared_resid, lags=[10])
print(f'残差平方的Ljung-Box p值: {lb_test_arch["lb_pvalue"].values[0]:.4f}')
# p值 < 0.05,说明存在ARCH效应
我的习惯:在做任何波动率模型之前,我都会先跑一遍ARCH效应检验。如果p值大于0.05,那GARCH模型基本不用考虑了——数据不支持。反过来,如果p值显著,那恭喜你,波动率建模有戏。
知识体系总览
下面这张图,是我自己整理的知识框架。你可以把它当作本章的“地图”。
这张图把四个核心特性串起来了。你会发现,它们之间是有逻辑关系的:先判断平稳性,再分析自相关结构,然后检验随机性,最后看波动率是否有聚集效应。每一步都为下一步做准备。
最后提醒一句:不要跳过这些检验直接建模。我曾经见过有人用非平稳数据跑GARCH,结果模型参数不稳定,预测结果时好时坏。花10分钟做检验,能省你10小时调试模型的时间。
公众号:蓝海资料掘金营,微信deep3321