σμ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 ロードマップ›モジュール 8 · 確率モデルと機械学習›トピック 8.5·~1 時間上級

バリアオプション

ロードマップ上の位置モジュール 8/11 · トピック 5/10
8.5 バリアオプション
—ラボスコア— / 100合計XP—連続日数—

落とし戸のあるオプション

バリアオプションは、オン・オフのスイッチが付いたコールまたはプットです。ノックアウト型は価格がバリアに触れると消滅し、ノックイン型は触れたときにだけ発生します。バリアは開始価格より上(アップ)にも下(ダウン)にも置けます。買い手はペイオフの一部を手放すため通常のオプションより安く、その点で、またストラクチャード商品で人気です。アジアンと同じく経路全体に依存するため、自然な道具はシミュレーションです。

import numpy as np

paths = np.array([[100.0, 105.0, 112.0, 108.0],      # stays below 115
                  [100.0,  98.0, 103.0, 110.0],      # stays below 115
                  [100.0, 120.0,  95.0,  90.0]])     # touches 115 on day 1, then falls

hit_up = paths.max(axis=1) >= 115.0                  # the maximum of each path against the barrier
print(hit_up)                                        # [False False  True]
print(paths.min(axis=1) <= 96.0)                     # a down barrier at 96: [False False  True]
# hit_barrier for both directions, with an error for anything else
import numpy as np

def hit_barrier(paths, barrier, direction):
    if direction == "up":
        return paths.max(axis=1) >= barrier
    if direction == "down":
        return paths.min(axis=1) <= barrier
    raise ValueError("direction must be 'up' or 'down'")

paths = np.array([[100.0, 110.0, 105.0], [100.0, 109.9, 105.0]])
print(hit_barrier(paths, 110.0, "up"))                  # [ True False]: touching the barrier exactly counts
try:
    hit_barrier(paths, 110.0, "sideways")
except ValueError as error:
    print("rejected:", error)

ノックアウト、ノックイン、その和

各経路で、通常のコールのペイオフ max(S_T - K, 0) とバリアのフラグを計算します。ノックアウト・コールはバリアに触れて*いない*経路でペイオフを残し、ノックイン・コールは触れた経路で残します。どの経路もどちらか一方なので、経路ごとに*イン+アウト=通常のコール*が成り立ちます。簡単な方を値付けすれば他方も得られ、コードの検算にも使えます。np.where(hit, a, b) は配列を要素ごとに選びます。

import numpy as np

paths = np.array([[100.0, 105.0, 112.0, 108.0],
                  [100.0,  98.0, 103.0, 110.0],
                  [100.0, 120.0,  95.0, 130.0]])
strike, barrier = 100.0, 115.0

vanilla = np.maximum(paths[:, -1] - strike, 0.0)
hit = paths.max(axis=1) >= barrier
knock_out = np.where(hit, 0.0, vanilla)
knock_in = np.where(hit, vanilla, 0.0)

print(vanilla, knock_out, knock_in)                      # [ 8. 10. 30.] [ 8. 10.  0.] [ 0.  0. 30.]
print(np.allclose(knock_in + knock_out, vanilla))        # True

値付けと離散監視バイアス

価格はこれまでと同じく割引した平均ペイオフです。ただしバリア判定に注意してください。シミュレーションは格子点の日付でしか価格を見ませんが、本物の連続バリアは日付の*間*でも破られます。ステップが少ないと、シミュレーションされた経路がバリアを見えないまま越えることがよくあり、ノックアウト・コールは高く、ノックインは安く出ます。ステップを増やすとバイアスは減り、バリアを約 0.5826 * sigma * sqrt(dt) ずらす補正でも減ります。

import numpy as np

def simulate_paths(s0, rate, sigma, t, steps, n_paths, rng):
    dt = t / steps
    z = rng.standard_normal((n_paths, steps))
    moves = (rate - 0.5 * sigma ** 2) * dt + sigma * np.sqrt(dt) * z
    return s0 * np.exp(np.hstack([np.zeros((n_paths, 1)), np.cumsum(moves, axis=1)]))

def knock_out_price(paths, strike, barrier, rate, t):
    vanilla = np.maximum(paths[:, -1] - strike, 0.0)
    payoff = np.where(paths.max(axis=1) >= barrier, 0.0, vanilla)
    return float(np.exp(-rate * t) * payoff.mean())

rng = np.random.default_rng(1)
for steps in (12, 52, 252):
    paths = simulate_paths(100.0, 0.05, 0.25, 1.0, steps, 40000, rng)
    print(steps, round(knock_out_price(paths, 100.0, 130.0, 0.05, 1.0), 3))        # the price falls as monitoring gets finer
バリアのリスクはギャップのリスクバリアの近くでは、バリアオプションのデルタとガンマが跳ね上がり、わずかな値動きでペイオフ全体がオンにもオフにもなります。そのためバリア付近のノックアウトのヘッジは難しく、デスクはその分を価格に上乗せします。バリア付近で誰が価格を左右できるかにも注意が必要です。市場を動かしたい誘惑は現実にあるため、契約はどの価格(終値や取引所の値)を採用するかを厳密に定めています。
  • 経路の最大値(アップ)または最小値(ダウン)がいずれかの日にバリアへ届けば触れたことになる。ノックアウトは接触で消え、ノックインは接触で発生する
  • 経路ごとに、ノックイン+ノックアウト=通常のコールのペイオフ。np.where(hit, a, b) で選ぶ。バリアオプションは通常のものより安い
  • 格子上のシミュレーションは日付の間の突破を見逃し、ノックアウトが高く出る。ステップを増やすかバリアをずらす。バリア付近のギャップリスクにも注意

次は対称変量法です。より少ないシミュレーションで同じモンテカルロ精度を得る方法です。

ラボ:採点される内容

  • hit_barrier でバリアへの接触を検出する25点
  • knock_out_call_payoff でオプションを消滅させる25点
  • knock_in_call_payoff でオプションを発生させる25点
  • barrier_price でペイオフを価格に変える25点
エクスプローラー · lab-106-1
▾ lab-106-1
…
bash · ブラウザ内
learner@quantcraft:~/lab-106-1$
Pythonは未読み込みPythonLn 1, Col 1Spaces: 4UTF-8

このトピックをリセットしますか?

lab-106-1 の内容はすべてスターターファイルに戻り、ターミナルも消去されます。現在の解答は失われ、元に戻せません。

リセットされるもの
  • main.py → スターターコード
  • ターミナル履歴とテスト出力
  • 未提出の変更
保持されるもの
  • ラボの最高スコア
  • XPと連続日数
  • 講義の進捗とメモ

コードを実行するにはログインしてください。

レクチャーはすべて無料で読めます。コードの実行、テスト、ラボの提出にはアカウントの作成かログインが必要です。

  • ブラウザ内IDEとbashターミナル。インストール不要
  • 即時採点、XP、ロードマップでの現在地
アカウントを作成ログイン
または
GitHubで続ける