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模型最头疼的部分。为什么?因为方差过程不可观测。你只能看到资产价格,看不到背后的方差路径。

我常用的方法有两种:

  1. 极大似然估计(MLE):用卡尔曼滤波或粒子滤波估计隐含方差,然后最大化似然函数
  2. 矩估计(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结合粒子滤波是最可靠的方法。记住:模型再漂亮,也要经过实盘数据的检验。

Heston模型知识体系 Heston模型 模型原理 数值实现 参数估计 资产价格过程 方差过程 Feller条件 蒙特卡洛模拟 QE方案 傅里叶变换 极大似然估计 矩估计 回测验证 核心:随机波动率 + 均值回复 + 杠杆效应

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