Files
DinQuant/backend_api_python/app/services/mt5_trading/symbols.py
T
TIANHE e095f9be7e Supports MT5 and forex trading.
Signed-off-by: TIANHE <TIANHE@GMAIL.COM>
2026-01-13 04:01:42 +08:00

145 lines
4.0 KiB
Python

"""
Symbol Mapping and Conversion for MT5
Handles forex symbol normalization and parsing.
"""
from typing import Tuple, Optional
# Common forex pairs with their typical MT5 symbol format
FOREX_PAIRS = {
# Major pairs
"EURUSD", "GBPUSD", "USDJPY", "USDCHF", "AUDUSD", "USDCAD", "NZDUSD",
# Cross pairs
"EURGBP", "EURJPY", "EURCHF", "EURAUD", "EURCAD", "EURNZD",
"GBPJPY", "GBPCHF", "GBPAUD", "GBPCAD", "GBPNZD",
"AUDJPY", "AUDCHF", "AUDCAD", "AUDNZD",
"NZDJPY", "NZDCHF", "NZDCAD",
"CADJPY", "CADCHF",
"CHFJPY",
# Exotic pairs
"USDMXN", "USDZAR", "USDTRY", "USDHKD", "USDSGD", "USDNOK", "USDSEK", "USDDKK",
"EURTRY", "EURMXN", "EURNOK", "EURSEK", "EURDKK", "EURPLN", "EURHUF", "EURCZK",
# Metals
"XAUUSD", "XAGUSD", "XAUEUR",
# Indices (CFD)
"US30", "US500", "USTEC", "UK100", "DE40", "JP225", "AU200",
# Crypto (if broker supports)
"BTCUSD", "ETHUSD", "LTCUSD", "XRPUSD",
}
def normalize_symbol(symbol: str, broker_suffix: str = "") -> str:
"""
Normalize symbol to MT5 format.
Different brokers may use different suffixes:
- No suffix: "EURUSD"
- With suffix: "EURUSDm", "EURUSD.raw", "EURUSD-ECN"
Args:
symbol: Symbol code (e.g., "EUR/USD", "EURUSD", "eurusd")
broker_suffix: Broker-specific suffix (e.g., "m", ".raw", "-ECN")
Returns:
Normalized MT5 symbol
"""
# Remove common separators and convert to uppercase
normalized = (symbol or "").strip().upper()
normalized = normalized.replace("/", "").replace("-", "").replace("_", "").replace(" ", "")
# Add broker suffix if provided
if broker_suffix:
# Check if symbol already has the suffix
if not normalized.endswith(broker_suffix.upper()):
normalized = normalized + broker_suffix
return normalized
def parse_symbol(symbol: str) -> Tuple[str, Optional[str]]:
"""
Parse symbol and extract base/quote currencies.
Args:
symbol: MT5 symbol (e.g., "EURUSD", "EURUSDm")
Returns:
(clean_symbol, market_type)
"""
clean = (symbol or "").strip().upper()
# Remove common broker suffixes
for suffix in ["M", ".RAW", "-ECN", ".STD", ".PRO", ".", "#"]:
if clean.endswith(suffix):
clean = clean[:-len(suffix)]
# Determine market type based on symbol pattern
if clean in FOREX_PAIRS or (len(clean) == 6 and clean.isalpha()):
return clean, "forex"
if clean.startswith("XAU") or clean.startswith("XAG"):
return clean, "metal"
if clean.startswith("BTC") or clean.startswith("ETH") or clean.startswith("LTC"):
return clean, "crypto"
if any(idx in clean for idx in ["US30", "US500", "USTEC", "UK100", "DE40", "JP225"]):
return clean, "index"
# Default to forex
return clean, "forex"
def get_lot_size_info(symbol: str) -> dict:
"""
Get lot size information for a symbol.
Standard forex lot sizes:
- Standard lot: 100,000 units
- Mini lot: 10,000 units
- Micro lot: 1,000 units
- Nano lot: 100 units
Args:
symbol: MT5 symbol
Returns:
Dict with lot size information
"""
clean, market_type = parse_symbol(symbol)
if market_type == "forex":
return {
"standard_lot": 100000,
"min_lot": 0.01,
"lot_step": 0.01,
"max_lot": 100.0,
}
if market_type == "metal":
# Gold/Silver typically uses oz
return {
"standard_lot": 100, # 100 oz
"min_lot": 0.01,
"lot_step": 0.01,
"max_lot": 50.0,
}
if market_type == "index":
return {
"standard_lot": 1,
"min_lot": 0.1,
"lot_step": 0.1,
"max_lot": 100.0,
}
# Default
return {
"standard_lot": 1,
"min_lot": 0.01,
"lot_step": 0.01,
"max_lot": 100.0,
}