Accuracy audit fixes: alerts carry validated per-TF trail plans + 13 more

Five-agent audit of the signal path; all fixes are delivery/measurement
side. Regression-proven behavior-neutral on validated signals: the 5yr
M15 replay reproduces the pinned book exactly (+210.65R PF 1.35
WR 57.8% MaxDD 33.90R). Tests 215 -> 221.

- TradeSetup carries trail_arm_r/trail_dist_r stamped from the producing
  config; Telegram trail plan reads them (was showing default 0.6/0.75
  instead of the validated M15 arm 0.70 / M5 dist 0.85)
- Alert dedup: fingerprint = (signal, quality, direction); entry movement
  compared against last-ALERTED entry/risk (old SL-dist bucket churned on
  ATR drift -> near-duplicate alerts every candle)
- Fresh fetches enforce the closed-bar boundary check with one 3s grace
  retry, then warn (feed lag no longer silently analyzes the prior bar)
- Backtest max DD includes the zero baseline (opening losing streaks
  were undercounted; pinned M15 DD unchanged)
- Replay loads the regime-filter TF when the H4-ADX gate is on (was a
  silent no-op for M5/M15 primaries)
- clear_cache under _cache_lock; dead ROC zero-cross branches removed
  from both engines (provably unreachable); RSS dates via
  parsedate_to_datetime (named zones no longer stamped UTC); event risk
  grades from the soonest event; .env inline comments stripped;
  auto-scan skips NEUTRAL like the breach path; report_to_dict honors
  the weight-0 skip rule and emits strategy/trail params; PB funnel
  counts spread_gate/accepted; session-filter fail-open paths log loudly
- Docs corrected to match validated code (H&S neckline = lower peak,
  exhaustion anchors farthest tracked swing) with re-validation warnings

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01R5soRUeRBi8kHMB99csrJy
This commit is contained in:
Harwinder Sekhon
2026-07-23 10:31:13 -04:00
parent 91b4a6bb5e
commit 89734c3a87
11 changed files with 329 additions and 68 deletions
+13 -9
View File
@@ -851,15 +851,10 @@ class GPUTechnicalEngine(TechnicalEngine):
signal = SignalStrength.SELL
conf = min(70, 50 + roc_abs * 10)
desc = f"ROC {roc_val:.2f}% — Falling momentum"
elif roc_prev <= 0 < roc_val:
signal = SignalStrength.BUY
conf = 65
desc = f"ROC crossed above zero (+{roc_val:.2f}%)"
elif roc_prev >= 0 > roc_val:
signal = SignalStrength.SELL
conf = 65
desc = f"ROC crossed below zero ({roc_val:.2f}%)"
else:
# Zero-crosses land in the branches above by definition (a
# cross up is positive and rising, a cross down negative and
# falling) — only flat/fading momentum reaches here.
signal = SignalStrength.NEUTRAL
conf = 30
desc = f"ROC {roc_val:+.2f}% — No clear momentum"
@@ -1690,6 +1685,13 @@ def run_backtest(
# ── Load all required timeframes ──
timeframes = [bt_cfg.primary_tf] + list(bt_cfg.confirmation_tfs)
# Live parity: analyze_pair also fetches the regime-filter TF when the
# optional H4-ADX gate is enabled. Without it here, M5/M15 primaries
# (whose confirm maps lack H4) would make _apply_regime_filter_live
# silently no-op in the replay while live applies the gate.
flt = platform_cfg.backtest
if flt.regime_filter and flt.regime_filter_timeframe not in timeframes:
timeframes.append(flt.regime_filter_timeframe)
# Pad lookback so earliest TF has enough warmup for EMA200, ADX, etc.
pad_days = max(60, bt_cfg.warmup_bars // 4)
fetch_start = bt_cfg.start - timedelta(days=pad_days)
@@ -1879,7 +1881,9 @@ def compute_metrics(trades: List[Trade]) -> Metrics:
gross_loss = float(-rs[losses_mask].sum()) if losses_mask.any() else 0.0
pf = gross_profit / gross_loss if gross_loss > 0 else float("inf")
cum = np.cumsum(rs)
# Prepend the flat starting equity so an opening losing streak counts
# as drawdown from 0, not from the first trade's already-negative cum.
cum = np.concatenate(([0.0], np.cumsum(rs)))
high_water = np.maximum.accumulate(cum)
drawdown = high_water - cum
max_dd = float(drawdown.max()) if len(drawdown) else 0.0