xauusd factory: block M5 shorts in transitioning regime, block all H1 shorts (rule-1 validated)

M5 sell_blocked_regimes=[transitioning]: 12mo +7.9R (PF 1.50->1.56, both halves up),
5yr +5.3R, OOS unchanged. H1 sell_min_quality=[]: 5yr +16.7R (PF 1.18->1.46),
OOS +11.5R (PF 1.13->1.74), 12mo -1.8R with both halves positive.
Campaign table, rejected and parked candidates: backtest/research/signal_log_campaign_2026-09.md

Claude-Session: https://claude.ai/code/session_01FC4s6VCURFte4dub4xKxYL
This commit is contained in:
Harwinder Sekhon
2026-09-03 20:47:33 -04:00
parent b1e880be4f
commit 9a8a2c1aa1
4 changed files with 236 additions and 2 deletions
+52
View File
@@ -141,6 +141,49 @@ validated price of riding trends, exits confirmed NOT the loss
driver: exit-management accounts for 1-2% of lost R vs 50%
wrong-direction / 49% entry-timing).
M5 override 6 — SELL block in transitioning regime (validated
2026-09-03, signal-log campaign, `backtest/research/
signal_log_campaign_2026-09.md`, $0.65 round trip, same CPU harness
as its control): sell_blocked_regimes=["transitioning"]. Live
evidence: M5 SELLs in transitioning were -7.9R/12mo PF 0.21 and
-5.2R/5yr PF 0.49 in the pinned books; the live signal log showed the
same pocket -7.9R/PF 0.56 over 3 weeks. Blocking: 12mo (2025-08-14 ->
2026-08-14) 557 trades +112.21R PF 1.56 (halves +55.9/+56.4, PF
1.54/1.58) vs control 575/+104.34R PF 1.50; 5yr (2021-08-01 ->
2026-08-04) 889/+133.7R PF 1.41 vs 911/+128.4R PF 1.39; OOS
2024-06-01 -> 2025-03-30 unchanged (86/+9.45R PF 1.33 — no such
trades in that window). Removed trades: 18 in 12mo (-7.9R), 22 in
5yr (-5.3R).
H1 override — all SELL entries blocked (validated 2026-09-03, same
campaign): sell_min_quality=[] (gate 6e in signal_aggregator.py
downgrades any actionable-grade SELL whose quality is not in this
list to D, so an empty list downgrades every H1 short). H1 shorts
were a structural loser: -16.7R over the 5yr control window and
-11.5R OOS. Blocking: 12mo (2025-08-04 -> 2026-08-04) 91/+22.2R
PF 1.79 (halves +20.6/+1.6) vs control 138/+24.0R PF 1.55 (12mo cost
-1.8R); 5yr 335/+49.1R PF 1.46 vs 515/+32.4R PF 1.18; OOS 78/+16.7R
PF 1.74 vs 104/+5.1R PF 1.13. New reference books:
backtest/oanda_h1_{12mo,5yr,oos}_065_20260903.csv,
backtest/oanda_m5_12mo_065_nopb_20260903.csv,
backtest/oanda_m5_5yr_065_nopb_20260903.csv.
Rejected/parked in the same campaign (see the memo for the full
table): M15 sell_min_quality=["B"] (5yr -8.9R, max DD 35.8 -> 41.4R
— REJECTED); Bollinger weight 0 in trending regimes on M5, M15, H1
(M5 -20.4R/12mo, -15.5R/5yr; M15 -24.0R/12mo — REJECTED across the
board); divergence disabled on M15 (R improves +7.4/+11.9/+13.5R
across windows but 5yr max DD 35.8 -> 47.4R with PF flat — DECLINED,
owner call); divergence disabled on H1 (+4.6/+8.1/+2.4R, PF
1.62/1.20/1.17) — PARKED: it passes rule 1 in combination with the
H1 no-short override (b12_h1_nosell_nodiv: 12mo 100/+23.1R PF 1.75,
halves +21.8/+1.3; 5yr 364/+58.7R PF 1.52; OOS 88/+21.9R PF 1.92 —
better than the no-shorts override alone on both 5yr and OOS), but
shipping it would route through technical.disabled_indicators, which
CLAUDE.md rule 8 reserves for experiments; owner decision, recommended
vehicle is an H1-only regime_weights_override with divergence weight
0 plus a fresh validation run.
M5 override 5 — EMA partial-alignment squash (validated 2026-08-04,
Oanda bars, $0.65 real spread): ema_partial_neutral True on M5 —
partial 4-EMA ribbon alignment (2-3 of 4 checks) scores NEUTRAL
@@ -216,6 +259,9 @@ def make_config(
# Wider trailing distance on M5 only — see module docstring.
config.signals.trail_arm_r = 0.60
config.signals.trail_dist_r = 0.85
# Block SELL entries while the regime is "transitioning" on M5
# only — see module docstring (M5 override 6).
config.xau_strategy.sell_blocked_regimes = ["transitioning"]
if primary_tf == Timeframe.M15:
# 1R minimum target on M15 only — see module docstring.
config.signals.tp_floor_rr = 1.0
@@ -226,4 +272,10 @@ def make_config(
# Later trailing-stop arm on M15 only — see module docstring.
config.signals.trail_arm_r = 0.70
config.signals.trail_dist_r = 0.75
if primary_tf == Timeframe.H1:
# Block every H1 SELL — the SELL quality gate (6e in
# signal_aggregator.py) downgrades any actionable-grade short
# to D when its grade is not in this list; an empty list means
# no grade qualifies. H1-only — see module docstring.
config.xau_strategy.sell_min_quality = []
return config