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_n9D_mfi_14_25_cci_20_-80_tp1.75_b150 → clear_loser_ci90_-1.43_n8E_A_rsi_7_below_25_b150_tp4_b800_adx35 → clear_loser_ci90_-4.26_n8520 strategies retired 2026-05-17 → config_signature_dupe_2026_05_17128 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.