3、GARCH模型家族:GARCH(1,1)模型原理与Python实现

波动率建模,说白了就是给市场的「脾气」画个像。你想想看,金融时间序列有个很烦人的特点——波动率会聚集。大涨之后往往跟着大跌,平静的日子总是扎堆出现。这就是我们常说的「波动率聚集效应」。

传统的计量模型,比如ARIMA,假设方差是恒定的。这在金融市场上根本行不通。我刚开始做波动率交易那会儿,用简单移动平均算波动率,结果被市场狠狠教育了一顿。后来才明白,我们需要一个能捕捉方差时变特征的模型。

GARCH模型就是干这个的。它全称是「广义自回归条件异方差模型」,1986年由Bollerslev提出。嗯,这里要注意,GARCH不是某个人的名字,而是Generalized Autoregressive Conditional Heteroskedasticity的缩写。

3.1 GARCH(1,1)的核心思想

GARCH(1,1)是GARCH家族里最常用的成员。为什么是(1,1)?第一个数字代表ARCH项的阶数,第二个数字代表GARCH项的阶数。说白了,就是用过去1期的残差平方和过去1期的方差,来预测当前的方差。

数学表达式长这样:

σ²_t = ω + α * ε²_{t-1} + β * σ²_{t-1}

其中:

  • σ²_t:当前时刻的条件方差
  • ω:常数项,代表长期平均方差
  • ε²_{t-1}:上一期的残差平方(新息项)
  • σ²_{t-1}:上一期的条件方差
  • α:ARCH系数,衡量新息对波动率的影响
  • β:GARCH系数,衡量波动率的持续性

关键约束条件:α + β < 1,且 α > 0,β > 0,ω > 0。如果α+β接近1,说明波动率冲击的衰减很慢,也就是波动率有很强的持续性。

我个人习惯把α+β叫做「波动率持久度」。我在项目中遇到过一只股票,它的α+β高达0.98,这意味着一次冲击的影响要持续很久才能消散。做期权交易时,这种股票的隐含波动率往往被高估。

3.2 为什么GARCH(1,1)够用了?

你可能会问:为什么不用更高阶的GARCH(p,q)?理论上,p和q可以取任意正整数。但实际应用中,GARCH(1,1)已经能解释大部分金融时间序列的波动率特征。

原因有三:

  1. 简洁性:参数少,估计稳定。高阶模型容易过拟合。
  2. 解释性:α和β的经济含义非常直观。
  3. 预测能力:大量实证研究表明,GARCH(1,1)的预测效果不输给更复杂的模型。

我曾经用GARCH(2,2)去拟合沪深300的日收益率,结果α₂和β₂都不显著。白白增加了计算量,效果却没提升。从那以后,我默认先用GARCH(1,1),除非有明确的理由需要更高阶。

3.3 Python实现:从数据到模型

好了,理论说完了,咱们直接上代码。我用的是arch库,这是Python里最成熟的GARCH实现之一。

3.3.1 安装与导入

# 安装
# pip install arch

import numpy as np
import pandas as pd
import yfinance as yf
from arch import arch_model
import matplotlib.pyplot as plt

3.3.2 获取数据

我用标普500指数做演示。取最近3年的日收益率数据。

# 下载数据
sp500 = yf.download('^GSPC', start='2021-01-01', end='2024-01-01')
returns = 100 * sp500['Adj Close'].pct_change().dropna()

print(f"样本量: {len(returns)}")
print(f"均值: {returns.mean():.4f}")
print(f"标准差: {returns.std():.4f}")

3.3.3 拟合GARCH(1,1)模型

# 定义模型
model = arch_model(returns, vol='Garch', p=1, q=1, 
                   mean='Constant', dist='Normal')

# 拟合
result = model.fit(update_freq=5)

# 输出结果
print(result.summary())

拟合结果会显示参数估计值、标准误、p值等信息。重点关注α和β的系数。

小技巧:如果数据量较大,可以设置update_freq=0来关闭迭代过程中的输出。我个人习惯先看一次完整的迭代过程,确认收敛正常。

3.3.4 解读输出结果

假设输出结果如下:

参数 系数 标准误 p值
ω (omega) 0.0214 0.0083 0.010
α (alpha[1]) 0.0876 0.0152 0.000
β (beta[1]) 0.9012 0.0167 0.000

α+β = 0.9888,非常接近1。这说明波动率的冲击会持续很长时间。对于期权交易者来说,这意味着一旦波动率上升,短期内很难降下来。

3.3.5 提取条件方差与预测

# 提取条件方差
conditional_vol = result.conditional_volatility

# 做未来5天的波动率预测
forecast = result.forecast(horizon=5)
forecast_variance = forecast.variance.iloc[-1]

print("未来5天预测方差:")
print(forecast_variance)

# 转换为标准差(日波动率)
forecast_vol = np.sqrt(forecast_variance)
print("\n未来5天预测波动率:")
print(forecast_vol)

避坑指南:我曾经犯过一个错误——直接用预测的日波动率去推算年化波动率。正确的做法是:年化波动率 = 日波动率 × √252。但要注意,这个公式假设收益率独立同分布,而GARCH模型恰恰否定了这个假设。所以,用GARCH预测的年化波动率只能作为参考。

3.4 模型诊断:你的模型靠谱吗?

拟合完模型,不能直接拿去用。得先做诊断检验。我一般看三个东西:

  1. 标准化残差的Ljung-Box检验:检验残差是否还存在自相关。如果p值大于0.05,说明模型已经充分捕捉了序列相关性。
  2. 标准化残差平方的Ljung-Box检验:检验是否还存在ARCH效应。如果p值大于0.05,说明GARCH模型已经充分解释了波动率聚集。
  3. 信息准则:AIC和BIC越小越好,用于模型比较。
# 标准化残差
std_resid = result.resid / result.conditional_volatility

# Ljung-Box检验
from statsmodels.stats.diagnostic import acorr_ljungbox

# 对标准化残差做检验
lb_resid = acorr_ljungbox(std_resid, lags=[10], return_df=True)
print("标准化残差的Ljung-Box检验:")
print(lb_resid)

# 对标准化残差平方做检验
lb_resid2 = acorr_ljungbox(std_resid**2, lags=[10], return_df=True)
print("\n标准化残差平方的Ljung-Box检验:")
print(lb_resid2)

3.5 可视化:一图胜千言

我习惯把收益率序列和条件波动率画在一起,直观感受模型的拟合效果。

fig, ax = plt.subplots(2, 1, figsize=(12, 8))

# 上子图:收益率
ax[0].plot(returns.index, returns, color='gray', alpha=0.6)
ax[0].set_title('标普500日收益率')
ax[0].set_ylabel('收益率 (%)')

# 下子图:条件波动率
ax[1].plot(conditional_vol.index, conditional_vol, color='blue', linewidth=1.5)
ax[1].set_title('GARCH(1,1)条件波动率')
ax[1].set_ylabel('波动率 (%)')
ax[1].set_xlabel('日期')

plt.tight_layout()
plt.show()

你会看到,条件波动率在收益率剧烈波动时会迅速上升,然后在平静期缓慢下降。这就是GARCH模型的核心价值——它让波动率「活」了起来。

3.6 核心知识体系

下面这张图总结了GARCH(1,1)的核心逻辑和实现流程:

GARCH(1,1) 核心知识体系 金融时间序列 σ²_t = ω + α·ε²_{t-1} + β·σ²_{t-1} 约束:α+β<1, α>0, β>0, ω>0 Python实现步骤 ① 数据准备 → ② 模型定义 → ③ 参数估计 → ④ 诊断检验 → ⑤ 波动率预测 应用场景 期权定价 | 风险管理 | 波动率套利 | VaR计算

3.7 几点补充

GARCH(1,1)虽然强大,但不是万能的。它假设正负冲击对波动率的影响是对称的。但在实际市场中,利空消息往往比利好消息引发更大的波动。这就是所谓的「杠杆效应」。如果你需要处理这种不对称性,可以考虑EGARCH或GJR-GARCH模型。

另外,GARCH模型对异常值很敏感。我在处理A股数据时,经常遇到涨停板导致的收益率截断。这种情况下,我会先做数据清洗,或者改用稳健估计方法。

核心要点回顾

  • GARCH(1,1)用过去1期残差和过去1期方差预测当前方差
  • α+β衡量波动率持续性,越接近1越持久
  • Python的arch库让实现变得非常简单
  • 拟合后必须做诊断检验,确保模型充分
  • GARCH(1,1)是起点,不是终点

好了,GARCH(1,1)的内容就到这里。这个模型是我做波动率交易时最常用的工具之一。它简单、有效、可解释。希望你能把它用起来,在实际数据上跑一跑,感受一下波动率建模的魅力。


公众号:蓝海资料掘金营,微信deep3321