σμ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.9·~40 分中級

乱数生成器とシード

ロードマップ上の位置モジュール 3/11 · トピック 9/12
3.9 乱数生成器とシード
—ラボスコア— / 100合計XP—連続日数—

ランダムだが再現できる

モンテカルロ・シミュレーション、ブートストラップ、多くの検定は乱数を必要としますが、誰も再現できない結果は価値がありません。デバッグも、モデルの2つのバージョンの比較も、同僚に見せることもできないからです。コンピュータが作る数は本当のランダムではありません。擬似乱数生成器は、シードと呼ばれる初期値から、乱数の抽出のように振る舞う長い列を作り出す決定論的なアルゴリズムです。同じシードなら、いつでもどのマシンでも同じ列になります。

import numpy as np

rng = np.random.default_rng(42)            # a Generator created from the seed 42
print(rng.random(3).round(4).tolist())     # [0.774, 0.4389, 0.8586]

rng = np.random.default_rng(42)            # the same seed again: the same numbers
print(rng.random(3).round(4).tolist())     # [0.774, 0.4389, 0.8586]

rng = np.random.default_rng(43)            # another seed: other numbers
print(rng.random(3).round(4).tolist())     # [0.6523, 0.0438, 0.02]

グローバルな状態ではなくGeneratorを使う

古いコードは np.random.seed(42) を1回呼び、あとはどこでも np.random.normal(...) を呼びます。すべてのコードが隠れたグローバルな生成器を1つ共有します。ライブラリや新しく数を引く行が増えると、他のすべての列がずれ、モデルと無関係な理由で結果が変わります。現代的な方法は np.random.default_rng(seed) で Generator オブジェクトを作り、明示的に受け渡すことです。random・normal・choice・integers などのメソッドがすべての抽出を行い、2つの生成器が干渉することはありません。

def simulate_returns(seed, n, mu, sigma):
    rng = np.random.default_rng(seed)              # a private generator, not the global one
    return rng.normal(mu, sigma, size=n)

first = simulate_returns(7, 5, 0.0005, 0.01)
second = simulate_returns(7, 5, 0.0005, 0.01)
print(first.round(5).tolist())                     # [0.00051, 0.00349, -0.00224, -0.00841, -0.00405]
print(np.array_equal(first, second))               # True   repeatable

リサンプリングと独立ストリーム

ブートストラップは履歴を再抽出して、統計量がどれだけ変わりえたかを調べます。手持ちのリターンと同じ数を復元抽出で引きます:rng.choice(returns, size=len(returns), replace=True)。同じ日が2回出ることも、一度も出ないこともあります。多くのシミュレーションを並列実行するとき、各プロセスにシード1、2、3を与えるのは、近いシードが独立とは限らないため危険です。np.random.SeedSequence(seed).spawn(k) は重ならないよう設計された k 個の子系列を作るので、それぞれから default_rng(child) を作りましょう。

data = np.array([1, 2, 3, 4, 5])
rng = np.random.default_rng(1)
print(rng.choice(data, size=len(data), replace=True).tolist())    # [3, 3, 4, 5, 1]   3 appears twice, 2 not at all

# Three independent streams from one seed, for three parallel simulations
children = np.random.SeedSequence(2026).spawn(3)
streams = [np.random.default_rng(child) for child in children]
print([stream.random(2).round(4).tolist() for stream in streams])
# [[0.3827, 0.0204], [0.6711, 0.158], [0.5508, 0.2339]]
rng = np.random.default_rng(42)
print(rng.integers(1, 7, size=8).tolist())             # [1, 5, 4, 3, 3, 6, 1, 5]   eight dice rolls, repeatable
rng = np.random.default_rng(42)
print(rng.uniform(-0.01, 0.01, 3).round(5).tolist())   # [0.00548, -0.00122, 0.00717]
意図してシードを固定し、記録する固定シードは実行を再現可能にしますが、品質管理ではありません。運の良いシードは悪いアイデアを良く見せることがあります。複数のシードで分析を行い、結論が保たれるか確認しましょう。使ったシードは表示またはログに残し、レポートの結果をいつでも再生成できるようにします。結果が好ましいからといってシードを選んではいけません。
  • rng = np.random.default_rng(seed):同じシードなら常に同じ数。メソッド random・normal・choice・integers を使う
  • 隠れたグローバル状態 np.random.seed ではなく、生成器を受け渡す
  • choice(..., replace=True) はブートストラップ。SeedSequence(seed).spawn(k) は k 個の独立ストリーム。使ったシードを記録する

次は分布からのサンプリングです。一様乱数を正規・指数・離散の抽出に変換します。

ラボ:採点される内容

  • make_rng で再現可能な乱数生成器を作る25点
  • simulate_returns で再現可能なリターンを生成する25点
  • bootstrap_returns で履歴をリサンプリングする25点
  • independent_streams で1つのシードを独立なストリームに分ける25点
エクスプローラー · lab-62-1
▾ lab-62-1
…
bash · ブラウザ内
learner@quantcraft:~/lab-62-1$
Pythonは未読み込みPythonLn 1, Col 1Spaces: 4UTF-8

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

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

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

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

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

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