Skip to content
Level 4 · Trading System ResearcherLessonPart 33 · page 3 of 430 min
30Minutes
7AFL functions
5Sources
StandardRequires
AFL functions taught here7

Sequence Risk and Risk of Ruin

“The same trades in a different order can end your account” is one of those statements that sounds obviously true and is, as usually explained, obviously false. Under the sizing rule most systematic traders actually use, reordering a fixed set of trades changes the terminal balance not at all. What it changes is the shape of the journey — and the journey is what decides whether you are still there for the end of it.

This lesson separates the two carefully, because getting them muddled produces both false alarms and false comfort. By the end you should be able to say precisely which quantities are order-dependent and which are not, define ruin in a way that can be computed, calculate it for the one case where a closed form exists, simulate it for the cases where none does, and explain why a positive expectancy traded at the wrong size still finishes at zero.

What reordering changes, and what it does not

Section titled “What reordering changes, and what it does not”

Take four trades expressed as growth factors: a 50 per cent gain, two 30 per cent losses, and a 40 per cent gain. Size every position as the same fraction of current equity, so each trade multiplies the account by its factor. Terminal equity is 1.5 × 0.7 × 0.7 × 1.4 = 1.029, a gain of 2.9 per cent, and that product is the same however you shuffle the four numbers. Multiplication does not care about order.

The path does.

Order Equity after each trade Deepest peak-to-valley fall
Gain, loss, loss, gain 1.50 → 1.05 → 0.735 → 1.029 51 per cent, from 1.50 down to 0.735
Loss, gain, loss, gain 0.70 → 1.05 → 0.735 → 1.029 30 per cent, twice, from 1.00 and from 1.05

Identical trades. Identical ending. One arrangement asks you to sit through a 51 per cent drawdown; the other never takes more than 30 per cent off a peak. That gap is sequence risk in its purest form, and it is entirely invisible in any summary statistic computed from the completed record.

Order starts to matter for the ending balance as soon as the account stops being a pure multiplier. Four ways that happens, all of them ordinary:

  • You stop. Any absorbing barrier — a personal limit, a margin call, an investor withdrawing, or simply losing the nerve to place the next order — truncates the sequence. A path that hits the barrier never gets to compound the trades that came after it.
  • The trade becomes untakeable. With a smaller account, a signal that would have been taken is skipped for want of capital. AmiBroker models this explicitly: SetOption( "MinShares", … ) and SetOption( "MinPosValue", … ) define the smallest position the backtester will open, and a signal that cannot be funded to that level is simply not entered. SetOption( "AllowPositionShrinking", … ) decides whether an underfunded position is opened smaller or dropped entirely. Change the order of the wins and losses and you change which signals fell below the line.
  • Sizing is not proportional at the edges. Round lots, indivisible contracts and a cap on simultaneous positions all break the neat multiplication near the extremes.
  • Money moves in or out on a calendar. Contributions and withdrawals happen on dates, not after trade numbers, so where the good stretch sits relative to them changes the result.

The first of those is the one this lesson is about.

The backtester contains an absorbing barrier of its own. Among the exit reasons the custom backtester reports is one called ruin, and SetOption( "DisableRuinStop", True ) turns it off. A note from AmiBroker’s author in the comments on the official ApplyStop() page describes it as a fixed ruin stop triggered by losing 99.96 per cent of the starting capital, and lists it as the first stop evaluated when several fire on the same bar.

It is not a risk-management tool — by the time it fires there is nothing left to manage. It is there so that a backtest of a catastrophically over-sized system terminates rather than producing nonsense. If you ever see it in a trade list, the correct response is not to disable it.

Risk of ruin, defined so it can be computed

Section titled “Risk of ruin, defined so it can be computed”

“Risk of ruin” is the probability that equity reaches a level at which you stop, at some point before the end of the horizon you care about.

Every clause in that sentence is a choice you have to make explicit:

  • The level. Zero is the wrong answer for anyone using proportional sizing, because proportional sizing never reaches zero — it only approaches it. The useful level is the one at which you would actually stop: down 30 per cent, down 50 per cent, below the amount your broker requires, below the point at which the strategy no longer justifies the effort.
  • Before the end of what horizon. Over an infinite horizon many systems reach any given level eventually. Over two hundred trades they may not. Ruin probability without a horizon is not a number.
  • At some point, not at the end. Touching the level counts, even if the equity curve recovers afterwards, because you were not there for the recovery.

AmiBroker does not report a metric called risk of ruin. What the Monte Carlo page gives you instead is the Lowest Eq. distribution, which is the same idea seen from the other side: the percentile table tells you what fraction of realizations dipped below any level you care to look up. If you want a specific probability against a specific threshold, you compute it — either from a formula, in the one case where a formula exists, or by simulation.

For a game with fixed-size bets, even money, a constant win probability p, independent trials and an unlimited number of them, the probability of ever losing a bankroll of N units is (q/p)^N, where q = 1 − p. It is the classical gambler’s-ruin result and it is worth working through once because the numbers are so unforgiving.

Take a genuine edge: p = 0.55, so q/p is about 0.818.

Bankroll, in units risked per bet Probability of ever being wiped out
5 units about 37 per cent
10 units about 13 per cent
20 units about 1.8 per cent
40 units about 0.03 per cent

The edge never changed. Only the size of each bet relative to the bankroll did, and the ruin probability fell by three orders of magnitude across the table. That relationship — ruin falling roughly geometrically as the bet shrinks — is the single most useful intuition in position sizing.

With proportional sizing and unequal outcome sizes, no tidy closed form is available. Simulation is the practical route, and it has the advantage that every assumption is visible in the code rather than buried in a derivation.

Given a win rate, a payoff ratio and a fraction of equity risked per trade, estimate how often a sequence of a given length touches a level you would call ruin — and watch what happens to that estimate as the risked fraction increases.

Complete runnable AFL

ruin-laboratory.afl
// ruin-laboratory.afl
// Part 33 - Monte Carlo and Robustness
//
// A synthetic sequence simulator. It answers one question: given a win rate, a
// payoff ratio and a fraction of equity risked per trade, how often does a
// sequence of trades reach a level you would call ruin before it reaches the
// end?
//
// EVERY NUMBER THIS FORMULA PRODUCES IS SYNTHETIC. There is no market data in
// it. The instrument you run it on supplies nothing but somewhere to put the
// output rows - change the symbol and the answers do not move. That is the
// point: this is arithmetic about sequences, not evidence about any market.
//
// ASSUMPTIONS, all of them wrong in the friendly direction:
// * Every trade is an independent draw. Real trades cluster by regime.
// * The win rate and the payoff ratio never change. In a real system both
// drift, and they drift together, and they get worse at the same time.
// * Wins are all the same size and losses are all the same size. Real
// outcome distributions have tails this model does not contain.
// * Costs are already inside the win and loss figures you type in.
// * Trading stops the moment equity touches the ruin level.
// A real system is riskier than this simulation says, not safer.
//
// Run it as an Exploration in the New Analysis window, applied to the CURRENT
// SYMBOL only, over any range that contains at least as many bars as there are
// risk levels. One row per risk level. Expect a few seconds of work: the
// default settings draw 800,000 random numbers.
// ---- Inputs (Parameters button in the Analysis window) --------------------
WinRatePct = Param( "Win rate (%)", 45, 5, 95, 1 );
PayoffRatio = Param( "Payoff (avg win / avg loss)", 1.8, 0.2, 10, 0.1 );
RiskStepPct = Param( "Risk step per row (%)", 1, 0.25, 5, 0.25 );
LevelCount = Param( "Risk levels (rows)", 8, 1, 20, 1 );
TradesPerPath = Param( "Trades per path", 100, 10, 500, 10 );
PathCount = Param( "Paths per risk level", 1000, 100, 5000, 100 );
RuinPct = Param( "Ruin level (% of start)", 50, 5, 95, 5 );
WinProb = WinRatePct / 100;
RuinLevel = RuinPct / 100; // equity is tracked as a multiple of 1.0
// ---- Where the output rows go --------------------------------------------
// The last LevelCount bars of the array carry one result row each. Writing
// into a zero array by index is exact; it does not depend on dates or on the
// analysis range lining up with anything.
if ( LevelCount > BarCount )
{
LevelCount = BarCount;
}
FirstRow = BarCount - LevelCount;
RowFlag = Cum( 0 );
RiskCol = Cum( 0 );
RuinCol = Cum( 0 );
BelowCol = Cum( 0 );
DoubleCol = Cum( 0 );
TypicalCol = Cum( 0 );
WorstCol = Cum( 0 );
AvgDDCol = Cum( 0 );
// ---- The simulation -------------------------------------------------------
for ( level = 0; level < LevelCount; level++ )
{
RiskFraction = RiskStepPct * ( level + 1 ) / 100;
// A 100 per cent risk fraction takes equity to zero in one trade and makes
// the logarithms below meaningless, so cap it just short of that.
if ( RiskFraction > 0.99 )
{
RiskFraction = 0.99;
}
WinStep = 1 + RiskFraction * PayoffRatio;
LossStep = 1 - RiskFraction;
RuinedPaths = 0;
BelowStart = 0;
DoubledUp = 0;
SumLogEnd = 0;
SumMaxDD = 0;
WorstEnd = 1000000000;
for ( path = 0; path < PathCount; path++ )
{
PathEquity = 1;
PathPeak = 1;
PathMaxDD = 0;
WasRuined = 0;
for ( trade = 0; trade < TradesPerPath; trade++ )
{
if ( mtRandom() < WinProb )
{
PathEquity = PathEquity * WinStep;
}
else
{
PathEquity = PathEquity * LossStep;
}
if ( PathEquity > PathPeak )
{
PathPeak = PathEquity;
}
Underwater = 1 - PathEquity / PathPeak;
if ( Underwater > PathMaxDD )
{
PathMaxDD = Underwater;
}
// Ruin is defined as touching the level at any point. Once touched
// we stop trading, which is what a person or a broker would do.
if ( PathEquity <= RuinLevel )
{
WasRuined = 1;
break;
}
}
RuinedPaths = RuinedPaths + WasRuined;
SumMaxDD = SumMaxDD + PathMaxDD;
SumLogEnd = SumLogEnd + log( PathEquity );
if ( PathEquity < 1 )
{
BelowStart = BelowStart + 1;
}
if ( PathEquity >= 2 )
{
DoubledUp = DoubledUp + 1;
}
if ( PathEquity < WorstEnd )
{
WorstEnd = PathEquity;
}
}
Row = FirstRow + level;
RowFlag[ Row ] = 1;
RiskCol[ Row ] = RiskFraction * 100;
RuinCol[ Row ] = 100 * RuinedPaths / PathCount;
BelowCol[ Row ] = 100 * BelowStart / PathCount;
DoubleCol[ Row ] = 100 * DoubledUp / PathCount;
TypicalCol[ Row ] = exp( SumLogEnd / PathCount ); // geometric mean
WorstCol[ Row ] = WorstEnd;
AvgDDCol[ Row ] = 100 * SumMaxDD / PathCount;
}
// ---- Output ---------------------------------------------------------------
Filter = RowFlag == 1;
SetOption( "NoDefaultColumns", True );
AddColumn( RiskCol, "Risk per trade %", 1.2 );
AddColumn( RuinCol, "Reached ruin %", 1.1 );
AddColumn( BelowCol, "Ended below start %", 1.1 );
AddColumn( DoubleCol, "Ended at 2x or better %", 1.1 );
AddColumn( TypicalCol, "Typical end multiple", 1.3 );
AddColumn( WorstCol, "Worst end multiple", 1.3 );
AddColumn( AvgDDCol, "Average worst drawdown %", 1.1 );

Download ruin-laboratory.afl160 lines

Three nested loops. The outer one walks a ladder of risk levels, one per output row. The middle one runs many independent paths at each level. The inner one plays out a single path, trade by trade: draw a uniform random number, multiply equity by the winning factor or the losing factor accordingly, update the running peak and the deepest fall from it, and stop the path immediately if equity has touched the ruin threshold.

Each completed path contributes to seven running totals — whether it was ruined, its deepest drawdown, the logarithm of its ending equity, whether it finished below its start, whether it finished at twice its start or better, and whether it was the worst ending so far. Averaging the logarithms rather than the equities is deliberate: the arithmetic mean of a set of multiplicative outcomes is dragged upwards by a handful of large winners and describes no typical path at all. The exponential of the mean logarithm is the geometric mean, which is the multiple a middling path actually achieves.

Writing results into arrays by index is what lets one exploration produce a table. The last several bars of the array carry one result row each, and Filter is set from a flag array written by the same loop, so the rows appear whatever dates the analysis range happens to cover.

  • Param( name, default, min, max, step ) — puts every assumption on the Parameters dialog, so the experiment can be re-run without editing code. Outside a chart it still returns the default, which is what makes the file self-documenting.
  • mtRandom() — a Mersenne Twister uniform random number in the range 0 to 1. The guide notes it is substantially better than the C runtime generator behind Random(), and that an unseeded call is initialised from the clock.
  • Cum( 0 ) — an array of zeros, used here as writable working storage.
  • log() and exp() — natural logarithm and its inverse, used to build the geometric mean.

One row per risk level, with the fraction of paths that reached ruin, the fraction that ended below their starting point, the fraction that at least doubled, the geometric-mean ending multiple, the worst ending multiple seen, and the average of each path’s deepest drawdown.

At the defaults — a 45 per cent win rate and a payoff ratio of 1.8, which is a comfortably positive edge — you should see the ruin column rise steeply as the risked fraction grows, and the geometric-mean multiple rise, peak and then fall. Those two things happening at once is the whole point of the exercise. Every number is synthetic; the shape of the relationship is what you are being asked to look at, not the values.

Set the payoff ratio so that the edge disappears — with a 45 per cent win rate, a payoff of 0.55 / 0.45, about 1.222, makes expectancy exactly zero — and confirm that the geometric-mean ending multiple collapses towards or below 1.0 at every risk level. Then set the win rate to 100 per cent and check that the ruin column is zero everywhere, which it must be if the loop logic is right. Finally, double the path count and confirm the percentages move by less than a percentage point or so; if they jump about, you are reading noise rather than a result.

  • Running it on a whole watch list. The formula never touches price data, so every symbol produces the same row. Apply it to the current symbol.
  • Too few bars. The output rows are written into the last bars of the array, so the analysis range must contain at least as many bars as there are risk levels.
  • Reading the ruin percentage as a property of your system. It is a property of the four numbers you typed in, under a model in which trades are independent and identically sized. Your trades are neither.
  • Raising trades-per-path and paths-per-level together. The work is the product of three numbers. Five thousand paths of five hundred trades across twenty levels is fifty million random draws and a long wait.

Replace the fixed win and loss factors with a draw from your own trade record — bucket your actual percentage outcomes and sample from those buckets instead. That removes the “all wins are the same size” assumption, which is the most obviously false one in the model. It does not remove the independence assumption, and no amount of work on the outcome distribution will.

There is a level of aggression at which a genuinely positive edge produces a negative long-run growth rate. It is not a subtle effect and it does not require anything to go wrong.

For a binary outcome — win a multiple b of the amount risked with probability p, lose the amount risked with probability q — risking a fraction f of equity on each trade gives an expected growth rate per trade of:

g(f) = p · ln(1 + f·b) + q · ln(1 − f)

Put the ruin laboratory’s defaults into it: p = 0.45, b = 1.8, so the expected profit per unit risked is 0.45 × 1.8 − 0.55 = 0.26. That is a substantial edge. Here is what g does as f grows:

Fraction of equity risked per trade Expected log growth per trade
5 per cent +0.0106
10 per cent +0.0165
14.4 per cent +0.0182, the maximum
20 per cent +0.0156
30 per cent −0.0019
40 per cent −0.0369

Past about 30 per cent, an edge worth 26 per cent of every unit risked becomes a machine for losing money — not because the edge failed, but because losses compound against a shrinking base while gains compound against it too. The maximum sits at (p·b − q)/b, the log-optimal fraction usually attributed to Kelly, and it is worth knowing chiefly as an upper bound that nobody sane approaches.

Why average return is a poor guide to survival

Section titled “Why average return is a poor guide to survival”

The final piece is a piece of arithmetic that catches people out constantly.

Alternate a 50 per cent gain and a 40 per cent loss indefinitely. The average of +50 and −40 is +5 per cent, which sounds like a good business. The compounded reality is 1.5 × 0.6 = 0.9 per pair of trades, a geometric mean of about −5.1 per cent per trade. An arithmetic mean of +5 per cent and a geometric mean of −5 per cent, from the same two numbers.

The gap is variance. For returns that are not too large, the compound growth rate is approximately the arithmetic mean minus half the variance: here, 0.05 − 0.45² / 2 = 0.05 − 0.101 = −0.051, which matches the exact figure closely. Volatility does not merely add uncertainty around the average outcome; it subtracts from it.

Two practical consequences follow. First, a strategy comparison based on average trade profit is not a comparison of growth rates, and the more volatile candidate is being flattered. Second, reducing position size reduces the variance term quadratically while reducing the mean only linearly, which is why cutting size can raise compound growth even though it lowers every individual trade’s profit.

AmiBroker’s report keeps the two apart, and the vocabulary is worth being careful with. Annual Return % — the metric string "CAR" — is a compounded figure and is therefore a growth rate. Avg. Profit/Loss % is an arithmetic average across trades and is not. They answer different questions, and a system with a healthy average trade and an unhealthy Annual Return % is telling you exactly where its variance went.

Reordering a fixed set of trades under proportional sizing leaves terminal equity untouched and changes maximum drawdown a great deal. Terminal equity becomes order-dependent as soon as the account can stop, can fail to fund a signal, or takes money in and out on a calendar — and AmiBroker models the funding case explicitly through its minimum-position settings.

Risk of ruin is only a number once you have named a threshold and a horizon. The classical formula gives it in closed form for fixed-stake, even-money, independent bets and shows ruin falling geometrically as the stake shrinks relative to the bankroll; for proportional sizing with unequal outcomes you simulate, and the ruin laboratory does that with every assumption visible.

Position size, not the edge, decides survival. There is a fraction beyond which a real edge produces negative expected growth, the peak sits at (p·b − q)/b, and the cost of being too aggressive is far greater than the cost of being too cautious. And because volatility subtracts from compound growth, the average trade is a poor guide to what the account actually does.

The next lesson turns the scepticism outward: everything in this part, and everything in Parts 31 and 32, is downstream of one backtest, and inherits every one of its flaws.

Check your understanding

Question 1. A system sizes every position as 5 per cent of current equity. You reorder its 200 trades at random. Which statement is correct?
Show the answer and why

Answer: Final equity is unchanged; maximum drawdown will generally change

Proportional sizing makes the account a product of growth factors, and multiplication is order-independent. The path is not: putting the losses together produces a much deeper peak-to-valley fall than spreading them out. This is why drawdown is the Monte Carlo output worth attending to.

Question 2. Which of these make terminal equity depend on the order of the trades? Select all that apply.
Show the answer and why

Answer: A rule that stops trading if equity falls 40 per cent below its peak, A minimum position size, below which a signal is skipped, A monthly contribution paid into the account

A stopping rule truncates the sequence, a minimum size changes which signals get taken depending on the balance at the time, and calendar cash flows land at fixed dates rather than at fixed points in the trade sequence. Pure proportional sizing on its own does not: it is exactly the case where order cancels out.

Question 3. Using (q/p)^N with p = 0.55, a bankroll of 10 units gives roughly a 13 per cent chance of ruin and 20 units roughly 1.8 per cent. What changed between the two?
Show the answer and why

Answer: The size of each bet relative to the bankroll halved

The edge is identical in both rows. Only the stake relative to the bankroll changed, and ruin probability falls geometrically as it shrinks. The formula assumes even-money, fixed-stake, independent bets over an unlimited horizon, so it is an intuition pump for trading rather than a calculation you can apply to it.

Question 4. A strategy alternates +50 per cent and −40 per cent trades. What is its compound growth per trade?
// two trades: 1.50 then 0.60
Show the answer and why

Answer: About −5 per cent, because 1.5 × 0.6 = 0.9 per pair

The arithmetic mean is +5 per cent and the geometric mean is about −5.1 per cent; the difference is the variance drag, approximately half the variance of the returns. The average trade profit is not a growth rate, which is why AmiBroker reports Annual Return % separately from Avg. Profit/Loss %.

Question 5. With a 45 per cent win rate and a payoff ratio of 1.8, expected log growth per trade peaks near f = 14.4 per cent and is negative by f = 30 per cent. What is the practical lesson?
Show the answer and why

Answer: A real edge traded too large still compounds to nothing, because losses compound against a shrinking base

The edge is a property of the rules and does not change with size. What changes is the compounding: beyond a certain fraction the drag from losses outweighs the contribution from wins, so growth turns negative while expectancy per unit risked stays firmly positive. The penalty for being above the optimum is far steeper than for being below it.

Sources for this lesson

5 verified · checked 2026-08-31

  1. 01AmiBroker User's Guide — Monte Carlo simulationamibroker.com/guide/h_montecarlo.html2026-08-31
  2. 02AFL Function Reference — SetOption§ MinShares, MinPosValue, AllowPositionShrinking, DisableRuinStopamibroker.com/guide/afl/setoption.html2026-08-31
  3. 03AFL Function Reference — ApplyStop§ Author's note on stop evaluation order and the fixed ruin stopamibroker.com/guide/afl/applystop.html2026-08-31
  4. 04AFL Function Reference — mtRandomamibroker.com/guide/afl/mtrandom.html2026-08-31
  5. 05AFL Function Reference — Paramamibroker.com/guide/afl/param.html2026-08-31

Every technical claim on this page was checked against the official AmiBroker documentation on the date shown. Where the course disagrees with folklore, the source is how you can tell which one to trust.