Fix M5 signal quality: ATR ordering bug, noise gates, blocked-hour leak (+41R)

ATR was computed after EMAs so ribbon compression detection was dead code
on all timeframes — moved ATR first. Extended M5 swing S/R lookback from
50 to 120 bars. Added four M5-specific filters: min score, MTF agreement,
confirmation TF conviction, and SELL quality cap (A-grade M5 shorts enter
at dip extremes and lose). Fixed backtester blocked-hour check to use bar
close time matching the live bot. Added trade geometry config (spread gate,
SL floor, TP floor RR) and primary_tf mismatch guard in backtester.

Backtest: 1133 trades/+11.5R → 419 trades/+41.0R, PF 1.03→1.37, DD 21→11R.
Train/val split positive in both halves (+19.8R/+21.3R).

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
This commit is contained in:
harwinder
2026-07-09 12:48:36 -04:00
parent d95d31a852
commit db16f209bb
4 changed files with 144 additions and 5 deletions
+6 -2
View File
@@ -85,11 +85,12 @@ class TechnicalEngine:
# ── Compute all indicators ─────────────────────────────────
disabled = set(self.config.disabled_indicators or [])
# ATR first: other indicators (EMA ribbon, candlestick) use it
if "atr" not in disabled: self._compute_atr(df, result)
if "ema_trend" not in disabled: self._compute_ema_signals(df, result)
if "rsi" not in disabled: self._compute_rsi(df, result)
if "bollinger" not in disabled: self._compute_bollinger(df, result)
if "adx" not in disabled: self._compute_adx(df, result)
if "atr" not in disabled: self._compute_atr(df, result)
self._compute_macd(df, result)
@@ -817,7 +818,10 @@ class TechnicalEngine:
def _compute_structure_sr(self, df: pd.DataFrame, result: TechnicalAnalysis):
"""Detect swing high/low structure levels for S/R and trade setup."""
lookback = min(self.config.swing_sr_lookback, len(df) - 1)
base_lookback = self.config.swing_sr_lookback
if result.timeframe in ("1min", "5min"):
base_lookback = max(base_lookback, 120)
lookback = min(base_lookback, len(df) - 1)
if lookback < 10:
return