σμ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 AcademyParcours
Ctrl K
AO
+1,2 k apprenants
ConnexionRejoindre ↗
Parcours
AO
+1,2 k apprenants
Connexion
  • English
  • Français
  • Español
  • العربية
  • Português
  • Deutsch
  • 日本語
  • 中文
Parcours Python›Module 3 · Probabilités et simulation Monte-Carlo›Sujet 3.9·~40 minIntermédiaire

Générateurs de nombres aléatoires et graines

Position dans le parcoursModule 3 sur 11 · Sujet 9 sur 12
3.9 Générateurs de nombres aléatoires et graines
—Score du lab— / 100XP total—Série—

Aléatoire, mais reproductible

La simulation de Monte Carlo, le bootstrap et de nombreux tests ont besoin de nombres aléatoires, mais un résultat que personne ne peut reproduire ne vaut rien : on ne peut ni le déboguer, ni comparer deux versions d'un modèle, ni le montrer à un collègue. Les nombres produits par un ordinateur ne sont pas vraiment aléatoires. Un générateur de nombres pseudo-aléatoires est un algorithme déterministe qui, à partir d'une valeur initiale appelée graine (seed), produit une longue suite qui se comporte comme des tirages aléatoires. Même graine, même suite, à chaque fois et sur chaque machine.

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]

Utilisez un Generator, pas un état global

Le code ancien appelle une fois np.random.seed(42) puis np.random.normal(...) n'importe où : tout le code partage un seul générateur global caché. Toute bibliothèque, ou toute nouvelle ligne qui tire un nombre, décale la suite de tous les autres, si bien que les résultats changent pour des raisons sans rapport avec votre modèle. La méthode moderne consiste à créer un objet Generator avec np.random.default_rng(seed) et à le transmettre explicitement. Ses méthodes random, normal, choice, integers font tous les tirages, et deux générateurs n'interfèrent jamais.

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

Rééchantillonnage et flux indépendants

Un bootstrap rééchantillonne l'historique pour voir de combien une statistique aurait pu varier : on tire autant de rendements qu'on en a, avec remise, avec rng.choice(returns, size=len(returns), replace=True). Certains jours apparaissent deux fois, d'autres pas du tout. Quand de nombreuses simulations tournent en parallèle, donner à chaque processus les graines 1, 2, 3 est risqué car des graines voisines ne sont pas garanties indépendantes. np.random.SeedSequence(seed).spawn(k) dérive k séquences filles conçues pour ne pas se recouvrir ; construisez un default_rng(child) pour chacune.

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]
Fixez la graine exprès, et notez-laUne graine fixe rend une exécution reproductible, mais ce n'est pas un contrôle de qualité : une graine chanceuse peut faire paraître bonne une mauvaise idée. Lancez l'analyse avec plusieurs graines et vérifiez que la conclusion tient. Affichez ou journalisez la graine utilisée pour qu'un résultat d'un rapport puisse toujours être régénéré, et ne choisissez jamais une graine parce qu'elle donne une réponse plus agréable.
  • rng = np.random.default_rng(seed) : la même graine donne toujours les mêmes nombres ; utilisez ses méthodes random, normal, choice, integers.
  • Transmettez le générateur plutôt que d'utiliser l'état global caché np.random.seed.
  • choice(..., replace=True) est un bootstrap ; SeedSequence(seed).spawn(k) donne k flux indépendants ; notez la graine utilisée.

À suivre : l'échantillonnage de lois, pour transformer des nombres aléatoires uniformes en tirages normaux, exponentiels et discrets.

Lab : ce qui est noté

  • Créer un générateur reproductible avec make_rng25 pts
  • Tirer des rendements reproductibles avec simulate_returns25 pts
  • Rééchantillonner l'historique avec bootstrap_returns25 pts
  • Séparer une graine en flux indépendants avec independent_streams25 pts
Explorateur · lab-62-1
▾ lab-62-1
…
bash · dans le navigateur
learner@quantcraft:~/lab-62-1$
Python pas encore chargéPythonLn 1, Col 1Spaces: 4UTF-8

Réinitialiser ce sujet ?

Tout le contenu de lab-62-1 revient aux fichiers de départ et le terminal est vidé. Votre solution actuelle sera perdue : cette action est irréversible.

Sera réinitialisé
  • main.py → code de départ
  • Historique du terminal et sortie des tests
  • Modifications non soumises
Sera conservé
  • Meilleur score du lab
  • Votre XP et votre série
  • Progression du cours et notes

Connectez-vous pour exécuter votre code.

Tous les cours sont gratuits à lire. Créez un compte ou connectez-vous pour exécuter du code, lancer les tests et soumettre ce lab.

  • IDE dans le navigateur avec terminal bash — rien à installer
  • Correction instantanée, XP et votre place dans le parcours
Créer un compteSe connecter
OU
Continuer avec GitHub