1. 这节究竟解决什么实际问题?¶
1.5 ARCH/GARCH 波动率聚集建模 的具体任务是:识别波动率聚集,并比较历史波动率与条件波动率预测的用途。 本节将明确输入数据、计算过程、输出及其在后续研究中的用途。
2. 前置知识¶
会使用百分比和基础代数;建议先完成环境配置。 对公式的理解应包括变量定义、单位、时间索引和适用条件。
3. 数据说明¶
示例优先使用仓库固定的日频复权收盘价;它不是实时行情,也不代表完整市场。 运行前先检查 shape、日期范围、列名与缺失值。数据看起来能画图,不代表它适合回答研究问题。
4. 学习目标与完成标准¶
完成本节后,你应能用自己的话解释 1.5 ARCH/GARCH 波动率聚集建模 的输入、计算步骤、输出和局限;尤其能说明:识别波动率聚集,并比较历史波动率与条件波动率预测的用途。 你还应能独立改动一个参数并解释结果为什么变化,指出至少一个会让结论失真的假设。
5. 核心概念与公式¶
价格表示资产在特定时点的水平,收益率表示两个时点之间的相对变化。多数跨资产比较使用收益率。
所有公式都先说明分子、分母、单位与时间尺度;年化前必须确认数据频率。 看到公式时依次确认:每个符号的单位是什么?它属于哪个时点?分母会不会为零?它是描述性统计量、预测量,还是交易规则?
6. 从零实现¶
原始代码按照“准备输入 → 计算中间量 → 得到结果 → 解释输出”的顺序组织。每个单元应检查变量类型、索引、维度和代表性数值。中间变量用于核对公式实现和定位计算错误。
7. 成熟库实现¶
NumPy/Pandas 帮你算数和对齐索引;统计库帮你估计参数,但不替你解释经济含义。 使用库函数前先阅读参数含义、默认值和返回对象;库输出一个数字,并不说明这个数字适合你的交易假设。
8. 两种实现的差异¶
从零实现便于核对公式、调试和识别隐含假设;成熟库适合在计算逻辑已验证后复用。两种实现依赖相同的数据口径与时点假设,使用库函数不会改变输入数据的质量。
9. 预期结果与检查范围¶
你应看到维度正确的 Series/DataFrame、日期单调递增的索引,以及能支持“识别波动率聚集,并比较历史波动率与条件波动率预测的用途。”这一任务的表格或图形。金融日收益率均值通常接近 0;若出现极大的连续收益、空表、全 NaN 或索引错位,应先排查数据和对齐逻辑,而不是立即相信结果。
10. 常见错误与诊断¶
把百分比和小数混用、忘记 dropna、把日频波动率直接叫作年化波动率、把相关性当因果。
诊断顺序:先打印类型与 shape,再看 head()/tail(),接着检查日期和 NaN,最后才比较数值。这样能避免在错误的对象上反复改参数。
11. 研究偏差检查¶
概念演示也要避免事后选择样本或只展示支持结论的时间段。 结果至少应与一个简单基线比较,并明确哪些部分只是教学演示。
12. 三层练习与答案¶
- 基础练习:验证一个中间变量、补全一段小计算,并用一句话解释输出。
- 研究练习:改变窗口、样本区间、约束或标的,记录哪些结论仍成立。
- 开放项目:把本节方法嵌入模块项目,报告基线、失败结果和限制。
题目在 ../exercises/01_financial_concepts/05_garch_volatility_exercises.ipynb;完整推导、可运行参考实现与结果检查在 ../solutions/01_financial_concepts/05_garch_volatility_solutions.ipynb。请先独立作答,再打开答案。
13. 延伸阅读¶
延伸材料:Investments(Bodie, Kane, Marcus)。阅读记录应包括研究假设、数据频率、样本范围和结论适用条件。
14. 下一节¶
下一节是 06_statistics_fundamentals。本节的概念会成为理解它的输入,而不是孤立的名词。
import numpy as np
import pandas as pd
import matplotlib.pyplot as plt
from scipy import stats
import warnings
warnings.filterwarnings('ignore')
np.random.seed(42)
print("依赖库加载完毕 ")
依赖库加载完毕
import matplotlib.pyplot as plt
# 1. 设置系统自带的中文字体(这里使用黑体 SimHei)
plt.rcParams['font.sans-serif'] = ['SimHei'] # 如果你想用微软雅黑,可以改成 ['Microsoft YaHei']
# 2. 解决更换字体后,负号(-)显示为方块的问题
plt.rcParams['axes.unicode_minus'] = False
1. 波动率聚集(Volatility Clustering)的直观展示¶
Random Walk(随机游走)假设收益率是 i.i.d. 正态分布——每天的波动率都是一样的常数。 但真实金融数据并非如此。
# 模拟两种收益率序列:iid 正态 vs GARCH(1,1)
n = 1000
# A. 简单 iid 正态(不现实)
iid_returns = np.random.normal(0, 0.01, n)
# B. 手动模拟 GARCH(1,1) 的波动率聚集
# sigma_t^2 = omega + alpha * epsilon_{t-1}^2 + beta * sigma_{t-1}^2
omega = 0.00001
alpha = 0.15
beta = 0.80
sigma2 = np.zeros(n)
garch_returns = np.zeros(n)
sigma2[0] = omega / (1 - alpha - beta) # 无条件方差
for t in range(1, n):
sigma2[t] = omega + alpha * garch_returns[t-1]**2 + beta * sigma2[t-1]
garch_returns[t] = np.random.normal(0, np.sqrt(sigma2[t]))
# 可视化对比
fig, axes = plt.subplots(2, 2, figsize=(14, 8))
axes[0,0].plot(iid_returns, color='steelblue', alpha=0.7, linewidth=0.8)
axes[0,0].set_title('IID 正态收益率(无波动率聚集)')
axes[0,0].set_ylabel('每日收益率')
axes[0,1].plot(garch_returns, color='darkorange', alpha=0.7, linewidth=0.8)
axes[0,1].set_title('GARCH(1,1) 模拟收益率(有波动率聚集)')
axes[0,1].set_ylabel('每日收益率')
axes[1,0].plot(iid_returns**2, color='steelblue', alpha=0.7, linewidth=0.8)
axes[1,0].set_title('IID 平方收益率(代理波动率)- 无规律随机')
axes[1,0].set_ylabel('收益率²')
axes[1,1].plot(sigma2, color='red', alpha=0.8, linewidth=1.0)
axes[1,1].set_title('GARCH(1,1) 条件方差 σ²_t(明显聚集)')
axes[1,1].set_ylabel('条件方差 σ²')
plt.suptitle('IID vs GARCH(1,1):波动率聚集的证明', fontsize=13)
plt.tight_layout()
plt.show()
print("注意 GARCH 右上图:有些时期波动率持续升高(如金融危机),有些时期持续平静")
注意 GARCH 右上图:有些时期波动率持续升高(如金融危机),有些时期持续平静
2. ARCH 模型原理¶
ARCH(p) - Autoregressive Conditional Heteroskedasticity(自回归条件异方差)
Engle(1982,获得诺贝尔经济学奖)提出:条件方差依赖于过去 p 个残差的平方:
$$\sigma_t^2 = \omega + \sum_{i=1}^{p} \alpha_i \epsilon_{t-i}^2$$
GARCH(p,q) - Generalized ARCH
Bollerslev(1986)在 ARCH 基础上加入了波动率的自回归项:
$$\sigma_t^2 = \omega + \sum_{i=1}^{p} \alpha_i \epsilon_{t-i}^2 + \sum_{j=1}^{q} \beta_j \sigma_{t-j}^2$$
最常用的 GARCH(1,1) 公式: $$\sigma_t^2 = \omega + \alpha \epsilon_{t-1}^2 + \beta \sigma_{t-1}^2$$
其中:
- $\omega$ (omega):长期背景方差
- $\alpha$:ARCH 效应(短期冲击持久性)
- $\beta$:GARCH 效应(长期波动率记忆性)
- 平稳条件:$\alpha + \beta < 1$(否则条件方差会爆炸)
# 检验 ARCH 效应:Ljung-Box 对平方残差的自相关性检验
from statsmodels.stats.diagnostic import acorr_ljungbox
# 对模拟 GARCH 数据计算平方收益率的自相关
lb_garch = acorr_ljungbox(garch_returns**2, lags=10, return_df=True)
lb_iid = acorr_ljungbox(iid_returns**2, lags=10, return_df=True)
print("GARCH 模拟数据 - 平方收益率的 Ljung-Box 检验(p值越小越有 ARCH 效应):")
print(lb_garch[['lb_stat', 'lb_pvalue']].round(4))
print("\nIID 数据 - 平方收益率的 Ljung-Box 检验:")
print(lb_iid[['lb_stat', 'lb_pvalue']].round(4))
GARCH 模拟数据 - 平方收益率的 Ljung-Box 检验(p值越小越有 ARCH 效应):
lb_stat lb_pvalue
1 181.6585 0.0
2 282.2214 0.0
3 385.7022 0.0
4 490.9699 0.0
5 565.0586 0.0
6 581.4905 0.0
7 586.8447 0.0
8 601.2193 0.0
9 607.1268 0.0
10 612.1537 0.0
IID 数据 - 平方收益率的 Ljung-Box 检验:
lb_stat lb_pvalue
1 0.4824 0.4873
2 2.3138 0.3145
3 2.6666 0.4459
4 2.8504 0.5832
5 3.1593 0.6754
6 4.2655 0.6408
7 4.4086 0.7317
8 4.7392 0.7851
9 6.1032 0.7296
10 6.2194 0.7965
3. 使用 arch 库拟合 GARCH 模型¶
arch 库是 Python 中最成熟的 GARCH 建模工具,由 Kevin Sheppard 维护。
try:
from arch import arch_model
# 将收益率转换为百分比(arch 库习惯)
returns_pct = garch_returns * 100
# 拟合 GARCH(1,1) 模型,使用 t 分布(更适合金融数据肥尾)
garch_model = arch_model(returns_pct, vol='Garch', p=1, q=1, dist='t')
result = garch_model.fit(disp='off')
print(result.summary())
# 提取拟合的条件波动率(年化)
fitted_vol = result.conditional_volatility * np.sqrt(252) / 100
plt.figure(figsize=(12, 4))
plt.plot(fitted_vol, color='red', label='GARCH(1,1) 条件波动率(年化)', linewidth=1.2)
plt.axhline(fitted_vol.mean(), color='gray', linestyle='--', alpha=0.7, label=f'均值={fitted_vol.mean():.3f}')
plt.title('GARCH(1,1) 估计的条件年化波动率')
plt.ylabel('年化波动率')
plt.legend()
plt.show()
# 预测未来 10 日的波动率
forecasts = result.forecast(horizon=10)
forecast_vol = np.sqrt(forecasts.variance.iloc[-1]) * np.sqrt(252) / 100
print("\n未来 10 日预测波动率(年化):")
for i, v in enumerate(forecast_vol, 1):
print(f" Day +{i}: {v:.4f}")
except ImportError:
print("请先安装 arch 库:!pip install arch")
print("这里展示拟合结果的期望输出格式:")
print(" omega = 4.32e-06 (长期方差)")
print(" alpha = 0.1489 (ARCH 效应)")
print(" beta = 0.8011 (GARCH 持久性)")
print(" alpha + beta = 0.9500 < 1.0(模型平稳)")
Constant Mean - GARCH Model Results
====================================================================================
Dep. Variable: y R-squared: 0.000
Mean Model: Constant Mean Adj. R-squared: 0.000
Vol Model: GARCH Log-Likelihood: -1662.25
Distribution: Standardized Student's t AIC: 3334.51
Method: Maximum Likelihood BIC: 3359.05
No. Observations: 1000
Date: Sat, Mar 07 2026 Df Residuals: 999
Time: 01:27:48 Df Model: 1
Mean Model
==========================================================================
coef std err t P>|t| 95.0% Conf. Int.
--------------------------------------------------------------------------
mu 0.1007 3.659e-02 2.752 5.916e-03 [2.900e-02, 0.172]
Volatility Model
==========================================================================
coef std err t P>|t| 95.0% Conf. Int.
--------------------------------------------------------------------------
omega 0.2014 5.963e-02 3.377 7.317e-04 [8.453e-02, 0.318]
alpha[1] 0.1835 3.571e-02 5.138 2.782e-07 [ 0.113, 0.253]
beta[1] 0.7070 5.489e-02 12.881 5.747e-38 [ 0.599, 0.815]
Distribution
=============================================================================
coef std err t P>|t| 95.0% Conf. Int.
-----------------------------------------------------------------------------
nu 100.1259 236.286 0.424 0.672 [-3.630e+02,5.632e+02]
=============================================================================
Covariance estimator: robust
未来 10 日预测波动率(年化): Day +1: 0.1546 Day +2: 0.1624 Day +3: 0.1690 Day +4: 0.1746 Day +5: 0.1795 Day +6: 0.1838 Day +7: 0.1875 Day +8: 0.1907 Day +9: 0.1936 Day +10: 0.1961
4. GARCH 在风险管理中的应用:动态 VaR¶
传统的 VaR 使用固定的历史波动率。GARCH-VaR 使用预测的当日条件波动率,更加精准。
# 用模拟数据演示动态 GARCH-VaR
confidence = 0.95
z_score = stats.norm.ppf(1 - confidence) # ≈ -1.645
# 静态 VaR(使用总体平均波动率)
static_vol = np.std(garch_returns)
static_var = z_score * static_vol
# 动态 GARCH-VaR(使用当日条件波动率)
dynamic_vol = np.sqrt(sigma2)
dynamic_var = z_score * dynamic_vol
plt.figure(figsize=(13, 5))
plt.plot(garch_returns, color='gray', alpha=0.5, linewidth=0.8, label='每日收益率')
plt.plot(dynamic_var, color='red', linewidth=1.5, label=f'动态 GARCH-VaR (95%)')
plt.axhline(static_var, color='blue', linestyle='--', linewidth=1.5, label=f'静态 VaR (95%): {static_var:.4f}')
plt.title('动态 GARCH-VaR vs 静态 VaR(95% 置信水平)')
plt.ylabel('收益率 / VaR')
plt.legend()
plt.show()
static_violations = (garch_returns < static_var).mean()
dynamic_violations = (garch_returns < dynamic_var).mean()
print(f"静态 VaR 实际违约率(应约为 5%): {static_violations:.2%}")
print(f"动态 GARCH-VaR 实际违约率(应约为 5%): {dynamic_violations:.2%}")
print("\n结论:动态 GARCH-VaR 更贴近 5% 目标,因为它在高波动期扩大了 VaR 区间")
静态 VaR 实际违约率(应约为 5%): 3.90% 动态 GARCH-VaR 实际违约率(应约为 5%): 4.20% 结论:动态 GARCH-VaR 更贴近 5% 目标,因为它在高波动期扩大了 VaR 区间
练习¶
- 对真实 AAPL 日收益率数据进行 Ljung-Box 平方收益率检验,是否存在显著的 ARCH 效应?
- 分别拟合 ARCH(1), GARCH(1,1), GJR-GARCH(1,1) 三种模型,用 AIC 和 BIC 判断哪个更优。
- 修改 GARCH(1,1) 中的 alpha 和 beta 参数,观察当 alpha+beta 接近 1.0 时,波动率预测曲线形态如何变化。
下一节 → 06_statistics_fundamentals.ipynb