Monte Carlo Simulation of a Strategy — Risk a Single Backtest Hides
A reader once sent me a backtest he was proud of: 55 percent hit rate, average winner 100 euro, average loser 80 euro, roughly 3,800 euro of expected profit on paper for a year. Solid enough numbers, so he opened a live account, risked five percent per trade and blew it in six months. He asked where the mistake was. The mistake was not in the strategy. It was treating one backtest as one truth, when Monte Carlo would have shown him that the same edge at that position size had roughly a 25 percent chance of ruining the account. That conversation is the reason I wrote this piece.
What a Monte Carlo simulation of a strategy actually is
Monte Carlo is a probabilistic method named after the Monaco casino. In trading the idea is disarmingly simple: instead of looking at the one equity curve history happened to produce, you generate a thousand new sequences with the same statistics but in a different order. Each one draws a different account picture, and together they form the cloud of possibilities that a single backtest of a strategy can never reveal.
A single backtest is one realisation of a random process. The variance around it can be enormous — that is how a repeated game with an edge behaves. Monte Carlo turns that variance into concrete numbers you can act on.
What questions Monte Carlo really answers
The simulation gives you three things a single backtest will not. The first is a realistic drawdown at the 95 percent confidence level — the depth you do not exceed in 95 of 100 simulations. The second is the probability of losing a meaningful portion of the account, say a drop below half of capital. The third is the range of plausible yearly outcomes while the edge stays constant.
In my illustrative scenario (clearly hypothetical), a strategy with a 55 percent hit rate, an average winner of 100 euro, an average loser of 80 euro and 200 trades a year has an expectancy near 19 euro per trade — 3,800 euro on paper. A one-thousand-run simulation produced a median near 3,750 euro. The worst five percent of runs ended around 500 euro of profit, the worst one percent at a loss of 800 euro and the best five percent at 7,500 euro. Same edge, fivefold gap.
How to run the simulation in Excel
A no-macros version is enough to understand your own strategy. In cells A1 to A5 you put the parameters: hit rate 0.55, average winner 100, average loser -80, number of trades 200 and starting capital 10,000. In cell B1 you enter =IF(RAND()<A1, A2, A3) — Excel's RAND function returns a value between zero and one, and whenever it lands below the hit rate you get a winner, otherwise a loser.
In cell C1 you add the starting capital to the value in B1, the first point of the equity curve. B2 reuses the same formula, and C2 adds the value in C1 to B2. Drag both columns down to row 200. That builds one full annual sequence.
Copying the two columns sideways one thousand times (B–C, D–E, F–G and so on) gives you a thousand independent equity curves. From the ending values you read =PERCENTILE.INC(range, 0.5) for the median, then 0.05 and 0.01 for the worst five and one percent. Pressing F9 recalculates every random draw, so each press becomes a fresh thousand-run study. That genuinely is enough.
How to do the same in Python with bootstrap
Python gives you two things Excel will not. First, speed — thousands of runs take seconds. Second, and more importantly, it lets you bootstrap: draw not from assumed parameters but from your real list of historical trades.
You import NumPy, load 100 or more real P/L numbers from a journal and write new_sequence = np.random.choice(history, size=200, replace=True). The np.cumsum function turns the list of P/L values into an equity curve, and a loop around it gives you one thousand independent runs. You read percentiles with np.percentile and use Matplotlib to draw a spaghetti plot — all one thousand curves overlaid with the median and the 5 to 95 percentile band highlighted.
Bootstrap preserves the real tails of your distribution — fat losses, clustered moves, the occasional record winner in a volatile week. Parametric assumptions will not, because they do not assume the distribution your trades actually have.
"Reward-to-risk ratios make almost no sense without including position sizing in the equation. Through position sizing, you can achieve almost any objective you'd like." — Van K. Tharp, Trade Your Way to Financial Freedom, McGraw-Hill, 2007
The honest limits — what Monte Carlo will not do
A parametric simulation rests on two assumptions: trades are independent and the outcome distribution is stationary. Both break in real markets at the same time. Losses cluster — when the regime shifts, several trades in a row move the same way, and a model drawing independent samples cannot reproduce that. Real drawdowns frequently look worse than the 1 percent percentile from Monte Carlo, not better.
The second issue is that the simulation uses the very history you fed it. If your 100 trades cover only an uptrend in EUR/USD, Monte Carlo will tell you nothing about how the strategy behaves through a long consolidation or a sharp 200-pip move after US data. That is why it pays to pair the simulation with walk-forward analysis, which tests the strategy across rolling windows and catches edge decay.
The darkest drawdowns often dig deeper than the 1 percent percentile from a clean Monte Carlo run. Treat the simulation as a probabilistic floor, not a ceiling, and read it alongside the broader risk management material on ForexMechanics for the long-form context.
What to do tomorrow to actually use this
- Pull a history of at least 100 closed trades from your journal or platform export. Anything below 100 leaves the statistics too noisy for Monte Carlo to say anything useful about the tails. If you have fewer, trade another six to twelve weeks and come back to this exercise with a fuller sample.
- Open a blank Excel sheet and build the parametric version described above. Plug in your own hit rate, average winner, average loser and capital, copy the columns one thousand times, press F9 and study the spread of ending values. That hour of work reveals how wide the range really is behind your "average year".
- Read off the 5 percent percentile of maximum drawdown from those thousand simulations. If it points to a 30 percent dent while your psychological tolerance ends around 15 percent, the problem is not the strategy but the position size. Halve the risk per trade and re-run until the 5 percent floor fits inside your pain threshold.
- After a year of live trading run the simulation again with the new history and compare it with last year's. If the median has slipped and the tails have widened, your edge is degrading — that is the moment to ask whether the strategy still works, not just how to keep nursing it along.
Sources & bibliography
-
Bank for International Settlements Minimum capital requirements for market risk (d457) · Bazylejski standard pokazujący, jak instytucje liczą ryzyko rynkowe za pomocą expected shortfall i historycznej symulacji — analog Monte Carlo dla portfela. www.bis.org ↗
-
Van Tharp Institute About Van K. Tharp · Strona biograficzna potwierdzająca autorstwo książki „Trade Your Way to Financial Freedom" (McGraw-Hill) i jego pracę nad position sizingiem oraz symulacją Monte Carlo dla traderów detalicznych. www.vantharp.com ↗
-
NumPy Developers numpy.random.choice — Random sampling · Oficjalna dokumentacja funkcji losowania z powtórzeniami; podstawowe narzędzie do bootstrap resampling listy transakcji w Pythonie. numpy.org ↗
-
Python Software Foundation random — Generate pseudo-random numbers · Oficjalna dokumentacja biblioteki standardowej Pythona z funkcjami random.choices i random.sample, używanymi do prostych symulacji Monte Carlo bez NumPy. docs.python.org ↗
Frequently asked
What is Monte Carlo simulation of a trading strategy?
Monte Carlo is a probabilistic method named after the Monaco casino. In trading you take a list of your historical trades or strategy parameters — hit rate, average winner, average loser — and instead of looking at the one equity curve history happened to produce, you generate a thousand new sequences in random order. Each has the same statistics but a different ordering of winners and losers, so each draws a different account picture. Why bother: a single backtest is one realisation. The variance around it can be enormous. The same strategy with an expectancy of about 19 euro per trade and 200 trades a year can produce a Monte Carlo median near 3,750 euro but a worst 5 percent percentile dropping to 500 euro and a best 5 percent shooting to 7,500 euro. The range is wide even when the edge stays constant. The most common misconception: "last year the strategy made 5,000 euro, so it should make about the same this year." Monte Carlo shows that with the edge held constant, the realistic range can stretch from 1,000 to 10,000 euro — and there is nothing pathological about it. That is just variance.
How do you run a Monte Carlo simulation in Excel?
The simplest version needs no macros or VBA. In cells A1 to A5 you put the parameters: hit rate (say 0.55), average winner (say 100), average loser (say -80), number of trades in a sequence (say 200) and starting capital (say 10,000). In cell B1 you write =IF(RAND()<A1, A2, A3), which simulates one trade — the RAND function returns a number between zero and one and if it lands below the hit rate you get a winner, otherwise a loser. In cell C1 you add the starting capital to the B1 result. Cells B2 and C2 use the same pattern, but C2 references C1. Drag both columns down to row 200. That builds one full annual sequence. Copy those two columns sideways one thousand times and you have one thousand independent equity curves. From the ending values you compute =PERCENTILE.INC(range, 0.5) for the median and the same with 0.05 and 0.01 to see the worst five and one percent of outcomes. Pressing F9 recalculates every random draw, so each press gives you a fresh thousand-run study. Excel handles this comfortably and it is enough to understand your own strategy honestly.
How do you implement Monte Carlo in Python?
Python does the same job faster and lets you bootstrap from a real trade history rather than only from parameters. You import NumPy, declare the strategy parameters and write a function that builds one outcome sequence — a random array of values between zero and one compared against the hit rate gives a vector of zeros and ones, and np.where turns that vector into average winners and losers. np.cumsum turns the list of individual P/L numbers into an equity curve. You run the function a thousand times in a loop, collect the ending values and compute percentiles with np.percentile. If you have a list of 100 or more real trades, you use np.random.choice(history, size=200, replace=True) to bootstrap — drawing a fresh 200-trade sequence with replacement from your own history, which preserves the true tails the parametric assumptions cannot reproduce. The Matplotlib library lets you draw a spaghetti plot, all 1,000 curves overlaid with the median and the 5 to 95 percentile band highlighted — a single picture says more about the risk of a strategy than ten tables would.
What are the limits of Monte Carlo in trading?
Monte Carlo is not divination and that should be said out loud. The basic assumption of a parametric simulation is that trades are independent and the outcome distribution is stationary. In real markets both conditions are often broken at the same time. Market losses tend to cluster — when the regime changes, several trades in a row can move the same way, which a model drawing independent samples from the same distribution cannot reproduce. That is why the blackest drawdowns in real trading often look worse than the 1 percent percentile from Monte Carlo, not better. The second problem is that the simulation uses the same statistics you pulled out of history. If your history covers only an uptrend in EUR/USD, Monte Carlo will tell you nothing about how the strategy behaves during a long consolidation. The practical consequence: treat the Monte Carlo output as a probabilistic floor, not a ceiling. If the worst 5 percent scenario points to a 30 percent drawdown, assume real trading can dig deeper. Size positions around that floor rather than around the median outcome — that way a real-world setback does not knock you out.