1. 这节究竟解决什么实际问题?¶
10.4 最大似然估计(MLE)在金融模型中的应用 的具体任务是:从似然函数估计模型参数,并用诊断检查估计是否可信。 本节将明确输入数据、计算过程、输出及其在后续研究中的用途。
2. 前置知识¶
掌握基础代数、概率和 Python 数组操作。 对公式的理解应包括变量定义、单位、时间索引和适用条件。
3. 数据说明¶
使用模拟或固定教学数据;重点是说明模型假设、估计误差和数值误差。 运行前先检查 shape、日期范围、列名与缺失值。数据看起来能画图,不代表它适合回答研究问题。
4. 学习目标与完成标准¶
完成本节后,你应能用自己的话解释 10.4 最大似然估计(MLE)在金融模型中的应用 的输入、计算步骤、输出和局限;尤其能说明:从似然函数估计模型参数,并用诊断检查估计是否可信。 你还应能独立改动一个参数并解释结果为什么变化,指出至少一个会让结论失真的假设。
5. 核心概念与公式¶
数学方法给出明确的计算规则;结论是否有效仍取决于研究对象、输入数据和模型假设。
先给出符号、单位、假设和可识别条件,再把公式翻译为逐步代码。 看到公式时依次确认:每个符号的单位是什么?它属于哪个时点?分母会不会为零?它是描述性统计量、预测量,还是交易规则?
6. 从零实现¶
原始代码按照“准备输入 → 计算中间量 → 得到结果 → 解释输出”的顺序组织。每个单元应检查变量类型、索引、维度和代表性数值。中间变量用于核对公式实现和定位计算错误。
7. 成熟库实现¶
数值库帮你稳定求解;理解目标函数、约束和诊断仍不可省略。 使用库函数前先阅读参数含义、默认值和返回对象;库输出一个数字,并不说明这个数字适合你的交易假设。
8. 两种实现的差异¶
从零实现便于核对公式、调试和识别隐含假设;成熟库适合在计算逻辑已验证后复用。两种实现依赖相同的数据口径与时点假设,使用库函数不会改变输入数据的质量。
9. 预期结果与检查范围¶
你应看到维度正确的 Series/DataFrame、日期单调递增的索引,以及能支持“从似然函数估计模型参数,并用诊断检查估计是否可信。”这一任务的表格或图形。金融日收益率均值通常接近 0;若出现极大的连续收益、空表、全 NaN 或索引错位,应先排查数据和对齐逻辑,而不是立即相信结果。
10. 常见错误与诊断¶
随机种子未固定、矩阵维度错配、优化约束遗漏、把数值收敛当成模型正确。
诊断顺序:先打印类型与 shape,再看 head()/tail(),接着检查日期和 NaN,最后才比较数值。这样能避免在错误的对象上反复改参数。
11. 研究偏差检查¶
数学模型的简化假设也会引入偏差;需要做参数敏感性与极端情景检查。 结果至少应与一个简单基线比较,并明确哪些部分只是教学演示。
12. 三层练习与答案¶
- 基础练习:验证一个中间变量、补全一段小计算,并用一句话解释输出。
- 研究练习:改变窗口、样本区间、约束或标的,记录哪些结论仍成立。
- 开放项目:把本节方法嵌入模块项目,报告基线、失败结果和限制。
题目在 ../exercises/10_math_methods/04_mle_estimation_exercises.ipynb;完整推导、可运行参考实现与结果检查在 ../solutions/10_math_methods/04_mle_estimation_solutions.ipynb。请先独立作答,再打开答案。
13. 延伸阅读¶
延伸材料:QuantEcon Python Lectures。阅读记录应包括研究假设、数据频率、样本范围和结论适用条件。
14. 下一节¶
完成本模块后,进入下一模块的概览页,并把本节的检查清单带到后续研究中。
import numpy as np
import pandas as pd
import matplotlib.pyplot as plt
from scipy import stats, optimize
np.random.seed(42)
print('Libraries loaded')
Libraries loaded
import matplotlib.pyplot as plt
# 1. 设置系统自带的中文字体(这里使用黑体 SimHei)
plt.rcParams['font.sans-serif'] = ['SimHei'] # 如果你想用微软雅黑,可以改成 ['Microsoft YaHei']
# 2. 解决更换字体后,负号(-)显示为方块的问题
plt.rcParams['axes.unicode_minus'] = False
1. MLE 基本原理¶
目标:找到参数 $\theta$ 使得观测数据 $\{x_1, ..., x_n\}$ 的联合概率最大。
对数似然函数(取 log 便于计算,不影响极值点位置): $$\log L(\theta) = \sum_{i=1}^n \log f(x_i; \theta)$$
MLE 估计量 $\hat{\theta}$ 是使对数似然最大化的参数值: $$\hat{\theta} = \arg\max_\theta \log L(\theta)$$
# 演示:用 MLE 拟合正态分布
np.random.seed(42)
true_mu = 0.0005
true_sigma = 0.012
data = np.random.normal(true_mu, true_sigma, 252)
# 分析解(正态 MLE 就是样本均值和标准差)
mu_hat = data.mean()
sigma_hat = data.std(ddof=0) # MLE 用 n 而非 n-1 做分母
print(f'正态分布 MLE 估计:')
print(f' 真实 μ={true_mu:.5f} 估计 μ={mu_hat:.5f}')
print(f' 真实 σ={true_sigma:.5f} 估计 σ={sigma_hat:.5f}')
# 手动计算对数似然
def normal_log_likelihood(params, data):
mu, sigma = params
if sigma <= 0:
return np.inf
return -np.sum(stats.norm.logpdf(data, mu, sigma))
# 数值最值化验证
result = optimize.minimize(normal_log_likelihood, [0, 0.01],
args=(data,), method='Nelder-Mead')
print(f'\n数值 MLE 验证: μ={result.x[0]:.5f}, σ={result.x[1]:.5f}')
正态分布 MLE 估计: 真实 μ=0.00050 估计 μ=0.00045 真实 σ=0.01200 估计 σ=0.01158 数值 MLE 验证: μ=0.00046, σ=0.01158
2. 学生 t 分布的 MLE:捕捉肥尾¶
# 模拟真实的肥尾收益率(df=4 的 t 分布)
np.random.seed(42)
true_df = 4.5
fat_returns = np.random.standard_t(true_df, 500) * 0.010
# 用 scipy.stats 进行 MLE 拟合
# 正态分布拟合
norm_params = stats.norm.fit(fat_returns)
# t 分布拟合
t_params = stats.t.fit(fat_returns) # (df, loc, scale)
x = np.linspace(fat_returns.min(), fat_returns.max(), 300)
fig, ax = plt.subplots(figsize=(10, 5))
ax.hist(fat_returns, bins=60, density=True, alpha=0.5, color='steelblue', label='模拟数据')
ax.plot(x, stats.norm.pdf(x, *norm_params), 'r-', lw=2, label=f'正态 MLE')
ax.plot(x, stats.t.pdf(x, *t_params), 'g-', lw=2, label=f't MLE (df={t_params[0]:.2f})')
ax.legend(); ax.set_xlim(-0.06, 0.06)
ax.set_title('正态 vs 学生 t 分布:MLE 拟合肥尾收益率')
ax.set_xlabel('日收益率'); plt.show()
# 计算对数似然值进行模型比较
ll_norm = stats.norm.logpdf(fat_returns, *norm_params).sum()
ll_t = stats.t.logpdf(fat_returns, *t_params).sum()
print(f'正态分布对数似然: {ll_norm:.2f} (参数数量 k=2)')
print(f't 分布对数似然: {ll_t:.2f} (参数数量 k=3)')
# AIC = 2k - 2 log L (越小越好)
aic_norm = 2*2 - 2*ll_norm
aic_t = 2*3 - 2*ll_t
print(f'正态 AIC: {aic_norm:.2f} t AIC: {aic_t:.2f}')
print(f'最优模型: {"正态" if aic_norm < aic_t else "t 分布"}')
正态分布对数似然: 1378.41 (参数数量 k=2) t 分布对数似然: 1480.01 (参数数量 k=3) 正态 AIC: -2752.83 t AIC: -2954.02 最优模型: t 分布
3. GARCH 参数的 MLE 估计¶
GARCH(1,1) 的条件似然函数:
$$\log L = -\frac{1}{2}\sum_{t=1}^T \left[\log(2\pi) + \log\sigma_t^2 + \frac{\epsilon_t^2}{\sigma_t^2}\right]$$
其中 $\sigma_t^2 = \omega + \alpha \epsilon_{t-1}^2 + \beta \sigma_{t-1}^2$
# 模拟 GARCH(1,1) 数据并用 MLE 估计参数
omega_true, alpha_true, beta_true = 5e-6, 0.10, 0.85
n = 1000
sigma2 = np.zeros(n)
eps = np.zeros(n)
sigma2[0] = omega_true / (1 - alpha_true - beta_true)
for t in range(1, n):
eps[t] = np.random.normal(0, np.sqrt(sigma2[t-1]))
sigma2[t] = omega_true + alpha_true*eps[t-1]**2 + beta_true*sigma2[t-1]
def garch_neg_loglik(params, returns):
omega, alpha, beta = params
if omega <= 0 or alpha <= 0 or beta <= 0 or alpha+beta >= 1:
return 1e10
n = len(returns)
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]
if np.any(sigma2 <= 0):
return 1e10
ll = -0.5 * (np.log(2*np.pi) + np.log(sigma2[1:]) + returns[1:]**2/sigma2[1:])
return -ll.sum()
result = optimize.minimize(
garch_neg_loglik, [1e-5, 0.08, 0.88], args=(eps,),
method='Nelder-Mead', options={'maxiter': 5000, 'xatol': 1e-7, 'fatol': 1e-7}
)
omega_est, alpha_est, beta_est = result.x
print('GARCH(1,1) MLE 估计结果:')
print(f' omega: 真实={omega_true:.2e} 估计={omega_est:.2e}')
print(f' alpha: 真实={alpha_true:.3f} 估计={alpha_est:.4f}')
print(f' beta: 真实={beta_true:.3f} 估计={beta_est:.4f}')
print(f' alpha+beta: 真实={alpha_true+beta_true:.3f} 估计={alpha_est+beta_est:.4f}')
GARCH(1,1) MLE 估计结果: omega: 真实=5.00e-06 估计=2.27e-06 alpha: 真实=0.100 估计=0.0677 beta: 真实=0.850 估计=0.9086 alpha+beta: 真实=0.950 估计=0.9763
练习¶
- 对比 arch 库(专业 GARCH 估计)与我们手动 MLE 的参数估计结果,哪个更准确?
- 在 t 分布的 MLE 中,打印不同 df(1到30)下的对数似然值,绘制「对数似然 vs df」曲线找最优自由度。
- 实现信息矩阵方法:计算 MLE 估计量的 Hessian 矩阵,从而得到参数的标准误和置信区间。
下一节 → ../01_financial_concepts/08_heavy_tails.ipynb