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的五个步骤串起来了。从数据输入开始,到模型设定、构建似然函数、数值优化,最后输出结果。每一步都有对应的Python代码实现。
总结一下: MLE是ARCH/GARCH模型估计的基石。虽然arch库帮我们省去了手写优化的麻烦,但理解MLE的原理能让你在遇到问题时知道从哪里下手。比如优化不收敛、参数不显著、分布假设不合适——这些问题的诊断都离不开对MLE的理解。
我个人建议,刚开始学的时候,先用模拟数据跑一遍,看看估计结果能不能还原真实参数。这样能建立直觉,后面处理真实数据时心里更有底。