DEV Community

Christian Pichichero
Christian Pichichero

Posted on

What Shuffling Your Trade History Actually Tells You (And What It Doesn't)

You ran a backtest. The equity curve is smooth, the drawdown is small, the Sharpe ratio looks respectable. Here's the thing nobody tells you early enough: that curve is one path. It's the result of your trades happening in exactly the order they happened. If trade #14 (a big loser) had landed right after trade #3 (another big loser) instead of scattered safely between winners, your drawdown number would be a different number, from the same trades, with the same win rate, same average win, same average loss.

That's the whole motivation for Monte Carlo trade resampling. You take the list of closed trades — just the P&L values, stripped of their original sequence — and you reshuffle them, thousands of times, rebuilding an equity curve for each shuffle. Two common flavors:

  • Permutation (resampling without replacement): every shuffle uses the exact same set of trades, just in a different order. Total return is fixed across all shuffles; only the path changes — which is precisely the point, since path determines drawdown and time-underwater.
  • Bootstrap (resampling with replacement): each shuffle draws trades at random, allowing repeats and omissions. This also perturbs the total return, not just the order, giving you a sense of variance from sample size, not just sequence.

Here's a minimal version of the permutation approach:

import numpy as np

def max_drawdown(equity):
 peak = np.maximum.accumulate(equity)
 dd = (equity - peak) / peak
 return dd.min()

def resample_paths(trade_returns, n_sims=5000):
 trades = np.array(trade_returns)
 results = []
 for _ in range(n_sims):
 shuffled = np.random.permutation(trades)
 equity = np.cumprod(1 + shuffled)
 results.append(max_drawdown(equity))
 return np.array(results)
Enter fullscreen mode Exit fullscreen mode

Run this on 80 closed trades and you don't get one drawdown number, you get a distribution of 5,000 drawdown numbers. That distribution is the actual output worth looking at. Somewhere in there is a 5th percentile case — a drawdown notably worse than the one your backtest happened to show you, built from the exact same trades. If your original backtest's drawdown sits near the friendly end of that distribution, your single equity curve got lucky on ordering, and you didn't know it because you only ever looked at the one path that occurred.

The median-path trap

Here's where people go wrong once they've done the resampling correctly: they look at the median drawdown, or the median final equity, and treat it as "the expected outcome going forward." This is a mistake for a specific mathematical reason, not just a vague warning. Max drawdown is a nonlinear, path-dependent statistic — it's the result of a maximum and a minimum operating over the whole sequence. When you average or take the median of thousands of nonlinear statistics, that summary number doesn't correspond to any real path anyone actually experiences. There is no shuffle in your simulation set whose drawdown equals the median drawdown by construction — it's a statistic about the population of outcomes, not a description of a plausible one.

Worse, the distribution of drawdowns is almost always right-skewed (bounded at zero, long tail toward catastrophic). The median underrepresents the tail. If you're using this number to size positions or set risk limits, you want the 90th or 95th percentile of drawdown, not the middle of the pack — the middle is the case where nothing went particularly wrong, which is not the case you're trying to survive.

The assumption everyone skips: independence

Both permutation and bootstrap resampling rest on one assumption: that each trade's outcome is independent of the trades around it — that shuffling the order doesn't destroy any real information, because there wasn't any sequential structure to begin with. For a lot of systematic strategies this is roughly fine. For momentum strategies, it's false, and it's false in a way that matters.

Momentum strategies, by construction, tend to produce autocorrelated trade outcomes: winning trades cluster during trending regimes, losing trades cluster during chop or reversals, because the underlying edge itself is regime-dependent. When you randomly permute those trades, you break up the clusters. This can cut both ways — sometimes it makes the resampled drawdowns look worse than reality, because it creates unlucky strings of losses that would never actually co-occur (a losing streak needs a choppy regime, and regimes don't get randomly interleaved trade-by-trade in real markets). Other times it hides the real risk, because the actual worst case is "strategy stops working when the regime changes for three months straight," and no reshuffling of historical trade P&L will manufacture a scenario the strategy never lived through.

Block bootstrapping — resampling contiguous chunks of trades instead of individual ones — partially addresses this by preserving some local correlation structure. It's a real improvement, not a full fix. It still can't invent a regime your strategy never traded through, and it still assumes the blocks are exchangeable, which is a weaker but still real assumption.

So what resampling actually tells you: how much of your backtest's apparent smoothness depended on the specific order the trades happened to arrive in, and how much of that ordering is even ordering you can trust reshuffling to explore honestly. It's a stress test on a fixed sample, not a forecast, and treating the tidy version — the median path, or a permutation test on a momentum book — as the expected future is the fast way to be surprised by a drawdown your simulation told you was rare.

Disclosure: I build Tradevo Verify, which runs this kind of resampling (among other checks) against closed-trade exports and reports the resulting distribution rather than a single pass/fail number — because the distribution is the honest answer and a single number usually isn't.

Top comments (0)