"""Symbol conversion utilities. CCXT uses 'BASE/QUOTE' format (e.g. "XAU/USD"). MT5 uses broker-specific names (e.g. "XAUUSD", "XAUUSDc", "XAUUSD.i"). The wrapper supports two strategies: 1. User-provided symbol_map in config (recommended, explicit) 2. Default: strip the "/" and concatenate (works for plain symbols) """ # Common quote currencies in forex/metals COMMON_QUOTES = [ "USD", "EUR", "GBP", "JPY", "CHF", "CAD", "AUD", "NZD", "CNY", "HKD", "SGD", "XAU", "XAG", "XPT", "XPD", ] def to_mt5_symbol(symbol, default_suffix=""): """Convert CCXT symbol (XAU/USD) to MT5 format (XAUUSD). Args: symbol: CCXT-format symbol (e.g. "XAU/USD" or "XAUUSDc") default_suffix: suffix to append when no symbol_map is provided (e.g. "c" for ECN, ".i" for CFD indices) Returns: str: MT5-format symbol """ # Already in MT5 format (no slash) if "/" not in symbol: return symbol base, quote = symbol.split("/", 1) return f"{base}{quote}{default_suffix}" def to_ccxt_symbol(symbol, quote=None): """Best-effort conversion from MT5 symbol to CCXT format. Args: symbol: MT5 symbol (e.g. "XAUUSDc") quote: Optional explicit quote currency to split on Returns: str: CCXT symbol (e.g. "XAU/USD" or original if cannot split) """ # Strip common suffixes cleaned = symbol for suffix in ("c", ".i", ".m", ".pro", ".ecn", ".raw", ".std"): if cleaned.endswith(suffix): cleaned = cleaned[: -len(suffix)] break # Try explicit quote first if quote and cleaned.endswith(quote) and len(cleaned) > len(quote): return f"{cleaned[: -len(quote)]}/{quote}" # Try common quotes for q in COMMON_QUOTES: if cleaned.endswith(q) and len(cleaned) > len(q): return f"{cleaned[: -len(q)]}/{q}" # Cannot split reliably return symbol