11、随机波动率模型:Heston模型原理、Heston模型的数值实现、模型参数估计
说到随机波动率,我第一个想到的就是Heston模型。为什么?因为它在业界太常用了。BS模型假设波动率是常数,这显然不现实。你想想看,市场恐慌时波动率飙升,平静时波动率走低,怎么可能一成不变?
Heston模型的核心思想很简单:波动率本身也是一个随机过程。它有自己的均值回复特性,有自己的波动率(专业术语叫“波动率的波动率”)。嗯,这听起来有点绕,但说白了就是——波动率会自己上下波动,而且它会慢慢回到一个长期均值水平。
核心要点:Heston模型解决了BS模型的两个硬伤——波动率微笑和波动率期限结构。我在项目中遇到过很多次,用BS模型定价深度虚值期权,价格偏差大到离谱。换成Heston模型后,拟合效果明显改善。
11.1 Heston模型原理
Heston模型由两个随机微分方程组成。一个是资产价格过程,另一个是方差过程。我习惯把这两个方程放在一起看,它们就像一对双胞胎,互相影响。
dS(t) = μ·S(t)·dt + √v(t)·S(t)·dW₁(t)
dv(t) = κ·(θ - v(t))·dt + σ·√v(t)·dW₂(t)
这里解释一下每个参数的含义:
| 参数 | 含义 | 典型取值范围 |
|---|---|---|
| κ (kappa) | 均值回复速度 | 1-5 |
| θ (theta) | 长期方差均值 | 0.01-0.05 |
| σ (sigma) | 波动率的波动率 | 0.1-0.5 |
| ρ (rho) | 资产与方差的相关系数 | -1 到 1 |
| v₀ | 初始方差 | 0.01-0.04 |
这里有个关键点:ρ(相关系数)。它刻画了资产价格和波动率之间的联动关系。ρ为负,意味着市场下跌时波动率上升——这就是著名的“杠杆效应”。我在做美股期权定价时,标普500的ρ通常在-0.7左右,这个值很典型。
避坑指南:我曾经在参数估计时忽略了Feller条件(2κθ > σ²),结果模拟出的方差路径经常变成负数。嗯,这会导致定价结果完全不可用。记住:Feller条件必须满足,否则你的方差过程会出问题。
11.2 Heston模型的数值实现
Heston模型没有解析解(除了欧式期权),所以我们需要用数值方法。我个人最常用的是蒙特卡洛模拟和傅里叶变换方法。
先看蒙特卡洛模拟。这里有个坑——直接离散化会导致方差为负。我建议用QE(Quadratic Exponential)方案,它专门处理这个问题。
import numpy as np
def heston_paths(S0, v0, kappa, theta, sigma, rho, r, T, N, M):
"""
S0: 初始价格
v0: 初始方差
kappa: 均值回复速度
theta: 长期方差
sigma: 波动率的波动率
rho: 相关系数
r: 无风险利率
T: 到期时间
N: 时间步数
M: 模拟路径数
"""
dt = T / N
S = np.zeros((M, N+1))
v = np.zeros((M, N+1))
S[:, 0] = S0
v[:, 0] = v0
for i in range(N):
# 生成相关随机数
Z1 = np.random.normal(0, 1, M)
Z2 = rho * Z1 + np.sqrt(1 - rho**2) * np.random.normal(0, 1, M)
# QE方案处理方差
m = theta + (v[:, i] - theta) * np.exp(-kappa * dt)
s2 = v[:, i] * sigma**2 * np.exp(-kappa * dt) / kappa * (1 - np.exp(-kappa * dt)) + \
theta * sigma**2 / (2*kappa) * (1 - np.exp(-kappa * dt))**2
psi = s2 / m**2
# 判断使用哪种采样方式
if psi < 1.5:
b2 = 2 / psi - 1 + np.sqrt(2/psi * (2/psi - 1))
a = m / (1 + b2)
v[:, i+1] = a * (np.sqrt(b2) + Z2)**2
else:
p = (psi - 1) / (psi + 1)
beta = (1 - p) / m
u = np.random.uniform(0, 1, M)
v[:, i+1] = np.where(u <= p, 0, np.log((1-p)/(1-u)) / beta)
# 更新资产价格
S[:, i+1] = S[:, i] * np.exp((r - 0.5*v[:, i])*dt + np.sqrt(v[:, i]*dt)*Z1)
return S, v
这段代码我用了很多次。注意看QE方案的处理逻辑——它根据psi的值选择不同的采样方式。当psi小于1.5时用卡方分布近似,否则用指数分布。这个阈值是经验值,我试过调整它,效果差别不大。
注意:蒙特卡洛模拟的收敛速度是O(1/√N),很慢。如果你需要高精度定价,建议用傅里叶变换方法。Heston模型的特征函数有闭式解,用FFT可以快速计算期权价格。
11.3 模型参数估计
参数估计是Heston模型最头疼的部分。为什么?因为方差过程不可观测。你只能看到资产价格,看不到背后的方差路径。
我常用的方法有两种:
- 极大似然估计(MLE):用卡尔曼滤波或粒子滤波估计隐含方差,然后最大化似然函数
- 矩估计(GMM):用样本矩匹配理论矩,简单粗暴但精度一般
我个人更倾向MLE。虽然计算量大,但结果更可靠。下面是一个简化的实现思路:
from scipy.optimize import minimize
def heston_log_likelihood(params, S, dt):
"""
计算Heston模型的对数似然
params: [kappa, theta, sigma, rho, v0]
"""
kappa, theta, sigma, rho, v0 = params
# 这里用扩展卡尔曼滤波估计隐含方差
# 实际项目中需要更复杂的粒子滤波
# 返回负对数似然(因为我们要最小化)
return -log_likelihood_value
# 参数估计
initial_guess = [2.0, 0.04, 0.3, -0.7, 0.04]
bounds = [(0.1, 10), (0.001, 0.1), (0.01, 1.0), (-1.0, 1.0), (0.001, 0.1)]
result = minimize(heston_log_likelihood, initial_guess,
args=(price_data, dt), bounds=bounds,
method='L-BFGS-B')
实战经验:我曾经用历史波动率作为v₀的初始值,结果收敛速度明显加快。另外,κ和θ的估计值对初始值很敏感,建议多试几组初始值,取似然值最大的那组。
参数估计完成后,一定要做回测验证。我习惯的做法是:用前6个月的数据估计参数,然后用后3个月的数据做样本外测试。如果定价误差超过5%,说明参数有问题,需要重新调整。
嗯,这里还要提一下参数稳定性的问题。Heston模型的参数应该随时间缓慢变化,而不是剧烈波动。如果你发现κ从2跳到10又跳回1,那说明你的估计方法有问题,或者市场结构发生了根本性变化。
总结一下:Heston模型是随机波动率模型的标杆。它用两个随机过程刻画了资产价格和波动率的动态关系。数值实现上,QE方案是蒙特卡洛模拟的首选;参数估计上,MLE结合粒子滤波是最可靠的方法。记住:模型再漂亮,也要经过实盘数据的检验。