金融时间序列建模全流程:从平稳性检验到GARCH波动率预测

发布时间:2026/10/14 0:48:41
金融时间序列建模全流程:从平稳性检验到GARCH波动率预测 1. 从第一节课到第四章金融时间序列到底在研究什么如果你正在啃金融相关的课程大概率会在一门叫《金融时间序列分析》或类似名字的课上卡住。这门课和传统统计学、计量经济学都不一样它研究的是股票收益率、汇率波动、债券利差这类有“先后顺序”的金融数据核心目标不是给人解释过去而是从历史数据里提炼规律给未来定价和风控提供依据。我当年学这门课的时候最大的感受是教材里全是公式推导但真到了自己拿数据上手第一反应是懵的。第四章通常是一个分水岭——前面的章节在铺垫概率论和统计基础到这一章才开始真正进入“金融时间序列”的核心如何把收益率序列建模、如何判断一个序列有没有记忆性、如何在随机波动中做出有意义的预测。这篇文章我会用一份课程笔记的视角把金融时间序列建模从数据预处理、平稳性检验、ARMA/ARIMA建模到波动率模型这条路完整讲一遍再附上我实际踩过的一些坑。无论你是刚学完概率论准备入门量化还是已经在做因子研究但建模部分一直停留在调包这篇内容应该都能帮你把“为什么这么做”补上。1.1 金融时间序列和普通统计数据的本质区别普通统计分析往往假设样本之间是独立的比如调查问卷里1000个人的身高理论上彼此不影响。但金融数据完全不同今天的股价和昨天的股价高度相关或者说今天的波动会让明天的波动也变大。这就是所谓的“时序依赖”。除此之外金融时间序列还有几个典型特征第一收益率序列的波动忽大忽小大波动后面往往跟着大波动小波动后面跟着小波动这叫波动率聚集效应第二极端值出现的概率比正态分布预测的要高也就是“肥尾”第三序列通常不是平稳的价格序列往往有趋势直接建模容易得到伪回归。所以处理金融数据第一件事就是彻底忘掉“样本独立”这个假设所有模型都要围绕自相关性、条件异方差性来重新组织思路。1.2 一条完整的金融时间序列建模流水线拿到任何金融序列不管是用Python还是R建模流程基本是固定的。我把第四章的笔记浓缩成一张流程清单后面每一步都按这个走数据获取与清洗除权除息处理、缺失值填补、异常值剔除。可视化与描述统计看走势图、自相关图、分布直方图。平稳性检验ADF检验、KPSS检验判断是否需要进行差分。模型识别与定阶观察ACF和PACF图结合信息准则定阶。参数估计与诊断拟合模型检验残差是否为白噪声。预测与回测用滚动窗口评估样本外表现。很多人拿到数据后直接跳去调包做预测这是最容易翻车的做法。金融时序模型对数据质量极度敏感缺一个异常值可能整个自相关系数的估计就变了。2. 拿到数据先别急建模平稳性检验是第一道砍2.1 数据清洗里的几个隐形陷阱我最早做课程作业时直接下载了一段日线收盘价就开始建模结果跑出来的模型预测结果离谱。后来才发现问题出在数据预处理上。金融数据清洗有三个坑特别容易忽略。一是复权问题。股票分红、送股之后价格会跳空如果你直接用原始价格序列相当于引入了一个“假信号”。做时间序列建模之前应该使用前复权或后复权数据让价格序列具有连续性。二是非交易日缺口。有些数据集把周末和节假日直接留空有些则会填充前值。如果直接填充你要确保自己知道填充的逻辑否则自相关性会被人为抬高。最稳妥的做法是把日期索引转成连续的时间戳再检查缺失率。三是极端值处理。比如一笔错误报价把收益率拉到±50%这种值如果不处理ACF和PACF图会被严重污染。我的习惯是先画出收益率分布图把超过5倍标准差的值筛出来人工确认是真实事件就保留是数据错误就剔除。2.2 平稳性到底意味着什么ADF检验怎么读平稳性分为严平稳和弱平稳。实际建模中我们只需要关心弱平稳也就是均值和自协方差不随时间变化。打个比方扔硬币的结果序列是平稳的因为每次的概率都一样过去的任何信息都不能帮你预测下一次是正是反。而股价序列不是平稳的因为今天的价格包含了既往所有信息一直在漂移。ADF检验是最常用的平稳性检验工具。Python里执行一次很简单from statsmodels.tsa.stattools import adfuller result adfuller(returns) print(fADF统计量: {result[0]:.4f}) print(fp值: {result[1]:.4f}) print(f临界值: {result[4]})关键看p值。如果p值小于0.05拒绝原假设说明序列是平稳的。对于对数收益率序列绝大多数情况下ADF检验都能直接通过因为收益率的均值围绕0附近波动不像价格序列那样有趋势。如果你发现自己拿到的收益率序列ADF不通过先回头检查是不是包含了未复权的价格或异常跳变。提示ADF检验的原假设是“序列存在单位根即非平稳”。很多初学者会记反一定要记住p值小才是平稳。2.3 差分操作与单整阶数的确定如果序列不平稳最常见的处理是差分。一阶差分后的序列平稳就说原序列是一阶单整记作I(1)。股价序列通常就是I(1)对数收益率其实就是价格序列的一阶差分所以它天然是平稳的。很多教材会把差分简单理解成“让序列变平稳的工具”但你要知道为什么。差分实际上是在移除序列中的趋势成分。对于带有随机游走性质的资产价格价格等于昨天的价格加上一个随机扰动这个随机扰动就是收益率一阶差分后剩下的正是这个扰动也就是我们要建模的对象。我自己的经验是能不差分就不差分因为差分会损失信息而且差分后的模型解释起来更绕。真正需要差分的场景是对数价格趋势项明显、且自相关在低阶衰减很慢的情况。如果你只是为了预测收益率直接用日收益率序列就够了不需要额外差分。3. ARMA与ARIMA建模收益率序列的精简武器3.1 从ACF和PACF判断模型类型把收益率序列稳定下来之后下一步是看它是否有自相关性。如果收益率之间完全独立那它的最优预测就是历史均值没必要建模。但现实中的收益率序列往往存在短期自相关特别是高频数据或某些特定资产这就轮到ARMA模型登场。ARMA(p,q)由自回归部分AR(p)和移动平均部分MA(q)组成。判断p和q最直接的工具就是ACF自相关函数和PACF偏自相关函数。如果ACF在q阶之后截尾PACF拖尾适合MA(q)模型如果PACF在p阶之后截尾ACF拖尾适合AR(p)模型如果两者都拖尾考虑ARMA(p,q)。说人话截尾就是相关系数在某一阶之后突然变成不显著拖尾就是相关系数慢慢衰减像一条逐渐平息的尾巴。from statsmodels.graphics.tsaplots import plot_acf, plot_pacf plot_acf(returns, lags40) plot_pacf(returns, lags40)但是请注意实际数据的ACF和PACF很少像教科书那么标准。我曾经看过一个收益率的ACF图在滞后7阶有个明显的尖峰其他都基本为零按规则应该选MA(7)但跑出来的模型怎么看怎么别扭。后来才知道那只是某次数据异常造成的假相关。图形判断只提供线索最终要靠信息准则来确认。3.2 信息准则定阶AIC和BIC的使用心得定阶不能完全靠肉眼看图还需要用AIC或BIC做量化比较。这两种准则都在惩罚模型复杂度BIC的惩罚力度更大倾向于选择更精简的模型。实操时我会写一个循环遍历p和q在0到5之间的所有组合记录每个模型的AIC和BICimport itertools import warnings from statsmodels.tsa.arima.model import ARIMA warnings.filterwarnings(ignore) best_aic float(inf) best_order None for p, q in itertools.product(range(6), range(6)): try: model ARIMA(returns, order(p, 0, q)) result model.fit() if result.aic best_aic: best_aic result.aic best_order (p, q) except Exception: continue print(f最优阶数: p{best_order[0]}, q{best_order[1]}, AIC{best_aic:.4f})不过要提醒自己AIC选出来的模型往往偏复杂尤其是在样本量大的时候。金融数据动辄几千条如果AIC选出了ARMA(5,4)这种高阶模型要格外小心过拟合。我的习惯是用BIC选一个作为基准模型再用AIC选一个作为对比模型如果两者预测效果接近就选BIC推荐的简化版。3.3 残差诊断Ljung-Box检验与QQ图模型拟合完之后残差诊断是绝对不能省的一步。如果残差里还有自相关结构说明模型没有把信息提取干净需要重新定阶。主流做法是Ljung-Box检验。from statsmodels.stats.diagnostic import acorr_ljungbox lb_test acorr_ljungbox(result.resid, lags[10, 20], return_dfTrue) print(lb_test)p值大于0.05说明残差是白噪声模型拟合可接受。如果p值很小说明信息残留需要调整阶数。同时我还会画残差的QQ图看是否接近正态分布。虽然ARMA模型不要求残差严格正态但明显的偏态会让后续的置信区间失真。实操心得残差诊断要在多个滞后阶数上做不要只看一个。我遇到过滞后5阶白噪声、滞后20阶却显著的情况说明模型在高频周期上还有遗漏。用10和20两个滞后是做诊断的起始值。3.4 滚动窗口预测样本外检验才是真本事很多人建模时模型训练集误差很低一拿去预测真实未来就垮掉。金融时序建模里唯一有说服力的评估方式是滚动时间窗口。思路很简单固定一个窗口长度比如500个交易日每次往前预测1步或5步把预测值和真实值对比然后滚动到下一期重复这个过程最后汇总误差指标。from sklearn.metrics import mean_absolute_error window 500 preds [] actuals [] for end in range(window, len(returns)): train returns[end - window:end] model ARIMA(train, order(best_order[0], 0, best_order[1])) fit model.fit() pred fit.forecast(steps1)[0] actual returns[end] preds.append(pred) actuals.append(actual) mae mean_absolute_error(actuals, preds) print(f滚动预测MAE: {mae:.6f})做完滚动预测再看实际效果你会发现收益率预测的误差大得吓人。这很正常金融时序的预测上限本身就低。ARMA模型对收益率的预测能力非常有限它的价值更多体现在捕捉短期动量或均值回复效应上而不是让你靠它稳定盈利。如果你的目的是预测波动率那就要进入下一章的内容了。4. 收益率预测价值有限波动率建模才是真功夫4.1 波动率聚集与金融序列的“肥尾”现象金融时间序列教材里第四章后面几乎都会马上引入ARCH和GARCH因为仅仅对收益率建模远远不够。收益率预测不了但收益率的波动率是可以被建模和预测的而且波动率预测在风险管理、期权定价、投资组合优化里非常关键。先看一个直观现象市场大跌之后往往还有大跌大涨之后波动也不会马上平息。这就是波动率聚集效应也就是大的变化倾向于跟随大的变化小的变化倾向于跟随小的变化。如果用ARMA模型处理这种情况残差不会表现出线性自相关但残差的平方会表现出显著自相关这就是ARCH效应的信号。肥尾和波动率聚集是一体两面的。正态分布预测的极端值概率太低了而真实收益率数据里极端值出现得比正态预测频繁得多。因为波动率本身在变当波动率高的时候收益率就像被“放大”了一样。4.2 如何检验ARCH效应对收益率序列判断是否需要用GARCH模型先做ARCH效应检验。最实用的方法是做残差的平方自相关检验或者直接使用ARCH检验。from statsmodels.stats.diagnostic import het_arch arch_test het_arch(returns, nlags10) print(fLM统计量: {arch_test[0]:.4f}) print(fp值: {arch_test[1]:.4f})如果p值很小说明序列存在显著的ARCH效应这时候就可以考虑GARCH模型了。另一个直观的方法是看残差平方的ACF图如果滞后阶上出现明显的相关性同样是ARCH效应存在的信号。4.3 GARCH(1,1)波动率建模的基准模型GARCH(1,1)是使用最广泛的波动率模型表达式为sigma_t^2 omega alpha * epsilon_{t-1}^2 beta * sigma_{t-1}^2通俗解释今天的波动率由三部分构成——长期平均波动率、昨天的新息冲击、昨天的波动率水平。alpha衡量的是对新信息的敏感度beta衡量的是波动率衰减的持续性。用Python拟合如下from arch import arch_model garch arch_model(returns * 100, volGARCH, p1, q1, meanAR, lags1) garch_fit garch.fit(dispoff) print(garch_fit.summary())注意我在这里把收益率乘以100这是为了数值稳定性。原始收益率可能只有0.001量级平方之后会非常小导致优化器收敛困难。乘以100后相当于把波动率调整到百分比量级模型更容易收敛参数解读也更直观。4.4 GARCH族扩展EGARCH、GJR-GARCH怎么选GARCH(1,1)有一个著名假设波动率对好消息和坏消息的响应是对称的。但实际市场存在“杠杆效应”即利空消息对波动率的冲击比利好消息更大。这时候要用非对称模型。GJR-GARCH在模型中引入一个虚拟变量捕捉好消息和坏消息的不对称性EGARCH对波动率取对数建模不要求参数非负也能捕捉不对称性。我的经验是对股票收益率序列非对称效应几乎总是显著的。如果你用GARCH(1,1)做出来但觉得拟合不够好可以先试试GJR-GARCH模型复杂度没有明显增加但对波动率尖峰的把控更好。注意事项GARCH家族的参数估计非常依赖初始值有时候会收敛到局部最优。建议多试几组随机种子或更换优化算法对比结果是否稳定。如果omega为负或者alphabeta大于1说明模型可能选错了。5. 一次完整的建模实战从数据到报告的复现流程5.1 案例背景与数据准备为了把这套流程完整串起来我用模拟项目X做一次全流程演练。模拟项目X的原始数据是某指数在过去5年的日收盘价共约1250个观测值。目标是构建一个收益率均值和波动率的联合预测模型。第一步是加载数据并计算对数收益率import pandas as pd import numpy as np df pd.read_csv(simulated_index.csv, parse_dates[date], index_coldate) close df[close] log_returns np.log(close / close.shift(1)).dropna()对数收益率的好处是它在数学上可以累加并且可以让序列更接近正态分布。当然这只是转换收益率的计算方式不会改变数据本身的分布特征。5.2 平稳性检验与ARMA定阶先做ADF检验from statsmodels.tsa.stattools import adfuller result adfuller(log_returns) print(fADF p值: {result[1]:.4f})p值小于0.01可以认为对数收益率序列平稳。接着看ACF和PACF图滞后1阶到3阶都出现了明显的相关性之后快速衰减。用AIC遍历找到的模型是ARMA(2,2)但BIC更倾向于ARMA(1,1)。我用滚动窗口对比了两个模型发现ARMA(1,1)在样本外的MAE略高一点点但差异小到可以忽略。最终选择ARMA(1,1)因为参数更少、更稳健。拟合ARMA(1,1)后对残差做了Ljung-Box检验from statsmodels.tsa.arima.model import ARIMA from statsmodels.stats.diagnostic import acorr_ljungbox arma_model ARIMA(log_returns, order(1, 0, 1)) arma_result arma_model.fit() lb acorr_ljungbox(arma_result.resid, lags[10, 20], return_dfTrue) print(lb)滞后10阶和20阶的p值都大于0.05说明残差没有明显的自相关性均值模型部分过得去。5.3 加入GARCH模型联合建模但对残差的平方再检验时het_arch检验的p值远小于0.01说明存在ARCH效应。于是把均值部分和波动率部分放在一起建模from arch import arch_model final_model arch_model( log_returns, meanAR, lags1, volGARCH, p1, q1 ) final_result final_model.fit(dispoff) print(final_result.summary())最终得到的alpha约0.08beta约0.89alphabeta约为0.97说明波动率衰减速度较慢一个冲击的影响会持续很久。这个数字在股票市场数据里非常典型波动率是高持续的。5.4 波动率预测的实际应用VaR计算波动率模型最有价值的应用之一是计算VaR。在正态性假设下95%置信水平的单日VaR可以近似为VaR -均值 - 1.65 * 预测波动率这意味着明天有95%的把握损失不会超过这个值。用上面拟合的GARCH模型做滚动预测得到每日的条件波动率序列再代入公式得到动态VaRforecasts final_result.forecast(start1000, horizon1) cond_var forecasts.variance.dropna() cond_vol np.sqrt(cond_var.iloc[:, 0]) var_95 -1.65 * cond_vol print(f最后一期95% VaR: {var_95.iloc[-1]:.2%})你会发现传统历史均值VaR是固定的而GARCH给出的VaR是高波动时期扩大、低波动时期收缩。这就是条件波动率的优势能根据最新的市场信息调整风险预估。需要强调的是这个例子里的VaR计算采用的是简化正态假设实际风险管理中还会结合历史模拟法或极值理论做修正。但作为课程笔记和入门实践这条路径已经把金融时间序列建模的核心环节全部趟了一遍。6. 那些年我踩过的坑整理成了一份避坑速查表6.1 常见问题与解决方案问题表现解决方案ADF检验总是不通过p值始终大于0.05检查是否用了未复权价格尝试对数差分检查样本区间是否包含极端跳空ACF和PACF图都拖尾定阶困难用AIC/BIC遍历对比若干组(p,q)不要只看图形模型残差Ljung-Box通过但平方残差不通过忽略ARCH效应做ARCH检验均值模型与GARCH模型联合建模GARCH模型不收敛optimizer返回错误或参数为负收益率乘以100更换优化器增加迭代次数简化模型阶数预测结果“太完美”训练集表现远好于测试集检查是否泄漏未来信息使用滚动窗口评估警惕高阶模型过拟合出现伪回归两个非平稳序列回归R平方很高先做协整检验确保变量平稳后再建模6.2 三条独家实操心得第一处理金融数据时一定要保留数据来源和预处理步骤的记录。金融数据源经常会更新或修正历史数据如果没有快照你的结果可能过几个月就无法复现了。我后来做任何数据分析项目第一步就是生成数据的哈希值并保存预处理脚本至少保证自己随时能复现。第二不要迷信某个模型“先进”或“更高级”。在金融时序分析里GARCH(1,1)和ARMA(1,1)这种简单模型在预测中表现稳定反而一些复杂模型因为参数过多样本外表现一塌糊涂。先跑通简单模型再逐步扩展这个顺序永远不会错。第三收益率序列建模的天花板需要明确认知。课程里教的模型能让我们理解数据背后的规律但在实际交易中靠单纯的时序模型做收益率预测几乎不可能稳定盈利。这些模型真正的主战场是风险管理和资产配置。把课程内容和应用场景对不上号是新手最容易产生的认知落差。6.3 给课程学习者的进阶建议如果你是配合课程看这篇笔记我强烈建议私下做一件事找一段自己感兴趣的资产数据把上面整个流程完整跑一遍然后试着把模型输出的结果和课程里的理论概念一一对应。所有知识点通过一组真实数据串起来之后才算是真正学会了。至于后续还可以怎么扩展路径也很清晰把单变量ARMA-GARCH扩展到多元的DCC-GARCH去研究多个资产间的动态相关性或者把均值方程里的AR换成机器学习模型如随机森林或LSTM再用GARCH族模型去拟合残差的波动率。这种混合架构在学术和工业界都有不少探索可以作为下一个阶段的练习方向。我个人在把这套流程跑通之后最大的体会是金融时间序列建模的门槛不在数学推导而在对整个分析流程每条决策背后原因的理解。当你遇到一个数据异常时能立刻判断出該回到哪一步重新处理才是从“会调包”走向“会建模”的真正分水岭。

关于本文作者

来自尧图内容编辑团队

尧图内容编辑团队 内容团队

尧图内容编辑团队

本文由尧图网络内容编辑团队执笔。团队由资深项目经理、前端工程师与设计师组成,所有内容均来自亲手交付的真实项目,先讲清问题、再给出可落地的解法。尧图深耕北京网站建设十年,服务过京华建材集团、智造科技等各行业客户,把一线经验沉淀为可复用的行业观察。

  • 十年建站经验,覆盖建材、制造、服务、文创等
  • 项目经理把关选题与事实准确性
  • 工程师与设计师联合撰写专业细节
  • 统一编辑规范,保证文风与排版一致
  • 每月复盘转化数据,迭代选题方向

延伸阅读

相关资讯与近期热门内容

深度阅读推荐

建站决策前值得细读的三篇

网站改版的5个关键决策
2024-08-12

网站改版的5个关键决策

什么时候该改版、改到什么程度、如何避免流量掉光,京华建材集团改版复盘给出答案。

获取专属建站方案

看完文章,把您的行业与预算告诉我们,免费获取一份量身定制的官网建设方案与报价。

立即免费咨询