The Strategy That Did Not Exist
We spent a week building a crypto strategy that did not exist.
The setup was a range fade. Take the 00:00 to 04:00 UTC range on BTC, ETH and SOL, and fade the break of it. Hourly bars, four years of data, 2022 through 2026.
The results were the kind you hope for. Out-of-sample R per trade came back at +0.33 on BTC, +0.32 on ETH, +0.45 on SOL. Win rates in the low sixties. Profitable in every single year of the sample. And the number that sold it: correlation with the other strategy in the book was roughly zero. Uncorrelated positive expectancy is the thing every systematic trader is actually hunting, and here it was.
It got far enough that we modelled what it would do to a prop challenge. On those numbers, a $100k combine stops being a coin flip.
The real numbers were negative. Minus $19 a trade on BTC, minus $3 on ETH, roughly zero on SOL. The actual win rate was 32 to 38 percent, not 61 to 66.
The part that stayed with us
The trade selection was correct. Against an independent implementation, the system found 886, 889 and 888 trades per symbol, and those counts matched exactly. The signal logic did precisely what it was designed to do. Every trade was identified properly, and then valued wrongly. The entire error lived in the P&L and exit accounting.
Which means every robustness check we ran was reading from the same broken number.
The out-of-sample split passed, on wrong P&L. Year-by-year consistency passed, on wrong P&L. The cross-strategy correlation passed, on wrong P&L. And the trade count reconciliation passed and was genuinely correct, which is the trap: one real, independent, correct check made the other three feel corroborated when they were all views of a single corrupted figure.
Robustness checks validate the pattern, not the arithmetic
This is the sentence we had not seen stated plainly anywhere, and the absence cost a week: out-of-sample splits, walk-forwards, Monte Carlo runs and consistency tables all validate the pattern in your results. None of them validate the arithmetic that produced the results. If your backtest shares code with your P&L calculation, every downstream check inherits the same possible bug, and stacking more of them adds confidence without adding information.
The bug was not caught by a smarter test. It was caught by rebuilding the P&L from scratch, separately, and getting a different answer. A clean re-implementation and an adversarial review pass landed on the same corrected number independently.
The ten-minute check that is not downstream
The rule now, before any result gets trusted: reconcile the engine's total P&L against a naive calculation done completely outside the engine. Sum of exit minus entry, times size, times direction. If those two numbers disagree, stop, because nothing downstream of the disagreement matters.
It takes about ten minutes. It is the only check in the entire stack that is not downstream of the thing being checked.
One footnote worth including because it is honest: once the accounting was fixed, both fading and following the range break lost money. There was no edge on either side of this idea. That is its own small lesson about why the broken version felt so good.
For informational purposes only. Past performance is not indicative of future results. Not financial advice.