From 35e9b3750da8122e23501ecefb9310ed7c735cb1 Mon Sep 17 00:00:00 2001 From: harwinder Date: Fri, 15 May 2026 19:46:31 -0400 Subject: [PATCH] =?UTF-8?q?Reduce=20A-grade=20TP=20when=20exhausted:=20RR?= =?UTF-8?q?=200.75=E2=86=920.50=20on=202+=20exhaustion=20tags?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit A-grade trades entered exhausted moves with TP too far (0.75R), causing 57.7% WR and +0.25R over 30 days. Store exhaustion_count on SignalReport and scale TP to B-level (1.0×) when A-grade has 2+ exhaustion tags. Backtest: A-grade 73.1% WR / +3.25R, B/C unchanged. --- signal_aggregator.py | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/signal_aggregator.py b/signal_aggregator.py index 3aea209..6a71ed7 100755 --- a/signal_aggregator.py +++ b/signal_aggregator.py @@ -74,6 +74,7 @@ class SignalReport: spread_estimate: float = 0.0 market_regime: str = "" volatility_regime: str = "" + exhaustion_count: int = 0 class SignalAggregator: @@ -547,6 +548,8 @@ class SignalAggregator: is_buy = report.final_score >= 0 quality_scale = self._TP_QUALITY_SCALE.get(report.signal_quality, 0.75) + if report.signal_quality == "A" and report.exhaustion_count >= 2: + quality_scale = 1.0 tp_mult = cfg.tp_atr_multiplier * quality_scale if is_buy: @@ -732,6 +735,7 @@ class SignalAggregator: # price is extended AND momentum indicators confirm deceleration. _EXHAUSTION_TAGS = {"exhaustion", "no_pullback", "rsi_extended", "bb_overextended"} exhaustion_count = sum(1 for name, _ in penalties if name in _EXHAUSTION_TAGS) + report.exhaustion_count = exhaustion_count momentum_fading = False if primary is not None and exhaustion_count >= 2: roc_ind = primary.indicators.get("roc")