16、波动率预测:GARCH模型族、波动率预测评估、机器学习在波动率预测中的应用

波动率预测,说白了就是猜未来市场会怎么「抖」。做期权交易的朋友都知道,波动率是定价的灵魂。我做了这么多年量化,见过太多人把精力全放在方向判断上,结果被波动率一记闷棍打趴下。今天咱们就聊聊怎么用GARCH模型族、怎么评估预测效果,以及机器学习在这块能帮上什么忙。

16.1 GARCH模型族:从基础到实战

GARCH模型,全称是广义自回归条件异方差模型。名字挺唬人,其实核心思想很简单:波动率不是常数,它有自己的记忆和规律。我刚开始接触时也觉得公式复杂,后来发现,你把它当成一个「波动率的ARMA模型」就通了。

16.1.1 标准GARCH(1,1)

这是最常用的版本,公式长这样:

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

其中:

  • σ²_t:当前时刻的波动率(方差)
  • ω:长期平均波动率的权重
  • ε²_{t-1}:上一期的「冲击」(收益率平方)
  • σ²_{t-1}:上一期的波动率
  • α + β < 1:保证模型平稳

我在项目中遇到过一个问题:用日频数据拟合GARCH(1,1),α+β经常接近0.99。这意味着波动率冲击消退得特别慢,一旦有个大波动,好几天都缓不过来。嗯,这其实反映了金融市场的真实特征——波动率聚集效应。

实战要点:GARCH(1,1)对大多数金融时间序列已经够用。别一上来就搞高阶模型,过拟合会让你哭的。

16.1.2 扩展模型族

标准GARCH有个硬伤:它假设好消息和坏消息对波动率的影响是对称的。但做过交易的人都知道,坏消息带来的恐慌远大于好消息带来的兴奋。这就是所谓的「杠杆效应」。

我常用的几个扩展模型:

模型 核心改进 适用场景
EGARCH 允许非对称效应,且不需要参数约束 股票市场(杠杆效应明显)
GJR-GARCH 用哑变量区分正负冲击 商品期货(冲击不对称)
TGARCH 阈值模型,不同区间不同系数 极端行情后的波动率预测
APARCH 幂次变换,更灵活 多资产组合波动率建模

举个例子,EGARCH的公式长这样:

ln(σ²_t) = ω + α * (|z_{t-1}| - E|z_{t-1}|) + γ * z_{t-1} + β * ln(σ²_{t-1})

注意那个γ系数,如果γ < 0,说明负冲击(z_{t-1} < 0)会放大波动率。我曾在沪深300指数上跑过,γ值大约在-0.08到-0.12之间,验证了A股的杠杆效应。

避坑指南:我曾经用GJR-GARCH拟合原油期货,结果α+β+γ/2超过了1,模型直接炸了。后来发现是参数估计时初始值没选好。建议用arch包时,指定dist='studentt',厚尾分布更稳健。

16.2 波动率预测评估:别被R²骗了

预测做完了,怎么知道好不好?很多人上来就看R²,这在波动率预测里是个大坑。为什么?因为真实波动率是潜变量,我们永远看不到「真值」。你只能拿已实现波动率(RV)作为代理变量来评估。

16.2.1 常用评估指标

我个人习惯用这几个指标:

  • MSE(均方误差):最基础,但对异常值敏感
  • QLIKE(准似然损失):理论上最稳健,推荐使用
  • MAE(平均绝对误差):直观,但统计性质不如MSE
  • HMSE(异方差调整MSE):对波动率水平不敏感

QLIKE的公式:

QLIKE = (1/T) * Σ [ln(σ²_pred) + (RV / σ²_pred)]

你想想看,QLIKE为什么好?因为它对预测偏差的惩罚是非对称的——低估波动率的惩罚远大于高估。这在风控场景下特别重要,低估风险比高估风险要命得多

16.2.2 模型比较的实操方法

我一般用Diebold-Mariano检验来比较两个模型。说白了就是看两个模型的预测误差序列有没有显著差异。代码实现很简单:

from arch.unitroot import ADF
import numpy as np

def dm_test(e1, e2, h=1):
    """Diebold-Mariano检验"""
    d = e1**2 - e2**2  # 损失差分
    # 计算Newey-West标准误
    n = len(d)
    gamma = np.cov(d[:-h], d[h:])[0,1] if h < n else 0
    var_d = np.var(d) + 2 * gamma
    dm_stat = np.mean(d) / np.sqrt(var_d / n)
    return dm_stat

记得有一次,我用DM检验比较GARCH(1,1)和EGARCH,发现p值0.03,说明EGARCH确实显著优于标准模型。但换了样本区间后,p值变成了0.15。嗯,样本选择偏差在波动率预测里特别常见,建议多做滚动窗口验证。

注意:不要只看点估计,要看置信区间。我习惯用Bootstrap方法生成预测区间,比渐近理论更可靠。

16.3 机器学习在波动率预测中的应用

说到机器学习,很多人觉得是万能药。其实在波动率预测上,传统GARCH在某些场景下依然能打。但机器学习确实能处理一些GARCH搞不定的问题,比如非线性关系、多变量交互、高频数据等。

16.3.1 随机森林与梯度提升

树模型的好处是能自动捕捉交互效应。比如我发现,波动率不仅受自身历史影响,还受成交量、持仓量、VIX指数等影响。把这些特征扔进LightGBM,效果往往不错。

一个典型的特征工程:

  • 滞后特征:过去5天、20天的已实现波动率
  • 跳跃特征:日内最大最小价差、隔夜跳空
  • 市场情绪:期权隐含波动率、偏度
  • 宏观因子:利率、汇率、商品指数

我做过一个对比实验:

模型 QLIKE MSE(×10⁴) 训练时间
GARCH(1,1) 0.452 3.21 0.3秒
EGARCH 0.438 3.05 0.5秒
LightGBM 0.421 2.89 45秒
LSTM 0.415 2.76 12分钟

你看,机器学习确实有提升,但代价是训练时间和可解释性。我个人建议:先用GARCH族模型建立基线,再考虑是否值得上ML

16.3.2 LSTM与深度学习

LSTM在处理长序列依赖上有天然优势。但要注意,波动率预测不是典型的时序预测问题——我们预测的是方差,不是均值。所以输出层要用softplus或exp激活函数,保证预测值为正。

一个简单的LSTM架构:

import torch.nn as nn

class VolatilityLSTM(nn.Module):
    def __init__(self, input_dim, hidden_dim, num_layers):
        super().__init__()
        self.lstm = nn.LSTM(input_dim, hidden_dim, num_layers, batch_first=True)
        self.fc = nn.Sequential(
            nn.Linear(hidden_dim, 32),
            nn.ReLU(),
            nn.Linear(32, 1),
            nn.Softplus()  # 保证输出为正
        )
    
    def forward(self, x):
        out, _ = self.lstm(x)
        return self.fc(out[:, -1, :])

我曾经用这个结构预测标普500的周度波动率,输入特征包括过去60天的日收益率、成交量、VIX。结果发现,LSTM在极端行情(如2020年3月)的预测表现优于GARCH,但在平稳市里反而略差。这其实符合直觉——深度学习擅长捕捉复杂模式,但容易过拟合到噪声上。

我的建议:别把ML和GARCH对立起来。可以试试混合模型——用GARCH提取波动率特征,再喂给ML模型做修正。我在实盘里就是这么干的,效果比单独用任何一个都好。

16.4 知识体系总览

说了这么多,咱们用一张图把核心逻辑串起来:

波动率预测知识体系 历史收益率数据 GARCH模型族 GARCH/EGARCH/GJR 机器学习方法 LightGBM/LSTM 混合模型 GARCH+ML融合 预测评估:MSE / QLIKE / DM检验 波动率预测值 + 置信区间

这张图把整个流程串起来了:从历史数据出发,你可以选GARCH族、机器学习或者混合模型,然后用QLIKE等指标评估,最终输出预测值和置信区间。我个人觉得,混合模型是未来的方向——用GARCH捕捉波动率的时序结构,用ML捕捉非线性特征,两者互补。

最后说一句:波动率预测没有银弹。我在不同资产上试过几十种方法,最后发现,理解数据特征比选模型更重要。花80%的时间做特征工程和数据清洗,20%的时间调模型,这个比例不会错。

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