σμ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 ロードマップ›モジュール 7 · アルファ研究とリスク›トピック 7.7·~55 分上級

ストレステスト

ロードマップ上の位置モジュール 7/11 · トピック 7/9
7.7 ストレステスト
—ラボスコア— / 100合計XP—連続日数—

「どれくらいの確率か」ではなく「もしそうなったら」を問う

VaRと期待ショートフォールは過去のリターンから推定するため、一度も起きていないことは見えません。ストレステストは確率を捨て、自分で選んだシナリオを当てはめます。株価20%暴落、金利2ポイント上昇、2008年危機の再現、通貨切り下げなどです。そのシナリオでのポートフォリオの損失はリスク委員会が議論できる数字であり、統計的な平均では隠れてしまう集中リスクを明らかにします。

import numpy as np

values = np.array([500000.0, 300000.0, -200000.0])       # two longs and a short
shocks = np.array([-0.20, -0.10, 0.15])                  # one scenario: stocks -20%, bonds -10%, the shorted stock +15%

pnl = values * shocks                                    # per position
print(pnl)                                               # [-100000.  -30000.  -30000.]  the short loses when its stock rises
print(float(pnl.sum()))                                  # -160000.0

シナリオの表

リスク部門は、歴史的(1987年、2008年、2020年3月)と仮想的なシナリオの常設リストを持っています。シナリオごとに1行、ポジションごとに1列の行列にすると、全シナリオの総損失は行列とベクトルの積1回、scenarios @ values で求まります。このベクトルの最小値が最悪のケースを示し、最も痛いシナリオがどれかも、ポートフォリオの弱点を教える情報です。

import numpy as np

values = np.array([500000.0, 300000.0, -200000.0])
scenarios = np.array([[-0.20, -0.10,  0.15],             # equity crash
                      [-0.05,  0.02,  0.00],             # mild dip
                      [ 0.10,  0.05, -0.20]])            # the shorted stock falls 20%: the short gains
names = ["equity crash", "mild dip", "rally and short gain"]

pnl = scenarios @ values
for name, p in zip(names, pnl):
    print(name, round(float(p)))
worst = int(np.argmin(pnl))
print("worst:", names[worst], round(float(pnl[worst])))
# A shock does not have to be a price move: a rate jump hits a bond through its duration
duration = 7.5                                    # years, from the duration topic
bond_value = 1_000_000.0
rate_jump = 0.02                                  # +2 percentage points
print(round(bond_value * (-duration * rate_jump)))   # -150000   the shock expressed as a price return of -15%

リバース・ストレステスト

問いを逆にします。資本バッファーなど、ある金額を失うにはショックがどれだけ大きくなければならないか。ショックの方向(たとえばすべてが一緒に下がる)を決め、係数 k で拡大します。損失は -k * (values @ direction) で k に線形なので、ちょうど loss_limit を失う係数は loss_limit / -(values @ direction) です。その方向で損が出ないなら、どんな係数でも限度に届きません。

import numpy as np

def reverse_stress(values, direction, loss_limit):
    change = float(values @ direction)
    if change >= 0:
        return float("inf")
    return loss_limit / -change

values = np.array([500000.0, 300000.0, -200000.0])
direction = np.array([-1.0, -1.0, 0.0])                    # the two longs fall by the same percentage
k = reverse_stress(values, direction, 100000.0)
print(round(k, 4))                                          # 0.125  a 12.5% fall in both longs loses 100,000
print(round(float(-(values @ (k * direction))), 2))         # 100000.0
シナリオは想像力の分しか役に立たない難しいのは、厳しくてもありうるシナリオを選び、危機時のように相関する資産を一緒に動かすことです。結果も確率のない点推定で、悪い数字はポジションを見直す理由になりますが、良い数字は想定しなかったシナリオについて何も保証しません。ストレステストはVaRを補うものであり、置き換えるものではありません。
  • ストレステストは選んだショックを当てはめる。ポジションの損益は values * shocks で、損は負、儲かるショートは正
  • 複数のシナリオは積1回 scenarios @ values で求まる。最小値が最悪ケースを示し、ポートフォリオの弱点を教える
  • リバース・ストレス:ちょうど限度額を失うショックの倍率は loss_limit / -(values @ direction)。その方向で損が出なければ無限大

次はARIMAモデルです。系列を自身の過去から予測します。

ラボ:採点される内容

  • shocked_pnl で各ポジションにショックを与える25点
  • scenario_pnl で多数のシナリオを一度に計算する25点
  • worst_scenario で最悪のケースを特定する25点
  • reverse_stress で損失から逆算する25点
エクスプローラー · lab-99-1
▾ lab-99-1
…
bash · ブラウザ内
learner@quantcraft:~/lab-99-1$
Pythonは未読み込みPythonLn 1, Col 1Spaces: 4UTF-8

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

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

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

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

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

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