σμ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 AcademySkills
Ctrl K
AO
+1.2k Learners
LoginJoin ↗
Skills
AO
+1.2k Learners
Login
  • English
  • Français
  • Español
  • العربية
  • Português
  • Deutsch
  • 日本語
  • 中文
Python skill›Module 3 · Probability & Monte Carlo simulation›Topic 3.9·~40 minIntermediate

Random number generators and seeds

Skill positionModule 3 of 11 · Topic 9 of 12
3.9 Random number generators and seeds
—Lab score— / 100Total XP—Streak—

Random, but repeatable

Monte Carlo simulation, bootstrapping and many tests need random numbers, yet a result nobody can reproduce is worthless: you cannot debug it, compare two versions of a model, or show a colleague. The numbers a computer produces are not truly random. A pseudo-random number generator is a deterministic algorithm that, from an initial value called the seed, produces a long sequence that behaves like random draws. Same seed, same sequence, every time and on every 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]

Use a Generator, not global state

Older code calls np.random.seed(42) once and then np.random.normal(...) anywhere: all the code shares one hidden global generator. Any library, or any new line that draws a number, shifts everyone else's sequence, so results change for reasons that have nothing to do with your model. The modern way is to create a Generator object with np.random.default_rng(seed) and pass it around explicitly. Its methods random, normal, choice, integers do all the drawing, and two generators never interfere.

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

Resampling and independent streams

A bootstrap resamples the history to see how much a statistic could have varied: draw as many returns as you have, with replacement, using rng.choice(returns, size=len(returns), replace=True). Some days appear twice, others not at all. When many simulations run in parallel, giving each process the seeds 1, 2, 3 is risky because nearby seeds are not guaranteed to be independent. np.random.SeedSequence(seed).spawn(k) derives k child sequences that are designed not to overlap; build one default_rng(child) from each.

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]
Seed on purpose, and record itA fixed seed makes a run repeatable, but it is not a quality control: a lucky seed can make a bad idea look good. Run the analysis with several seeds and check that the conclusion survives. Print or log the seed you used, so a result in a report can always be regenerated, and never choose a seed because it gives a nicer answer.
  • rng = np.random.default_rng(seed): the same seed always gives the same numbers; use its methods random, normal, choice, integers.
  • Pass the generator around instead of using the hidden global state np.random.seed.
  • choice(..., replace=True) is a bootstrap; SeedSequence(seed).spawn(k) gives k independent streams; log the seed you used.

Next up: sampling from distributions, turning uniform random numbers into normal, exponential and discrete draws.

Lab · what's graded

  • Create a repeatable generator with make_rng25 pts
  • Draw reproducible returns with simulate_returns25 pts
  • Resample history with bootstrap_returns25 pts
  • Split one seed into independent streams with independent_streams25 pts
Explorer · lab-62-1
▾ lab-62-1
…
bash · in-browser
learner@quantcraft:~/lab-62-1$
Python not loaded yetPythonLn 1, Col 1Spaces: 4UTF-8

Reset this topic?

Everything in lab-62-1 goes back to the starter files and the terminal is cleared. Your current solution will be lost — this can't be undone.

Will be reset
  • main.py → starter code
  • Terminal history and test output
  • Unsubmitted changes
Will be kept
  • Best lab score
  • Your XP and streak
  • Lecture progress and notes

Log in to run your code.

Every lecture is free to read. Create an account or log in to run code, run tests and submit this lab.

  • In-browser IDE with a bash terminal — nothing to install
  • Instant grading, XP and your place on the skill
Create an accountLog in
OR
Continue with GitHub