From de6424ec3f14b9f8e2a2de605e4fe52df590054f Mon Sep 17 00:00:00 2001 From: Harwinder Sekhon Date: Thu, 16 Jul 2026 16:08:13 -0400 Subject: [PATCH] =?UTF-8?q?Enable=20BUY-only=20chart=20patterns=20on=20M5?= =?UTF-8?q?=20=E2=80=94=20bearish=20patterns=20dropped=20(OOS=20negative)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit M5 factory: cp_enabled=True, patterns limited to inv_hs, double_bottom, bull_flag. SELL CP lost -3.21R on OOS; BUY CP: +41.8R PF 1.60 on 12mo, +65.2R PF 1.48 on 5yr. Adds +22R to the M5 book (708 vs 513 trades). M15/H1 remain disabled — all failed OOS validation. Co-Authored-By: Claude Opus 4.6 Claude-Session: https://claude.ai/code/session_015kby7M9JwpYi8NQT2yoxAi --- CLAUDE.md | 4 ++-- pair_signals/xauusd.py | 12 ++++++++++++ test_all.py | 13 +++++++++---- 3 files changed, 23 insertions(+), 6 deletions(-) diff --git a/CLAUDE.md b/CLAUDE.md index fdd8d5a..cdd76a3 100644 --- a/CLAUDE.md +++ b/CLAUDE.md @@ -100,7 +100,7 @@ Tracked currencies `["USD","XAU"]`; pre-event buffer 30 min, post-event 15 min ( | `sell_blocked_hours_utc` | [] | bar-CLOSE UTC hours where SELLs are blocked (BUYs unaffected). Per-TF factory opt-in: M15 sets [15, 17] (NY-session shorts, −33.5R/165 over 5 yr, negative 6/6 years both halves; BUYs there +34.3R) | | M5 gates | | `m5_require_mtf=True`; `m5_min_abs_score=30`; `m5_min_confirmation_score=10`; `m5_sell_max_quality=["B","C"]` (A-grade M5 SELLs enter at dip extremes and lose); `m5_blocked_vol_regimes=["high"]` (chop transition — 61% WR, negative total); `m5_sell_min_rsi=25` | | M5 pullback (XAU_PB) | | `m5_pullback_enabled=True`; min score 15 (lower than gate 1 BY DESIGN — mid-pullback scores weaker); ADX ≥ 20; RSI zones BUY (35,62) / SELL (38,65); retrace depth 0.8–6.0 ATR from the 40-bar swing extreme; band touch within 3 bars. Validated +38.3R/254 trades on 12mo | -| Chart patterns (XAU_CP) | `cp_enabled=False` | `cp_*` fields: pattern list, lookback 72, swing window 3, recency 20, double tolerance 0.5 ATR, height 1.5–8 ATR, H&S head ≥0.5 ATR / shoulder tol 1.0 ATR, flag pole ≥3 ATR in ≤12 bars, flag 3–12 bars retracing 10–50%. Textbook defaults, NOT tuned. **Default OFF** — enable per-TF in the factory only with validation evidence | +| Chart patterns (XAU_CP) | `cp_enabled=False` | `cp_*` fields: pattern list, lookback 72, swing window 3, recency 20, double tolerance 0.5 ATR, height 1.5–8 ATR, H&S head ≥0.5 ATR / shoulder tol 1.0 ATR, flag pole ≥3 ATR in ≤12 bars, flag 3–12 bars retracing 10–50%. Textbook defaults, NOT tuned. **Default OFF** — M5 factory enables BUY-only (`inv_hs`, `double_bottom`, `bull_flag`); M15/H1/H4 remain disabled (all failed OOS validation 2026-07-16). Bearish patterns (`hs_top`, `double_top`, `bear_flag`) dropped: SELL CP lost −3.21R on OOS, −4.52R on M15 5yr | Legacy fields kept for config-shape compatibility: `rsi_entry_threshold`, `min_ema200_tfs`, `allow_generic_trades`, `buy_threshold_boost`, `m5_confirmation_bars`, `h4_rsi_gate=False`, `h1_macd_gate=False`, `rsi_zone` — not wired to current gates. @@ -252,7 +252,7 @@ Two entry points: Default confirmation map (same in the factory, the bot, and backtest usage): M5→[M15,H1], M15→[M5,H1], H1→[M15,H4], H4→[H1,D1]. (Exception: `main.py`'s CLI map uses M5→[M1,M15] — a CLI-only quirk.) **xauusd.py**: `PlatformConfig()` defaults + per-TF overrides (each validated; full evidence in the module docstring): -- M5: append hour 9 UTC to blocked hours (6-yr replay: +207.1R vs +178.3R; M15-and-up keep hour 9 — it's positive there). +- M5: append hour 9 UTC to blocked hours (6-yr replay: +207.1R vs +178.3R; M15-and-up keep hour 9 — it's positive there). Enable BUY-only chart patterns (`cp_enabled=True`, `cp_patterns=["inv_hs","double_bottom","bull_flag"]`): 12mo +41.8R PF 1.60 (train/val both positive), 5yr +65.2R PF 1.48; bearish CP dropped (OOS −3.21R). M15/H1 CP remain disabled (all failed OOS). - M15: `signals.tp_floor_rr = 1.0` (1R minimum targets: +116.6R vs +105.4R, better in train/val and 2024 OOS; **hurts M5** — per-TF only) and `xau_strategy.blocked_regimes = ["trending"]` (mixed regime block: +65.7R vs +59.0R). A transitioning-SELL block was tested and REJECTED (bull-market artifact — failed 2024 OOS). - M15: `xau_strategy.sell_blocked_hours_utc = [15, 17]` (SELL-only hour block, validated 2026-07-16 on the 5-yr replay: +176.7R PF 1.31 vs +144.9R PF 1.22, better in all 6 years, both 12-mo halves, and 2024-06→2025-03 OOS; M5 cross-TF pocket confirms; BUYs at those hours are +34.3R so `blocked_hours_utc` would be wrong). - The factory must stay equivalent to inline `PlatformConfig(symbol=XAUUSD, …)` + overrides — **enforced by a test in test_all.py**. diff --git a/pair_signals/xauusd.py b/pair_signals/xauusd.py index 7ea1c7a..c911232 100644 --- a/pair_signals/xauusd.py +++ b/pair_signals/xauusd.py @@ -47,6 +47,15 @@ SELL-only rather than a blocked_hours_utc entry. Mechanism: NY-session shorts against gold's macro uptrend (15:00 = post-US-data reversal window, 17:00 = London close unwind). +M5 override 2 (2026-07-16, chart patterns): enable BUY-only CP on M5 +(inv_hs, double_bottom, bull_flag — drop all bearish patterns). SELL CP +is consistently negative (OOS -3.21R, 5yr +3.76R PF 1.07) while BUY CP +is positive across windows: 12mo +41.8R PF 1.60 (train +9.66 PF 1.30, +val +32.17 PF 1.86), 5yr +65.2R PF 1.48, OOS +1.20R (aggregate +positive, val half -1.89R — marginal but sign-correct in aggregate). +Gold's structural uptrend makes bullish patterns fundamentally more +reliable. M15/H1 fail on all windows; M5-only enablement. + M5 override (validated 2026-07-11, full-history replay 2020-03-17 -> 2026-07-11 + 12mo book, $0.30 spread): block hour 9 UTC (post-London- open whipsaw). Hour-9 entries lost -28.9R/143 over 6 years, negative @@ -83,6 +92,9 @@ def make_config( config.xau_strategy.blocked_hours_utc = ( config.xau_strategy.blocked_hours_utc + [9] ) + # BUY-only chart patterns on M5 — see module docstring. + config.xau_strategy.cp_enabled = True + config.xau_strategy.cp_patterns = ["inv_hs", "double_bottom", "bull_flag"] if primary_tf == Timeframe.M15: # 1R minimum target on M15 only — see module docstring. config.signals.tp_floor_rr = 1.0 diff --git a/test_all.py b/test_all.py index 2a917fa..3e09260 100644 --- a/test_all.py +++ b/test_all.py @@ -1841,11 +1841,13 @@ def _(): inline_cfg.xau_strategy.blocked_regimes = ["trending"] inline_cfg.xau_strategy.sell_blocked_hours_utc = [15, 17] if tf == Timeframe.M5: - # Documented per-TF override (see xauusd.py docstring): - # hour 9 UTC blocked on M5 only. + # Documented per-TF overrides (see xauusd.py docstring): + # hour 9 UTC blocked + BUY-only chart patterns on M5 only. inline_cfg.xau_strategy.blocked_hours_utc = ( inline_cfg.xau_strategy.blocked_hours_utc + [9] ) + inline_cfg.xau_strategy.cp_enabled = True + inline_cfg.xau_strategy.cp_patterns = ["inv_hs", "double_bottom", "bull_flag"] assert dataclasses.asdict(factory_cfg) == dataclasses.asdict(inline_cfg), \ f"XAU factory config differs from inline construction for {tf}" @@ -2439,13 +2441,16 @@ def _(): assert rep.trade_setup.strategy == "XAU_CP" -@test("CP pipeline: disabled by default — no XAU_CP setups anywhere") +@test("CP pipeline: disabled by default except M5 BUY-only") def _(): assert XauStrategyConfig().cp_enabled is False assert PlatformConfig().xau_strategy.cp_enabled is False from pair_signals import PAIR_PRODUCERS - for tf in (Timeframe.M5, Timeframe.M15, Timeframe.H1, Timeframe.H4): + for tf in (Timeframe.M15, Timeframe.H1, Timeframe.H4): assert PAIR_PRODUCERS[XAUUSD](tf, None).xau_strategy.cp_enabled is False + m5_cfg = PAIR_PRODUCERS[XAUUSD](Timeframe.M5, None) + assert m5_cfg.xau_strategy.cp_enabled is True + assert m5_cfg.xau_strategy.cp_patterns == ["inv_hs", "double_bottom", "bull_flag"] cfg = PlatformConfig() cfg.primary_timeframe = Timeframe.H1 cfg.confirmation_timeframes = [Timeframe.M15, Timeframe.H4]