Add M5 pullback entry archetype (XAU_PB): +140R combined on 12mo

Second entry pattern alongside the Keltner-breakout: enter on the
retrace to EMA21/KC-mid in an established trend (EMA >=3/4 aligned,
ADX>=20, retrace 0.8-6.0 ATR, resumption bar closed back in trend
direction, RSI in the pullback zone not the extended zone). Detector
lives in the aggregator so live and GPU-backtest paths share one
implementation. Own gates (composite floor 15, confirmation-TF
agreement, MTF opposition, vol regimes) with a diagnostic funnel.

PB stops anchor to the pullback swing (~$8 vs ~$25 breakout), so PB
trades get their own trail arm (0.8R vs 0.6R global) via per-trade
trail overrides in the backtester and strategy-aware Telegram alerts.

12mo M5 validation: PB alone +38.3R/254 trades, positive both halves
and all four full quarters; core breakout trades undisturbed
(+101.9R vs +105.0 baseline). Combined book: 549 trades (~10.6/wk),
+140.1R, PF 1.78, DD 11.3R vs 311 trades/+105.0R before.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
This commit is contained in:
harwinder
2026-07-09 14:21:59 -04:00
parent 3ac36525b0
commit dad2432d4d
5 changed files with 270 additions and 21 deletions
+12 -7
View File
@@ -255,18 +255,23 @@ def format_signal_message(report: SignalReport) -> str:
lines.append(f"⚖️ RR: 1:{ts.risk_reward:.1f}")
# Trailing-stop management plan (matches backtested exit model:
# TP1 is advisory, the trail is the exit).
# TP1 is advisory, the trail is the exit). Pullback setups run
# tighter stops and arm later.
sig_cfg = PlatformConfig().signals
if sig_cfg.trail_arm_r > 0:
is_pb = getattr(ts, "strategy", "GENERIC") == "XAU_PB"
arm_r = sig_cfg.pb_trail_arm_r if is_pb else sig_cfg.trail_arm_r
dist_r = sig_cfg.pb_trail_dist_r if is_pb else sig_cfg.trail_dist_r
if arm_r > 0:
risk_dist = abs(ts.entry_price - ts.stop_loss)
if ts.direction == "BUY":
arm_price = ts.entry_price + sig_cfg.trail_arm_r * risk_dist
arm_price = ts.entry_price + arm_r * risk_dist
else:
arm_price = ts.entry_price - sig_cfg.trail_arm_r * risk_dist
trail_dist = sig_cfg.trail_dist_r * risk_dist
arm_price = ts.entry_price - arm_r * risk_dist
trail_dist = dist_r * risk_dist
tag = " (pullback)" if is_pb else ""
lines.append(
f"🔄 Trail: once price hits `{_fmt_price(arm_price)}` "
f"(+{sig_cfg.trail_arm_r:.1f}R), trail SL ${trail_dist:.2f} "
f"🔄 Trail{tag}: once price hits `{_fmt_price(arm_price)}` "
f"(+{arm_r:.1f}R), trail SL ${trail_dist:.2f} "
f"behind best price — TP1 is a guide, let it run"
)