405ddffc9c
- data_provider: strip yfinance imports, _YF_FALLBACK_MAP, _YF_INTERVALS, _YF_SESSION, _fetch_yfinance_fallback, _resample_h1_to_h4. Twelve Data is the sole data source. Raises DataUnavailableError if a pair isn't on the configured plan. - config: remove XAU/USD and XAG/USD from SYMBOL_DISPLAY and PIP_VALUES. PIP_VALUES is now empty (defaults: 0.01 JPY pairs, 0.0001 forex). - signal_aggregator: clear _PER_SYMBOL_TFS overrides (was XAU/XAG-only). - telegram_bot: replace XAUUSD with AUDUSD in SCAN_PAIRS. Auto-load .env via python-dotenv so TELEGRAM_BOT_TOKEN/GEMINI_API_KEY are picked up. - requirements: drop yfinance and curl_cffi dependencies. End-to-end pipeline test passes: data freshness, news engine (Gemini live), signal aggregator, and the /signal+/scan code paths in telegram_bot. Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
218 lines
6.4 KiB
Python
218 lines
6.4 KiB
Python
"""
|
|
Forex Signal Intelligence Platform - Configuration
|
|
===================================================
|
|
Central configuration for all signal analysis parameters.
|
|
Tuned by market regime and validated against historical performance.
|
|
"""
|
|
|
|
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"
|
|
|
|
|
|
# ── Major Forex Pairs ──────────────────────────────────────────────
|
|
MAJOR_PAIRS = [
|
|
"EURUSD=X", "GBPUSD=X", "USDJPY=X", "USDCHF=X",
|
|
"AUDUSD=X", "USDCAD=X", "NZDUSD=X",
|
|
]
|
|
|
|
CROSS_PAIRS = [
|
|
"EURGBP=X", "EURJPY=X", "GBPJPY=X", "AUDJPY=X",
|
|
"EURCHF=X", "CADJPY=X",
|
|
]
|
|
|
|
# Symbol display names
|
|
SYMBOL_DISPLAY = {
|
|
"EURUSD=X": "EUR/USD", "GBPUSD=X": "GBP/USD",
|
|
"USDJPY=X": "USD/JPY", "USDCHF=X": "USD/CHF",
|
|
"AUDUSD=X": "AUD/USD", "USDCAD=X": "USD/CAD",
|
|
"NZDUSD=X": "NZD/USD", "EURGBP=X": "EUR/GBP",
|
|
"EURJPY=X": "EUR/JPY", "GBPJPY=X": "GBP/JPY",
|
|
"AUDJPY=X": "AUD/JPY", "EURCHF=X": "EUR/CHF",
|
|
"CADJPY=X": "CAD/JPY",
|
|
}
|
|
|
|
# Pip values per symbol (used for trade setup risk/reward in pips)
|
|
# Forex defaults: 0.01 for JPY pairs, 0.0001 for everything else.
|
|
# Override here for any pair that doesn't follow that rule.
|
|
PIP_VALUES: Dict[str, float] = {}
|
|
|
|
|
|
@dataclass
|
|
class TechnicalConfig:
|
|
"""Technical indicator parameters - carefully tuned for Forex."""
|
|
# EMA periods (fast/medium/slow for trend detection)
|
|
ema_fast: int = 9
|
|
ema_medium: int = 21
|
|
ema_slow: int = 50
|
|
ema_trend: int = 200
|
|
|
|
# RSI
|
|
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 (12, 26, 9 is standard)
|
|
macd_fast: int = 12
|
|
macd_slow: int = 26
|
|
macd_signal: int = 9
|
|
|
|
# Bollinger Bands
|
|
bb_period: int = 20
|
|
bb_std: float = 2.0
|
|
|
|
# ATR for volatility measurement
|
|
atr_period: int = 14
|
|
|
|
# Stochastic
|
|
stoch_k: int = 14
|
|
stoch_d: int = 3
|
|
stoch_overbought: float = 80.0
|
|
stoch_oversold: float = 20.0
|
|
|
|
# ADX for trend strength
|
|
adx_period: int = 14
|
|
adx_strong_trend: float = 25.0
|
|
adx_very_strong: float = 50.0
|
|
|
|
# Ichimoku
|
|
ichimoku_tenkan: int = 9
|
|
ichimoku_kijun: int = 26
|
|
ichimoku_senkou_b: int = 52
|
|
|
|
# Volume profile
|
|
volume_ma_period: int = 20
|
|
|
|
# Pivot points lookback
|
|
pivot_lookback: int = 1
|
|
|
|
# Divergence detection — how many bars back to scan for swing points
|
|
divergence_lookback: int = 14
|
|
|
|
|
|
@dataclass
|
|
class SignalConfig:
|
|
"""Signal scoring and filtering parameters."""
|
|
# Minimum confidence to emit a signal (0-100)
|
|
min_confidence: float = 55.0
|
|
|
|
# Weight allocation for composite score
|
|
weights: Dict[str, float] = field(default_factory=lambda: {
|
|
"trend": 0.25, # EMA alignment, ADX
|
|
"momentum": 0.20, # RSI, Stochastic, MACD
|
|
"volatility": 0.15, # BB position, ATR regime
|
|
"price_action": 0.15, # Candlestick patterns, S/R
|
|
"multi_timeframe": 0.15,# Higher TF confirmation
|
|
"sentiment": 0.10, # News/calendar events
|
|
})
|
|
|
|
# Multi-timeframe confirmation requirements
|
|
mtf_agreement_threshold: int = 2 # out of 3 timeframes must agree
|
|
|
|
# Signal cooldown (prevent over-signalling same direction)
|
|
cooldown_bars: int = 5
|
|
|
|
# Risk management defaults
|
|
default_risk_pct: float = 1.0 # risk 1% per trade
|
|
default_rr_ratio: float = 2.0 # 1:2 risk-reward minimum
|
|
|
|
|
|
@dataclass
|
|
class NewsConfig:
|
|
"""Economic calendar and sentiment configuration."""
|
|
# High-impact event currencies
|
|
tracked_currencies: List[str] = field(default_factory=lambda: [
|
|
"USD", "EUR", "GBP", "JPY", "CHF", "AUD", "CAD", "NZD"
|
|
])
|
|
|
|
# Event impact levels to monitor
|
|
min_impact: str = "medium" # "low", "medium", "high"
|
|
|
|
# Pre-event buffer (minutes before high-impact event = no new signals)
|
|
pre_event_buffer_minutes: int = 30
|
|
|
|
# Post-event volatility window (minutes after event = cautious signals)
|
|
post_event_buffer_minutes: int = 15
|
|
|
|
# News sentiment sources
|
|
rss_feeds: List[str] = field(default_factory=lambda: [
|
|
"https://www.forexlive.com/feed/",
|
|
"https://www.fxstreet.com/rss",
|
|
])
|
|
|
|
|
|
@dataclass
|
|
class PlatformConfig:
|
|
"""Overall platform configuration."""
|
|
technical: TechnicalConfig = field(default_factory=TechnicalConfig)
|
|
signals: SignalConfig = field(default_factory=SignalConfig)
|
|
news: NewsConfig = field(default_factory=NewsConfig)
|
|
|
|
# Default pairs to monitor
|
|
watch_pairs: List[str] = field(default_factory=lambda: MAJOR_PAIRS)
|
|
|
|
# Analysis timeframes (primary + confirmation)
|
|
primary_timeframe: Timeframe = Timeframe.H1
|
|
confirmation_timeframes: List[Timeframe] = field(
|
|
default_factory=lambda: [Timeframe.M15, Timeframe.D1]
|
|
)
|
|
|
|
# Data source
|
|
data_lookback_days: int = 60 # enough for 200 EMA on H1
|
|
|
|
# Twelve Data API key (overridden by TWELVE_DATA_API_KEY env var)
|
|
twelve_data_api_key: str = "f563b22ffdfd4924955decbe1a8c8701"
|
|
|
|
# Dashboard refresh interval (seconds) — used as a fallback when a
|
|
# timeframe isn't covered by cache_ttl_per_tf below.
|
|
refresh_interval: int = 300 # 5 minutes
|
|
|
|
# Per-timeframe cache TTL (seconds). A 1-week candle changes less
|
|
# often than a 15-minute candle, so caching them all for the same
|
|
# 5 minutes wastes API credits. Tuned to roughly match each TF's
|
|
# bar interval so we re-fetch about once per new bar.
|
|
cache_ttl_per_tf: Dict[Timeframe, int] = field(default_factory=lambda: {
|
|
Timeframe.M1: 60, # 1 minute
|
|
Timeframe.M5: 150, # 2.5 minutes
|
|
Timeframe.M15: 300, # 5 minutes
|
|
Timeframe.M30: 600, # 10 minutes
|
|
Timeframe.H1: 1800, # 30 minutes
|
|
Timeframe.H4: 7200, # 2 hours
|
|
Timeframe.D1: 14400, # 4 hours
|
|
Timeframe.W1: 86400, # 24 hours
|
|
})
|
|
|
|
# Logging
|
|
log_level: str = "INFO"
|
|
log_signals_to_file: bool = True
|
|
signal_log_path: str = "signals.log"
|