7. 局部波动率模型:Dupire公式推导
局部波动率模型,说白了就是解决一个核心问题:我们能不能从市场上看到的期权价格,反推出未来的波动率长什么样?
BS模型假设波动率是常数,这显然不现实。我在2015年做期权做市商系统时,就被这个假设坑过——用BS算出来的理论价格,跟市场实际报价总是对不上。后来我才明白,市场已经把对未来波动率的预期,全部隐含在期权价格里了。
Dupire公式就是干这个的。它告诉我们:给定一个完整的期权价格曲面,我们可以唯一地反解出局部波动率函数。
7.1 从BS到局部波动率
先回顾一下BS框架下的期权定价公式。对于欧式看涨期权,价格C满足:
C = C(S, K, T, r, σ)
其中S是标的价格,K是行权价,T是到期时间,r是无风险利率,σ是波动率。
BS模型假设σ是常数。但市场数据告诉我们,隐含波动率随K和T变化。这就产生了所谓的波动率微笑和期限结构。
局部波动率模型的想法很直接:允许波动率是标的价格和时间的函数,即σ(S, t)。这样,标的资产价格过程变为:
dS = μS dt + σ(S, t) S dW
关键问题是:如何从期权价格反推σ(S, t)?
核心思想:Dupire公式给出了从期权价格曲面到局部波动率曲面的显式映射。你不需要做复杂的参数估计,只需要对期权价格求偏导。
7.2 Dupire公式的推导
推导过程其实不复杂。我们从Fokker-Planck方程出发,也叫前向Kolmogorov方程。
假设标的价格S服从:
dS = rS dt + σ(S, t) S dW
那么,在风险中性测度下,S在时间T的概率密度函数p(S, T)满足:
∂p/∂T = -∂(rS p)/∂S + ½ ∂²(σ² S² p)/∂S²
另一方面,欧式看涨期权的价格可以写成:
C(K, T) = e^{-rT} ∫_{K}^{∞} (S - K) p(S, T) dS
这里的关键是:对C关于K和T求偏导,就能把p和σ联系起来。
我对C求关于K的一阶偏导:
∂C/∂K = -e^{-rT} ∫_{K}^{∞} p(S, T) dS = -e^{-rT} (1 - P(S ≤ K))
再求二阶偏导:
∂²C/∂K² = e^{-rT} p(K, T)
嗯,这里要注意:二阶偏导给出了风险中性密度。这个关系在量化金融里非常基础,我每次做模型校验都会用到它。
接下来,对C关于T求偏导。利用莱布尼茨法则:
∂C/∂T = -rC + e^{-rT} ∫_{K}^{∞} (S - K) ∂p/∂T dS
把Fokker-Planck方程代入,经过一番整理(这里我省略了中间步骤,感兴趣的同学可以自己推一下),最终得到:
∂C/∂T = -rK ∂C/∂K + ½ σ²(K, T) K² ∂²C/∂K²
解出σ²(K, T):
σ²(K, T) = (∂C/∂T + rK ∂C/∂K) / (½ K² ∂²C/∂K²)
这就是Dupire公式。
实用技巧:在实际计算中,∂C/∂T和∂²C/∂K²都需要用数值方法近似。我建议用中心差分法,边界处用前向或后向差分。网格要足够密,否则导数会很不稳定。
7.3 公式的直观理解
Dupire公式看起来有点吓人,但它的含义其实很直观:
- 分子:∂C/∂T + rK ∂C/∂K,衡量的是期权价格随时间的变化,减去由于利率引起的调整
- 分母:½ K² ∂²C/∂K²,衡量的是期权价格对行权价的曲率,也就是凸性
- 比值:局部波动率平方 = 时间变化率 / 凸性
为什么会这样?你想想看:凸性越大,说明市场对未来价格的不确定性越强,对应的局部波动率就应该越高。
我在做波动率曲面构建时,经常用这个公式来校验模型是否合理。如果算出来的局部波动率出现负值,那一定是输入数据有问题——要么是期权价格有套利机会,要么是数值求导精度不够。
7.4 数值实现要点
把Dupire公式用到实际数据上,有几个坑要避开:
- 数据插值:市场给的期权价格通常是离散的,需要先插值成光滑曲面。我习惯用三次样条,但要注意避免过拟合
- 边界处理:在K很小或很大时,∂²C/∂K²可能接近零,导致局部波动率发散。需要加一些正则化
- 时间导数:∂C/∂T需要小心处理,尤其是近月合约。我建议用对数时间网格
避坑指南:我曾经在计算∂²C/∂K²时用了太粗糙的网格,结果局部波动率曲面出现了明显的锯齿。后来改用自适应网格,并在每个点用5点差分公式,问题才解决。记住:数值稳定性比精度更重要。
7.5 代码示例:Dupire公式实现
下面是一个Python实现的核心部分。假设我们已经有了插值后的期权价格曲面C(K, T):
import numpy as np
from scipy.interpolate import RectBivariateSpline
def dupire_local_vol(K_grid, T_grid, C_surface, r):
"""
从期权价格曲面计算局部波动率
参数:
K_grid: 行权价网格 (1D array)
T_grid: 到期时间网格 (1D array)
C_surface: 期权价格曲面 (2D array, shape=[len(T), len(K)])
r: 无风险利率
返回:
local_vol: 局部波动率曲面 (2D array)
"""
# 对价格曲面做样条插值
spline = RectBivariateSpline(T_grid, K_grid, C_surface)
# 计算偏导数
dC_dT = spline(T_grid, K_grid, dx=1, dy=0) # ∂C/∂T
dC_dK = spline(T_grid, K_grid, dx=0, dy=1) # ∂C/∂K
d2C_dK2 = spline(T_grid, K_grid, dx=0, dy=2) # ∂²C/∂K²
# 构造网格
K_mesh, T_mesh = np.meshgrid(K_grid, T_grid)
# Dupire公式
numerator = dC_dT + r * K_mesh * dC_dK
denominator = 0.5 * K_mesh**2 * d2C_dK2
# 防止除零和负值
denominator = np.maximum(denominator, 1e-10)
local_var = numerator / denominator
local_var = np.maximum(local_var, 0) # 方差不能为负
return np.sqrt(local_var)
个人经验:这段代码在实际使用时,我建议加上一个平滑步骤。比如对算出来的局部波动率再做一次二维高斯滤波,能有效去除数值噪声。另外,永远不要直接用原始市场报价,一定要先做插值和去噪。
7.6 局部波动率曲面的可视化
下面我用SVG画一个典型的局部波动率曲面结构图,展示从期权价格到局部波动率的映射关系:
7.7 实际应用中的注意事项
Dupire公式在理论上很完美,但实际应用中会遇到几个问题:
| 问题 | 原因 | 解决方案 |
|---|---|---|
| 局部波动率出现负值 | 期权价格存在套利机会 | 先做无套利校准,再计算 |
| 曲面不平滑 | 数值求导放大噪声 | 用正则化样条插值 |
| 近月合约不稳定 | 时间导数对短期数据敏感 | 增加时间网格密度 |
| 深度虚值/实值区域发散 | 二阶导接近零 | 加截断或先验约束 |
核心要点:Dupire公式是连接期权市场和局部波动率的桥梁。它告诉我们,只要市场是完整的、无套利的,局部波动率就唯一存在。这个结论在衍生品定价和风险管理中有着根本性的意义。
我个人习惯在构建波动率曲面时,先用Dupire公式算一个初始的局部波动率,然后用它作为后续随机波动率模型的校准起点。这样既能保证与市场一致,又能引入更丰富的动态特征。
好了,Dupire公式的推导就讲到这里。记住:公式本身不难,难的是数值实现和数据处理。多动手写代码,多跟市场数据打交道,你自然就能掌握其中的门道。