波动率聚集与 ARCH / GARCH
直觉
上一节看到「收益均值难预测」。但收益的波动却有结构——大波动后面常跟着大波动,小波动跟着小波动,这叫波动率聚集。ARCH/GARCH 正是建模这种「方差随时间变化」的工具,是风险管理和期权定价的核心。
GARCH(1,1)
条件方差 依赖上一期的冲击与上一期的方差:
假设 ,用最大似然估计参数 。
「人话」解释:α、β、α+β 各是什么?
- :新冲击对方差的拉动(昨天大涨大跌,今天波动放大多少);
- :旧方差的惯性(波动自身的记忆);
- :持续性,越接近 1,波动「记忆」越长、冲击的影响消退越慢(>1 则不稳定、爆炸)。 在成熟市场常在 0.95~0.99。
可运行案例:用 MLE 估计 GARCH(1,1)
Pyodide 没有 arch 包,我们手写 GARCH(1,1) 的对数似然,用 scipy.optimize 估计参数(运行稍慢,几十秒级)。
import numpy as np
import pandas as pd
import matplotlib.pyplot as plt
from scipy.optimize import minimize
df = pd.read_csv('/data/spy_daily.csv', parse_dates=['date']).set_index('date')
r = np.log(df['adj_close'] / df['adj_close'].shift(1)).dropna().values
r = r - r.mean() # 去均值(假设零均值条件方差)
def neg_loglik(params):
omega, alpha, beta = params
if omega <= 1e-8 or alpha < 0 or beta < 0 or alpha + beta >= 0.999:
return 1e10
n = len(r); sig2 = np.empty(n); sig2[0] = r.var()
for t in range(1, n):
sig2[t] = omega + alpha * r[t-1]**2 + beta * sig2[t-1]
return 0.5 * np.sum(np.log(2*np.pi) + np.log(sig2) + r**2 / sig2)
res = minimize(neg_loglik, [r.var()*0.1, 0.08, 0.88],
method='Nelder-Mead', options={'maxiter': 3000, 'xatol': 1e-6})
omega, alpha, beta = res.x
print(f"GARCH(1,1) 估计: omega={omega:.2e} alpha={alpha:.3f} beta={beta:.3f}")
print(f"持续性 alpha+beta={alpha+beta:.3f} (合成数据真值约 α=0.08 β=0.90)")
# 重建条件波动率(年化)
sig2 = np.empty(len(r)); sig2[0] = r.var()
for t in range(1, len(r)):
sig2[t] = omega + alpha * r[t-1]**2 + beta * sig2[t-1]
cond_vol = np.sqrt(sig2) * np.sqrt(252)
plt.figure(figsize=(9, 3.5))
plt.plot(df.index[1:], cond_vol, color='crimson', lw=0.9)
plt.title('GARCH(1,1) 条件波动率(年化):可见波动聚集')
plt.ylabel('年化波动率'); plt.tight_layout(); plt.show()
小结
- 收益均值难预测,但波动有聚集结构,可用 GARCH 建模;
- GARCH(1,1):,MLE 估计;
- 衡量持续性,接近 1 表示波动记忆长。
小测验
1. GARCH 建模的核心对象是?
2. α+β 接近 1 说明?
3. GARCH(1,1) 参数通常用什么方法估计?