A 60% win rate, and the account can still lose half of itself on the way.
That is not a rare streak of bad luck. With a 60% win rate, an average win of 0.8 times the average loss, and 10% of the balance at stake on every trade, 36.860% of 5,000 simulated paths touched half of the starting balance at least once within 200 trades. The 10% is not arbitrary: it is the Kelly fraction for those inputs.
Every number below is a calculation from our free in-browser calculator, with the same formulas and the same random seed. It is not a forecast and not trading advice. The model is deliberately simple: every trade is independent, wins b units per unit risked with probability p and loses 1 unit with probability 1 − p, with p and b fixed. So a win multiplies the balance by (1 + b × stake) and a loss by (1 − stake). No fees, no slippage, no correlation between trades.
1. The win rate does not decide where the balance goes
At least three numbers do:
- p, the win rate;
- b, the payoff ratio: average win ÷ average loss;
- r, the stake: what one trade can lose, as a share of the balance at that moment.
At p = 0.6 and b = 0.8 the edge per trade is small: 0.6 × 0.8 − 0.4 × 1 = 0.08 units per unit risked. The same edge can take very different roads depending on r, because the balance compounds. After a 50% fall, getting back to the start takes a 100% gain.
2. The Kelly fraction: the growth-maximising stake
The Kelly criterion gives the stake that maximises the expected logarithmic growth of the balance:
f* = p − (1 − p) / b For p = 0.6 and b = 0.8, f* = 0.6 − 0.4 / 0.8 = 0.10, so 10% of the balance. For p = 0.5 and b = 1.5 it is 16.667%. The formula assumes b > 0 and a stake between 0 and 1; a negative result only means the inputs carry no edge in this model, and it is not a signal of any kind.
The same formula also marks where more stake starts to hurt. Expected log growth per trade, g(f) = p·ln(1 + b·f) + (1 − p)·ln(1 − f), at p = 0.6 and b = 0.8:
| Stake | g(f) per trade |
|---|---|
| 2% | +0.00144 |
| 5% | +0.00302 |
| 10% (Kelly) | +0.00403 |
| 15% | +0.00299 |
| 20% | −0.00021 |
For these inputs, growth peaks at 10% and falls on both sides; at a 20% stake the expected log growth is negative in this model. Half-Kelly (5%) keeps about 75% of the peak growth, again for these inputs.
One more assumption sits inside the formula: it treats p and b as known. In practice they are estimates from a limited trade log. If the estimate leans optimistic, the "optimal" stake is already too large.
3. Risk of ruin: look at the spread of paths, not the average
Kelly maximises long-run growth. An account does not live the average, though; it lives one path. So we ran 5,000 seeded paths with the same inputs and counted the share whose balance fell to 50% of the starting balance at least once within 200 trades. The table also shows the median maximum drawdown, the largest peak-to-trough decline on each path (in brackets: the 95% Wilson interval for the share, inside this model):
| Stake | Touched −50% | 95% interval | Median max drawdown |
|---|---|---|---|
| 2% | 0.020% | 0.004% – 0.113% | 17.582% |
| 5% | 7.300% | 6.611% – 8.054% | 39.769% |
| 10% (Kelly) | 36.860% | 35.533% – 38.207% | 67.596% |
At exactly the Kelly stake, more than one path in three fell to half of the starting balance along the way, and the 95th percentile of the maximum drawdown was 89.595%. Among the three stakes tested, the growth-maximising 10% also had the largest median maximum drawdown.
4. Losing streaks under independent outcomes
Losses come in runs even at 60%. The probability of at least one run of 8 losses within 200 trades is 7.356% (exact, by dynamic programming). At a 50% win rate it is 31.970%.
The table compares fixed stakes only; a stake that changes after losses was not simulated. In this model, a larger fixed stake sits further down the table, where the spread of paths is wider.
5. Reproduce it
This is the core of the calculator in Python, with the same seeded generator. It prints the Kelly fraction and the three ruin shares above, to the last digit:
import math def mulberry32(seed): # the same small seeded generator the browser calculator uses s = seed & 0xFFFFFFFF def rnd(): nonlocal s s = (s + 0x6D2B79F5) & 0xFFFFFFFF v = ((s ^ (s >> 15)) * (1 | s)) & 0xFFFFFFFF v ^= (v + (((v ^ (v >> 7)) * (61 | v)) & 0xFFFFFFFF)) & 0xFFFFFFFF return ((v ^ (v >> 14)) & 0xFFFFFFFF) / 4294967296 return rnd def ruin_share(p, b, r, trades=200, floor=0.50, paths=5000, seed=20261114): """Share of paths whose balance touches (1 - floor) of the start at least once.""" rnd, win, loss = mulberry32(seed), math.log1p(r * b), math.log1p(-r) limit, hits = math.log1p(-floor), 0 for _ in range(paths): eq, ruined = 0.0, False for _ in range(trades): eq += win if rnd() < p else loss ruined = ruined or eq <= limit + 1e-12 hits += ruined return hits / paths p, b = 0.60, 0.8 print(f"Kelly fraction: {p - (1 - p) / b:.3%}") for r in (0.02, 0.05, 0.10): print(f"risk {r:.0%}: {ruin_share(p, b, r):.3%} of 5,000 paths touched -50%") Our output:
Kelly fraction: 10.000% risk 2%: 0.020% of 5,000 paths touched -50% risk 5%: 7.300% of 5,000 paths touched -50% risk 10%: 36.860% of 5,000 paths touched -50% Working in log space turns compounding into addition and makes the threshold test a single comparison; it is still floating-point arithmetic, hence the small tolerance.
6. A limit you decided is easier to keep when a machine keeps it
A limit set by calculation is only as good as whatever enforces it. One design hands that job to a small local process:
- the numbers come first: daily loss limit, per-strategy limit, what to do when the data feed goes stale;
- when a configured limit is detected as touched, it tries to stop the process you named, without asking you in the moment;
- every stop is written to a log chained with SHA-256, so an edit to one entry breaks the chain on the next check (rewriting the whole chain is only caught against a copy kept elsewhere);
- configuration files are read, not used, to check the permission settings declared there and to flag secrets written into them.
A tool built this way places no trades and predicts nothing. Stopping a process does not close open positions or cancel resting orders, and it is not a guaranteed loss ceiling.
Try it with your own numbers
The calculator runs in the browser: win rate, payoff, stake, number of trades, the drawdown you treat as ruin and the streak length. Your inputs are not sent anywhere. The same page shows the local audit-and-stop kit, its file list and its tests:
https://zero-trace-ship-kit.zeus-lab-global.workers.dev/quant/?utm_source=devto
Calculation only. These are formula values and seeded simulation values, not a forecast, and not a recommendation of any trade, broker or product.
Disclosure: this article was drafted with AI assistance (Claude) and checked against the measured results and the published files. Product links lead to our own paid kits.
