From 18abff85a38682bb1650ddd764f1a580345e3635 Mon Sep 17 00:00:00 2001 From: Harwinder Sekhon Date: Sat, 11 Jul 2026 10:22:33 -0400 Subject: [PATCH] XAU M15: adopt 1R minimum targets (per-timeframe, validated) Finishes PENDING_STEPS Step 1 under the current trailing-exit model. M15 window 2025-03-30->2026-05-31 ($0.30 spread): +116.6R/460 PF 1.83 vs +105.4R/424 PF 1.82 baseline, better in both halves; 2024-06-> 2025-03 OOS +30.3R vs +10.6R control. Same knob on the pinned M5 book is WORSE (+131.3R/699 vs +139.9R/547) so the override is M15-only in the xauusd factory, per the pre-registered decision rule. M5 path unchanged (factory override gated on primary_tf == M15). --- pair_signals/xauusd.py | 19 ++++++++++++++++--- test_all.py | 4 ++++ 2 files changed, 20 insertions(+), 3 deletions(-) diff --git a/pair_signals/xauusd.py b/pair_signals/xauusd.py index b5c3e72..ed40d2a 100644 --- a/pair_signals/xauusd.py +++ b/pair_signals/xauusd.py @@ -1,9 +1,19 @@ """XAU/USD signal producer config. PlatformConfig defaults ARE the tuned XAU/USD configuration, so this -factory only sets the timeframes. It must stay equivalent to the inline -PlatformConfig(symbol=XAUUSD, ...) construction used by the backtester -and Telegram bot (enforced by a test in test_all.py). +factory sets the timeframes plus documented per-timeframe overrides. +It must stay equivalent to the inline PlatformConfig(symbol=XAUUSD, +...) construction (plus those overrides) used by the backtester and +Telegram bot (enforced by a test in test_all.py). + +M15 override (validated 2026-07-11, window 2025-03-30 -> 2026-05-31, +$0.30 spread, trailing exits): tp_floor_rr 1.0 scores +116.6R/460 +trades PF 1.83 vs +105.4R/424 PF 1.82 at the 0.5 default — better in +both train/val halves and on the 2024-06 -> 2025-03 out-of-sample +window (+30.3R vs +10.6R; the 1R floor lifts targets past the min-TP +spread gate, roughly doubling trades there). The same knob HURTS M5 +(pinned 12mo book: +131.3R/699 vs +139.9R/547, PF 1.55 vs 1.78), so it +is per-timeframe, exactly as PENDING_STEPS' decision rule anticipated. """ from typing import Optional, List @@ -26,4 +36,7 @@ def make_config( config.confirmation_timeframes = ( confirmation_tfs or _CONFIRMATIONS.get(primary_tf, [Timeframe.M15, Timeframe.H4]) ) + if primary_tf == Timeframe.M15: + # 1R minimum target on M15 only — see module docstring. + config.signals.tp_floor_rr = 1.0 return config diff --git a/test_all.py b/test_all.py index 0718da7..775f66a 100644 --- a/test_all.py +++ b/test_all.py @@ -1844,6 +1844,10 @@ def _(): primary_timeframe=tf, confirmation_timeframes=ctfs, ) + if tf == Timeframe.M15: + # Documented per-TF override (see xauusd.py docstring): + # 1R minimum targets validated for M15 only. + inline_cfg.signals.tp_floor_rr = 1.0 assert dataclasses.asdict(factory_cfg) == dataclasses.asdict(inline_cfg), \ f"XAU factory config differs from inline construction for {tf}"