Why most backtests lie
A beautiful equity curve is the easiest thing in the world to produce and one of the least meaningful. This chapter is the hinge of the whole course: it shows, with math, exactly how a backtest fools you — noise mistaken for signal, the same data mined until something shines, the best of many random tries dressed up as a discovery. Understanding this is what makes the testing chapters that follow feel necessary rather than pedantic.
Noise vs signal#
Every price series is part signal (a real, repeatable relationship) and mostly noise (random fluctuation that will never repeat). A backtest cannot tell you which is which — it faithfully reports the performance of whatever pattern you fit, real or imaginary. The trouble is that noise is structured enough to look like signal over any finite sample. Given a few hundred bars, you can always find a rule that would have traded them beautifully; the question is whether that rule describes the market or just that particular slice of history.
The cruel asymmetry: a real edge is small and noisy (recall the standard error from Statistics for traders), while an over-fit rule looks clean and large. So the more impressive a backtest looks, the more suspicious you should be — real edges are usually thin and ugly.
Data mining & p-hacking#
Data mining is searching the same dataset for a pattern that works; p-hacking is doing it until something clears a significance bar. Both exploit a loophole in the p-value from earlier: a p-value is only valid for a single, pre-registered test. Try many, and you have quietly changed the experiment.
Every knob is a hidden test. Sweeping a lookback from 10 to 200, trying five stop multiples, three targets, and four instruments is strategies. Report the best one’s p-value as if you had run one test and you are lying to yourself with arithmetic. This is the most common way honest people produce dishonest backtests.
The multiple-testing problem#
Here is the math that should scare you. If you run independent tests on pure noise, each with a false-positive rate , the probability that at least onecomes up “significant” is:
Work with the complement. Under the null (pure noise), one test is a false positive with probability , so it is not a false positive with probability . If the tests are independent, the probability that all of them clear (no false positive anywhere) is the product . “At least one false positive” is the complement of “none,” so
Bonferroni correction. To hold the family-wise error at across tests, test each at the stricter threshold . The guarantee needs no independence — it follows from Boole’s union bound, :
Why it matters for trading. Every knob you sweep is a hidden test, so your effective is enormous and the honest per-config threshold is brutal. The best config in an 11,460-strategy sweep must clear roughly , not , before its p-value means anything.
At , just 14 tests give you better-than-even odds of a spurious winner; at 100 tests it is 99.4% certain. You will find a strategy in random data if you look at enough of them — guaranteed.
SmaCross: fast MA over (10 values), slow MA over (8 values), across 4 instruments — backtests. Even if none of these has a real edge, at you expect of them to clear “” by luck alone, and the very best might post a profit factor near 1.6 with a gorgeous curve. Report thatone as if it were your single idea and you have p-hacked without meaning to. The honest bar is not 0.05 but Bonferroni’s — and the real defense is to hold out data the sweep never touched and see if the winner repeats.The expected maximum of N random strategies#
It gets worse: not only will one clear the bar, the best of your random tries will look genuinely spectacular. If you generate strategies with no real edge — each a standard Normal Sharpe — the expected value of the maximum grows without bound as you add more:
The maximum sits near the level that you expect to be exceeded about once in draws — i.e. where . For a standard Normal the upper tail decays like . Keeping the dominant exponential factor, set
The maximum of iid standard Normals concentrates tightly around this level (extreme-value theory sharpens it to , but the leading term is what bites). Why it matters for trading: a Sharpe estimate is approximately Normal around its true value, so the best of edge-free strategies has an expected Sharpe of about standard errors above zero purely from selection. Growing the search only makes the winner look better while its true edge stays exactly zero — best-of-N noise is indistinguishable from skill until you test out-of-sample.
Test 1,000 worthless strategies and the best one has an expected Sharpe around standard errors from zero — a “3-sigma discovery” that is pure selection. The winner of a large search is not the one with an edge; it is the one that got luckiest. Its out-of-sample performance regresses straight back to zero.

This is why edgekit’s validation layer carries a deflated_sharpe — a Sharpe adjusted down for the number of trials it took to find the winner — precisely to undo the inflation.
from edgekit.validation import deflated_sharpe
# You searched ~200 configs and kept the best. Its raw Sharpe is inflated
# by that selection — deflate it against the number of trials.
ds = deflated_sharpe(winning_returns, n_trials=200, sr_std=0.03)
print(ds) # a Sharpe that survives this is one selection didn't manufactureThe base-rate fallacy — the most important lesson in the chapter#
A significant p-value answers — the chance noise fakes your result. But the question you actually care about is the reverse: — given that my backtest cleared the bar, how likely is the edge real? These are not the same number, and confusing them is the base-rate fallacy. Bayes’ theorem converts one into the other:
The term is the prior base rate — the fraction of strategies you try that have a genuine edge — and it is low. Real edges are rare; most ideas are variations on noise. Plug in a sober prior , a test with power , and the usual false-positive rate :
Read that again: even a correctly significant, backtest is more likely false than true — a 54% chance the “edge” is noise — before you account for any data mining. And multiple testing makes it strictly worse: searching many configs inflates the effective far above 0.05 (that is what the result above measures), so the denominator balloons and the posterior collapses toward zero.
Overfitting, formalised#
Overfitting is fitting the noise instead of the signal. Any model flexible enough will drive its in-sample error toward zero by memorising the particular wiggles of the training data — wiggles that carry no information about the future. The tell is a widening gap between in-sample and out-of-sample performance:
When you choose parameters to minimiseerror on the training sample, you are optimising against that sample’s particular noise as well as its signal. Decompose an estimator’s expected error into bias, variance, and irreducible noise:
Adding complexity buys lower bias but higher variance — and variance is the part that does not transfer to new data. In-sample error keeps falling because the fit absorbs both terms; out-of-sample error can only fall until the variance term overtakes the bias saving, then it rises. Why it matters for trading: the gap is not measurable from the backtest alone — the training curve always looks good — which is exactly why you must hold out unseen data (walk-forward) to estimate it rather than assume it away. See Overfitting detection.
As you add parameters or search harder, in-sample error keeps falling but out-of-sample error turns around and rises — the model has started describing this history rather than the market. The sweet spot is well before the in-sample minimum; past it you are buying backtest beauty with live losses.

Regime dependence#
Even a genuinely-fit strategy carries a hidden assumption: that the future resembles the past. Markets move through regimes— trending vs ranging, calm vs volatile, low-rate vs high-rate — and an edge that was real in one regime can vanish or invert in the next. A backtest spanning a single long bull market can look flawless and be measuring nothing but that regime’s tailwind (the difference between alpha and beta, covered in Alpha vs beta).
The defense is to insist an edge holds across regimes, not just on average. Split the history — bull vs bear, high-vol vs low-vol — and demand the edge survive each slice, not merely the blended whole. A result that only appears when you pool everything together is a regime artifact wearing an edge’s clothing.
Regression to the mean & survivorship bias#
Selection has a quantitative signature. Write any measured performance as truth plus noise, , where is the strategy’s real skill and is sampling luck. When you pick the top result of a search, you preferentially pick strategies whose was large and positive — so the winner’s luck is, on average, positive, and its future performance regresses toward the mean. The best linear estimate of the truth given the observation shrinks it toward the population average :
The reliability is the share of observed variance that is real signal. When noise dominates , and your best forecast of a chart-topping backtest’s future is the average — near zero edge. This is regression to the mean stated as an equation, and it is the deep reason out-of-sample numbers come in below in-sample ones.
Survivorship bias is the same conditioning applied by the world instead of by you. The strategies, funds, and coins you can study are the ones that survived; the failures were delisted and deleted. Conditioning on survival lifts the observed mean above the true mean, , so a backtest on today’s tradable universe is quietly scored on a sample hand-picked for having done well.
This is why the gauntlet exists#
Every failure above has the same shape — a result that looks real in-sample and dies out-of-sample — and each has a specific antidote. Stacked together, they are the gauntlet:
| The lie | The defense | Where in edgekit |
|---|---|---|
| Noise mistaken for signal | Permutation test — could shuffled data fake this? | validation.mcpt |
| Best-of-N selection inflation | Deflated Sharpe — penalise for trials | validation.deflated_sharpe |
| Overfitting to one period | Walk-forward — test on unseen data | validation.walk_forward |
| Regime dependence | Regime splits — demand edge in each | the gauntlet |
| One lucky historical path | Monte Carlo — resample the outcome | Monte Carlo |
| Costs quietly ignored | Cost stress — 2× the fees, does it live? | costs.cost_stress |
ORB and SmaCross strategies in edgekit are here to practice that adversarial process, and most of them do not survive it. That is the point.Next: with the math of edge and the ways it deceives us in hand, Part III turns to building — starting with the Anatomy of a strategy. The defenses named above are built out in full in Part IV, beginning with Backtesting fundamentals.

