In this article
After this article, you can
- Separate sampled results from results executed before a stopping threshold.
- Recalculate capital and check the threshold after every step.
Terms at hand: Backtesting · Drawdown · Trading Plan · Expectancy · Position size (lot quantity)
A backtest shows one realised path, while a simulation explores paths generated by stated assumptions. Position sizing and the order or resampling of outcomes can change drawdown substantially. This article explains how to build a simple model and why its risk estimates are conditional, rather than claiming a verified probability of ruin for an unspecified live account.
What a Monte Carlo simulation of a strategy actually is
Monte Carlo uses repeated random draws from a specified model. Shuffling a fixed list of cash outcomes changes its path but preserves its total; resampling with replacement can change both. A parametric model, such as independent wins and losses with specified amounts, produces another distribution. State which procedure you use and how sizing updates equity.
Begin with a hand-checkable path. In a hypothetical fixed-cash model starting at 1,000 units, outcomes +200, −100, −100 give capital 1,000 → 1,200 → 1,100 → 1,000 and maximum drawdown 200/1,200 = 16.67%. Reordering to −100, −100, +200 gives 1,000 → 900 → 800 → 1,000 and maximum drawdown 20%. Both finish at 1,000. Include starting capital among the peaks; omitting it understates the initial-loss path.
A stopping threshold can change a permutation's final result
Without a stop rule, +300 −150 −150 sums to zero. Now add our condition: check capital after each result and stop if it reaches 850 USD or less. Order changes both the path and which results are executed.
Scroll sideways to see the full diagram
Three fictional net USD results, without reinvestment or flows. Check the threshold after the entire step. The 850 USD threshold is only an exercise rule, not financial advice. Two paths do not describe probabilities. Python — random.shuffle documents shuffling an existing sequence; our stopping condition is a separate part of this example.
Separate the sampled list from the list actually executed under the model rules. If a strategy stops, has a capital constraint or changes volume, merely shuffling historical results and adding every item does not reproduce that behaviour.
What questions Monte Carlo really answers
A defined simulation can estimate maximum drawdowns, threshold crossings and terminal outcomes. Calculate maximum drawdown for each complete path first, then take its distribution’s percentile. A pointwise equity percentile band is not one executed path. State the quantile convention: interpolation and discrete distributions can change how much of a sample lies below a percentile. None of these model summaries is a guaranteed future loss limit.
For an explicit hypothetical model with 200 independent trades, win probability 0.55, fixed gains of €100 and losses of €80, mean profit is €19 per trade and €3,800 overall. If K is the number of wins, terminal profit is €180K − €16,000. Exact binomial quantiles using the smallest K with cumulative probability at least the target give €920 at 1 percent, €1,640 at 5 percent, €3,800 at 50 percent and €5,960 at 95 percent. A finite simulation will vary around this model; these are calculated distribution values, not a claimed experiment.
How to run the simulation in Excel
For an English-language Excel exercise, enter A1 = 0.55, A2 = 100, A3 = −80 and A4 = 10,000: a hypothetical probability, two fixed net cash outcomes and starting capital. Put =$A$4 in C1, =C1 in D1 and zero in E1. Row 1 is the state before any trade. In B2 use =IF(RAND()<$A$1,$A$2,$A$3). The absolute references keep the parameters fixed when formulas are copied.
Use =C1+B2 in C2, =MAX(D1,C2) in D2 and =(D2-C2)/D2 in E2. Copy B2:E2 through row 201 for exactly 200 trades. Terminal capital is C201 and maximum drawdown is =MAX(E1:E201). The horizon is 200 trades, not necessarily one calendar year. This cash model needs separate trading-stop, margin and insolvency rules before practical use.
For another path, copy the complete B:E block and its initial row, keeping the $A$ parameter references fixed. Across a chosen number of paths, keep terminal-capital percentiles separate from percentiles of each path’s maximum drawdown. In =PERCENTILE.INC(range,0.95), 0.95 selects the upper tail of positive drawdown magnitudes; a capital-outcome lower tail instead uses 0.05 or 0.01. The function can interpolate. RAND recalculates with the worksheet, so preserve sampled values and assumptions for the exact experiment being reviewed.
How to do the same in Python with bootstrap
Python makes repeated resampling and analysis convenient, but Excel can also sample historical values with suitable formulas. Bootstrapping draws from recorded outcomes rather than only an assumed fixed win and loss. Either tool requires an explicit choice about dependence, sizing, costs and the history being sampled.
For a defined non-empty history of net cash outcomes, NumPy’s Generator can draw new_sequence = rng.choice(history,size=200,replace=True). Include the initial state: equity = np.concatenate(([initial_capital], initial_capital + np.cumsum(new_sequence))). Calculate each path’s running peaks and drawdowns before summarising maxima. Record the data, seed, library version, horizon, cost and stopping rules. More simulated paths reduce computational noise; they do not create new observations of market conditions.
A controlled comparison can use the same random draws. Let outcome be +1 when uniform U is below the chosen threshold, otherwise −1; thresholds A=0.55 and B=0.60 are hypothetical. Expected outcomes are 0.10 and 0.20, giving expected difference B−A of 0.10. With shared U, the difference is +2 only on 0.55 ≤ U < 0.60, otherwise zero: variance is 4 × 0.05 − 0.10² = 0.19. With independent draws, variances add to 0.99 + 0.96 = 1.95. The shared construction reduces noise in this comparison, not uncertainty about the market probabilities, and need not help arbitrary strategies.
Sampling individual historical trades with replacement retains the observed outcome values but breaks their original order and does not preserve loss clusters. It cannot generate an unobserved tail event. If dependence is relevant, consider sampling blocks and stress scenarios, while acknowledging that a limited history may still miss future regimes.
State the sizing rule as part of the simulation. Fixed cash outcomes and risk recalculated from changing equity produce different path behaviour and cannot be treated as the same experiment.
The honest limits — what Monte Carlo will not do
The independent fixed-probability example assumes a stable distribution and independence. Monte Carlo methods are not restricted to those assumptions: models can include dependence and changing regimes. The challenge is specifying and validating them. Compare model behaviour with the recorded history and stress plausible conditions outside it.
Resampling a history cannot reveal outcomes absent from it. Review dependence, overlapping exposures and market coverage, and add explicitly separate stress scenarios. Walk-forward analysis can organise successive development and evaluation periods; it does not automatically detect edge decay or turn previously inspected data into a fresh test.
A simulated percentile is neither a guaranteed ceiling nor a proven probabilistic floor for live drawdown. Specify the failure threshold before the study and test costs, missing fills, gaps and stopping rules separately. In an idealised model that loses exactly 5% of current capital on every loss, capital after n losses is initial capital × 0.95n, positive for finite n; a practical stop threshold, minimum margin or larger loss changes that model. See the risk management material in our library.
What to do tomorrow to actually use this
- Gather a defined history. Record the trade count, costs, risk per trade and market coverage before resampling. A small sample makes tail estimates uncertain, but there is no universal cutoff at 100. Continue collecting data in a demo or historical study rather than trading merely to reach a count.
- Build and check the model. First enter the two three-trade paths above by hand and confirm both terminal values and drawdowns. Then preserve a random sample before running more paths; a fixed hour of work or one thousand runs is not proof of model adequacy.
- Inspect the adverse drawdown tail. If drawdowns are positive loss magnitudes, inspect the 95th percentile, not the 5th; if they are negative signed changes, state that convention. Include starting equity when measuring peaks, and specify the sizing rule. Lowering risk may reduce model drawdown, but rerun and check the model rather than assuming a guaranteed proportional change.
- Compare revised histories consistently. Record changes in input coverage, units, costs and risk rules before interpreting a changed median or tail. A lower model median alone does not identify a causal deterioration in live edge.
Check your understanding
One short exercise
About 2 min · you can try again
Start at 1,000 USD. Stop after a step leaves capital ≤ 850 USD. The sampled order is −150, −150, +300 USD. What is the executed trial's result?
- 1,000 USD, because the entire sampled list sums to zero.
- 850 USD; the two remaining results are not executed.
- 700 USD, because both losses must be executed first.
Check the answer and explanation
850 USD; the two remaining results are not executed.
The first −150 USD reaches 850 USD. The rule is checked after that step, so later results are not added.
Frequently asked
What is Monte Carlo simulation of a trading strategy?
It repeatedly samples from a defined model. Shuffling a fixed cash list changes order but preserves total; sampling with replacement can change both. For the stated hypothetical 200 independent trades with probability 0.55, gains EUR 100 and losses EUR 80, mean profit is EUR 19 per trade. Under the specified exact binomial convention, the 5th-percentile terminal profit is EUR 1,640, not an invented observed run. Neither that model nor a historical backtest proves a future distribution.
How do you run a Monte Carlo simulation in Excel?
Preserve row 1 as the initial state. For A1=0.55, A2=100, A3=−80 and A4=10,000, set C1=$A$4, D1=C1 and E1=0. Use B2=IF(RAND()<$A$1,$A$2,$A$3), C2=C1+B2, D2=MAX(D1,C2), E2=(D2−C2)/D2. Copy rows 2 through 201 for 200 trades, retaining absolute parameter references. MAX(E1:E201) gives the path’s maximum drawdown. Copy the whole block for another path and save the sampled values before recalculation. Excel syntax and separators depend on language settings.
How do you implement Monte Carlo in Python?
Specify the data and model first. A non-empty cash history can be sampled with rng.choice(history,size=200,replace=True); initial capital must be prepended to the cumulative-sum path before measuring peaks. Repeated samples reuse observed values and break their original order: they do not preserve unobserved true tails or loss clusters. Record the seed, library version, units, fees and stopping rules, then check a deterministic hand example before interpreting percentiles.
What are the limits of Monte Carlo in trading?
The result is conditional on data, parameters and the model. Basic independent resampling loses temporal dependence and cannot invent an absent market shock. Monte Carlo as a method can also use dependent or changing-regime models, whose assumptions need their own evaluation. A percentile is not a proven live-risk floor or ceiling, and sizing around it does not guarantee that a real setback will be survived.
Sources & bibliography
-
Bank for International Settlements Minimum capital requirements for market risk (d457) · The Basel standard describes the market-risk capital framework, including expected-shortfall measures. www.bis.org ↗
-
Van Tharp Institute About Van K. Tharp · A biography describes Tharp's books and his work on position sizing and trading-system simulation. www.vantharp.com ↗
-
NumPy Developers numpy.random.choice — Random sampling · Official NumPy documentation of random sampling, including sampling with replacement for bootstrap-style calculations. numpy.org ↗
-
Python Software Foundation random — Generate pseudo-random numbers · Official Python documentation of pseudorandom sampling, including choices and sample, for use in simulation code. docs.python.org ↗
-
Microsoft RAND function · Uniform values in [0, 1) recalculate with the worksheet; retain parameters and sampled values for a reproducible exercise. support.microsoft.com ↗
-
Microsoft PERCENTILE.INC function · Percentile parameter is between 0 and 1 and can use interpolation; terminal-capital and maximum-drawdown samples answer different questions. support.microsoft.com ↗
-
NumPy Developers numpy.random.Generator.choice · Sampling with or without replacement from a supplied array; it does not create unobserved market outcomes. numpy.org ↗
