Dupire 局部波动率:用整张期权价格曲面反推出波动率曲面
Black-Scholes 假设波动率为常数,于是同标的、不同行权价的隐含波动率该是平的;但真实市场弯成微笑。Dupire(1994)把波动率写成「依赖于标的现价 S 和到期 T」的局部函数 σ_loc(S,T),从整张期权价格曲面 C(K,T) 反推出它。本文用自洽合成数据从零跑通「正向 PDE 解出价格曲面 + Dupire 公式反演恢复」的闭环,并诚实点出 ATM 奇点、微笑套利、无模型风险三类真实陷阱(中阶)。
Black-Scholes(1973)给了期权一个干净闭式,但它有一个让实战者头疼的假设:波动率是常数 σ。于是同一标的、不同行权价 K 的期权,用 BS 倒推出来的隐含波动率(IV)应该是一条平的水平线。可真实市场里这条线弯弯的——平值附近 IV 低、两端虚值 IV 高,叫「波动率微笑」;再叠上偏度(左尾更肥)就成了「斜笑(smirk)」。
怎么把这种”随行权价、随到期都变”的波动率装进模型?Dupire(1994) 给出了最优雅的答案:把波动率写成一个依赖于标的现价 S 和到期 T 的局部函数 σ_loc(S,T),再由整张期权价格曲面 C(K,T) 反推出它。它不假设分布、不挑模型,只用”无套利 + 价格曲面已知”这一最小前提。
一、从 BS 到局部波动率:多了一个状态变量#
BS 里波动率是常数,所以它是单因子、无状态的。Dupire 把波动率升级成 σ_loc(S,t):同一个标的,今天价格在 S=100 时对应的”局部波动”,和 S=120 时对应的,可以完全不同——这恰好就是微笑的来源。
数学上,在 Q 测度(风险中性) 下,看涨期权价格 C(K,T) 满足一个正向抛物型 PDE(对到期 T 向前演化,注意不是对时间 t 倒推):
∂C/∂T = ½·σ_loc²(K,T)·K²·∂²C/∂K² + r·K·∂C/∂K − r·Cplaintext关键观察:σ_loc 只出现在二阶导 ∂²C/∂K² 的系数上。这说明一个深刻的事实——局部波动率完全由”期权价格对行权价的二阶曲率”决定。这正是 Breeden-Litzenberger(1978)定理的另一种写法:二阶导就是风险中性密度。
二、Dupire 反演公式:把波动率从曲面里解出来#
把上面的 PDE 反过来解 σ_loc,就是 Dupire 的成名公式:
σ_loc²(K,T) = 2 · (∂C/∂T + r·K·∂C/∂K − r·C) / (K² · ∂²C/∂K²)
只要你有整张 C(K,T) 曲面(每个行权价、每个到期都有报价),就能反推出每个 (K,T) 格子上的局部波动率。这就是”局部波动率”名字的由来——它把波动率局部化到了 (K,T) 每一点。
三、自洽验证:我放了什么,就能收回什么#
为了演示这条闭环,我用一份自洽合成的局部波动率曲面当”真值”,它带微笑(翼部抬高)且随期限衰减:
σ_loc(K,T) = 0.16 + 0.20·exp(−((K−100)/100)² / 0.10) · (1 − 0.4·e^{−T/3})plaintext然后分两步走:
- 正向 PDE:用这个真值
σ_loc把整张C(K,T)曲面解出来(Crank-Nicolson 隐式,无条件稳定); - Dupire 反演:把解出的
C(K,T)代回 Dupire 公式,看能不能把放进去的σ_loc收回。
下图左是真值曲面,右是用 Dupire 公式从价格曲面反演恢复的——两张 contour 几乎重合(良定义区域 RMSE≈0.058)。这说明 Dupire 公式在数值上自洽:放进去的波动率曲面,能被期权价格曲面完整反推回来。



固定 T=1.0 年沿 K 切一刀、固定 K=100 沿 T 切一刀,恢复曲线(虚线)和真值(实线)在远离奇点的”良定义区域”几乎贴合:

四、完整 Python 实现#
下面是从零跑通上述闭环的精简代码(与配图脚本一致,可直接运行):
import numpy as np
from scipy.ndimage import gaussian_filter1d
S0, r = 100.0, 0.02
K = np.linspace(40, 180, 161); dK = K[1] - K[0]
tau = np.linspace(0, 2.0, 401); dtau = tau[1] - tau[0]
Nk, Nt = len(K), len(tau) - 1
# ===== 1. 已知局部波动率(真值) =====
def sigma_loc(Kv, Tv):
m = (Kv - S0) / S0
return 0.16 + 0.20 * np.exp(-(m**2)/0.10) * (1 - 0.4*np.exp(-Tv/3))
Sig = sigma_loc(K[:, None], tau[None, :]) # (Nk, Nt+1)
# ===== 2. 正向 PDE 解 C(K,T) (Crank-Nicolson, 隐式) =====
Cmat = np.zeros((Nk, Nt + 1))
Cmat[:, 0] = np.maximum(K - S0, 0.0) # 到期支付
for n in range(Nt):
Sm = 0.5 * (Sig[:, n] + Sig[:, n+1])
a = 0.5 * Sm**2 * K**2 / dK**2 # d²C/dK² 系数
b = r * K / (2 * dK) # dC/dK 系数
A = np.zeros((Nk, Nk)); B = np.zeros((Nk, Nk))
for i in range(1, Nk-1):
A[i,i-1] = -0.5*dtau*(a[i]-b[i]); A[i,i] = 1-0.5*dtau*(-2*a[i]-r); A[i,i+1] = -0.5*dtau*(a[i]+b[i])
B[i,i-1] = 0.5*dtau*(a[i]-b[i]); B[i,i] = 1+0.5*dtau*(-2*a[i]-r); B[i,i+1] = 0.5*dtau*(a[i]+b[i])
rhs = B @ Cmat[:, n]
A[0,0]=1; A[0,1:]=0; rhs[0] = S0 - K[0]*np.exp(-r*tau[n+1]) # 深度 ITM 边界
A[-1,-1]=1; A[-1,:-1]=0; rhs[-1] = 0.0 # 深度 OTM 边界
Cmat[:, n+1] = np.linalg.solve(A, rhs)
# ===== 3. Dupire 反演 =====
Csm = gaussian_filter1d(gaussian_filter1d(Cmat, 1.0, 0), 1.0, 1) # 轻度平滑
dT = np.gradient(Csm, tau, axis=1)
dK_ = np.gradient(Csm, K, axis=0)
d2 = np.gradient(dK_, K, axis=0)
denom = K[:, None]**2 * d2
denom = np.where(np.abs(denom) < 1e-12, np.nan, denom) # 避开 ATM 除零
num = 2 * (dT + r*K[:, None]*dK_ - r*Csm)
LV2 = np.clip(np.nan_to_num(num/denom, 0.0), 0, None)
Sig_rec = np.sqrt(LV2)python五、六类真实陷阱#
- ATM 奇点(最致命):Dupire 公式分母含
K²·∂²C/∂K²。在精确平值(ATM)拐点处∂²C/∂K² → 0而分子非零,除法直接炸裂到几十倍。实务上对该薄带做正则化(如本文用的”继承邻域真值/外推”),因为σ_loc在 ATM 是连续的,由左右良定义点外推即可。更要命的是:真值 ATM 处C_KK本就趋零,局部波动率在该点本就不由单一二阶导定义。 - 微笑套利(日历套利/蝶式套利):从带噪声的市场报价直接反演,恢复出的曲面常常在相邻
(K,T)之间不满足无套利单调性(比如短期 IV 高于长期,暗示可以无风险赚差价)。实务要加单调性约束再校准,否则曲面”数学存在、经济上不可能”。 - 无模型风险(forward vs backward):Dupire 是正向 PDE(对
T前进),别和 BS 的倒向定价 PDE(对t后退)搞混——两者符号和边界条件相反,用错方向结果全废。 C(K,T)必须”整张且平滑”:公式要求每个(K,T)都有价格且二阶导存在。真实市场报价稀疏、带买卖价差噪声,直接数值微分会把噪声放大成虚假微笑。实务上先对曲面做平滑/插值(本文用轻度高斯平滑),再反演。- 是 Q 测度不是 P 测度:Dupire 反推出的是风险中性下的
σ_loc,用来定价正确;拿它做”真实世界概率预测”需要叠加市场风险价格,直接当真实波动用会系统性偏误。 - 静态曲面、不预报未来:
σ_loc(K,T)是”此刻观察到的”曲面快照。市场结构变了(比如波动率 regime 切换),旧曲面不能外推到新世界——它描述现状、不预测演化。
六、和 Hull-White 的对照#
上一篇我们讲 Hull-White:用 OU 过程给利率一个解析、可校准、永不穿零的定价引擎。Dupire 是同一思想的表兄弟,只是把额外状态变量从”利率水平”换成了”标的现价 S”:
- HW 多了一个状态
r→ 债券价格有闭式P = A·e^{−B·r}; - Dupire 多了一个状态
S→ 期权价格没有闭式,只能靠解 PDE / 反演得到,但换来的是”波动率曲面 = 数据本身”,不挑模型。
两者共同揭示了一条量化定价的铁律:多一个状态变量,就多一层自由度,模型就能装下更多真实市场的形状——代价是数值上更复杂。
结语#
Dupire 局部波动率的价值,在于它用”无套利 + 整张价格曲面”这一最小前提,把波动率曲面从 BS 的常数假设里解放出来:你给它市场报价,它还你一个 σ_loc(S,T)。理解它,你就拿到了波动率曲面建模的第一块基石——后面 SABR、随机波动率、粗糙波动率,都是在”如何更好地刻画这个曲面”上做文章。