σμ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 学习路线›模块 4 · 固定收益与期权定价›主题 4.15·~55分钟中级

二叉树模型

路线进度模块 4/11 · 主题 15/16
4.15 二叉树模型
—实验得分— / 100总 XP—连续天数—

只能上涨或下跌的股票

布莱克-斯科尔斯是一个公式,而二叉树是一套步骤,它能为公式无法处理的东西定价。把到期时间切成很多小步。每一步股价要么乘以 u 上涨,要么乘以 d 下跌,别无其他。走 n 步后有 n + 1 个可能的价格。到期时的收益已知,更早的每个节点的价值是它所通向的两个节点的折现平均,因此可以从后往前把树填到现在。

# One step: a stock at 100 goes to 110 or 90. What is a call with strike 100 worth?
import math

spot, strike, rate, dt = 100, 100, 0.05, 1.0
u, d = 1.10, 0.90
p = (math.exp(rate * dt) - d) / (u - d)             # the probability that makes the stock grow at the rate
up_value, down_value = max(spot * u - strike, 0), max(spot * d - strike, 0)
price = math.exp(-rate * dt) * (p * up_value + (1 - p) * down_value)
print(round(p, 4), round(up_value, 4), down_value)   # 0.7564 10.0 0
print(round(price, 4))                               # 7.1947

这个概率不是预测

数 p 的选法使股票平均以无风险利率增长:p * u + (1 - p) * d = exp(rate * dt),由此 p = (exp(rate * dt) - d) / (u - d)。它是风险中性概率:一种让复制组合等于其折现期望的手段,而不是对股价走向的看法。为了匹配观察到的波动率,Cox–Ross–Rubinstein(CRR)树取 u = exp(sigma * sqrt(dt)) 和 d = 1 / u,于是先涨后跌会回到同一价格,树可以重合:n 步在末端只有 n + 1 个节点。

# The CRR parameters and the prices at expiry of a 3-step tree
import math

def crr_parameters(rate, sigma, t, steps):
    dt = t / steps
    u = math.exp(sigma * math.sqrt(dt))
    d = 1 / u
    p = (math.exp(rate * dt) - d) / (u - d)
    return u, d, p

def terminal_prices(spot, u, d, steps):
    return [spot * u ** j * d ** (steps - j) for j in range(steps + 1)]

u, d, p = crr_parameters(0.05, 0.2, 1, 3)
print(round(u, 4), round(d, 4), round(p, 4))                     # 1.1224 0.8909 0.5438
print([round(price, 2) for price in terminal_prices(100, u, d, 3)])

倒推

到期时每个节点就是收益,看涨期权为 max(S - K, 0)。往前一列,每个值是 exp(-rate * dt) * (p * value_up + (1 - p) * value_down),这一列少一个节点。经过 n 次扫描后只剩一个值,就是今天的价格。在 Python 中,一列就是一个列表,一次扫描就是对相邻两项的列表推导。随着步数增加,价格收敛到布莱克-斯科尔斯价格,二叉树复现了公式的结果。

# The tree price of a European call approaches the Black-Scholes price as steps grow
import math

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

def bs_call(spot, strike, rate, sigma, t):
    d1 = (math.log(spot / strike) + (rate + 0.5 * sigma ** 2) * t) / (sigma * math.sqrt(t))
    return spot * norm_cdf(d1) - strike * math.exp(-rate * t) * norm_cdf(d1 - sigma * math.sqrt(t))

def european_call_tree(spot, strike, rate, sigma, t, steps):
    dt = t / steps
    u = math.exp(sigma * math.sqrt(dt))
    d = 1 / u
    p = (math.exp(rate * dt) - d) / (u - d)
    disc = math.exp(-rate * dt)
    values = [max(spot * u ** j * d ** (steps - j) - strike, 0) for j in range(steps + 1)]
    for _ in range(steps):
        values = [disc * (p * values[j + 1] + (1 - p) * values[j]) for j in range(len(values) - 1)]
    return values[0]

print("Black-Scholes", round(bs_call(100, 100, 0.05, 0.2, 1), 4))
for steps in (1, 5, 25, 100, 400):
    print(steps, round(european_call_tree(100, 100, 0.05, 0.2, 1, steps), 4))

提前行权

二叉树真正的长处是公式无法定价的期权:美式期权可以在任何时候行权。在每个节点,持有人比较两个价值——继续持有的折现期望,和立即行权的收益——并取较大者。算法的全部改动就是每个节点多一个 max。对于不分红股票的看涨期权,提前行权永远不划算,但看跌期权可能划算:深度实值的看跌期权今天拿现金更值钱,所以美式看跌期权比欧式的贵。

# An American put against a European put: the early-exercise premium
import math

def tree(spot, strike, rate, sigma, t, steps, american):
    dt = t / steps
    u = math.exp(sigma * math.sqrt(dt))
    d = 1 / u
    p = (math.exp(rate * dt) - d) / (u - d)
    disc = math.exp(-rate * dt)
    values = [max(strike - spot * u ** j * d ** (steps - j), 0) for j in range(steps + 1)]
    for i in range(steps - 1, -1, -1):
        values = [disc * (p * values[j + 1] + (1 - p) * values[j]) for j in range(i + 1)]
        if american:
            values = [max(values[j], strike - spot * u ** j * d ** (i - j)) for j in range(i + 1)]
    return values[0]

for spot in (80, 100, 120):
    european = tree(spot, 100, 0.05, 0.2, 1, 200, False)
    american = tree(spot, 100, 0.05, 0.2, 1, 200, True)
    print(spot, round(european, 4), round(american, 4), "premium", round(american - european, 4))
慢而通用胜过快而专用对于普通欧式期权,闭式公式瞬间且精确,没人会用二叉树。当收益中含有决策(提前行权)、障碍或离散股息时,二叉树才派上用场:在每个节点加入相应规则。代价是时间:n 步大约需要 n**2 / 2 次节点更新,而且收敛并不平滑(误差会随行权价落在节点之间的位置而摆动),所以要用几个不同的步数来比较检验。
  • 每一步股价乘以 u 或 d;CRR 的取法 u = exp(sigma * sqrt(dt))、d = 1 / u 使树重合,n 步只有 n + 1 个末端节点。
  • 倒推定价:先取到期收益,再对每个节点算 exp(-rate * dt) * (p * up + (1 - p) * down);p 是风险中性概率,不是预测。
  • 步数多了会收敛到布莱克-斯科尔斯;美式期权只是在每个节点多一个 max(继续持有, 行权),这正是二叉树存在的理由。

下一节:有限差分法,在网格上求解期权的偏微分方程来定价。

实验:评分内容

  • 用 crr_parameters 确定上涨与下跌幅度25 分
  • 用 terminal_prices 列出到期时的价格25 分
  • 用 european_call_tree 沿二叉树倒推25 分
  • 用 american_put_tree 允许提前行权25 分
资源管理器 · lab-80-1
▾ lab-80-1
…
bash · 浏览器内
learner@quantcraft:~/lab-80-1$
Python 尚未加载PythonLn 1, Col 1Spaces: 4UTF-8

要重置此主题吗?

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

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

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

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

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