σμ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 ロードマップ›モジュール 3 · 確率とモンテカルロ・シミュレーション›トピック 3.10·~45 分中級

分布からのサンプリング

ロードマップ上の位置モジュール 3/11 · トピック 10/12
3.10 分布からのサンプリング
—ラボスコア— / 100合計XP—連続日数—

一様から何でも

乱数生成器は0から1まで一様に散らばった数を返します。モデルは別の形を必要とします。30%の確率で勝つコイン、約定間の待ち時間、正規分布の日次リターンなどです。やり方は同じで、一様な数に変換をかけます。最も一般的なのが逆変換法で、欲しい分布の累積分布関数を F とすると、一様な u に対する F⁻¹(u) はちょうどその分布に従います。配列全体を一度に変換できます。

import math
import numpy as np

# Exponential waiting times with rate 2 (a mean of 0.5): F(x) = 1 - exp(-rate * x), so x = -ln(1 - u) / rate
u = np.array([0.1, 0.5, 0.9])
print((-np.log(1 - u) / 2).round(4).tolist())       # [0.0527, 0.3466, 1.1513]   small u, short wait

rng = np.random.default_rng(42)
waits = -np.log(1 - rng.random(100000)) / 2
print(round(waits.mean(), 2))                        # 0.5   the mean is 1 / rate

離散的な結果と正規分布

離散的な結果の表なら、rng.choice(values, size=n, p=probs) が逆変換を代わりに行い、各値をその確率で引きます。たとえば -2%、0%、+3% の日をそれぞれ20%、50%、30%の確率で引く場合です。正規分布では分布関数の逆関数に単純な式がないため、古典的な手法がボックス=ミュラー法です。独立な一様乱数 u1、u2 から、z = sqrt(-2 ln u1) * cos(2 pi u2) が標準正規乱数になります。実際には rng.standard_normal(n) のほうが速いですが、一度書いてみれば魔法ではないとわかります。

rng = np.random.default_rng(3)
days = rng.choice(np.array([-0.02, 0.0, 0.03]), size=100000, p=[0.2, 0.5, 0.3])
print(round(days.mean(), 3), round(0.2 * -0.02 + 0.3 * 0.03, 3))     # 0.005 0.005   the sample mean matches the table

rng = np.random.default_rng(42)
u1, u2 = 1 - rng.random(100000), rng.random(100000)                   # 1 - random() is in (0, 1]: never log(0)
z = np.sqrt(-2 * np.log(u1)) * np.cos(2 * math.pi * u2)
print(abs(z.mean()) < 0.02, round(z.std(), 2))                        # True 1.0   mean 0 and standard deviation 1

標準正規乱数から価格へ

標準正規乱数 z はショックで、それが価格をどう動かすかはモデルが決めます。株の標準的なモデルである幾何ブラウン運動では、t 年後の価格は S = S0 * exp((mu - 0.5 sigma²) t + sigma sqrt(t) z) です。これは対数正規で、常に正であり、実際の価格のように右に長い裾を持ちます。-0.5 sigma² の項は平均を補正し、期待価格がちょうど mu で成長するようにします:E[S] = S0 * exp(mu t)。中央値は平均より低く、典型的な結果が平均的な結果より悪い理由です。

s0, mu, sigma, t = 100, 0.08, 0.2, 1
z = np.array([-1.0, 0.0, 1.0])                         # a one-sigma loss, no shock, a one-sigma gain
prices = s0 * np.exp((mu - 0.5 * sigma ** 2) * t + sigma * math.sqrt(t) * z)
print(prices.round(2).tolist())                         # [86.94, 106.18, 129.69]   all positive, the upside is larger

draws = np.random.default_rng(0).standard_normal(100000)
simulated = s0 * np.exp((mu - 0.5 * sigma ** 2) * t + sigma * math.sqrt(t) * draws)
print(round(simulated.mean(), 1), round(s0 * math.exp(mu * t), 1))     # 108.3 108.3   the mean grows at mu
print(round(float(np.median(simulated)), 1))                           # 106.1         the median is lower
# NumPy ships ready-made samplers too; the transformations above show what they do inside
z = np.random.default_rng(0).standard_normal(100000)
print(abs(z.mean()) < 0.02, round(z.std(), 2))      # True 1.0   standard_normal: mean 0, standard deviation 1
サンプラーは既知の事実で検証するサンプラーを書いたら、大きな標本を引き、平均・標準偏差・確率を正確な値と比べましょう。レート2の指数分布なら平均0.5、正規分布なら0と1、価格なら S0 * exp(mu t) です。符号の誤りや平方根の書き忘れは、もっともらしく見えて間違った数を生み、こうした確認だけがそれを明らかにします。
  • 逆変換法:一様乱数に分布関数の逆関数を適用する。指数分布は -ln(1 - u) / rate
  • rng.choice(values, size=n, p=probs) は離散表から抽出し、ボックス=ミュラー sqrt(-2 ln u1) * cos(2 pi u2) は正規乱数を作る
  • t 年後の価格は S0 * exp((mu - 0.5 sigma²) t + sigma sqrt(t) z)(対数正規)。大標本は常に正確な平均と照らして確認する

次はモンテカルロ推定です。式のない平均や確率を、乱数の抽出で計算します。

ラボ:採点される内容

  • sample_discrete で結果の表から抽出する25点
  • exponential_from_uniform で分布関数の逆関数を使う25点
  • box_muller で釣鐘型の乱数を作る25点
  • terminal_prices でショックを将来価格に変換する25点
エクスプローラー · lab-63-1
▾ lab-63-1
…
bash · ブラウザ内
learner@quantcraft:~/lab-63-1$
Pythonは未読み込みPythonLn 1, Col 1Spaces: 4UTF-8

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

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

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

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

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

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