Published
My pair-hotness report has a hard sample floor: MIN_N_PER_CONFIG = 20. Below 20 trades, a row gets stamped NEED_MORE_DATA and I don't trust the numbers enough to show a verdict at all. Above it, the row gets judged. I built that floor to stop single-digit samples from masquerading as edge. I didn't build it to catch what it actually caught this pass: two rows that cleared the floor by exactly zero trades to spare, and both came back with a win rate of absolute zero.
Two unrelated pairs, the same exact loss
KGENUSDT Β· range_bounce ran 20 trades off 262 raw signals. 1000LUNCUSDT Β· volume_reversal ran 20 trades off 190 raw signals. Different coin, different strategy family, different signal volume feeding in. Both come out the other side with a losing streak of 20 -- meaning every trade in the sample lost, back to back, no win anywhere to break the streak. Both land on net return of -9.2%. Not similar. Identical, to one decimal place.
| Pair / family | Trades | Win rate | PF | Net % | Streak | Verdict |
|---|---|---|---|---|---|---|
| KGENUSDT Β· range_bounce | 20 | -- | -- | -9.2% | 20 | NO_LIVE |
| 1000LUNCUSDT Β· volume_reversal | 20 | -- | -- | -9.2% | 20 | NO_LIVE |
| FHEUSDT Β· range_bounce | 24 | 20.8% | 0.452 | -4.79% | 7 | NO_LIVE |
That dash isn't a formatting choice. Win rate is wins divided by trades, and with zero wins in the numerator the function returns nothing worth printing, so my code falls back to the same placeholder it uses for a row that never got enough signals to compute anything. Scroll my full report and a true no-data row looks exactly like a 100%-loss row at a glance. The only tell is the trade count sitting at a real number instead of a dash of its own.
The strange part: zero wins isn't the worst number in the table
Here's what stopped me. FHEUSDT, right below those two in the same report, actually won some trades -- 20.8% of 24 of them -- and still lost more than twice as much per trade. KGENUSDT and 1000LUNCUSDT each averaged -0.46% net per trade across their 20-trade washout (-9.2% Γ· 20). FHEUSDT averaged -0.20% per trade in raw terms but still posted a worse profit factor (0.452) and a deeper proportional bleed once you account for its wins barely denting the losses. A dead-even string of small losses, with literally no wins to offset any of them, still cost less per trade than a strategy that won one time in five. That only works if whatever stop or exit logic is capping each individual loss tightly enough that twenty losers in a row still adds up to single-digit damage -- the entries were wrong 20/20 times, but the exits weren't letting any single one of those twenty run.
What I'm changing in the table
I'm adding a fourth state. Right now the verdict pipeline only knows LIVE_READY, NO_LIVE, and NEED_MORE_DATA. A row that clears the sample floor with a 0% win rate is a different failure mode than a row that clears it with a 35% win rate and a bad profit factor -- the first one means my entry signal is actively wrong on this pair/family combo, not just under-optimized. I'm tagging those ZERO_EDGE so they stop hiding behind the same dash as a row I simply haven't sampled yet. If you're running your own backtest pipeline and filtering on a sample floor, check whether your win-rate field can return zero and null the exact same way -- it's an easy bug to never notice, because both outcomes tell you "don't go live," just for completely different reasons. You can sanity-check your own per-trade loss size against a win rate of zero with the breakeven win-rate calculator -- plug in your stop and target and see what win rate you'd need just to breathe, let alone profit, when the entry itself isn't the problem you think it is.