Optimize EUR/USD M5 signals: 1:1 RR, vol/agreement filters, disable XAU

Tune EUR/USD M5 trade setup for balanced profitability (78% WR, 1.68 PF,
+26.5R over 365d) under a 4-hour max hold constraint. Key changes:
- Raise min RR from 0.5 to 1.0 and TP multiplier from 1.0x to 1.2x ATR
- Tighten SL floor (0.8→0.7 ATR) and swing lookback (50→20 bars)
- Add volatility regime filter (block "high" vol) and min 6 indicator
  agreement gate in signal_aggregator
- Raise BE trigger 0.2→0.3R, buffer 0.05→0.15R to let winners run
- Block weak hours (06, 09, 11 UTC) on M5
- Disable XAU strategy checks by default; XAU pair config re-enables

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
This commit is contained in:
harwinder
2026-05-21 19:12:58 -04:00
parent 9a2b1f7b1e
commit 25791fefa3
5 changed files with 38 additions and 11 deletions
+7 -5
View File
@@ -142,7 +142,7 @@ class XauStrategyConfig:
Legacy gates (H4 RSI, H1 MACD, M15 score) removed in favor of Legacy gates (H4 RSI, H1 MACD, M15 score) removed in favor of
new momentum indicators (ROC, Keltner squeeze, ADX slope). new momentum indicators (ROC, Keltner squeeze, ADX slope).
""" """
enabled: bool = True enabled: bool = False
rsi_entry_threshold: float = 45.0 rsi_entry_threshold: float = 45.0
tp_atr_multiplier: float = 1.0 tp_atr_multiplier: float = 1.0
sl_atr_multiplier: float = 2.0 sl_atr_multiplier: float = 2.0
@@ -172,10 +172,10 @@ class EurUsdStrategyConfig:
allowed_weekdays: List[int] = field(default_factory=lambda: [0, 1, 2, 3, 4]) allowed_weekdays: List[int] = field(default_factory=lambda: [0, 1, 2, 3, 4])
min_atr_threshold: float = 0.0003 min_atr_threshold: float = 0.0003
sl_atr_multiplier: float = 1.5 sl_atr_multiplier: float = 1.5
tp_atr_multiplier: float = 1.0 tp_atr_multiplier: float = 1.2
sl_atr_floor: float = 1.2 sl_atr_floor: float = 1.2
min_rr_ratio: float = 0.5 min_rr_ratio: float = 1.0
mature_move_threshold: float = 35.0 mature_move_threshold: float = 35.0
tp_quality_scale: Dict[str, float] = field(default_factory=lambda: { tp_quality_scale: Dict[str, float] = field(default_factory=lambda: {
"A+": 1.0, "A": 1.0, "B": 1.0, "C": 1.0, "A+": 1.0, "A": 1.0, "B": 1.0, "C": 1.0,
@@ -188,8 +188,10 @@ class EurUsdStrategyConfig:
min_entry_score: float = 38.0 min_entry_score: float = 38.0
min_entry_confidence: float = 60.0 min_entry_confidence: float = 60.0
block_rsi_extended: bool = True block_rsi_extended: bool = True
be_trigger_r: float = 0.2 blocked_vol_regimes: List[str] = field(default_factory=list)
be_buffer_r: float = 0.05 min_indicator_agreement: int = 0
be_trigger_r: float = 0.3
be_buffer_r: float = 0.15
@dataclass @dataclass
+5 -2
View File
@@ -77,12 +77,15 @@ def make_config(
if primary_tf == Timeframe.M5: if primary_tf == Timeframe.M5:
config.technical.regime_weights_override = M5_REGIME_WEIGHTS config.technical.regime_weights_override = M5_REGIME_WEIGHTS
config.eur_strategy.sl_atr_floor = 0.8 config.technical.swing_sr_lookback = 20
config.eur_strategy.sl_atr_floor = 0.7
config.eur_strategy.sl_atr_multiplier = 0.3 config.eur_strategy.sl_atr_multiplier = 0.3
config.eur_strategy.min_quality_grades = ["A+", "A", "B"] config.eur_strategy.min_quality_grades = ["A+", "A", "B"]
config.eur_strategy.min_entry_score = 35.0 config.eur_strategy.min_entry_score = 35.0
config.eur_strategy.min_entry_confidence = 50.0 config.eur_strategy.min_entry_confidence = 50.0
config.backtest.blocked_hours_utc = [22, 23, 0, 1, 2, 3, 4, 19, 21] config.eur_strategy.blocked_vol_regimes = ["high"]
config.eur_strategy.min_indicator_agreement = 6
config.backtest.blocked_hours_utc = [22, 23, 0, 1, 2, 3, 4, 6, 9, 11, 19, 21]
else: else:
config.technical.regime_weights_override = EUR_REGIME_WEIGHTS config.technical.regime_weights_override = EUR_REGIME_WEIGHTS
+1
View File
@@ -16,6 +16,7 @@ def make_config(
confirmation_tfs: Optional[List[Timeframe]] = None, confirmation_tfs: Optional[List[Timeframe]] = None,
) -> PlatformConfig: ) -> PlatformConfig:
config = PlatformConfig() config = PlatformConfig()
config.xau_strategy.enabled = True
config.primary_timeframe = primary_tf config.primary_timeframe = primary_tf
config.confirmation_timeframes = ( config.confirmation_timeframes = (
confirmation_tfs or _CONFIRMATIONS.get(primary_tf, [Timeframe.M15, Timeframe.H4]) confirmation_tfs or _CONFIRMATIONS.get(primary_tf, [Timeframe.M15, Timeframe.H4])
+21
View File
@@ -474,6 +474,27 @@ class SignalAggregator:
f"EUR CONF FILTER: {report.final_confidence} < {eur.min_entry_confidence}" f"EUR CONF FILTER: {report.final_confidence} < {eur.min_entry_confidence}"
) )
report.signal_quality = "F" report.signal_quality = "F"
if (report.signal_quality != "F"
and eur.blocked_vol_regimes
and report.volatility_regime):
if report.volatility_regime in eur.blocked_vol_regimes:
report.warnings.append(
f"EUR VOL FILTER: {report.volatility_regime} volatility blocked"
)
report.signal_quality = "F"
if (report.signal_quality != "F"
and eur.min_indicator_agreement > 0
and report.primary_analysis):
agree_count = sum(
1 for ind in report.primary_analysis.indicators.values()
if ind.signal.value != 0
and (ind.signal.value > 0) == (report.final_score > 0)
)
if agree_count < eur.min_indicator_agreement:
report.warnings.append(
f"EUR AGREE FILTER: {agree_count} < {eur.min_indicator_agreement}"
)
report.signal_quality = "F"
if (report.signal_quality != "F" if (report.signal_quality != "F"
and eur.block_rsi_extended and eur.block_rsi_extended
and report.primary_analysis): and report.primary_analysis):
+4 -4
View File
@@ -190,7 +190,7 @@ def _():
@test("XauStrategyConfig defaults") @test("XauStrategyConfig defaults")
def _(): def _():
xc = XauStrategyConfig() xc = XauStrategyConfig()
assert xc.enabled is True assert xc.enabled is False
assert xc.rsi_entry_threshold == 45.0 assert xc.rsi_entry_threshold == 45.0
assert xc.min_ema200_tfs == 2 assert xc.min_ema200_tfs == 2
assert xc.allow_generic_trades is True assert xc.allow_generic_trades is True
@@ -1656,7 +1656,7 @@ def _():
assert 6 not in cfg.allowed_weekdays # No Sunday assert 6 not in cfg.allowed_weekdays # No Sunday
assert cfg.min_atr_threshold == 0.0003 assert cfg.min_atr_threshold == 0.0003
assert cfg.sl_atr_floor == 1.2 assert cfg.sl_atr_floor == 1.2
assert cfg.min_rr_ratio == 0.5 assert cfg.min_rr_ratio == 1.0
assert cfg.mature_move_threshold == 35.0 assert cfg.mature_move_threshold == 35.0
assert cfg.tp_quality_scale["A+"] == 1.0 assert cfg.tp_quality_scale["A+"] == 1.0
assert cfg.tp_quality_scale["A"] == 1.0 assert cfg.tp_quality_scale["A"] == 1.0
@@ -1755,13 +1755,13 @@ def _():
config.eur_strategy.blocked_regimes = [] config.eur_strategy.blocked_regimes = []
config.eur_strategy.min_quality_grades = [] config.eur_strategy.min_quality_grades = []
assert config.eur_strategy.sl_atr_floor == 1.2 assert config.eur_strategy.sl_atr_floor == 1.2
assert config.eur_strategy.min_rr_ratio == 0.5 assert config.eur_strategy.min_rr_ratio == 1.0
agg = SignalAggregator(config) agg = SignalAggregator(config)
df = make_ohlcv(n=300, start_price=1.0800, trend=0.0003, seed=77) df = make_ohlcv(n=300, start_price=1.0800, trend=0.0003, seed=77)
dfs = {Timeframe.H1: df, Timeframe.M15: df, Timeframe.H4: df} dfs = {Timeframe.H1: df, Timeframe.M15: df, Timeframe.H4: df}
report = agg.analyze_from_dataframes(symbol=EURUSD, dfs=dfs, use_news=False) report = agg.analyze_from_dataframes(symbol=EURUSD, dfs=dfs, use_news=False)
if report.trade_setup: if report.trade_setup:
assert report.trade_setup.risk_reward >= 0.5 - 0.01 assert report.trade_setup.risk_reward >= 1.0 - 0.01
# ══════════════════════════════════════════════════════════════════════ # ══════════════════════════════════════════════════════════════════════