The Illusion You Could Have Sold at the High
— MFE's Traps
MFE (peak favorable move) only tells you whether the price was touched — not that you could have actually sold there. That's not where the story ends: while measuring this number ourselves, we got it wrong three separate times. We publish all three, unfiltered.
MFE Means "Touched," Not "Sold"
MFE (Maximum Favorable Excursion) is the highest return reached within a set window after entry. It's well suited to checking whether a signal predicted a real move, but it never guarantees you could have actually sold at that peak. We covered that gap already in the post-surge pullback article.
This article goes one level deeper. It's not about the metric itself — it's about three times our own code measuring MFE was wrong. In all three cases the sample size and row counts looked completely normal, which is exactly why they took so long to find.
Mistake ① Window Mismatch — the Picks Got 3x More Time
Corrected 2026-08-13 · 249 matured picks recomputed · source: price_history
The universe baseline (population average) measured MFE over a 20-trading-day window. But for the picks our scanner selected, we used the highest price over the entire tracking period (up to 60 trading days). Two numbers with the same name were actually measured with rulers of different lengths — the picks got up to 3x more time to run.
| Metric | Before Fix | After Fix (True 20-Day Window) |
|---|---|---|
| Pick average MFE | 19.43% | 14.84% |
| Peaked after day 20 | 42.6% | |
| mfe_atr lift | +12.8pp | −1.5pp |
After the fix, the one positive lift that had survived on this entire site disappeared. We thought we were asking "how much does it move within 20 days," but in reality only the pick side kept getting credit for new highs made after day 20. The most sobering number here is that 42.6% of the sample didn't peak until after day 20 — with a 20-day window alone, close to half the cases have their answer cut off before it arrives.
Mistake ② Immortal-Time Bias — It Also Broke MFE's Significance
Corrected 2026-08-11 · 440 picks · source: track-service denominator recompute
The main story of this bug lives in the hit-rate denominator article (a 39.8%→30.7% correction, covered there in full) — this article's focus is the separate fact that the same bias also reached into the statistical significance of the MFE lift.
For picks that hadn't yet reached 20 trading days, the rule counted only the ones that had already hit their target into the denominator, while excluding the rest that hadn't hit it yet in the same period. Of 210 still-open picks, 40 early hitters were counted in and 170 non-hitters were left out — treating a "we don't know yet" state as if it were a permanently-favorable outcome, hence the name immortal time bias.
| Lift | Before | After | Verdict |
|---|---|---|---|
| mfe15 | +11.1pp | +2.9pp | CI lower 24.1 < expected 27.9 — lost significance |
| mfe_atr | +16.4pp | +12.6pp | CI lower 15.5 > expected 8.6 — held |
The number we'd called "this site's first positive result" before the fix turned out to be a product of the bias. The one claim that survives is narrow: the edge only holds on a volatility-adjusted basis (MFE ≥ 6×ATR). The more forgiving fixed +15% threshold (mfe15) is no longer significant after the fix, and we keep that fact next to the one that did survive.
Mistake ③ Missing Data Counted as "Miss" — a Row Count Can't Catch This
Corrected 2026-08-05 · 4,650 rows affected · source: crypto_signal_stats rebuild
Our crypto 168-hour label (fwd_ret_168h) was entirely NULL. Two causes
overlapped.
- A window boundary — the data-fetch API treated the window's end timestamp as exclusive, so the candle exactly at the 168-hour mark was never in the response. 24h and 72h fell inside the window and were fine; only 168h was always empty.
- Missing-data handling — the label function returned "miss" (False, counted as a failure) instead of "can't judge" (None, excluded from the denominator) whenever the value was absent.
What made this combination dangerous is that it didn't look empty — it quietly skewed downward. The row count (n_univ) matched the other, healthy labels exactly at 2,568. Only the base rate came out wrong: 0%, when the real figure was 19.2%.
What the Three Have in Common
In all three, the sample size and row counts were fine, and the arithmetic itself was correct. The problem was always the definition of what was actually being measured — one side of a window ran longer than the other (①), a denominator rule let future information leak in silently (②), or missing data got misclassified as failure (③). Arithmetic checks alone can't catch any of the three.