← Blog
Jul 24, 2026·11 min read·backtesting, walk-forward, deflated sharpe, fail taxonomy, methodology

I ran 93,265 crypto backtests over 96 days. 1,676 passed — but really only 35 parent strategies did.

A labeled failure corpus of 91,589 dead crypto strategies with structured fail_reason tags. This post publishes the taxonomy, the search-method comparison (with the honest caveat that undercuts the headline number), and the $200 real-money loss that proves paper does not equal live.

The number

As of 2026-07-24 00:00 UTC: 93,265 backtests in the research_results table. 1,676 passed all six gates (1.80%).

Exact query:

SELECT COUNT(*), SUM(passed) FROM research_results WHERE created_at < '2026-07-24';

The corpus is live at /research and grows about 1,000 rows per day, so the number is a few thousand higher by the time you read this.

The window we're actually looking at

This matters more than any other caveat and it should be near the top: the corpus covers 2026-04-19 through 2026-07-23 — 96 calendar days. Walk-forward validation trains on the first ~67 days and validates on the last ~29 days (Math.floor(candles.length * 0.7) at s1-combine.js:1982).

Every claim in this post is conditional on that window's market regime. A rolling multi-year walk-forward is on the roadmap and does not exist today. Read every number below with that scope in mind. If you were expecting 3 years of data, this isn't that. It's 96 days of data run through six gates.

The one live experiment: $200 → $146.31

The single most decision-relevant number in this post. Not paper.

  • Deposited $200 to Kraken on 2026-05-07 via a live-trading engine
  • 19 real trades executed against the strategy the research lab said was the strongest
  • Balance on 2026-06-16: $146.31, down 26.85%, 31.6% win rate
  • Trade-by-trade record: on /proof under "Real capital lessons"

Paper expectation for those 19 trades was positive. Reality delivered −26.85%.

Every backtest number below this line is paper. Discount accordingly. The one time we ran real capital against our own graduated strategy, the paper prediction and the reality diverged by more than 25 percentage points on a small n. That gap is the entire game.

What killed 98% of them

Here is the fail_reason breakdown across the 91,589 that failed. Denominators are against the full 93,265.

Killed by Count % of tested
cross_coin_overfit 42,778 45.87%
signal_density_too_low 27,332 29.31%
val_pnl_negative 11,408 12.23%
holdout_failed 7,867 8.44%
quality_zero 1,166 1.25%
regime_stratified_fail 807 0.87%
fluke_leak_retroactive_2026_05_15 132 0.14%
val_trades_insufficient 74 0.08%
val_sharpe_low 69 0.07%
untagged (writeback bug) 568 0.61%

That last row is disclosure. "Every dead strategy is supposed to get a fail_reason tag." As of this post, 568 non-passing rows have no tag because of a bug in the writeback path we're still debugging. That gap is the ceiling on how confident I can be in the table.

Now the four killers:

cross_coin_overfit (45.9%). The rule at s1-combine.js:2817: of the coins where the strategy fires ≥10 validation trades (must be at least 3 such coins), at least half must show positive per-trade P&L. It is a permissive gate. A strategy that works on 2 of 3 qualifying coins passes it. Almost half of the corpus still fails this permissive test. That failure rate is the story — most "edges" don't even survive a lenient multi-asset check.

signal_density_too_low (29.3%). All 27,332 rows tagged this way have bt_val_trades = 0. It is a null-signal detector, not a statistical significance gate. Strategies that fire zero times on the validation slice die here. It is not applied against low-but-nonzero trade counts — see "what we don't trust" below for how that affects the survivor set.

val_pnl_negative (12.2%). Train on the first 70% of the data, validate on the held-out 30%. If the validation half loses money after fees, kill. The most obvious failure mode. Retail backtesters typically don't split at all.

holdout_failed (8.4%). After walk-forward, we hold out an additional frozen slice the researcher never touches. If a strategy passes walk-forward but fails on the true holdout, it means the researcher (me) probably peeked at the walk-forward too many times. That gate exists because the researcher shouldn't be trusted.

The tiny numbers at the bottom are the interesting ones. fluke_leak_retroactive_2026_05_15 means we found a bug in our own gate on May 15, went back through the corpus, and killed 132 strategies that had passed under the bug. That is not a normal fail_reason — it is an audit event. Publishing it is a promise to keep doing it when we find more bugs.

What "1,676 survivors" actually means

Not 1,676 independent edges. Not 1,676 different ideas.

Across the entire pass set, only 35 distinct parent lineages produced any passing descendant. Nearly every "survivor" is a variant of one of those 35 parents. The corpus doesn't contain 1,676 discoveries; it contains 35 discoveries and their siblings.

That's a much smaller and much more overfit story than the headline count suggests. I'm calling it out here because it's the single largest way "1.80% pass rate" would mislead a reader who takes the number at face value.

The mutation-type table (with the honest reading)

Here are all 21 methods the search has ever tried, ranked by trial count:

Method Trials Passed Pass % Distinct parents
random 89,656 3 0.003% 0
exit_sweep 3,436 0 0.0% 16
bars_mutation 1,528 965 63.2% 4
cross_pollinate 1,108 537 48.5% 55
regime_seed 742 19 2.6% 0
seed 495 0 0.0% 0
session_seed 270 0 0.0% 0
vol_confirm_seed 224 0 0.0% 0
vol_breakout_seed 220 0 0.0% 0
multi_tf_seed 167 6 3.6% 0
tp_mutation 96 82 85.4% 4
seed_short 93 0 0.0% 0
adx_mutation 64 64 100% 4
seed_highertf 63 0 0.0% 0
seed_breakout 54 0 0.0% 0
regime_generator 37 0 0.0% 0
exit_tweak 32 0 0.0% 0
param_sweep 9 0 0.0% 0
seed_tf15 7 0 0.0% 0
add_filter 4 0 0.0% 0
combine 1 0 0.0% 0

The honest reading

The eye-catching row is bars_mutation at 63.2%. It doesn't say what a reader would assume.

bars_mutation's 1,528 trials come from exactly 4 distinct parent strategies. Each parent gets swept 382 ways. That is a local parameter sweep around 4 already-passing strategies, not a global search method. The 63% pass rate reflects local smoothness of the fitness landscape around known winners: sweep hold-length around a strategy that already works, and it usually still works. That is not evidence that adaptive search beats random search in general.

Same story with tp_mutation (85%, 4 parents) and adx_mutation (100%, 4 parents). Both are local sensitivity sweeps around a tiny number of parents. Reporting them as pass-rate categories comparable to random search is a category error.

cross_pollinate at 48.5% is more informative — 1,108 trials from 55 distinct parent pairings. That's closer to a real search method and its pass rate holds up.

Random search at 0.003% is also not what it looks like. "Random" configs are drawn from a pre-filtered region (5m timeframe, session hours, adxMax caps, top-N coin basket). It is undirected sampling inside a region we already know is edge-friendly, and it still finds a passing strategy 3 times in 89,656 tries. That is a comment on how sparsely tradeable configurations are distributed even inside a known-friendly region, not a comment on random search as a general technique.

The line I'd change if I could: in an earlier draft I wrote "exit tuning is not where the edge lives." That's contradicted by tp_mutation (85%) and adx_mutation (100%), which are exit/filter tuning applied to already-winning parents. The honest version: exits are a stability lever on winners (tp_mutation, adx_mutation work), not an edge-creation lever on losers (exit_sweep 0 of 3,436). Different operation, different result.

Live examples from the kill log

Every retired strategy has a kill_reason field written at the moment it died:

  • M_9leroi_tp3.1 → clear_loser_ci90_-5.21_n9
  • D_mfi_14_25_cci_20_-80_tp1.75_b150 → clear_loser_ci90_-1.43_n8
  • E_A_rsi_7_below_25_b150_tp4_b800_adx35 → clear_loser_ci90_-4.26_n8
  • 520 strategies retired 2026-05-17 → config_signature_dupe_2026_05_17
  • 128 strategies retired 2026-05-15 → fluke_leak_retro_2026_05_15

One note on the asymmetry: we kill at n=8-9 with an aggressive one-sided CI, and we accept passers with as few as n=8. That's a deliberate fail-fast asymmetry, not a hidden inconsistency. If we let losing strategies run to 30 trades before killing them, they take up forward-test slots that could go to promising candidates. It also means the passer set includes a meaningful chunk of small-n strategies — see the next section.

What we don't fully trust

This section is doing a lot of work in this post. Please read it.

The 1,676 survivor count over-represents ~35 real ideas. Only 35 distinct parent_ids produced any passing descendant across the entire corpus. Treat "1,676" as "~35 parent lineages plus 1,641 nearby variants." If tomorrow's regime shifts against those 35 lineages, essentially nothing in the survivor pool remains.

41.5% of survivors have fewer than 30 validation trades. The minimum passed n is 8. Validation confidence intervals on 8-trade samples are wide. We use cross-coin + regime + holdout gates to compensate for low n, but a reader should treat low-n survivors as weaker evidence than the raw count implies. Rationale for the low bar is in the fail-fast asymmetry above.

Deflated Sharpe under-corrects by an unknown factor. DSR assumes IID trials with a known N. Adaptive search where children descend from parents that already passed is not IID. Effective N is probably 10-100× the naive count and there is no defensible estimator. This is our largest known methodological weakness.

The corpus is 96 days of one specific 2026 crypto regime. Rolling multi-year walk-forward does not exist yet. Everything below "1,676 survivors" is conditional on that window's market state.

Gate thresholds have been retroactively wrong twice this year. The May 2 audit killed 549 previously-approved strategies. The May 15 audit killed 128 more. Both times we found a leak in one of our own gates. There is no reason to think the current gate set is bug-free. The 1,676 count could drop next audit.

The v2_forge phantom-portfolio number is not reliable. On 2026-07-24 we caught an accounting bug in the phantom-portfolio layer: zombie positions were being force-closed at pnl=0 and stuffed into the stats block as losses. Every v2_forge P&L number in older posts is suspect. We're not publishing v2_forge P&L in this post because we can't reconstruct the correct number without a full-history replay that hasn't been built.

Our own live-trading watchdog missed a $53 real-money loss for six weeks because the peak/drawdown counter in the state file was broken. Disclosed on /proof. See the companion post on that engineering failure.

How to falsify this

If a reader finds a real error, please tell me. If the next gate audit kills more than 20% of the current 1,676 survivors, I'll publish a retraction with the same visibility as this post got.

The kill log is at /research. The methodology page is /methodology. A sanitized full-CSV of the corpus (fail_reason taxonomy + gate-level detail) will be published to Kaggle when the export is ready — the current lab-only export has open reconciliation gaps we do not want to ship until they are closed.

Why publish this at all

The retail crypto strategy market has an incentive structure where publishing winners is profitable and publishing losers is not. As a result, the volume of public crypto backtests is heavily biased toward strategies that either work or look like they work. The full population of tested strategies is invisible to anyone outside the tester.

This corpus is one attempt to publish a slice of that invisible population, with the specific gate that killed each strategy, on a permissive license. 93,265 tested. 91,589 killed. 1,676 passed. ~35 real underlying discoveries. 568 untagged. One real-money loss.

If you want to run your own strategy through the same six gates, /prove takes plain-English descriptions and returns a verdict in about 90 seconds using the same engine. There's a good chance yours ends up tagged cross_coin_overfit or signal_density_too_low. Now you know why.


Comments and pushback at support@stratproof.com or @stratproof on X. If you find a bug in the gates or the taxonomy, please tell me and I'll write about it next.

Test this on your own strategy

Have a strategy in mind? Run it through the same engine.

Paste a description, tweet, or rule set. We test it on 3 years of real Binance data with honest fees and walk-forward validation. Free, no signup.