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:
@@ -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
|
||||||
|
|||||||
@@ -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
|
||||||
|
|
||||||
|
|||||||
@@ -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])
|
||||||
|
|||||||
@@ -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
@@ -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
|
||||||
|
|
||||||
|
|
||||||
# ══════════════════════════════════════════════════════════════════════
|
# ══════════════════════════════════════════════════════════════════════
|
||||||
|
|||||||
Reference in New Issue
Block a user