""" ForexBot2 — Configuration ========================================== """ from dataclasses import dataclass, field from enum import Enum from typing import Dict, List class Timeframe(Enum): M1 = "1min" M5 = "5min" M15 = "15min" M30 = "30min" H1 = "1h" H4 = "4h" D1 = "1d" W1 = "1wk" class SignalStrength(Enum): STRONG_BUY = 2 BUY = 1 NEUTRAL = 0 SELL = -1 STRONG_SELL = -2 class MarketSession(Enum): SYDNEY = "sydney" TOKYO = "tokyo" LONDON = "london" NEW_YORK = "new_york" OVERLAP_LONDON_NY = "london_ny_overlap" # ── Supported pairs ────────────────────────────────────────────── USDJPY = "USDJPY=X" EURUSD = "EURUSD=X" GBPUSD = "GBPUSD=X" XAUUSD = "XAU/USD" PAIR_INFO = { USDJPY: {"display": "USD/JPY", "pip_value": 0.01, "is_jpy_pair": True}, EURUSD: {"display": "EUR/USD", "pip_value": 0.0001, "is_jpy_pair": False}, GBPUSD: {"display": "GBP/USD", "pip_value": 0.0001, "is_jpy_pair": False}, XAUUSD: {"display": "XAU/USD", "pip_value": 0.10, "is_jpy_pair": False}, } # Defaults (kept for backward compat) DISPLAY = "USD/JPY" PIP_VALUE = 0.01 @dataclass class TechnicalConfig: """Technical indicator parameters.""" ema_fast: int = 9 ema_medium: int = 21 ema_slow: int = 50 ema_trend: int = 200 rsi_period: int = 14 rsi_overbought: float = 70.0 rsi_oversold: float = 30.0 rsi_neutral_upper: float = 60.0 rsi_neutral_lower: float = 40.0 macd_fast: int = 12 macd_slow: int = 26 macd_signal: int = 9 bb_period: int = 20 bb_std: float = 2.0 atr_period: int = 14 adx_period: int = 14 adx_strong_trend: float = 25.0 adx_very_strong: float = 50.0 divergence_lookback: int = 14 swing_sr_lookback: int = 50 disabled_indicators: List[str] = field(default_factory=lambda: ["divergence"]) ema_mode: str = "4ema" # "4ema" (9/21/50/200) or "2ema" (50/200) rsi_mode: str = "classic" # "classic" or "regime_aware" macd_as_pillar: bool = False # promote MACD histogram to scored signal regime_weights_override: Dict[str, Dict[str, float]] = field(default_factory=dict) @dataclass class SignalConfig: """Signal scoring & filtering.""" min_confidence: float = 55.0 weights: Dict[str, float] = field(default_factory=lambda: { "ema_trend": 1.0, "rsi": 1.5, "bollinger": 1.5, "candlestick": 1.0, "divergence": 1.5, }) mtf_agreement_threshold: int = 2 # of 3 timeframes cooldown_bars: int = 5 default_risk_pct: float = 1.0 default_rr_ratio: float = 2.0 atr_sl_multiplier: float = 1.5 min_rr_ratio: float = 0.5 @dataclass class NewsConfig: """Economic calendar & sentiment.""" tracked_currencies: List[str] = field(default_factory=lambda: ["USD", "JPY", "EUR", "GBP", "XAU"]) min_impact: str = "medium" pre_event_buffer_minutes: int = 30 post_event_buffer_minutes: int = 15 rss_feeds: List[str] = field(default_factory=lambda: [ "https://investinglive.com/feed/", "https://www.fxstreet.com/rss", ]) @dataclass class XauStrategyConfig: """XAU/USD v3 weighted confluence strategy. 1-year grid-optimized (768 combos): 52.4% WR, PF 2.34, +40.3R (63 trades). Entry: M15 score>=80 + H4 RSI confirms + H1 MACD confirms + RSI 40-65 zone. Exit: Partial — 1R→BE, 2R→close 50%, 3R→close rest. SL=2.5×ATR. Blocked: Fri/Sat/Sun, hours 1/3/9/10/19/22/23. """ enabled: bool = True rsi_entry_threshold: float = 45.0 tp_atr_multiplier: float = 0.9 sl_atr_multiplier: float = 2.0 min_ema200_tfs: int = 2 allow_generic_trades: bool = True blocked_hours_utc: List[int] = field(default_factory=lambda: [0, 1, 2, 5, 6, 21, 22, 23]) # v2 confluence fixes weekend_filter: bool = True min_atr_threshold: float = 1.0 v2_sl_atr_multiplier: float = 2.5 buy_threshold_boost: int = 0 m5_confirmation_bars: int = 0 # v3 gates (grid-optimized on 1Y Mon-Thu data) h4_rsi_gate: bool = True # require H4 RSI >55 for BUY, <45 for SELL h1_macd_gate: bool = True # require H1 MACD histogram >0 for BUY, <0 for SELL rsi_zone: tuple = (40.0, 65.0) # only enter when M15 RSI in this zone allowed_weekdays: List[int] = field(default_factory=lambda: [0, 1, 2, 3, 4]) # Mon-Fri sell_min_quality: List[str] = field(default_factory=lambda: ["A+", "A", "B"]) # SELL needs B+ @dataclass class EurStrategyConfig: """EUR/USD pullback-buy strategy (BUY only). Fixes: no entries at BB resistance, no overbought RSI, no chasing. Entry: M15 RSI 30–55 (pullback zone) + BB position < 0.75 + price > EMA200 on M15 & H1 + near EMA21 (not chasing). Exit: TP=0.7×ATR, SL=1.5×ATR. """ enabled: bool = True rsi_lower: float = 30.0 rsi_upper: float = 55.0 bb_max_position: float = 0.75 chase_max_atr_from_ema21: float = 1.5 tp_atr_multiplier: float = 0.7 sl_atr_multiplier: float = 1.0 blocked_hours_utc: List[int] = field(default_factory=lambda: [0, 1, 2, 3, 4, 5, 6, 16, 17, 18, 19, 21, 22, 23]) # If False, suppress generic trade setups when the EUR pullback gate # didn't fire. Backtest (90-day M15): True → +4.70R, False → +9.50R. # Generic momentum buys at trending_up extremes are pure drag. allow_generic_trades: bool = False # Minimum number of M15 scored indicators (ema_trend, rsi, bollinger, # adx, candlestick) that must independently signal BUY for the # pullback gate to fire. The strategy hardcodes Quality=A and # Score=+40 — without this, the "A" grade fires on weak technical # confluence. 0 = disabled (legacy behavior). min_indicators_agree: int = 2 @dataclass class GbpStrategyConfig: """GBP/USD pullback-buy strategy (BUY only). 1-year optimized: hour 17 blocked. Entry: M15 RSI 30–55 + BB < 0.75 + price > EMA200 on M15 & H1 + within 1.5×ATR of EMA21. Exit: TP=0.7×ATR, SL=1.0×ATR. """ enabled: bool = True rsi_lower: float = 30.0 rsi_upper: float = 55.0 bb_min_position: float = 0.0 bb_max_position: float = 0.75 chase_max_atr_from_ema21: float = 1.5 tp_atr_multiplier: float = 0.7 sl_atr_multiplier: float = 1.0 blocked_hours_utc: List[int] = field(default_factory=lambda: [0, 1, 2, 3, 4, 5, 6, 14, 15, 16, 17, 19, 21, 22, 23]) # See EurStrategyConfig.allow_generic_trades — same rationale. allow_generic_trades: bool = False # See EurStrategyConfig.min_indicators_agree — same rationale. min_indicators_agree: int = 2 @dataclass class FilterConfig: """Regime and session filters for live signals.""" regime_filter: bool = False regime_filter_timeframe: Timeframe = Timeframe.H4 regime_filter_min_adx: float = 25.0 session_filter: bool = True session_start_hour: int = 7 session_end_hour: int = 16 blocked_hours_utc: List[int] = field(default_factory=lambda: [0, 1, 2, 5, 6, 8, 12, 13, 14, 15, 16, 17, 21, 22, 23]) # Universal weekday filter (0=Mon ... 6=Sun). Default Mon-Thu — Fridays # have wide spreads from US session ending and weekend gap risk; XAU/USD # has its own stricter allowed_weekdays in xau_strategy that overrides. allowed_weekdays: List[int] = field(default_factory=lambda: [0, 1, 2, 3]) sell_filter: bool = True sell_allowed_pairs: List[str] = field(default_factory=lambda: ["XAU/USD"]) @dataclass class PlatformConfig: """Top-level configuration.""" technical: TechnicalConfig = field(default_factory=TechnicalConfig) signals: SignalConfig = field(default_factory=SignalConfig) news: NewsConfig = field(default_factory=NewsConfig) backtest: FilterConfig = field(default_factory=FilterConfig) xau_strategy: XauStrategyConfig = field(default_factory=XauStrategyConfig) eur_strategy: EurStrategyConfig = field(default_factory=EurStrategyConfig) gbp_strategy: GbpStrategyConfig = field(default_factory=GbpStrategyConfig) symbol: str = USDJPY display_name: str = DISPLAY pip_value: float = PIP_VALUE primary_timeframe: Timeframe = Timeframe.H1 confirmation_timeframes: List[Timeframe] = field( default_factory=lambda: [Timeframe.M15, Timeframe.H4] ) data_lookback_days: int = 60 twelve_data_api_key: str = "f563b22ffdfd4924955decbe1a8c8701" refresh_interval: int = 300 scan_pairs: List[str] = field( default_factory=lambda: [EURUSD, GBPUSD, XAUUSD] ) scan_timeframes: List[Timeframe] = field( default_factory=lambda: [Timeframe.M5, Timeframe.M15, Timeframe.H1, Timeframe.H4] ) candle_buffer_seconds: int = 10 ws_cooldown_seconds: int = 300 log_level: str = "INFO"