σμimplied vol · KKmax(S−K,0)udβρᵢⱼfat tailsPnL · drawdownyield · TdSₜ = μSₜ dt + σSₜ dWₜC = S·N(d₁) − K·e⁻ʳᵀ·N(d₂)σₚ² = wᵀΣwd₁ = [ln(S/K) + (r + σ²⁄2)T] ⁄ σ√T∂V/∂t + ½σ²S²·∂²V/∂S² + rS·∂V/∂S − rV = 0Sharpe = (Rₚ − R_f) ⁄ σₚVaR₉₉ = μ − 2.33σρ = Cov(X,Y) ⁄ σₓσᵧE[Rₚ] = Σ wᵢ·E[Rᵢ]φ(x) = e^(−x²/2) ⁄ √2πE[Rᵢ] = R_f + βᵢ(E[Rₘ] − R_f)dXₜ = θ(μ − Xₜ)dt + σ dWₜσ²ₜ = ω + α·ε²ₜ₋₁ + β·σ²ₜ₋₁C − P = S − K·e⁻ʳᵀΔ = ∂V/∂S Γ = ∂²V/∂S²p = (e^(rΔt) − d) ⁄ (u − d)rₜ = ln(Pₜ ⁄ Pₜ₋₁)E[Xₜ₊₁ | ℱₜ] = Xₜf* = (bp − q) ⁄ bz = (x − μ) ⁄ σP(N=k) = λᵏe^(−λ) ⁄ k!Θ = −∂V/∂Tν = S·φ(d₁)·√Tmin wᵀΣw s.t. wᵀμ = μ*∫Σ∂∞λ
σμimplied vol · KKmax(S−K,0)udβρᵢⱼfat tailsPnL · drawdownyield · TdSₜ = μSₜ dt + σSₜ dWₜC = S·N(d₁) − K·e⁻ʳᵀ·N(d₂)σₚ² = wᵀΣwd₁ = [ln(S/K) + (r + σ²⁄2)T] ⁄ σ√T∂V/∂t + ½σ²S²·∂²V/∂S² + rS·∂V/∂S − rV = 0Sharpe = (Rₚ − R_f) ⁄ σₚVaR₉₉ = μ − 2.33σρ = Cov(X,Y) ⁄ σₓσᵧE[Rₚ] = Σ wᵢ·E[Rᵢ]φ(x) = e^(−x²/2) ⁄ √2πE[Rᵢ] = R_f + βᵢ(E[Rₘ] − R_f)dXₜ = θ(μ − Xₜ)dt + σ dWₜσ²ₜ = ω + α·ε²ₜ₋₁ + β·σ²ₜ₋₁C − P = S − K·e⁻ʳᵀΔ = ∂V/∂S Γ = ∂²V/∂S²p = (e^(rΔt) − d) ⁄ (u − d)rₜ = ln(Pₜ ⁄ Pₜ₋₁)E[Xₜ₊₁ | ℱₜ] = Xₜf* = (bp − q) ⁄ bz = (x − μ) ⁄ σP(N=k) = λᵏe^(−λ) ⁄ k!Θ = −∂V/∂Tν = S·φ(d₁)·√Tmin wᵀΣw s.t. wᵀμ = μ*∫Σ∂∞λ
uantCraft Academy学习路线
Ctrl K
AO
+1.2k 位学习者
登录加入 ↗
学习路线
AO
+1.2k 位学习者
登录
  • English
  • Français
  • Español
  • العربية
  • Português
  • Deutsch
  • 日本語
  • 中文
Python 学习路线›模块 11 · 生产系统与前沿模型›主题 11.8·~1小时 30分钟专家

局部波动率

路线进度模块 11/11 · 主题 8/13
11.8 局部波动率
—实验得分— / 100总 XP—连续天数—

每个行权价和时间对应一个波动率

Black-Scholes 用一个波动率给看涨期权定价,而市场对每个行权价和到期日都报出不同的隐含波动率。局部波动率模型让波动率依赖于价格水平和时间 sigma(S, t),同时只保留一个随机源,从而一次性复现所有这些价格。Bruno Dupire 在 1994 年证明这个函数不是猜测:它由看涨期权价格曲面 C(K, T) 通过一个公式决定,因此市场的期权价格可以直接变成一个模型,用来与之一致地为奇异期权定价。

# What "one volatility" means: a call priced with Black-Scholes at three strikes, all with the same sigma
import math

def norm_cdf(x):
    return 0.5 * (1 + math.erf(x / math.sqrt(2)))

def bs_call(s, k, t, r, q, sigma):
    d1 = (math.log(s / k) + (r - q + sigma ** 2 / 2) * t) / (sigma * math.sqrt(t))
    d2 = d1 - sigma * math.sqrt(t)
    return s * math.exp(-q * t) * norm_cdf(d1) - k * math.exp(-r * t) * norm_cdf(d2)

for k in (90, 100, 110):
    print(k, round(bs_call(100.0, k, 1.0, 0.03, 0.01, 0.2), 4))      # 90 14.6592   100 8.8273   110 4.8947   one sigma, three different prices

Dupire 公式

Dupire 公式用看涨期权价格曲面的三个导数来表达局部方差:sigma_loc^2(K, T) = (dC/dT + (r - q) * K * dC/dK + q * C) / (0.5 * K^2 * d2C/dK2)。分子含有价格的时间斜率(必须非负,否则期限更长的期权反而更便宜:日历套利),分母含有蝶式 d2C/dK2,它与市场隐含的价格概率密度成正比,必须为正。真实报价有噪声且稀疏,导数要在平滑后的曲面上用有限差分计算:一阶导数是 (f(x+h) - f(x-h)) / (2h),二阶导数是 (f(x+h) - 2 f(x) + f(x-h)) / h^2。有噪声的曲面会给出剧烈波动的局部波动率,所以先平滑隐含波动率曲面是标准做法。

# Dupire's formula recovers a constant volatility from Black-Scholes prices (finite differences on the price surface)
import math

def norm_cdf(x):
    return 0.5 * (1 + math.erf(x / math.sqrt(2)))

def bs_call(s, k, t, r, q, sigma):
    d1 = (math.log(s / k) + (r - q + sigma ** 2 / 2) * t) / (sigma * math.sqrt(t))
    return s * math.exp(-q * t) * norm_cdf(d1) - k * math.exp(-r * t) * norm_cdf(d1 - sigma * math.sqrt(t))

def local_variance(call, dc_dt, dc_dk, d2c_dk2, strike, r, q):
    return (dc_dt + (r - q) * strike * dc_dk + q * call) / (0.5 * strike ** 2 * d2c_dk2)

s, k, t, r, q, sigma = 100.0, 105.0, 1.0, 0.03, 0.01, 0.2
hk, ht = 0.5, 0.002
c = bs_call(s, k, t, r, q, sigma)
dc_dt = (bs_call(s, k, t + ht, r, q, sigma) - bs_call(s, k, t - ht, r, q, sigma)) / (2 * ht)
dc_dk = (bs_call(s, k + hk, t, r, q, sigma) - bs_call(s, k - hk, t, r, q, sigma)) / (2 * hk)
d2c_dk2 = (bs_call(s, k + hk, t, r, q, sigma) - 2 * c + bs_call(s, k - hk, t, r, q, sigma)) / hk ** 2
print(round(math.sqrt(local_variance(c, dc_dt, dc_dk, d2c_dk2, k, r, q)), 3))        # 0.2   the constant volatility comes back

在模拟中使用曲面

模拟时,把 sigma_loc 在价格网格上制成表(每个时间片一张),每一步按当前价格查表,网格点之间做线性插值,网格之外保持端点值不变。一步就是用该波动率做通常的对数欧拉更新 S * exp((mu - sigma^2/2) dt + sigma sqrt(dt) dz)。与 Heston 相比,局部波动率在构造上恰好拟合今天的期权价格,但其未来微笑的动态不现实(偏斜会随时间变平),这对依赖未来微笑的产品很重要。因此许多交易台使用两者混合的局部-随机波动率。

# A lookup on a strike grid (linear inside, flat outside) and one simulation step with the volatility it returns
import math

def lookup_local_vol(strikes, vols, k):
    if k <= strikes[0]:
        return vols[0]
    if k >= strikes[-1]:
        return vols[-1]
    for i in range(1, len(strikes)):
        if k <= strikes[i]:
            weight = (k - strikes[i - 1]) / (strikes[i] - strikes[i - 1])
            return vols[i - 1] + weight * (vols[i] - vols[i - 1])

strikes, vols = [90.0, 100.0, 110.0], [0.25, 0.20, 0.18]
print(lookup_local_vol(strikes, vols, 95.0), round(lookup_local_vol(strikes, vols, 105.0), 4), lookup_local_vol(strikes, vols, 200.0))      # 0.225 0.19 0.18

s = 100.0
sigma = lookup_local_vol(strikes, vols, s)
print(round(s * math.exp((0.05 - sigma ** 2 / 2) * (1 / 252) + sigma * math.sqrt(1 / 252) * 0.5), 4))       # 100.6439
# A calendar arbitrage shows up as a negative numerator: a longer option priced below a shorter one
def numerator(call, dc_dt, dc_dk, strike, r, q):
    return dc_dt + (r - q) * strike * dc_dk + q * call

print(round(numerator(10.0, 1.2, -0.45, 100.0, 0.03, 0.01), 6))           # 0.4     prices rise with maturity: a valid local variance
print(numerator(10.0, -2.0, -0.45, 100.0, 0.03, 0.01) < 0)                # True    prices fall with maturity: no model can fit this
完美拟合不等于完美模型由于局部波动率是从价格中读出来的,它在构造上会复现这些价格,包括其中的噪声和报价里的任何套利。使用 Dupire 之前要检查曲面无套利(蝶式和日历价差非负),对得到的波动率设上下限,并把模拟出的价格与你出发时用的普通期权对照。一个与今天完美吻合的模型明天仍可能对冲得很差,因为它对微笑如何变动的看法过于简单。
  • 局部波动率 sigma(S, t) 用一个随机源复现整张看涨期权价格曲面;Dupire 公式直接由价格给出它。
  • Dupire:(dC/dT + (r - q) K dC/dK + q C) / (0.5 K^2 d2C/dK2);在平滑且无套利的曲面上用有限差分求导。
  • 每一步按当前价格查 sigma_loc 来模拟;今天的完美拟合并不保证明天微笑动态的真实性。

下一节:SABR 模型,带闭式微笑的随机波动率模型。

实验:评分内容

  • 用 finite_difference 做数值求导25 分
  • 在 local_variance 中应用 Dupire 公式25 分
  • 用 lookup_local_vol 读取曲面25 分
  • 用 local_vol_step 模拟一步25 分
资源管理器 · lab-137-1
▾ lab-137-1
…
bash · 浏览器内
learner@quantcraft:~/lab-137-1$
Python 尚未加载PythonLn 1, Col 1Spaces: 4UTF-8

要重置此主题吗?

lab-137-1 中的所有内容将恢复为初始文件,终端也会被清空。你当前的解答将丢失,且无法撤销。

将被重置
  • main.py → 初始代码
  • 终端历史与测试输出
  • 未提交的更改
将被保留
  • 实验最高得分
  • 你的 XP 与连续天数
  • 讲解进度与笔记

登录后即可运行你的代码。

所有讲解均可免费阅读。创建账户或登录后,即可运行代码、运行测试并提交此实验。

  • 浏览器内 IDE 与 bash 终端,无需安装
  • 即时评分、XP 以及你在学习路线中的位置
创建账户登录
或
使用 GitHub 继续