波动率建模实战:ARCH族与Heston模型详解及Python实现
波动率这个东西,做过衍生品定价或者量化风控的人应该都有体会——它不像价格那样能直接观察到,却又实实在在影响着每一个定价结果和每一笔风险敞口。我刚入行那会儿,总觉得历史波动率算一算就够了,后来被市场反复教育才明白,波动率本身是动态的、有时变特征的,静态假设在真实市场里根本站不住脚。这也是为什么ARCH族模型和Heston模型会成为这个领域绕不开的两套工具:前者从离散时间序列出发刻画波动率的聚集与均值回复,后者从连续时间随机过程出发给波动率一个独立的随机源。这篇内容我打算把这两套模型的来龙去脉、参数含义、实操实现和踩坑经验完整梳理一遍,适合有一定统计基础、正在做波动率建模或者准备上手期权定价的朋友参考,零基础也能看懂大框架,有基础的可以直接抄代码跑起来。
1. 为什么波动率必须动态建模
1.1 静态波动率假设的三个致命伤
先说清楚问题,才知道工具为什么长这样。静态波动率假设——也就是假设波动率是一个常数——在教科书里很常见,Black-Scholes公式就是建立在这个前提上的。但真实市场里这个假设有三个绕不过去的硬伤。
第一个是波动率聚集。你去看任何一只标的的收益率序列,大幅波动往往扎堆出现,平静期也是一段一段的。今天暴跌3%,明天大概率还是高波动,不会立刻回到均值。这种“大波动跟着大波动”的现象,静态模型完全无法刻画,因为它假设每一天的波动率都一样。
第二个是均值回复。波动率不会无限发散,长期来看它会围绕某个中枢上下摆动。市场恐慌时波动率飙升,但终究会回落;市场沉寂时波动率低迷,也终究会被某个事件打破。这种拉回中枢的倾向,是动态模型必须捕捉的特征。
第三个是杠杆效应。收益率和波动率之间往往存在负相关——价格下跌时波动率上升得更猛,上涨时波动率反而温和。这个不对称性在股票市场尤其明显,静态模型对此无能为力。
我见过不少团队一开始用滚动窗口算历史波动率硬套,回测看着还行,一到实盘遇到波动率 regime 切换就原形毕露。所以动态建模不是锦上添花,是刚需。
1.2 离散与连续两条技术路线
面对动态波动率,业界发展出两条主要技术路线,正好对应标题里的两个模型。
ARCH族走的是离散时间路线。它把波动率看成过去残差平方的函数——今天的方差取决于昨天、前天……的冲击大小。Engle在1982年提出ARCH,Bollerslev在1986年扩展成GARCH,核心思想是“波动的记忆”。这条路线的优势是实现简单、参数少、对历史数据拟合直接,特别适合做风险度量(比如VaR)和波动率预测。
Heston走的是连续时间随机波动率路线。它假设波动率本身服从一个均值回复的随机过程(CIR过程),并且和标的价格的布朗运动相关。这条路线的优势是能给出期权定价的解析解(特征函数形式),能同时刻画波动率微笑和期限结构,特别适合做衍生品定价和校准。
两条路线不是替代关系,而是互补关系。做风控预测我优先用GARCH,做期权定价我优先用Heston。下面分别拆开讲。
2. ARCH族模型:从ARCH到GARCH的演进逻辑
2.1 ARCH(q)的基本结构与参数含义
ARCH(q)模型的核心表达式很简洁。设收益率 $r_t = \mu + \varepsilon_t$,其中 $\varepsilon_t = \sigma_t z_t$,$z_t$ 是标准化的白噪声(通常假设标准正态或t分布)。关键在于条件方差:
$$\sigma_t^2 = \omega + \sum_{i=1}^{q} \alpha_i \varepsilon_{t-i}^2$$
三个参数各有分工。$\omega > 0$ 是方差的基准水平,相当于“底噪”;$\alpha_i \geq 0$ 衡量过去第 $i$ 期冲击对当前方差的影响权重。所有 $\alpha_i$ 之和必须小于1,否则方差会发散——这是平稳性条件,实操中一定要检查。
q的阶数怎么定?理论上可以用信息准则(AIC/BIC)或者残差平方的自相关图来判断。但我实测下来,金融收益率数据用q=1到q=5基本够用,阶数再高容易过拟合,而且参数估计的稳定性会下降。ARCH(q)有个明显缺陷:要刻画长记忆需要很大的q,参数一多估计就飘。
2.2 GARCH(1,1)为什么成为事实标准
Bollerslev的改进很巧妙——在ARCH的基础上加入方差自身的滞后项:
$$\sigma_t^2 = \omega + \alpha \varepsilon_{t-1}^2 + \beta \sigma_{t-1}^2$$
别小看这个 $\beta \sigma_{t-1}^2$ 项,它让模型能用很少的参数捕捉长记忆。GARCH(1,1)只有三个参数,却能拟合绝大多数金融时间序列的波动率特征,这就是它成为业界默认选择的原因。
参数解读要记牢:$\alpha$ 是“冲击反应系数”,越大说明市场对新信息反应越剧烈;$\beta$ 是“记忆衰减系数”,越大说明波动率越粘滞、回复越慢;$\alpha + \beta$ 是“持续性”,越接近1说明波动率冲击消退越慢。股票指数上 $\alpha+\beta$ 经常在0.95以上,意味着一次冲击要很久才消化完。
注意:$\alpha + \beta < 1$ 是协方差平稳的必要条件。如果估计出来接近甚至超过1,说明模型设定有问题,或者数据存在结构性断点,别硬套。
2.3 非对称扩展:EGARCH与GJR-GARCH
标准GARCH有个盲点——它对正负冲击一视同仁,因为用的是 $\varepsilon_{t-1}^2$。但前面说的杠杆效应要求模型能区分涨跌。两个常用扩展:
EGARCH(Nelson, 1991)对对数方差建模,天然保证方差为正,还能直接刻画不对称:
$$\ln \sigma_t^2 = \omega + \alpha(|z_{t-1}| - E|z_{t-1}|) + \gamma z_{t-1} + \beta \ln \sigma_{t-1}^2$$
其中 $\gamma$ 就是不对称系数,负值表示负冲击推高波动更猛。
GJR-GARCH(Glosten-Jagannathan-Runkle)更直观,加一个示性项:
$$\sigma_t^2 = \omega + \alpha \varepsilon_{t-1}^2 + \gamma \varepsilon_{t-1}^2 I(\varepsilon_{t-1}<0) + \beta \sigma_{t-1}^2$$
$I(\cdot)$ 是示性函数,负冲击时多一个 $\gamma$ 的额外影响。我一般先用GARCH(1,1)打底,如果残差诊断发现不对称性显著,再上GJR-GARCH,参数解释更符合直觉。
3. Heston模型:连续时间随机波动率的完整拆解
3.1 双随机过程的数学结构
Heston模型的核心是把波动率也变成一个随机过程,用两个耦合的随机微分方程描述:
$$dS_t = \mu S_t dt + \sqrt{v_t} S_t dW_t^S$$ $$dv_t = \kappa(\theta - v_t)dt + \xi \sqrt{v_t} dW_t^v$$
其中 $dW_t^S$ 和 $dW_t^v$ 的相关系数是 $\rho$。五个参数各司其职:$\kappa$ 是方差过程的均值回复速度,$\theta$ 是长期方差水平,$\xi$ 是方差过程的波动率(vol of vol),$\rho$ 是价格与方差的相关性(通常为负,对应杠杆效应),$v_0$ 是初始方差。
这个结构妙在哪?方差过程 $v_t$ 用的是CIR过程(平方根过程),保证了方差非负(只要满足Feller条件 $2\kappa\theta > \xi^2$)。而 $\rho$ 的存在让模型能生成波动率微笑——这正是Black-Scholes做不到的。
3.2 特征函数与半解析定价
Heston模型之所以在业界流行,关键在于它给出了期权价格的半解析解。通过傅里叶变换,欧式看涨期权价格可以写成:
$$C = S_0 P_1 - K e^{-rT} P_2$$
其中 $P_1, P_2$ 是通过特征函数积分得到的概率。特征函数的显式形式涉及复对数,这里不展开完整推导,但要提醒一个实操大坑:复对数分支问题。直接按教科书公式实现,在某些参数区域会出现积分不连续,导致定价结果跳变。业界常用“Little Heston Trap”修正方案(Albrecher等,2007),把特征函数里的复数项处理成连续形式。我早期没注意这个,校准出来的参数看着合理,定价却偶尔离谱,排查了半天才发现是分支切割问题。
3.3 参数校准:从市场期权价格反推
Heston的参数不能像GARCH那样直接从收益率序列估计,得用市场期权价格反推——这就是校准(calibration)。目标是最小化模型价格与市场价格的加权误差:
$$\min_{\Theta} \sum_{i} w_i (C_i^{model}(\Theta) - C_i^{market})^2$$
实操要点:权重 $w_i$ 通常用Vega加权或者按1/买卖价差加权,避免深度虚值期权主导目标函数。优化算法我推荐先用差分进化(Differential Evolution)做全局搜索,再用Levenberg-Marquardt做局部精修。直接上局部优化极容易陷进局部最优,校准出来的参数虽然拟合当前曲面,但毫无经济意义。
提示:校准前务必检查期权数据的流动性,剔除买卖价差异常大、成交量极低的合约。垃圾数据进,垃圾参数出,这是铁律。
4. 实操落地:两套模型的完整实现路径
4.1 环境准备与数据预处理
我习惯用Python做原型,statsmodels和arch包能覆盖GARCH族,Heston需要自己写或者用QuantLib。先装依赖:
pip install numpy pandas scipy statsmodels arch matplotlib数据预处理有几个关键动作。第一,收益率用对数收益率 $r_t = \ln(P_t/P_{t-1})$,别用简单收益率。第二,检查平稳性,ADF检验走一遍。第三,处理极端值——但别随便删,金融数据的厚尾是真实特征,删了反而失真,建议用Winsorize在1%和99%分位截尾。第四,样本量至少1000个交易日,否则GARCH参数估计不稳。
4.2 GARCH建模的完整代码流程
先跑一个标准GARCH(1,1):
import numpy as np import pandas as pd from arch import arch_model # 假设 returns 是已经算好的对数收益率序列(百分比形式) returns = returns * 100 # arch包建议用百分比,数值更稳定 model = arch_model(returns, vol='Garch', p=1, q=1, dist='t') res = model.fit(disp='off') print(res.summary())这里dist='t'很关键。金融收益率厚尾明显,用正态分布会低估极端风险,t分布的自由度参数能自动适配厚尾程度。我对比过,同一组数据用正态和t分布,VaR回测的通过率差很多,t分布明显更稳。
拟合完看几个东西:$\alpha+\beta$ 是否小于1、t分布自由度是否合理(通常3到8之间)、标准化残差是否还有自相关。如果残差诊断不过关,考虑加GARCH阶数或者换GJR-GARCH。
预测波动率:
forecast = res.forecast(horizon=5, reindex=False) print(forecast.variance.iloc[-1])horizon=5给出未来5期的方差预测。注意GARCH的预测会随期限拉长收敛到长期方差 $\omega/(1-\alpha-\beta)$,这是均值回复的体现。
4.3 Heston定价与校准的实现骨架
Heston的特征函数实现(采用Little Heston Trap修正):
import numpy as np def heston_char_func(u, T, kappa, theta, xi, rho, v0): """Heston特征函数,带分支修正""" d = np.sqrt((rho*xi*1j*u - kappa)**2 + xi**2*(1j*u + u**2)) g = (kappa - rho*xi*1j*u - d) / (kappa - rho*xi*1j*u + d) C = (kappa*theta/xi**2) * ((kappa - rho*xi*1j*u - d)*T - 2*np.log((1 - g*np.exp(-d*T))/(1 - g))) D = ((kappa - rho*xi*1j*u - d)/xi**2) * ((1 - np.exp(-d*T))/(1 - g*np.exp(-d*T))) return np.exp(C + D*v0 + 1j*u*np.log(1.0)) # 假设S0=1归一化定价用数值积分(如Gauss-Legendre或梯形法)对特征函数做傅里叶逆变换。校准部分用scipy的差分进化:
from scipy.optimize import differential_evolution def calibrate_heston(market_prices, strikes, maturities, S0, r): def objective(params): kappa, theta, xi, rho, v0 = params # 参数边界约束 if 2*kappa*theta <= xi**2: # Feller条件 return 1e6 error = 0.0 for K, T, C_mkt in zip(strikes, maturities, market_prices): C_model = heston_price(S0, K, T, r, kappa, theta, xi, rho, v0) error += (C_model - C_mkt)**2 return error bounds = [(0.1, 10), (0.01, 1), (0.01, 2), (-0.99, 0.99), (0.01, 1)] result = differential_evolution(objective, bounds, seed=42, maxiter=200) return result.x参数边界要设合理:$\kappa$ 别超过10,$\xi$ 别超过2,$\rho$ 严格在(-1,1)内。Feller条件作为硬约束加进去,否则校准出来的参数可能让方差过程触零,定价失真。
4.4 两套模型的对比与选用建议
| 维度 | GARCH族 | Heston |
|---|---|---|
| 时间框架 | 离散 | 连续 |
| 数据输入 | 收益率序列 | 期权价格曲面 |
| 参数数量 | 3-5个 | 5个 |
| 主要用途 | 波动率预测、VaR | 期权定价、曲面拟合 |
| 实现难度 | 低 | 中高 |
| 计算速度 | 快 | 校准较慢 |
| 波动率微笑 | 不直接刻画 | 天然支持 |
选用逻辑很简单:做风险管理和波动率预测,GARCH够用且高效;做衍生品定价和需要拟合波动率曲面,Heston更合适。有些团队会把两者结合——用GARCH预测的波动率作为Heston的初始 $v_0$ 或者先验,这个思路我在几个项目里试过,校准稳定性确实有提升。
5. 常见问题与排查技巧实录
5.1 GARCH参数估计不收敛怎么办
这是最高频的问题。排查顺序:先看数据有没有极端值或者结构性断点,画个收益率时序图一眼就能发现;再看样本量够不够,少于500个观测基本别指望稳定;然后检查是否用了百分比形式,原始小数形式的收益率数值太小,优化器容易卡住。如果都正常还不收敛,试试换优化器(arch包支持method='nm'或'bfgs'),或者给参数加初值。
5.2 Heston校准结果不稳定、参数乱跳
校准不稳定通常三个原因。第一,市场数据质量差,深度虚值期权价格噪声大,建议按Vega加权或者只保留平值附近±2个标准差的合约。第二,目标函数多峰,局部优化必然陷进去,必须用全局优化打底。第三,参数冗余——Heston的五个参数在某些数据下存在近似共线性,可以固定 $\theta$ 或者 $v_0$ 减少自由度。我一般固定 $v_0$ 为短期ATM隐含波动率的平方,校准其余四个。
5.3 波动率预测总是滞后于市场
GARCH的预测本质上是“向后看”的,它依赖历史冲击,遇到突发的 regime 切换必然滞后。这不是模型bug,是结构特性。缓解办法:一是缩短估计窗口(比如用滚动500天而非全样本),让模型更快适应;二是在预测里加入外生变量(如VIX类指标、成交量),用GARCH-X扩展;三是设置预测的置信区间,别只给点估计,把不确定性也呈现出来。
5.4 常见问题速查表
| 问题现象 | 可能原因 | 排查动作 |
|---|---|---|
| GARCH方差不收敛 | 数据含极端值/断点 | 画时序图,Winsorize处理 |
| $\alpha+\beta \geq 1$ | 非平稳或结构断点 | 分段估计,检查样本期 |
| Heston定价跳变 | 复对数分支问题 | 采用Little Trap修正 |
| 校准参数无经济意义 | 局部最优 | 换全局优化,加参数约束 |
| 预测滞后市场 | 模型结构特性 | 缩短窗口,加外生变量 |
| 期权价格拟合好但曲面扭曲 | 权重设置不当 | 改Vega加权,剔除低流动性合约 |
实操心得:每次建模前先做数据质量报告——缺失值比例、极端值分布、平稳性检验、自相关结构,这四样过一遍,能省掉后面80%的排查时间。我吃过亏,曾经因为一个未处理的数据断点,GARCH参数估计完全失真,查了两天才定位到。
最后分享一个我在实际项目里反复验证的小技巧:GARCH和Heston不要孤立地用,做期权定价时用GARCH预测的短期波动率去约束Heston的 $v_0$,做风险度量时用Heston校准出的长期方差 $\theta$ 去校验GARCH预测的收敛水平,两套模型互相印证,比单打独斗靠谱得多。这个思路后续还可以往跳跃扩散、粗糙波动率方向扩展,但那是另一个话题了。