ARTICLE DETAIL

资讯详情

深耕商务建站与企业官网运营的一线实战洞察。

波动率预测实战:ARCH族与Heston模型对比与Python实现

波动率预测实战:ARCH族与Heston模型对比与Python实现 1. 为什么波动率预测值得单独拎出来做做量化研究的人迟早会撞上一堵墙收益率的方向极难预测但波动率却呈现出相当明显的聚集性和均值回归特征。这个不对称性恰恰是波动率建模最有意思的地方。我最初接触这块内容是因为一个做期权对冲的朋友抱怨用历史标准差算出来的对冲比例总是滞后于市场行情剧烈的时候对冲不足行情平淡的时候又过度对冲来回被摩擦成本吃掉利润。后来我们把方向转到动态波动率预测上用ARCH族模型和Heston模型分别做了一套流程情况才有所改善。所谓动态预测波动率核心思路是承认波动率不是常数而是随时间变化、并且今天的波动会受到昨天波动的影响。ARCH模型自回归条件异方差模型和Heston模型是两条典型路线前者从离散时间序列出发用过去的残差平方来刻画当前方差后者从连续时间随机波动率出发把方差本身建模成一个随机过程并且允许方差和价格之间存在相关性。两者解决的问题是同一个——给未来的波动率一个可量化、可回测的估计值但建模哲学、数据要求、实现难度完全不同。这篇内容适合三类人看一是刚接触金融时间序列、想搞明白ARCH到底在算什么的新手二是有一定统计基础、准备把波动率预测接入期权定价或风险管理的从业者三是想对比离散模型和连续模型实际表现、需要一套可复现流程的研究者。我会把两条路线的原理、参数估计、实操步骤、踩坑经验都摊开讲代码用Python数据用模拟生成的收益率序列保证你可以直接抄作业跑通。需要提前说明的是波动率预测没有银弹。ARCH族模型对数据频率敏感Heston模型对参数校准的初值极其挑剔两者在实际使用中都需要大量调试。我下面分享的是基于常见实践总结出来的一套相对稳妥的做法不是教科书式的唯一答案。2. 两条技术路线的整体设计与选型考量2.1 ARCH族模型从离散残差里挖信息ARCH模型的基本直觉非常朴素如果昨天市场大幅波动今天大概率也不会太平静。用数学语言说就是条件方差依赖于过去若干期的残差平方。最基础的ARCH(p)模型把当前方差写成过去p期残差平方的线性组合但它的一个明显缺陷是——要刻画长记忆性p需要取得很大参数一多就不好估计。于是有了GARCH模型在ARCH的基础上加入了过去方差的滞后项相当于用更少的参数捕捉了更长的记忆。我实际用下来GARCH(1,1)几乎是默认起点绝大多数金融收益率序列用这一个配置就能拿到不错的拟合效果。再往上还有EGARCH和GJR-GARCH专门处理杠杆效应——也就是负收益冲击对方差的抬升比正收益更明显这个现象。股票类数据里这个效应很常见如果你做的是股指或者个股建议直接上带杠杆项的变体别在对称GARCH上浪费时间。选ARCH族模型的理由很直接实现简单、参数少、可解释性强、对中等规模数据友好。缺点是它把波动率当成一个由历史决定的确定性函数没有给方差本身的随机性留空间长期预测会趋于均值对极端行情的刻画偏弱。2.2 Heston模型把方差也当成随机过程Heston模型的出发点不一样。它假设标的资产价格服从带随机波动率的扩散过程方差本身遵循一个均值回归的平方根过程同时价格和方差之间存在相关系数ρ。这个设定带来了两个好处一是方差不会变成负数平方根项起到了保护作用二是可以刻画波动率微笑——不同行权价的隐含波动率不一致这个现象在Black-Scholes框架下是解释不了的。代价是参数变多了均值回归速度κ、长期方差θ、波动率的波动率σ、相关系数ρ加上初始方差v0一共五个参数。这五个参数没法像GARCH那样直接用极大似然一步估出来通常要用期权市场价格做校准或者用收益率序列做滤波估计。校准过程对初值敏感目标函数还容易陷入局部最优这是Heston落地时最大的拦路虎。我选择把两者放在一起讲是因为它们在实际工作中往往是互补的GARCH适合做短期的、基于历史数据的方差预测Heston适合做需要和期权价格保持一致的场景。下面这张表是我总结的选型对照你可以根据自己的数据条件和应用目标对号入座。对比维度ARCH族以GARCH(1,1)为代表Heston随机波动率时间设定离散连续核心参数数量3个左右5个数据要求单一收益率序列即可收益率序列或期权价格面板估计难度低极大似然直接优化高需校准且对初值敏感长记忆刻画一般长期预测回归均值由均值回归速度控制杠杆效应需用EGARCH/GJR变体通过相关系数ρ内建典型应用风险管理、短期波动预测期权定价、波动率微笑拟合2.3 为什么不用单一模型打天下有人会问既然Heston更高级为什么不直接用Heston我的经验是模型复杂度要和数据质量、使用场景匹配。如果你手上只有一段日频收益率没有可靠的期权报价Heston的校准基本是空中楼阁五个参数里至少有两三个估不准。反过来如果你要做的是期权做市或者结构化产品定价只用GARCH预测出来的方差去套Black-Scholes会系统性低估尾部风险。所以我的建议是分场景纯历史数据、追求稳健和可解释走GARCH路线有期权数据、需要和市场价格对齐走Heston校准路线。两者都跑一遍做交叉验证反而能帮你发现数据里的异常。接下来我按这个思路把两条路线的细节分别拆开。3. ARCH族模型的核心细节与实操要点3.1 数据预处理平稳性是第一道关收益率序列进模型之前必须先做平稳性检验。ARCH类模型要求的是残差序列而残差来自均值方程。我的标准流程是先对价格取对数收益率然后用ADF检验判断序列是否平稳不平稳就差分接着拟合一个简单的均值方程常数项或者AR(1)把残差拿出来做ARCH效应检验。这里有个新手常犯的错误直接拿原始价格序列去拟合GARCH。价格是非平稳的拟合出来的结果毫无意义。另一个坑是均值方程选得太复杂ARMA阶数堆得很高结果残差里反而混入了模型设定误差ARCH效应检验失真。我的做法是均值方程尽量简单常数均值或者AR(1)足够把复杂度留给方差方程。ARCH效应检验用Ljung-Box检验残差平方的自相关性或者用Engle的LM检验。如果p值很小说明残差平方存在显著自相关可以上ARCH族模型如果检验不显著说明没有明显的条件异方差硬上GARCH只会过拟合。import numpy as np import pandas as pd from statsmodels.tsa.stattools import adfuller from statsmodels.stats.diagnostic import acorr_ljungbox # 模拟一段带波动聚集的收益率序列 np.random.seed(42) n 2000 omega, alpha, beta 1e-6, 0.08, 0.90 returns np.zeros(n) variance np.zeros(n) variance[0] omega / (1 - alpha - beta) for t in range(1, n): variance[t] omega alpha * returns[t-1]**2 beta * variance[t-1] returns[t] np.random.normal(0, np.sqrt(variance[t])) # 平稳性检验 adf_result adfuller(returns) print(fADF统计量: {adf_result[0]:.4f}, p值: {adf_result[1]:.4f}) # 残差平方的Ljung-Box检验 lb_result acorr_ljungbox(returns**2, lags[10], return_dfTrue) print(lb_result)这段代码里我特意用GARCH(1,1)过程生成了数据这样后面拟合时能对照真实参数看估计精度。ADF的p值应该远小于0.05Ljung-Box的p值也应该很小两个条件都满足才继续往下走。3.2 模型定阶与参数估计别迷信信息准则定阶这件事AIC和BIC是常用工具但我在实操中发现金融收益率序列上信息准则给出的最优阶数往往偏高而且不同样本区间结论不稳定。更务实的做法是先固定GARCH(1,1)作为基准看参数是否显著、残差是否还有ARCH效应如果残差检验通过就别再折腾高阶模型了。GARCH(1,1)在绝大多数场景下已经够用这是业界共识不是偷懒。参数估计用极大似然。GARCH模型的似然函数是非线性的优化器选择有讲究。我一般用BFGS或者Nelder-Mead并且给参数加约束omega0alpha≥0beta≥0alphabeta1保证方差平稳。alphabeta接近1说明波动率持续性很强这在股指数据里很常见如果估计出来大于等于1说明模型设定有问题或者数据里有结构突变需要回头检查。from arch import arch_model # 拟合GARCH(1,1)均值方程用常数 am arch_model(returns * 100, volGarch, p1, q1, meanConstant, distNormal) res am.fit(dispoff) print(res.summary()) # 提取条件波动率 conditional_vol res.conditional_volatility / 100注意这里我把收益率放大了100倍再拟合这是为了数值稳定性——原始收益率量级太小优化器容易在容差附近震荡。拟合完再把波动率除回去。这个细节很多教程不会提但实测下来对收敛帮助很大。3.3 杠杆效应处理EGARCH和GJR怎么选如果残差诊断发现负冲击对方差的抬升明显强于正冲击就该上非对称模型。EGARCH用对数方差建模天然保证方差为正但参数解释起来绕GJR-GARCH在方差方程里加了一个示性项负残差时多一个gamma项形式直观估计也稳。我个人更常用GJR-GARCH因为它的参数直接对应坏消息的额外影响汇报结果时好解释。判断要不要上非对称模型可以看拟合后的残差是否还有符号相关的结构或者直接对比对称和非对称模型的样本外预测误差。我做过几组对比股指数据上GJR-GARCH的样本外表现通常比对称GARCH好5%到10%以预测误差的均方根衡量但个股数据上差异就没那么明显因为个股的特质性噪声更大。注意非对称项不是越多越好。加了gamma项之后模型对极端值的敏感度上升如果样本里有未处理的结构突变比如某段时期的政策冲击gamma会被异常值带偏。上非对称模型前先检查样本区间内有没有明显的断点。3.4 样本外滚动预测别用全样本拟合去预测未来这是实操中最容易出错的地方。很多人拟合一次模型然后拿它预测未来几十期这在样本内看着很美样本外一塌糊涂。正确做法是滚动窗口或者扩展窗口用前T期数据拟合预测第T1期窗口往前滚一格重新拟合预测T2期如此往复。滚动窗口的长度选择是个权衡窗口太短参数估计方差大窗口太长对结构变化的响应慢。我的经验是日频数据用500到1000个观测值比较合适具体看数据的波动特征是否稳定。扩展窗口适合样本量不大、且你相信参数长期稳定的场景。window 750 predictions [] for i in range(window, len(returns) - 1): train returns[i-window:i] * 100 am_roll arch_model(train, volGarch, p1, q1, meanConstant, distNormal) res_roll am_roll.fit(dispoff) forecast res_roll.forecast(horizon1, reindexFalse) pred_var forecast.variance.values[-1, 0] / 10000 predictions.append(np.sqrt(pred_var)) predictions np.array(predictions) actual np.abs(returns[window1:])这段滚动预测跑起来会慢2000个观测、750窗口大概要拟合1200多次每次几毫秒到几十毫秒不等。如果数据量更大可以考虑并行化或者用已拟合参数的近似更新但后者会损失精度我不太推荐在正式研究里用。4. Heston模型的实现难点与校准策略4.1 模型结构五个参数各管什么Heston的随机微分方程组是这样的价格过程带一个方差项方差过程是一个均值回归的CIR型过程两个布朗运动之间的相关系数是ρ。五个参数里κ控制方差回归长期均值θ的速度σ是方差本身的波动率俗称vol of volρ刻画杠杆效应v0是初始方差。理解这几个参数的作用对后面校准至关重要。κ大方差回归快波动率的持续性弱κ小方差偏离后长时间不回来。σ大方差路径抖动剧烈隐含波动率微笑的尾部更厚。ρ为负负收益伴随方差上升这是股票市场的典型特征。v0决定当前时点的波动率水平校准时要给它一个合理的初值否则整个优化会跑偏。4.2 校准的目标函数期权价格还是隐含波动率Heston校准的标准做法是最小化模型价格和市场价格之间的差异。但直接用价格做目标函数有个问题不同行权价、不同期限的期权价格量级差异很大深度实值和深度虚值的价格差好几个数量级优化器会被大数主导。更稳的做法是用隐含波动率作为目标因为隐含波动率的量级相对均匀而且市场参与者本来就更关注波动率而非价格。目标函数我一般写成加权最小二乘对每个期权算模型隐含波动率和市场隐含波动率之差按vega或者按1/价差加权。vega加权的好处是给平值附近的期权更高权重因为那里的流动性好、报价可靠虚值期权报价噪声大权重低一些合理。from scipy.optimize import minimize def heston_implied_vol(params, S, K, T, r): # 这里用特征函数加数值积分算Heston看涨期权价格 # 再反解隐含波动率具体实现略去积分细节 pass def calibration_objective(params, market_data): kappa, theta, sigma, rho, v0 params # 参数约束检查 if kappa 0 or theta 0 or sigma 0 or abs(rho) 1 or v0 0: return 1e10 if 2 * kappa * theta sigma**2: # Feller条件 return 1e10 total_error 0.0 for row in market_data: model_vol heston_implied_vol((kappa, theta, sigma, rho, v0), row[S], row[K], row[T], row[r]) if model_vol is None or np.isnan(model_vol): return 1e10 weight row.get(vega, 1.0) total_error weight * (model_vol - row[market_vol])**2 return total_errorFeller条件2κθσ²是保证方差过程不触及零的充分条件校准时加上这个约束能避免很多数值问题。不过实务中有些市场的校准结果会违反Feller条件这时候要么放松约束接受方差可能触及零要么换用带边界的变体。我的建议是先用约束跑如果目标函数降不下去再考虑放松。4.3 初值选择校准成败的一半Heston校准对初值极其敏感这是我在实际项目里踩得最深的坑。同一组市场数据初值给得不好优化器收敛到一个明显不合理的局部最优五个参数里有两三个跑到边界上。后来我总结了一套初值设定流程v0用平值期权的短期隐含波动率平方这个最直接θ用长期期权的隐含波动率平方或者历史波动率的长期均值κ给1到3之间的值对应半衰期几个月σ给0.3到0.6这是股票指数常见的vol of vol量级ρ给-0.5到-0.7股票市场负相关是常态。更稳妥的做法是多起点优化在合理范围内随机撒几十组初值每组都跑一遍局部优化最后取目标函数最小的那组。计算量大一些但能显著降低陷入局部最优的概率。我做过对比单起点校准的失败率目标函数残差明显偏大大概三成多起点能压到一成以下。4.4 数值实现特征函数积分的坑Heston的期权价格没有解析解要靠特征函数加傅里叶变换或者数值积分。这里有两个数值陷阱一是积分上限截断截得太早精度不够截得太晚计算量爆炸二是被积函数在高频段振荡普通积分器会失真。我一般用Gauss-Legendre求积积分上限取到200到500节点数64到128实测精度和速度平衡得不错。另一个选择是用Carr-Madan的FFT方法一次变换能算出一整条行权价上的价格适合校准这种需要批量计算的场景。但FFT的网格参数设置比较讲究阻尼因子选不好会有混叠误差新手建议先用直接积分把逻辑跑通再上FFT优化速度。注意特征函数里的复数运算要小心分支切割。Heston特征函数涉及复数的平方根和对数不同实现方式比如little Heston trap问题会导致数值结果差异。建议用文献里公认的稳定形式别自己随手推导。5. 实操全流程从数据到预测的完整链路5.1 环境准备与依赖清单整套流程用Python实现核心依赖就几个numpy做数值计算pandas处理数据scipy做优化和积分arch包做GARCH族拟合matplotlib画图。arch包是专门做ARCH族模型的接口比statsmodels友好条件波动率、预测、诊断都有现成方法。pip install numpy pandas scipy arch matplotlib版本上arch建议用6.0以上早期版本在预测接口上有一些不一致。scipy的优化器在不同版本间行为略有差异如果校准结果和别人的对不上先检查scipy版本。5.2 数据构造与基准设定为了让你能直接复现我用模拟数据走完整流程。真实场景下你换成自己的收益率序列和期权面板即可但要注意真实数据的缺失值、停牌、除权处理这些预处理工作模拟数据里没有。基准设定上我用已实现波动率rolling标准差作为对照。任何波动率模型如果样本外预测还不如简单的滚动标准差那就没有使用价值。这个基准看起来朴素但在很多市场上很难被显著超越是检验模型是否过拟合的第一道筛子。# 已实现波动率基准20日滚动窗口 realized_vol pd.Series(returns).rolling(20).std().values5.3 GARCH预测与Heston预测的对接两条路线的输出都是未来一段时间的波动率但口径要对齐。GARCH给的是日频条件标准差Heston给的是瞬时方差对应的波动率。做对比时要把Heston的瞬时波动率在预测区间上积分再开方得到和GARCH同口径的区间波动率。这一步如果偷懒直接拿v0开方去比结论会系统性偏差。对接的具体做法GARCH预测第t1期的日波动率Heston用当前校准的参数模拟方差路径取第t1期的期望方差开方。两者都是条件期望意义下的波动率可以直接比。我一般会算三个指标预测误差的均方根、平均绝对误差、以及预测值和实际值的秩相关。秩相关这个指标容易被忽略但它反映的是模型对波动率高低排序的判断能力对交易信号构建比绝对误差更重要。5.4 回测与结果解读回测阶段最忌讳的是只看平均误差。波动率预测的误差分布是右偏的少数极端行情的预测误差会主导平均值。我习惯把误差按实际波动率分位数分组看低波动区间、中波动区间、高波动区间分别算误差。好的模型应该在各个区间都表现稳定而不是只在平静期准、一遇风暴就失灵。从我做过的多组对比看GARCH(1,1)在短期1到5天预测上通常不输Heston甚至因为参数少、估计稳而略占优势Heston的优势体现在中长期20天以上和需要与期权价格保持一致的场景。这个结论和很多文献一致但具体到你的数据上还是要自己跑一遍才知道。预测周期GARCH(1,1)相对误差Heston相对误差备注1天较低略高GARCH参数少短期响应快5天接近接近两者差异缩小20天略高较低Heston均值回归结构发挥作用60天偏高较低GARCH长期预测趋于常数这张表是示意性的具体数值取决于数据和校准质量不要当成普适结论。我列出来是想说明一个趋势模型选择要和预测周期匹配别拿短期最优的模型去做长期预测。6. 常见问题与排查技巧实录6.1 GARCH拟合不收敛或参数跑到边界这是最高频的问题。原因通常有三类数据量级不合适、样本里有极端值、模型设定和数据结构不匹配。排查顺序我一般是先检查收益率有没有放大到合理量级乘以100是常用做法再看样本里有没有超过5个标准差的异常点有的话做缩尾处理最后检查alphabeta是否接近1如果估计值顶到0.999说明波动率持续性极强可能需要考虑结构突变或者换用IGARCH。缩尾处理要谨慎不能无脑砍掉极端值因为极端值本身携带了波动率信息。我的做法是只对超过某个阈值比如8个标准差的点做温和缩尾保留其方向但压缩幅度处理完重新做ARCH效应检验。6.2 Heston校准结果不稳定换个样本就变参数不稳定是Heston的固有毛病尤其是σ和ρ这两个参数在不同样本区间上估计值可能差很多。缓解办法有几个一是增加校准用的期权数量单期限单行权价的信息量不够二是固定部分参数比如把ρ固定在-0.6附近只校准其余四个牺牲一点拟合精度换稳定性三是用正则化在目标函数里加参数偏离先验的惩罚项。我实际项目里常用第二种固定ρ和κ只校准θ、σ、v0。这样做的理由是ρ和κ在样本内的可识别性最差而θ、σ、v0对价格的影响更直接。代价是模型对杠杆效应的刻画变弱如果你的应用场景特别依赖偏度那就不能这么简化。6.3 预测值出现负方差或NaNGARCH预测出负方差基本不可能参数约束保证了但Heston模拟路径时如果参数违反Feller条件方差过程可能触及零甚至数值上变成负数后续开方就出NaN。解决办法是在模拟时用full truncation格式即方差取max(v, 0)或者用反射格式。这两种格式在文献里都有讨论full truncation实现简单偏差可接受我一般用它。另一个NaN来源是特征函数积分时的数值溢出。检查积分上限是不是设得太大或者被积函数里有没有除以接近零的量。调试时把中间变量打印出来定位到具体是哪一步出的问题比盲目调参高效得多。6.4 样本外表现远差于样本内这是过拟合的典型信号。GARCH族模型过拟合的情况相对少因为参数少Heston过拟合很常见尤其是校准期权数量不多、又放开全部五个参数的时候。判断方法很简单留出一段样本外数据看预测误差是不是显著大于样本内。如果是减少参数、增加校准数据、或者加正则化。还有一个容易被忽略的原因样本内外市场状态不同。如果样本内是低波动期样本外赶上高波动期任何模型都会表现差。这种情况下要做的是检查模型对波动率状态切换的响应速度而不是一味调模型参数。6.5 常见问题速查表现象可能原因排查动作GARCH不收敛数据量级过小收益率乘以100再拟合参数顶到边界样本含极端值或结构突变检查异常点分段拟合Heston校准慢单起点全参数多起点固定部分参数预测出现NaN方差路径为负或积分溢出用full truncation调积分上限样本外误差大过拟合或市场状态切换减参数、加正则、分段评估隐含波动率反解失败价格超出无套利边界检查报价剔除异常期权7. 我在两条路线之间反复横跳后的几点体会做了这么多轮对比我最大的感受是模型的选择从来不是哪个更先进而是哪个和你的数据、你的使用场景更匹配。GARCH族模型像一把趁手的螺丝刀结构简单、上手快、在大多数日常场景下够用Heston像一套精密仪器调好了能对准期权市场的细微结构但调不好的时候还不如螺丝刀。我见过太多人一上来就啃Heston结果卡在校准上几个月最后连一个能用的波动率预测都没做出来。另一个体会是关于评估。波动率预测的评估比收益率预测的评估更难因为真实波动率不可观测你只能拿已实现波动率或者隐含波动率做代理而这两个代理本身都有误差。我的做法是同时用多个代理评估结论一致才采信。如果GARCH在已实现波动率口径下好、在隐含波动率口径下差那说明它捕捉的是历史波动特征而不是市场预期用的时候要心里有数。最后分享一个实操小技巧不管用哪个模型都保留一个简单的滚动标准差作为兜底。当模型预测和基准偏离过大时比如超过三倍先别急着相信模型回头检查数据和参数。这个习惯帮我避免过好几次因为数据错误导致的离谱预测。波动率建模这件事稳健比精巧重要能持续跑通、结果可解释比追求某个指标上的微弱优势更有价值。
返回列表
PREV
查看更多资讯
NEXT
返回资讯列表