4. 模型估计方法:极大似然估计(MLE)原理、在Python中的实现(arch库)

聊到ARCH和GARCH模型的估计,绕不开一个核心方法——极大似然估计(MLE)。说白了,MLE就是帮我们找到一组参数,让当前观测到的数据出现的概率最大。你想想看,如果模型参数选得好,那数据出现的可能性就应该很高,对吧?

我个人习惯把MLE理解成「反向推理」。我们不知道参数是多少,但我们手里有数据。那就反过来问:什么样的参数,最有可能生成我们看到的这些数据?答案就是MLE要找的。

4.1 MLE的基本原理

对于ARCH/GARCH模型,我们假设残差服从某个条件分布(通常是正态分布或t分布)。给定前t-1期的信息,第t期的残差的条件分布是已知的。那么整个样本的联合似然函数,就是每个时间点条件密度的乘积。

嗯,这里要注意:实际计算时我们取对数,把乘积变成求和。这就是对数似然函数(log-likelihood)。最大化对数似然,等价于最大化原始似然,但数值上更稳定。

核心思想:

  • 假设残差服从某种分布(如正态分布)
  • 写出每个观测值的条件概率密度
  • 将所有密度值相乘得到似然函数
  • 取对数后,用数值优化算法求最大值

我在项目中遇到过一个问题:如果数据有异常值,正态分布的MLE估计会变得很不稳定。后来我改用t分布,效果好了很多。所以选择分布假设,不是随便拍脑袋的。

4.2 ARCH/GARCH模型的对数似然函数

以GARCH(1,1)为例,假设残差服从正态分布。那么第t期的条件方差是:

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

条件密度是:

f(ε_t | I_{t-1}) = (1 / √(2πσ²_t)) * exp(-ε²_t / (2σ²_t))

取对数后,整个样本的对数似然函数就是:

L = - (T/2) * log(2π) - (1/2) * Σ [log(σ²_t) + ε²_t / σ²_t]

其中T是样本量。我们的目标就是找到ω、α、β,让这个L最大。

我的小技巧: 实际写代码时,我习惯先算残差序列,再迭代计算条件方差序列。这样逻辑清晰,调试也方便。我曾经因为顺序搞反,折腾了一下午才找到bug。

4.3 数值优化方法

对数似然函数通常没有解析解,得靠数值优化。常用的方法有:

  • BFGS:拟牛顿法,收敛快,我一般首选
  • Nelder-Mead:单纯形法,不需要梯度,但慢一些
  • L-BFGS-B:带边界约束的BFGS,适合参数有范围限制的情况

为什么需要边界约束?因为GARCH模型的参数必须满足非负约束(ω>0, α≥0, β≥0),而且α+β<1才能保证平稳性。这些约束在优化时必须考虑进去。

避坑指南: 我曾经遇到过优化不收敛的情况,后来发现是初始值设得太离谱。建议用简单方法(比如用ARCH(1)的结果)作为GARCH的初始值,能大大提升收敛概率。

4.4 Python中的实现:arch库

好在Python的arch库把这些都封装好了。我们不需要手写MLE优化过程,直接调用就行。但理解背后的原理,能帮我们更好地调参和诊断问题。

下面是一个完整的例子:

import numpy as np
import pandas as pd
from arch import arch_model

# 生成模拟数据
np.random.seed(42)
n = 1000
omega = 0.1
alpha = 0.3
beta = 0.6

returns = np.zeros(n)
sigma2 = np.zeros(n)
sigma2[0] = omega / (1 - alpha - beta)

for t in range(1, n):
    sigma2[t] = omega + alpha * returns[t-1]**2 + beta * sigma2[t-1]
    returns[t] = np.sqrt(sigma2[t]) * np.random.normal(0, 1)

# 拟合GARCH(1,1)模型
model = arch_model(returns, vol='Garch', p=1, q=1, dist='normal')
result = model.fit(disp='off')

# 查看结果
print(result.summary())

运行这段代码,你会看到估计出的参数和真实值(ω=0.1, α=0.3, β=0.6)比较接近。当然样本量越大,估计越准。

4.5 结果解读

result.summary()会输出一张表格,包含:

参数 估计值 标准误 z统计量 p值
ω 0.095 0.032 2.97 0.003
α 0.285 0.048 5.94 0.000
β 0.615 0.055 11.18 0.000

p值都小于0.05,说明参数显著。α+β=0.9,小于1,模型是平稳的。

个人经验: 我一般会先看α+β是否接近1。如果非常接近(比如0.99以上),可能数据有IGARCH特征,这时候标准GARCH可能不太合适。另外,我也会检查标准化残差(残差除以条件标准差)是否还有自相关或ARCH效应,这是模型诊断的重要一步。

4.6 不同分布假设的影响

arch库支持多种分布:

  • dist='normal':正态分布,最常用,但对厚尾数据不够鲁棒
  • dist='t':t分布,能捕捉厚尾特征,我处理金融数据时常用
  • dist='skewt':偏斜t分布,还能处理不对称性

怎么选?我建议先用正态分布跑一遍,然后看残差的QQ图。如果尾部偏离明显,就换t分布。如果偏斜也明显,再考虑skewt。

# 使用t分布
model_t = arch_model(returns, vol='Garch', p=1, q=1, dist='t')
result_t = model_t.fit(disp='off')
print(result_t.summary())

4.7 知识体系总览

下面这张图总结了MLE在ARCH/GARCH模型中的核心逻辑:

MLE估计ARCH/GARCH模型的核心流程 输入:收益率序列 r₁, r₂, ..., rₜ 设定模型:GARCH(p,q) + 分布假设 (正态/t/偏斜t) 构建对数似然函数 L(θ) L = -½ Σ[log(σ²ₜ) + ε²ₜ/σ²ₜ] + 常数 数值优化:最大化 L(θ) BFGS / Nelder-Mead / L-BFGS-B 输出:参数估计值 + 诊断 数据准备 模型设定 似然函数 优化求解 结果输出

这张图把MLE的五个步骤串起来了。从数据输入开始,到模型设定、构建似然函数、数值优化,最后输出结果。每一步都有对应的Python代码实现。

总结一下: MLE是ARCH/GARCH模型估计的基石。虽然arch库帮我们省去了手写优化的麻烦,但理解MLE的原理能让你在遇到问题时知道从哪里下手。比如优化不收敛、参数不显著、分布假设不合适——这些问题的诊断都离不开对MLE的理解。

我个人建议,刚开始学的时候,先用模拟数据跑一遍,看看估计结果能不能还原真实参数。这样能建立直觉,后面处理真实数据时心里更有底。


专注资料整理