4. 局部波动率模型:Dupire公式推导、局部波动率曲面的构建、有限差分法求解

局部波动率模型,说白了就是把波动率从「常数」变成「随时间和标的价格变化」的函数。嗯,这听起来很直观——市场里波动率本来就不是一成不变的嘛。但真正让我觉得这东西厉害,是在一次做奇异期权定价的时候。当时用BS模型算出来的价格和交易台报的差了好几个点,换成局部波动率模型后,误差直接缩到可接受范围。从那以后,我就再也没小看过这个模型。

4.1 为什么需要局部波动率?

BS模型假设波动率是常数,这显然不现实。你想想看,标的价格跌到历史低位时,市场恐慌情绪上来,波动率肯定飙升。反过来,价格在高位横盘时,波动率可能反而下降。这就是所谓的「波动率微笑」或「偏斜」现象。

我个人习惯把局部波动率理解为「瞬时波动率的快照」。它告诉我们:在某个特定时间点、某个特定价格水平上,标的资产的瞬时波动率是多少。这比BS模型里那个「一刀切」的常数波动率要精准得多。

核心思想:局部波动率 σ(S,t) 是一个确定性函数,它完美复刻了市场上所有欧式期权的隐含波动率曲面。换句话说,只要你把市场上所有期权的价格输入进去,Dupire公式就能反推出这个函数。

4.2 Dupire公式推导——从BS到局部波动率

Dupire公式的推导,其实是从BS的偏微分方程出发的。我记得第一次看这个推导时,觉得它像变魔术一样——怎么就能从期权价格反推出波动率呢?

咱们先回顾一下BS方程:

∂V/∂t + ½ σ² S² ∂²V/∂S² + rS ∂V/∂S - rV = 0

这里V是期权价格,S是标的价格,t是时间,r是无风险利率。注意,这里的σ是常数。

Dupire的聪明之处在于:他把期权价格看成是执行价格K和到期时间T的函数,而不是S和t的函数。然后他利用Fokker-Planck方程(也叫前向Kolmogorov方程)重新推导了期权价格随K和T的变化规律。

推导的关键步骤是这样的:

  1. 从BS方程出发,把V(S,t)换成V(S₀, K, T),其中S₀是当前标的价格
  2. 利用风险中性定价公式:V = e^{-rT} ∫ max(S_T - K, 0) q(S_T) dS_T
  3. 对K求二阶导,得到风险中性密度:q(K) = e^{rT} ∂²V/∂K²
  4. 对T求一阶导,结合Fokker-Planck方程,最终得到Dupire公式

最终结果长这样:

σ²_loc(K, T) = (∂V/∂T + rK ∂V/∂K) / (½ K² ∂²V/∂K²)

我的经验:实际计算时,∂V/∂T和∂²V/∂K²都是通过数值差分得到的。我曾经踩过一个坑——直接用期权价格做差分,结果噪声大得离谱。后来改用隐含波动率曲面做插值,再转回期权价格,效果就好多了。

4.3 局部波动率曲面的构建

有了Dupire公式,下一步就是构建局部波动率曲面了。说白了,就是三步走:

  1. 获取市场数据:收集不同执行价格K和到期时间T的期权价格(或隐含波动率)
  2. 构建隐含波动率曲面:对离散的数据点做插值,得到一个光滑的曲面
  3. 应用Dupire公式:在曲面上计算偏导数,得到局部波动率

这里有个坑要注意——直接对期权价格做插值很容易出问题。我建议的做法是:

  • 先把期权价格转成隐含波动率
  • 在隐含波动率空间做插值(用样条或SVI参数化)
  • 再转回期权价格,然后计算偏导数

下面是我常用的一个流程示意图:

局部波动率曲面构建流程 市场数据获取 期权价格 / 隐含波动率 曲面插值 样条 / SVI参数化 Dupire公式 计算偏导数 局部波动率曲面 σ_loc(K, T) 三维曲面 有限差分法求解 PDE 定价奇异期权 / 计算风险敞口

4.4 有限差分法求解局部波动率PDE

有了局部波动率曲面,接下来怎么用?最常见的场景就是给奇异期权定价。这时候需要求解局部波动率下的PDE:

∂V/∂t + ½ σ²_loc(S,t) S² ∂²V/∂S² + rS ∂V/∂S - rV = 0

这个方程没有解析解,只能用数值方法。我个人最常用的是有限差分法(FDM)。

4.4.1 网格划分

首先把时间和价格空间都离散化:

  • 时间网格:t₀, t₁, ..., t_N,步长Δt
  • 价格网格:S₀, S₁, ..., S_M,步长ΔS

注意,价格网格最好在标的当前价格附近加密。为什么?因为大部分期权价值都集中在这个区域。我曾经试过均匀网格,结果计算量大了好几倍,精度还没提高多少。

4.4.2 差分格式选择

常用的差分格式有三种:

格式 精度 稳定性 我的建议
显式差分 一阶 有条件稳定(CFL条件) 不推荐,时间步长限制太严格
隐式差分 一阶 无条件稳定 可以,但需要解三对角方程组
Crank-Nicolson 二阶 无条件稳定 强烈推荐,精度和稳定性兼顾

避坑指南:我曾经在Crank-Nicolson格式上吃过亏——当局部波动率变化剧烈时,它会产生数值振荡。后来我改用Rannacher修正(前两步用全隐式,后面用C-N),问题就解决了。

4.4.3 边界条件

求解PDE还需要边界条件。对于看涨期权:

  • S=0时:V(0,t) = 0
  • S→∞时:V(S,t) ≈ S - Ke^{-r(T-t)}
  • 到期时:V(S,T) = max(S-K, 0)

嗯,这里有个细节——上边界不能取太大,否则计算量太大。我一般取S_max = 4倍的当前标的价格,基本够用。

4.5 代码实现要点

下面是一个简化的Crank-Nicolson求解框架:

# 伪代码:Crank-Nicolson求解局部波动率PDE
def crank_nicolson(S_grid, T_grid, sigma_loc, r, K):
    M = len(S_grid) - 1  # 价格网格数
    N = len(T_grid) - 1  # 时间网格数
    dt = T_grid[1] - T_grid[0]
    ds = S_grid[1] - S_grid[0]
    
    # 初始化矩阵A和B(三对角)
    A = np.zeros((M-1, M-1))
    B = np.zeros((M-1, M-1))
    
    for i in range(1, M):
        S = S_grid[i]
        sigma = sigma_loc(S, 0)  # 取当前时间层的局部波动率
        
        # 构建系数
        alpha = 0.25 * dt * (sigma**2 * S**2 / ds**2 - r * S / ds)
        beta = -0.5 * dt * (sigma**2 * S**2 / ds**2 + r)
        gamma = 0.25 * dt * (sigma**2 * S**2 / ds**2 + r * S / ds)
        
        # 填充A和B矩阵
        # ...(具体填充略)
    
    # 时间迭代
    V = np.maximum(S_grid - K, 0)  # 到期时的payoff
    for n in range(N-1, -1, -1):
        # 更新局部波动率
        sigma_loc_n = sigma_loc(S_grid, n*dt)
        # 解线性方程组 A * V_new = B * V_old
        V[1:-1] = solve_tridiagonal(A, B @ V[1:-1])
    
    return V

性能优化:如果网格很大(比如1000×1000),每次迭代都解三对角方程组会有点慢。我建议用Thomas算法,复杂度只有O(M),比通用LU分解快一个数量级。

4.6 实际应用中的注意事项

最后,分享几个我在实战中总结的经验:

  • 数据清洗:市场上的期权报价经常有异常值。我一般会先剔除深度虚值和深度实值的期权,它们的流动性差,报价不可靠。
  • 插值方法:在隐含波动率曲面上做插值时,用三次样条比线性插值好得多。但要注意样条的边界条件——自然样条在边界处容易振荡,我习惯用clamped样条。
  • 时间处理:Dupire公式里的时间导数∂V/∂T,实际计算时要用到不同到期日的期权数据。如果到期日间隔不均匀,建议先插值到统一的时间网格上。
  • 数值稳定性:当∂²V/∂K²接近零时(比如深度实值或虚值期权),Dupire公式的分母会很小,导致局部波动率爆炸。我一般会加一个小的正则化项,或者直接截断。

局部波动率模型虽然比BS复杂,但它能捕捉到市场的真实波动特征。我个人觉得,它是从「理论模型」走向「实战应用」的关键一步。嗯,今天就先聊到这里,下次有机会再聊聊随机波动率模型——那又是另一个故事了。


专注资料整理