Free lesson · Risk
Monte Carlo: your backtest is one draw from a distribution
Open interactive lessonPractice calculationsExplore labs
Start with the idea
A simulation is a way to propagate assumptions into a distribution of outcomes. It cannot create information about events absent from the model. Distinguish shuffling, IID bootstrap, block bootstrap and parametric simulation before interpreting a confidence band.
Symbols, units & horizon
- Rₜ: simulated decimal return in period t
- V₀: initial capital
- Vₜ: compounded wealth
- n: observations or simulation count as specified
- α: selected probability
- q: empirical quantile
- ceil: round up to the next integer
- Seed: reproducible pseudorandom generator state
When and why to use this
Use Monte Carlo for sizing sensitivity and to compare possible wealth paths under clearly specified distributions. Use stress scenarios to cover risks the fitted sample may not contain.
A backtest gives one equity curve. Monte Carlo asks: given this strategy's per-trade distribution, what are the other equity curves that could have happened? Resample the trades with replacement (bootstrap), or shuffle their order, thousands of times; collect max drawdown, final equity, and Sharpe from each run; report the distribution.
- The 95th-percentile max drawdown is your sizing constraint, not the backtest's drawdown.
- The fraction of runs that hit −50% is your probability of ruin at this size.
- The spread of final equity is a picture of how much luck is in one path.
Build a simulated wealth path and empirical quantile
- Given sampled return Rₜ, update . Apply the drawdown recursion to each path, storing one MDD per path.
- Sort B simulated MDDs. A specified quantile convention selects or interpolates the α fraction of this ordered list. Event frequency is .
- For independent simulated paths, Monte Carlo sampling SE of an event frequency is approximately . This is simulation noise, not uncertainty about whether the market model is correct.
If 20 of 400 paths cross a 50% drawdown, estimated model frequency=.05 and Monte Carlo SE≈.0109, or 1.09 percentage points.
Python implementation
Self-contained teaching example. Python 3.10+; dependencies and input conventions are shown in the code and notation. Run in your own Python environment.
from random import Random
from math import ceil
def bootstrap_path(observed_returns, periods, starting_wealth=100, seed=7):
"""IID resampling demo, not a dependence-preserving block bootstrap."""
rng, path = Random(seed), [starting_wealth]
for _ in range(periods):
path.append(path[-1]*(1+rng.choice(observed_returns)))
return path
def empirical_quantile(values, probability):
"""Inverse empirical CDF: nearest-rank convention."""
if not values or not 0 < probability <= 1:
raise ValueError("Nonempty values and probability in (0,1] required")
return sorted(values)[ceil(len(values)*probability)-1]
print(bootstrap_path([.02,-.01,.005], 5))Continue learning
Risk Management — all lessons- Start with gains, losses and an ordered sample
- Value at Risk and maximum drawdown: two views of the bad days
- Sharpe ratio: return per unit of risk
- Kelly criterion: the fraction that maximises growth
- Monte Carlo: your backtest is one draw from a distribution
- Expected shortfall and scenario risk
Quantitative finance and development glossary · Python resources and libraries · Research sources and limitations