The Analyst's Path

Monte Carlo simulation

Running a model thousands of times with inputs drawn at random from chosen distributions, to see the spread of possible outcomes rather than a single answer. It suits problems where several uncertain inputs interact and no formula gives the distribution directly. Its output is only as good as the input ranges and, above all, the correlations between them: growth and margins drawn independently when they really rise and fall together produce a distribution that looks far tighter than it should.

6 min readPart of Time-Series, Monte Carlo & Intro ML-for-FinanceOpen in course mode

Advertisement

Step back to QM1.01's law of large numbers: the average of many independent draws from a distribution converges to that distribution's true expectation. A Monte Carlo simulation is nothing more than that law, deliberately weaponized: whenever you cannot solve for a quantity in closed form (the distribution of a multi-asset portfolio's ten-day loss, say) you instead simulate a large number of plausible future paths from a specified data-generating process, compute your statistic of interest on each simulated path, and let the law of large numbers hand you the distribution you couldn't derive by hand.

How do you build a Monte Carlo simulation?

Building one, step by step: (1) specify the data-generating process, a distributional assumption for each risk factor's return (here, normal, for tractability; more exotic assumptions are a QD-region topic); (2) draw random variates, modern software draws from a high-quality pseudo-random generator, and fixing a seed makes the exercise exactly reproducible, the same determinism discipline the quant-code region's backtesting harness enforces by hard rule; (3) impose realistic correlation across factors via a Cholesky decomposition of the covariance matrix (multiplying independent standard-normal draws by the Cholesky factor manufactures correlated draws with exactly your target covariance structure, so a rupee-leg return, a dollar-leg return, and an FX return move together the way real markets do); (4) simulate many paths and compute the statistic of interest on each, here, a portfolio loss; (5) summarize the distribution, a percentile for Value-at-Risk (VaR), a tail average for Expected Shortfall (ES / CVaR), which unlike VaR also tells you how bad the tail beyond the threshold typically is. Worked Example 6 builds exactly this, from scratch, on a two-asset INR/USD book, and cross-checks the simulated VaR against a closed-form parametric (delta-normal) estimate, the two agree closely, as they should when both share the same joint-normality assumption.

Advertisement

How many simulations are enough?

Convergence is not free, and it is not fast. A Monte Carlo estimate's standard error shrinks with the square root of N: SE ≈ σ/√N. Going from 1,000 to 10,000 simulations (10× more work) buys only √10 ≈ 3.16× more precision, and 10,000 to 100,000 buys another 3.16×. Worked Example 6's convergence table shows exactly this in a repeated VaR estimate's standard error. This is why serious Monte Carlo work invests in variance-reduction techniques rather than cranking N upward forever. The one built by hand here is antithetic variates: for every draw z, also use its mirror image -z and average the paired outcomes, so an error inflating the estimate on one side is matched by an offsetting error on the other. Two more are named for vocabulary only (their code build is a QD-region topic): control variates (correct your simulated estimate of an unknown quantity using the known error in a correlated quantity you can already value in closed form) and importance sampling (oversample the rare, painful tail scenarios, then reweight, so a fixed budget spends its draws where the risk lives). Worked Example 6's antithetic experiment delivers an under-taught lesson: the technique works spectacularly on a smooth, near-linear statistic (the mean P&L) and only marginally on a rough, tail-order-statistic like VaR₉₅, worth knowing before you assume a variance-reduction trick pays off uniformly.

{
  "skill": "Drafting a Monte Carlo scenario narrative or sanity-checking a simulation's setup, without letting the tool silently change your assumptions",
  "use": "Describe your simulation's data-generating process (distributions, correlations, horizon) to a chat assistant and ask it to draft the plain-English scenario write-up an investment committee would read, or to sanity-check your correlation matrix and Cholesky setup for an obvious modeling slip.",
  "tools": ["Chat assistants — Claude, ChatGPT, Gemini", "A code-aware assistant to review your simulation script's structure (not to write the risk conclusion for you)"],
  "prompt": "Here is my Monte Carlo setup: <ASSETS, VOLS, CORRELATIONS, HORIZON, N>. First, check whether my correlation matrix is a valid (positive semi-definite) correlation matrix and flag anything that looks like a typo (e.g., a correlation above 1 or a sign that looks backwards). Second, draft a two-paragraph plain-English risk-committee summary of what a VaR of <X> at this horizon means and does not mean.",
  "verify": "Recompute the VaR and ES yourself from your own simulation output before you let any drafted narrative near a real number; a model can produce a fluent risk memo around a VaR figure it never actually computed. Re-run your own convergence check (does the estimate stabilize as N rises the way the 1/sqrt(N) law predicts?) — that stability check is yours to run, not the assistant's to assert.",
  "diy": "You must be able to build the simulation loop, the Cholesky step, and the percentile/tail-average calculation yourself first — that is this section and Worked Example 6. AI can draft the memo around your number; it must never generate the number.",
  "market": "US"
}

How do you run a Monte Carlo simulation in Python?

Modern Data Analysis. Every simulation in this section runs on plain numpy: numpy.random.default_rng(seed) for a reproducible generator, numpy.linalg.cholesky for the correlation-imposing step, and pure vectorized array arithmetic for the "simulate many paths at once" trick that makes 100,000 scenarios run in a fraction of a second rather than a slow Python loop, the vectorization habit itself is DA1.01's (NumPy & Vectorized Computation) opening lesson, and it is exactly what the scratch script behind this module's worked examples used throughout. The production risk system, historical, parametric, and Monte Carlo VaR side by side, a VaR backtest against realized P&L, stress and scenario analysis on a full portfolio: is QD2.02's (Risk Systems in Code) job, ties explicitly to Hull's treatment of the same material, and is where a real desk's risk engine actually lives. This module teaches you to build the simulation once, by hand, and to read every number it produces correctly; QD2.02 teaches you to run it in production.

Advertisement

Part of the course

This page is part of Time-Series, Monte Carlo & Intro ML-for-Finance (QM1.03), in Phase 12: Finance Plus, AI and the quant-code track. The module's flashcards and its quiz are in the app.