diff --git a/CLAUDE.md b/CLAUDE.md index ce11831..041247a 100644 --- a/CLAUDE.md +++ b/CLAUDE.md @@ -80,7 +80,7 @@ EMA 9/21/50/200 (`ema_mode`: `"4ema"` default or `"2ema"` = 50/200 only); RSI 14 | `min_tp_cost_multiple` | 30.0 | spread gate: TP1 distance must be ≥ cost × this (= $9 on XAU). Validated: sub-$10 targets lose | | `min_sl_pips` | 80.0 | SL floor in pips ($8 on XAU) | | `tp_floor_rr` | 0.5 | TP1 floor as fraction of SL distance (M15 factory overrides to 1.0) | -| `trail_arm_r` / `trail_dist_r` | 0.6 / 0.75 | trailing exit: once favorable excursion ≥ arm×risk, stop trails dist×risk behind best price (bar-close updates). TP1 becomes advisory. Validated on 12mo M5: +105R vs +48R static. 0 disables. **M15 factory overrides arm to 0.70** (2026-07-21 grid: +210.7R vs +175.7R/5yr, OOS +57.4 vs +45.4; DD clause waived by owner — see factory docstring) | +| `trail_arm_r` / `trail_dist_r` | 0.6 / 0.75 | trailing exit: once favorable excursion ≥ arm×risk, stop trails dist×risk behind best price (bar-close updates). TP1 becomes advisory. Validated on 12mo M5: +105R vs +48R static. 0 disables. **M15 factory overrides arm to 0.70** (2026-07-21 grid: +210.7R vs +175.7R/5yr, OOS +57.4 vs +45.4; DD clause waived by owner); **M5 factory overrides dist to 0.85** (same-day grid: +231.2R vs +218.0R/5yr, OOS +14.5 vs +11.1, DD flat; dist 1.0 rejected at the OOS gate — see factory docstring) | | `pb_trail_arm_r` / `pb_trail_dist_r` | 0.8 / 0.75 | trailing params for XAU_PB pullback trades (tighter stops → arm later) | ### NewsConfig @@ -254,6 +254,7 @@ Default confirmation map (same in the factory, the bot, and backtest usage): M5 **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). 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). +- M5: `signals.trail_dist_r = 0.85` (wider trail leash, arm stays 0.60; validated 2026-07-21 5-yr grid: +231.2R PF 1.30 vs +218.0R PF 1.29, halves and OOS better, DD flat; dist 1.0 had the best in-sample total but FAILED the OOS gate. XAU_PB keeps pb_trail 0.8/0.75; opposite finding vs M15's later-arm — per-TF divergence again). - 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). - M15: `signals.trail_arm_r = 0.70` (trail arms later; validated 2026-07-21, 5-yr grid arm 0.3-0.9 × dist 0.5-1.0: +210.7R PF 1.35 WR 57.8% vs +175.7R PF 1.30 WR 53.9% at the 0.6 default; both halves and OOS better; 5yr max-DD clause 33.9R vs 29.1R waived by owner. Earlier-arm cells uniformly worse. M5 keeps 0.6). diff --git a/pair_signals/xauusd.py b/pair_signals/xauusd.py index 15f1f73..b575426 100644 --- a/pair_signals/xauusd.py +++ b/pair_signals/xauusd.py @@ -56,6 +56,24 @@ 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 3 (validated 2026-07-21, 5-year replay 2021-07-21 -> +2026-07-21, $0.30 spread, next-open fills): trailing distance widened +to 0.85R (trail_dist_r 0.85, arm unchanged 0.60). Same 30-cell grid +methodology as the M15 campaign, opposite finding — on M5 the arm +point barely matters; the winners need a looser leash. Plateau arm +0.5-0.7 x dist 0.85-1.0 (+227..+234R vs +218.0R baseline). Chosen +0.60/0.85 (OOS-preferred end of the plateau): +231.2R/2028 PF 1.30 WR +57.1% vs +218.0R PF 1.29 WR 58.3%; both last-12mo halves better +(+56.8/+115.1 vs +54.0/+104.6); 5yr max DD 19.2R vs 19.1R (noise); +OOS 2024-06-01 -> 2025-03-30 +14.5R DD 15.1 vs +11.1R DD 15.4. +dist 1.0 was REJECTED at the OOS gate (+10.9R < baseline +11.1R) +despite the best in-sample total (+233.3R) — the OOS ordering picks +the conservative plateau edge. XAU_PB trades are unaffected (they +carry their own validated pb_trail 0.8/0.75 per-trade overrides); +XAU_CP trades share the global params and were validated inside this +book. M15 arms at 0.70 (its own campaign, same day); the divergence +is the per-TF pattern yet again. + M15 override 4 (validated 2026-07-21, 5-year replay 2021-07-21 -> 2026-07-21, $0.30 spread, next-open fills): trailing-stop arm raised to 0.70R (trail_arm_r 0.70, dist unchanged 0.75). Full grid arm 0.3-0.9 x @@ -115,6 +133,9 @@ def make_config( # 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"] + # Wider trailing distance on M5 only — see module docstring. + config.signals.trail_arm_r = 0.60 + config.signals.trail_dist_r = 0.85 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 714e053..503c623 100644 --- a/test_all.py +++ b/test_all.py @@ -1878,6 +1878,8 @@ def _(): ) inline_cfg.xau_strategy.cp_enabled = True inline_cfg.xau_strategy.cp_patterns = ["inv_hs", "double_bottom", "bull_flag"] + inline_cfg.signals.trail_arm_r = 0.60 + inline_cfg.signals.trail_dist_r = 0.85 assert dataclasses.asdict(factory_cfg) == dataclasses.asdict(inline_cfg), \ f"XAU factory config differs from inline construction for {tf}"