Login Page - Create Account

Support Board


Date/Time: Tue, 16 Dec 2025 22:23:23 +0000



Interactive Brokers - IBKR - Symbol mapping.

View Count: 30

[2025-12-16 11:14:17]
G369 - Posts: 27
Please find below a comprehensive IB -> Sierra Chart symbol mapping code in Python.

Hope this helps....

====================================================================================

#!/usr/bin/env python3
"""
Ultimate IB to Sierra Chart Symbol Mapper
==========================================
Generates a complete Sierra Chart Global Symbol Settings XML file
mapping ALL Interactive Brokers symbols for:
- US Stocks (NASDAQ, NYSE, AMEX) - 11,000+
- India Stocks (NSE, BSE) - 200+
- Japan Stocks (TSE) - 100+
- China Stocks (SSE, SZSE, HKEX) - 100+
- Germany Stocks (XETRA) - 75+
- Futures (137+ base symbols × 12 months) - 1,400+
- Forex (100+ pairs)
- Indices (50+)
- CFDs (Index, Forex, Commodity) - 55+
- ETFs (US, Europe, Asia, India) - 200+

Total: 13,500+ symbols

Author: Auto-generated for Sierra Chart + Interactive Brokers
"""

import os
import sys
import urllib.request
import ssl
import xml.etree.ElementTree as ET
from xml.dom import minidom
from datetime import datetime, timedelta
from typing import List, Dict, Tuple, Set, Optional
import re
import shutil

# ============================================================================
# CONFIGURATION
# ============================================================================

OUTPUT_FILE = "CustomSymbolSettings.interactive_brokers.trading.xml"
SIERRA_CHART_PATH = r"C:\Users\Quantum\Downloads\SierraChart\SymbolSettings"

# Contract months
MONTH_CODES = {
1: 'F', 2: 'G', 3: 'H', 4: 'J', 5: 'K', 6: 'M',
7: 'N', 8: 'Q', 9: 'U', 10: 'V', 11: 'X', 12: 'Z'
}

# ============================================================================
# FUTURES DEFINITIONS
# ============================================================================

FUTURES = {
# MICRO INDEX FUTURES (CME)
"MES": {"exchange": "CME", "name": "Micro E-mini S&P 500", "tick": 0.25, "value": 1.25, "mult": 5, "months": "HMUZ", "currency": "USD"},
"MNQ": {"exchange": "CME", "name": "Micro E-mini Nasdaq-100", "tick": 0.25, "value": 0.50, "mult": 2, "months": "HMUZ", "currency": "USD"},
"MYM": {"exchange": "CME", "name": "Micro E-mini Dow", "tick": 1.0, "value": 0.50, "mult": 0.5, "months": "HMUZ", "currency": "USD"},
"M2K": {"exchange": "CME", "name": "Micro E-mini Russell 2000", "tick": 0.10, "value": 0.50, "mult": 5, "months": "HMUZ", "currency": "USD"},

# MICRO METALS (COMEX)
"MGC": {"exchange": "COMEX", "name": "Micro Gold", "tick": 0.10, "value": 1.00, "mult": 10, "months": "GJMQVZ", "currency": "USD"},
"SIL": {"exchange": "COMEX", "name": "Micro Silver", "tick": 0.005, "value": 5.00, "mult": 1000, "months": "FHKNUZ", "currency": "USD"},
"MHG": {"exchange": "COMEX", "name": "Micro Copper", "tick": 0.0005, "value": 1.25, "mult": 2500, "months": "FHKNUZ", "currency": "USD"},

# MICRO ENERGY (NYMEX)
"MCL": {"exchange": "NYMEX", "name": "Micro WTI Crude Oil", "tick": 0.01, "value": 1.00, "mult": 100, "months": "FGHJKMNQUVXZ", "currency": "USD"},
"MNG": {"exchange": "NYMEX", "name": "Micro Natural Gas", "tick": 0.001, "value": 1.00, "mult": 1000, "months": "FGHJKMNQUVXZ", "currency": "USD"},

# MICRO TREASURY YIELDS (CBOT)
"2YY": {"exchange": "CBOT", "name": "Micro 2-Year Yield", "tick": 0.001, "value": 1.00, "mult": 1000, "months": "HMUZ", "currency": "USD"},
"5YY": {"exchange": "CBOT", "name": "Micro 5-Year Yield", "tick": 0.001, "value": 1.00, "mult": 1000, "months": "HMUZ", "currency": "USD"},
"10Y": {"exchange": "CBOT", "name": "Micro 10-Year Yield", "tick": 0.001, "value": 1.00, "mult": 1000, "months": "HMUZ", "currency": "USD"},
"30Y": {"exchange": "CBOT", "name": "Micro 30-Year Yield", "tick": 0.001, "value": 1.00, "mult": 1000, "months": "HMUZ", "currency": "USD"},

# MICRO CRYPTO (CME)
"MBT": {"exchange": "CME", "name": "Micro Bitcoin", "tick": 5.0, "value": 0.50, "mult": 0.1, "months": "FGHJKMNQUVXZ", "currency": "USD"},
"MET": {"exchange": "CME", "name": "Micro Ether", "tick": 0.05, "value": 0.50, "mult": 0.1, "months": "FGHJKMNQUVXZ", "currency": "USD"},

# MICRO FX (CME)
"M6E": {"exchange": "CME", "name": "Micro EUR/USD", "tick": 0.0001, "value": 1.25, "mult": 12500, "months": "HMUZ", "currency": "USD"},
"M6A": {"exchange": "CME", "name": "Micro AUD/USD", "tick": 0.0001, "value": 1.00, "mult": 10000, "months": "HMUZ", "currency": "USD"},
"M6B": {"exchange": "CME", "name": "Micro GBP/USD", "tick": 0.0001, "value": 0.625, "mult": 6250, "months": "HMUZ", "currency": "USD"},
"MJY": {"exchange": "CME", "name": "Micro JPY/USD", "tick": 0.000001, "value": 1.25, "mult": 1250000, "months": "HMUZ", "currency": "USD"},
"MCD": {"exchange": "CME", "name": "Micro CAD/USD", "tick": 0.0001, "value": 1.00, "mult": 10000, "months": "HMUZ", "currency": "USD"},
"MSF": {"exchange": "CME", "name": "Micro CHF/USD", "tick": 0.0001, "value": 1.25, "mult": 12500, "months": "HMUZ", "currency": "USD"},
"MIR": {"exchange": "CME", "name": "Micro INR/USD", "tick": 0.00001, "value": 1.00, "mult": 100000, "months": "FGHJKMNQUVXZ", "currency": "USD"},

# E-MINI INDEX FUTURES (CME)
"ES": {"exchange": "CME", "name": "E-mini S&P 500", "tick": 0.25, "value": 12.50, "mult": 50, "months": "HMUZ", "currency": "USD"},
"NQ": {"exchange": "CME", "name": "E-mini Nasdaq-100", "tick": 0.25, "value": 5.00, "mult": 20, "months": "HMUZ", "currency": "USD"},
"YM": {"exchange": "CBOT", "name": "E-mini Dow ($5)", "tick": 1.0, "value": 5.00, "mult": 5, "months": "HMUZ", "currency": "USD"},
"RTY": {"exchange": "CME", "name": "E-mini Russell 2000", "tick": 0.10, "value": 5.00, "mult": 50, "months": "HMUZ", "currency": "USD"},
"EMD": {"exchange": "CME", "name": "E-mini S&P MidCap 400", "tick": 0.10, "value": 10.00, "mult": 100, "months": "HMUZ", "currency": "USD"},
"NKD": {"exchange": "CME", "name": "Nikkei 225 Dollar", "tick": 5.0, "value": 25.00, "mult": 5, "months": "HMUZ", "currency": "USD"},
"NIY": {"exchange": "CME", "name": "Nikkei 225 Yen", "tick": 5.0, "value": 2500, "mult": 500, "months": "HMUZ", "currency": "JPY"},

# FULL SIZE INDEX (CME)
"SP": {"exchange": "CME", "name": "S&P 500 Full Size", "tick": 0.10, "value": 25.00, "mult": 250, "months": "HMUZ", "currency": "USD"},
"ND": {"exchange": "CME", "name": "Nasdaq-100 Full Size", "tick": 0.25, "value": 25.00, "mult": 100, "months": "HMUZ", "currency": "USD"},

# METALS (COMEX)
"GC": {"exchange": "COMEX", "name": "Gold", "tick": 0.10, "value": 10.00, "mult": 100, "months": "GJMQVZ", "currency": "USD"},
"SI": {"exchange": "COMEX", "name": "Silver", "tick": 0.005, "value": 25.00, "mult": 5000, "months": "FHKNUZ", "currency": "USD"},
"HG": {"exchange": "COMEX", "name": "Copper", "tick": 0.0005, "value": 12.50, "mult": 25000, "months": "FHKNUZ", "currency": "USD"},
"PL": {"exchange": "NYMEX", "name": "Platinum", "tick": 0.10, "value": 5.00, "mult": 50, "months": "FJNV", "currency": "USD"},
"PA": {"exchange": "NYMEX", "name": "Palladium", "tick": 0.05, "value": 5.00, "mult": 100, "months": "HMUZ", "currency": "USD"},

# E-MINI METALS (COMEX)
"QO": {"exchange": "COMEX", "name": "E-mini Gold", "tick": 0.25, "value": 12.50, "mult": 50, "months": "GJMQVZ", "currency": "USD"},
"QI": {"exchange": "COMEX", "name": "E-mini Silver", "tick": 0.0125, "value": 31.25, "mult": 2500, "months": "FHKNUZ", "currency": "USD"},
"QC": {"exchange": "COMEX", "name": "E-mini Copper", "tick": 0.001, "value": 12.50, "mult": 12500, "months": "FHKNUZ", "currency": "USD"},

# ENERGY (NYMEX)
"CL": {"exchange": "NYMEX", "name": "Crude Oil WTI", "tick": 0.01, "value": 10.00, "mult": 1000, "months": "FGHJKMNQUVXZ", "currency": "USD"},
"NG": {"exchange": "NYMEX", "name": "Natural Gas", "tick": 0.001, "value": 10.00, "mult": 10000, "months": "FGHJKMNQUVXZ", "currency": "USD"},
"RB": {"exchange": "NYMEX", "name": "RBOB Gasoline", "tick": 0.0001, "value": 4.20, "mult": 42000, "months": "FGHJKMNQUVXZ", "currency": "USD"},
"HO": {"exchange": "NYMEX", "name": "Heating Oil", "tick": 0.0001, "value": 4.20, "mult": 42000, "months": "FGHJKMNQUVXZ", "currency": "USD"},
"BZ": {"exchange": "NYMEX", "name": "Brent Crude Oil", "tick": 0.01, "value": 10.00, "mult": 1000, "months": "FGHJKMNQUVXZ", "currency": "USD"},

# E-MINI ENERGY (NYMEX)
"QM": {"exchange": "NYMEX", "name": "E-mini Crude Oil", "tick": 0.025, "value": 12.50, "mult": 500, "months": "FGHJKMNQUVXZ", "currency": "USD"},
"QG": {"exchange": "NYMEX", "name": "E-mini Natural Gas", "tick": 0.005, "value": 12.50, "mult": 2500, "months": "FGHJKMNQUVXZ", "currency": "USD"},

# TREASURY (CBOT)
"ZB": {"exchange": "CBOT", "name": "30-Year T-Bond", "tick": 0.03125, "value": 31.25, "mult": 1000, "months": "HMUZ", "currency": "USD"},
"UB": {"exchange": "CBOT", "name": "Ultra T-Bond", "tick": 0.03125, "value": 31.25, "mult": 1000, "months": "HMUZ", "currency": "USD"},
"ZN": {"exchange": "CBOT", "name": "10-Year T-Note", "tick": 0.015625, "value": 15.625, "mult": 1000, "months": "HMUZ", "currency": "USD"},
"TN": {"exchange": "CBOT", "name": "Ultra 10-Year T-Note", "tick": 0.015625, "value": 15.625, "mult": 1000, "months": "HMUZ", "currency": "USD"},
"ZF": {"exchange": "CBOT", "name": "5-Year T-Note", "tick": 0.0078125, "value": 7.8125, "mult": 1000, "months": "HMUZ", "currency": "USD"},
"ZT": {"exchange": "CBOT", "name": "2-Year T-Note", "tick": 0.0078125, "value": 15.625, "mult": 2000, "months": "HMUZ", "currency": "USD"},
"Z3N": {"exchange": "CBOT", "name": "3-Year T-Note", "tick": 0.0078125, "value": 7.8125, "mult": 1000, "months": "HMUZ", "currency": "USD"},
"TWE": {"exchange": "CBOT", "name": "20-Year T-Bond", "tick": 0.03125, "value": 31.25, "mult": 1000, "months": "HMUZ", "currency": "USD"},

# SOFR/FED FUNDS (CBOT/CME)
"SR3": {"exchange": "CME", "name": "3-Month SOFR", "tick": 0.0025, "value": 6.25, "mult": 2500, "months": "HMUZ", "currency": "USD"},
"SR1": {"exchange": "CME", "name": "1-Month SOFR", "tick": 0.00125, "value": 5.2083, "mult": 4166.67, "months": "FGHJKMNQUVXZ", "currency": "USD"},
"ZQ": {"exchange": "CBOT", "name": "30-Day Fed Funds", "tick": 0.00125, "value": 5.2083, "mult": 4166.67, "months": "FGHJKMNQUVXZ", "currency": "USD"},

# GRAINS (CBOT)
"ZC": {"exchange": "CBOT", "name": "Corn", "tick": 0.25, "value": 12.50, "mult": 50, "months": "HKNUZ", "currency": "USD"},
"ZS": {"exchange": "CBOT", "name": "Soybeans", "tick": 0.25, "value": 12.50, "mult": 50, "months": "FHKNQUX", "currency": "USD"},
"ZW": {"exchange": "CBOT", "name": "Wheat", "tick": 0.25, "value": 12.50, "mult": 50, "months": "HKNUZ", "currency": "USD"},
"ZM": {"exchange": "CBOT", "name": "Soybean Meal", "tick": 0.10, "value": 10.00, "mult": 100, "months": "FHKNQUVZ", "currency": "USD"},
"ZL": {"exchange": "CBOT", "name": "Soybean Oil", "tick": 0.01, "value": 6.00, "mult": 600, "months": "FHKNQUVZ", "currency": "USD"},
"ZO": {"exchange": "CBOT", "name": "Oats", "tick": 0.25, "value": 12.50, "mult": 50, "months": "HKNUZ", "currency": "USD"},
"ZR": {"exchange": "CBOT", "name": "Rough Rice", "tick": 0.005, "value": 10.00, "mult": 2000, "months": "FHKNUX", "currency": "USD"},
"KE": {"exchange": "CBOT", "name": "KC HRW Wheat", "tick": 0.25, "value": 12.50, "mult": 50, "months": "HKNUZ", "currency": "USD"},

# MINI GRAINS (CBOT)
"XC": {"exchange": "CBOT", "name": "Mini Corn", "tick": 0.125, "value": 1.25, "mult": 10, "months": "HKNUZ", "currency": "USD"},
"XW": {"exchange": "CBOT", "name": "Mini Wheat", "tick": 0.125, "value": 1.25, "mult": 10, "months": "HKNUZ", "currency": "USD"},
"XK": {"exchange": "CBOT", "name": "Mini Soybeans", "tick": 0.125, "value": 1.25, "mult": 10, "months": "FHKNQUX", "currency": "USD"},

# LIVESTOCK (CME)
"LE": {"exchange": "CME", "name": "Live Cattle", "tick": 0.025, "value": 10.00, "mult": 400, "months": "GJMQVZ", "currency": "USD"},
"GF": {"exchange": "CME", "name": "Feeder Cattle", "tick": 0.025, "value": 12.50, "mult": 500, "months": "FHJKQUVX", "currency": "USD"},
"HE": {"exchange": "CME", "name": "Lean Hogs", "tick": 0.025, "value": 10.00, "mult": 400, "months": "GJKMNQVZ", "currency": "USD"},

# DAIRY (CME)
"DC": {"exchange": "CME", "name": "Class III Milk", "tick": 0.01, "value": 2.00, "mult": 200, "months": "FGHJKMNQUVXZ", "currency": "USD"},
"DY": {"exchange": "CME", "name": "Dry Whey", "tick": 0.001, "value": 4.40, "mult": 4400, "months": "FGHJKMNQUVXZ", "currency": "USD"},
"CB": {"exchange": "CME", "name": "Cash-Settled Butter", "tick": 0.001, "value": 2.00, "mult": 2000, "months": "FGHJKMNQUVXZ", "currency": "USD"},
"CSC": {"exchange": "CME", "name": "Cash-Settled Cheese", "tick": 0.001, "value": 2.00, "mult": 2000, "months": "FGHJKMNQUVXZ", "currency": "USD"},
"GNF": {"exchange": "CME", "name": "Nonfat Dry Milk", "tick": 0.001, "value": 4.40, "mult": 4400, "months": "FGHJKMNQUVXZ", "currency": "USD"},

# SOFTS (NYBOT/ICE)
"CC": {"exchange": "NYBOT", "name": "Cocoa", "tick": 1.0, "value": 10.00, "mult": 10, "months": "HKNUZ", "currency": "USD"},
"KC": {"exchange": "NYBOT", "name": "Coffee", "tick": 0.05, "value": 18.75, "mult": 375, "months": "HKNUZ", "currency": "USD"},
"CT": {"exchange": "NYBOT", "name": "Cotton", "tick": 0.01, "value": 5.00, "mult": 500, "months": "HKNVZ", "currency": "USD"},
"SB": {"exchange": "NYBOT", "name": "Sugar #11", "tick": 0.01, "value": 11.20, "mult": 1120, "months": "HKNV", "currency": "USD"},
"OJ": {"exchange": "NYBOT", "name": "Orange Juice", "tick": 0.05, "value": 7.50, "mult": 150, "months": "FHKNUX", "currency": "USD"},
"DX": {"exchange": "NYBOT", "name": "US Dollar Index", "tick": 0.005, "value": 5.00, "mult": 1000, "months": "HMUZ", "currency": "USD"},

# CURRENCY FUTURES (CME)
"6E": {"exchange": "CME", "name": "Euro FX", "tick": 0.00005, "value": 6.25, "mult": 125000, "months": "HMUZ", "currency": "USD"},
"6J": {"exchange": "CME", "name": "Japanese Yen", "tick": 0.0000005, "value": 6.25, "mult": 12500000, "months": "HMUZ", "currency": "USD"},
"6B": {"exchange": "CME", "name": "British Pound", "tick": 0.0001, "value": 6.25, "mult": 62500, "months": "HMUZ", "currency": "USD"},
"6C": {"exchange": "CME", "name": "Canadian Dollar", "tick": 0.00005, "value": 5.00, "mult": 100000, "months": "HMUZ", "currency": "USD"},
"6A": {"exchange": "CME", "name": "Australian Dollar", "tick": 0.0001, "value": 10.00, "mult": 100000, "months": "HMUZ", "currency": "USD"},
"6S": {"exchange": "CME", "name": "Swiss Franc", "tick": 0.0001, "value": 12.50, "mult": 125000, "months": "HMUZ", "currency": "USD"},
"6N": {"exchange": "CME", "name": "New Zealand Dollar", "tick": 0.0001, "value": 10.00, "mult": 100000, "months": "HMUZ", "currency": "USD"},
"6M": {"exchange": "CME", "name": "Mexican Peso", "tick": 0.00001, "value": 5.00, "mult": 500000, "months": "HMUZ", "currency": "USD"},
"6L": {"exchange": "CME", "name": "Brazilian Real", "tick": 0.00005, "value": 5.00, "mult": 100000, "months": "FGHJKMNQUVXZ", "currency": "USD"},
"6R": {"exchange": "CME", "name": "Russian Ruble", "tick": 0.00001, "value": 5.00, "mult": 2500000, "months": "HMUZ", "currency": "USD"},
"6Z": {"exchange": "CME", "name": "South African Rand", "tick": 0.00001, "value": 5.00, "mult": 500000, "months": "HMUZ", "currency": "USD"},

# E-MINI CURRENCY (CME)
"E7": {"exchange": "CME", "name": "E-mini Euro FX", "tick": 0.0001, "value": 6.25, "mult": 62500, "months": "HMUZ", "currency": "USD"},
"J7": {"exchange": "CME", "name": "E-mini Japanese Yen", "tick": 0.000001, "value": 6.25, "mult": 6250000, "months": "HMUZ", "currency": "USD"},

# VOLATILITY/CRYPTO (CFE/CME)
"VIX": {"exchange": "CFE", "name": "VIX Volatility Index", "tick": 0.05, "value": 50.00, "mult": 1000, "months": "FGHJKMNQUVXZ", "currency": "USD"},
"VXM": {"exchange": "CFE", "name": "Mini VIX", "tick": 0.05, "value": 5.00, "mult": 100, "months": "FGHJKMNQUVXZ", "currency": "USD"},
"BTC": {"exchange": "CME", "name": "Bitcoin", "tick": 5.0, "value": 25.00, "mult": 5, "months": "FGHJKMNQUVXZ", "currency": "USD"},
"ETH": {"exchange": "CME", "name": "Ether", "tick": 0.25, "value": 12.50, "mult": 50, "months": "FGHJKMNQUVXZ", "currency": "USD"},

# EUREX (DTB)
"FDAX": {"exchange": "DTB", "name": "DAX Index", "tick": 0.5, "value": 12.50, "mult": 25, "months": "HMUZ", "currency": "EUR"},
"FDXM": {"exchange": "DTB", "name": "Mini-DAX", "tick": 1.0, "value": 5.00, "mult": 5, "months": "HMUZ", "currency": "EUR"},
"FDXS": {"exchange": "DTB", "name": "Micro-DAX", "tick": 1.0, "value": 1.00, "mult": 1, "months": "HMUZ", "currency": "EUR"},
"FESX": {"exchange": "DTB", "name": "Euro Stoxx 50", "tick": 1.0, "value": 10.00, "mult": 10, "months": "HMUZ", "currency": "EUR"},
"FSXE": {"exchange": "DTB", "name": "Euro Stoxx 50 Mini", "tick": 1.0, "value": 1.00, "mult": 1, "months": "HMUZ", "currency": "EUR"},
"FGBL": {"exchange": "DTB", "name": "Euro Bund", "tick": 0.01, "value": 10.00, "mult": 1000, "months": "HMUZ", "currency": "EUR"},
"FGBM": {"exchange": "DTB", "name": "Euro Bobl", "tick": 0.01, "value": 10.00, "mult": 1000, "months": "HMUZ", "currency": "EUR"},
"FGBS": {"exchange": "DTB", "name": "Euro Schatz", "tick": 0.005, "value": 5.00, "mult": 1000, "months": "HMUZ", "currency": "EUR"},
"FGBX": {"exchange": "DTB", "name": "Euro Buxl", "tick": 0.02, "value": 20.00, "mult": 1000, "months": "HMUZ", "currency": "EUR"},
"FVS": {"exchange": "DTB", "name": "VSTOXX", "tick": 0.05, "value": 50.00, "mult": 100, "months": "FGHJKMNQUVXZ", "currency": "EUR"},
"FSMI": {"exchange": "DTB", "name": "SMI Index", "tick": 1.0, "value": 10.00, "mult": 10, "months": "HMUZ", "currency": "CHF"},

# UK/EUROPE (ICEEU)
"Z": {"exchange": "ICEEU", "name": "FTSE 100 Index", "tick": 0.5, "value": 5.00, "mult": 10, "months": "HMUZ", "currency": "GBP"},
"R": {"exchange": "ICEEU", "name": "Long Gilt", "tick": 0.01, "value": 10.00, "mult": 1000, "months": "HMUZ", "currency": "GBP"},
"FCE": {"exchange": "MONEP", "name": "CAC 40 Index", "tick": 0.5, "value": 5.00, "mult": 10, "months": "FGHJKMNQUVXZ", "currency": "EUR"},

# HONG KONG (HKFE)
"HSI": {"exchange": "HKFE", "name": "Hang Seng Index", "tick": 1.0, "value": 50.00, "mult": 50, "months": "FGHJKMNQUVXZ", "currency": "HKD"},
"MHI": {"exchange": "HKFE", "name": "Mini Hang Seng Index", "tick": 1.0, "value": 10.00, "mult": 10, "months": "FGHJKMNQUVXZ", "currency": "HKD"},
"HHI": {"exchange": "HKFE", "name": "H-Shares Index", "tick": 1.0, "value": 50.00, "mult": 50, "months": "FGHJKMNQUVXZ", "currency": "HKD"},
"MCH": {"exchange": "HKFE", "name": "Mini H-Shares Index", "tick": 1.0, "value": 10.00, "mult": 10, "months": "FGHJKMNQUVXZ", "currency": "HKD"},
"HTI": {"exchange": "HKFE", "name": "Hang Seng Tech Index", "tick": 1.0, "value": 50.00, "mult": 50, "months": "FGHJKMNQUVXZ", "currency": "HKD"},

# JAPAN (OSE.JPN)
"N225": {"exchange": "OSE.JPN", "name": "Nikkei 225", "tick": 10.0, "value": 10000, "mult": 1000, "months": "HMUZ", "currency": "JPY"},
"N225M": {"exchange": "OSE.JPN", "name": "Nikkei 225 Mini", "tick": 5.0, "value": 500, "mult": 100, "months": "FGHJKMNQUVXZ", "currency": "JPY"},
"TOPIX": {"exchange": "OSE.JPN", "name": "TOPIX", "tick": 0.5, "value": 5000, "mult": 10000, "months": "HMUZ", "currency": "JPY"},
"MTPX": {"exchange": "OSE.JPN", "name": "Mini TOPIX", "tick": 0.25, "value": 250, "mult": 1000, "months": "FGHJKMNQUVXZ", "currency": "JPY"},
"JGB": {"exchange": "OSE.JPN", "name": "10-Year JGB", "tick": 0.01, "value": 10000, "mult": 1000000, "months": "HMUZ", "currency": "JPY"},

# SINGAPORE (SGX)
"SGXNK": {"exchange": "SGX", "name": "SGX Nikkei 225", "tick": 5.0, "value": 2500, "mult": 500, "months": "HMUZ", "currency": "JPY"},
"XINA50": {"exchange": "SGX", "name": "FTSE China A50", "tick": 2.5, "value": 2.50, "mult": 1, "months": "FGHJKMNQUVXZ", "currency": "USD"},
"NIFTY": {"exchange": "SGX", "name": "SGX Nifty 50", "tick": 0.5, "value": 1.00, "mult": 2, "months": "FGHJKMNQUVXZ", "currency": "USD"},
"TW": {"exchange": "SGX", "name": "MSCI Taiwan Index", "tick": 0.1, "value": 10.00, "mult": 100, "months": "FGHJKMNQUVXZ", "currency": "USD"},
"IN": {"exchange": "SGX", "name": "CNX Nifty", "tick": 0.5, "value": 1.00, "mult": 2, "months": "FGHJKMNQUVXZ", "currency": "USD"},

# AUSTRALIA (SNFE)
"SPI": {"exchange": "SNFE", "name": "SPI 200 Index", "tick": 1.0, "value": 25.00, "mult": 25, "months": "HMUZ", "currency": "AUD"},
"AP": {"exchange": "SNFE", "name": "SPI 200", "tick": 1.0, "value": 25.00, "mult": 25, "months": "HMUZ", "currency": "AUD"},
"YT": {"exchange": "SNFE", "name": "10-Year Australian Bond", "tick": 0.005, "value": 50.00, "mult": 10000, "months": "HMUZ", "currency": "AUD"},
"XT": {"exchange": "SNFE", "name": "3-Year Australian Bond", "tick": 0.01, "value": 100.00, "mult": 10000, "months": "HMUZ", "currency": "AUD"},

# KOREA (KSE)
"K200": {"exchange": "KSE", "name": "KOSPI 200", "tick": 0.05, "value": 25000, "mult": 500000, "months": "HMUZ", "currency": "KRW"},
"K200M": {"exchange": "KSE", "name": "Mini KOSPI 200", "tick": 0.02, "value": 2000, "mult": 100000, "months": "FGHJKMNQUVXZ", "currency": "KRW"},

# TAIWAN (TAIFEX)
"TX": {"exchange": "TAIFEX", "name": "TAIEX Index", "tick": 1.0, "value": 200, "mult": 200, "months": "FGHJKMNQUVXZ", "currency": "TWD"},
"MTX": {"exchange": "TAIFEX", "name": "Mini TAIEX", "tick": 1.0, "value": 50, "mult": 50, "months": "FGHJKMNQUVXZ", "currency": "TWD"},

# MEXICO (MEXDER)
"IPC": {"exchange": "MEXDER", "name": "IPC Index", "tick": 5.0, "value": 50, "mult": 10, "months": "HMUZ", "currency": "MXN"},
}

# ============================================================================
# FOREX PAIRS
# ============================================================================

FOREX_PAIRS = [
# Major Pairs
("EUR", "USD"), ("GBP", "USD"), ("USD", "JPY"), ("USD", "CHF"),
("AUD", "USD"), ("USD", "CAD"), ("NZD", "USD"),

# Cross Pairs - EUR
("EUR", "GBP"), ("EUR", "JPY"), ("EUR", "CHF"), ("EUR", "AUD"),
("EUR", "CAD"), ("EUR", "NZD"), ("EUR", "SEK"), ("EUR", "NOK"),
("EUR", "DKK"), ("EUR", "PLN"), ("EUR", "HUF"), ("EUR", "CZK"),
("EUR", "TRY"), ("EUR", "ZAR"), ("EUR", "MXN"), ("EUR", "SGD"),
("EUR", "HKD"),

# Cross Pairs - GBP
("GBP", "JPY"), ("GBP", "CHF"), ("GBP", "AUD"), ("GBP", "CAD"),
("GBP", "NZD"), ("GBP", "SEK"), ("GBP", "NOK"), ("GBP", "DKK"),
("GBP", "PLN"), ("GBP", "HUF"), ("GBP", "CZK"), ("GBP", "TRY"),
("GBP", "ZAR"), ("GBP", "MXN"), ("GBP", "SGD"), ("GBP", "HKD"),

# Cross Pairs - JPY
("AUD", "JPY"), ("CAD", "JPY"), ("CHF", "JPY"), ("NZD", "JPY"),
("SGD", "JPY"), ("HKD", "JPY"), ("SEK", "JPY"), ("NOK", "JPY"),
("MXN", "JPY"), ("ZAR", "JPY"), ("TRY", "JPY"),

# Cross Pairs - CHF
("AUD", "CHF"), ("CAD", "CHF"), ("NZD", "CHF"), ("SGD", "CHF"),

# Cross Pairs - AUD
("AUD", "CAD"), ("AUD", "NZD"), ("AUD", "SGD"), ("AUD", "HKD"),

# Cross Pairs - CAD
("CAD", "CHF"), ("CAD", "SGD"), ("CAD", "HKD"),

# Cross Pairs - NZD
("NZD", "CAD"), ("NZD", "SGD"), ("NZD", "HKD"),

# USD Pairs with EM
("USD", "SGD"), ("USD", "HKD"), ("USD", "SEK"), ("USD", "NOK"),
("USD", "DKK"), ("USD", "PLN"), ("USD", "HUF"), ("USD", "CZK"),
("USD", "TRY"), ("USD", "ZAR"), ("USD", "MXN"), ("USD", "BRL"),
("USD", "RUB"), ("USD", "INR"), ("USD", "CNH"), ("USD", "CNY"),
("USD", "KRW"), ("USD", "TWD"), ("USD", "THB"), ("USD", "IDR"),
("USD", "MYR"), ("USD", "PHP"), ("USD", "VND"), ("USD", "AED"),
("USD", "SAR"), ("USD", "ILS"), ("USD", "EGP"), ("USD", "NGN"),
("USD", "KES"), ("USD", "CLP"), ("USD", "COP"), ("USD", "PEN"),
("USD", "ARS"),

# Metals Spot
("XAU", "USD"), ("XAG", "USD"), ("XAU", "EUR"), ("XAG", "EUR"),
("XAU", "GBP"), ("XAG", "GBP"), ("XAU", "JPY"), ("XAG", "JPY"),
("XAU", "AUD"), ("XAG", "AUD"), ("XAU", "CHF"), ("XPT", "USD"),
("XPD", "USD"),
]

# ============================================================================
# INDICES
# ============================================================================

INDICES = {
# US Indices
"SPX": {"exchange": "CBOE", "name": "S&P 500 Index", "currency": "USD"},
"NDX": {"exchange": "NASDAQ", "name": "Nasdaq-100 Index", "currency": "USD"},
"DJI": {"exchange": "CME", "name": "Dow Jones Industrial Average", "currency": "USD"},
"DJIA": {"exchange": "CME", "name": "Dow Jones Industrial Average", "currency": "USD"},
"RUT": {"exchange": "RUSSELL", "name": "Russell 2000 Index", "currency": "USD"},
"VIX": {"exchange": "CBOE", "name": "CBOE Volatility Index", "currency": "USD"},
"OEX": {"exchange": "CBOE", "name": "S&P 100 Index", "currency": "USD"},
"MID": {"exchange": "CBOE", "name": "S&P MidCap 400 Index", "currency": "USD"},
"SML": {"exchange": "CBOE", "name": "S&P SmallCap 600 Index", "currency": "USD"},
"SOX": {"exchange": "PHLX", "name": "PHLX Semiconductor Index", "currency": "USD"},
"BKX": {"exchange": "PHLX", "name": "KBW Bank Index", "currency": "USD"},
"XLF": {"exchange": "NYSE", "name": "Financial Select Sector", "currency": "USD"},
"XLE": {"exchange": "NYSE", "name": "Energy Select Sector", "currency": "USD"},
"XLK": {"exchange": "NYSE", "name": "Technology Select Sector", "currency": "USD"},
"XLV": {"exchange": "NYSE", "name": "Health Care Select Sector", "currency": "USD"},
"XLI": {"exchange": "NYSE", "name": "Industrial Select Sector", "currency": "USD"},
"XLY": {"exchange": "NYSE", "name": "Consumer Discretionary", "currency": "USD"},
"XLP": {"exchange": "NYSE", "name": "Consumer Staples", "currency": "USD"},
"XLB": {"exchange": "NYSE", "name": "Materials Select Sector", "currency": "USD"},
"XLU": {"exchange": "NYSE", "name": "Utilities Select Sector", "currency": "USD"},
"XLRE": {"exchange": "NYSE", "name": "Real Estate Select Sector", "currency": "USD"},
"XLC": {"exchange": "NYSE", "name": "Communication Services", "currency": "USD"},

# European Indices
"DAX": {"exchange": "DTB", "name": "DAX 40 Index", "currency": "EUR"},
"ESTX50": {"exchange": "DTB", "name": "Euro Stoxx 50", "currency": "EUR"},
"FTSE": {"exchange": "LSE", "name": "FTSE 100 Index", "currency": "GBP"},
"CAC40": {"exchange": "SBF", "name": "CAC 40 Index", "currency": "EUR"},
"AEX": {"exchange": "AEB", "name": "AEX Index", "currency": "EUR"},
"SMI": {"exchange": "EBS", "name": "Swiss Market Index", "currency": "CHF"},
"IBEX35": {"exchange": "SIBE", "name": "IBEX 35 Index", "currency": "EUR"},
"FTSEMIB": {"exchange": "BVME", "name": "FTSE MIB Index", "currency": "EUR"},
"BEL20": {"exchange": "BELFOX", "name": "BEL 20 Index", "currency": "EUR"},
"ATX": {"exchange": "VSE", "name": "ATX Index", "currency": "EUR"},
"OMXS30": {"exchange": "OMS", "name": "OMX Stockholm 30", "currency": "SEK"},
"OMXC20": {"exchange": "CSE", "name": "OMX Copenhagen 20", "currency": "DKK"},
"OMXH25": {"exchange": "HEX", "name": "OMX Helsinki 25", "currency": "EUR"},
"PSI20": {"exchange": "BVL", "name": "PSI 20 Index", "currency": "EUR"},
"WIG20": {"exchange": "WSE", "name": "WIG 20 Index", "currency": "PLN"},

# Asian Indices
"N225": {"exchange": "OSE.JPN", "name": "Nikkei 225 Index", "currency": "JPY"},
"TOPX": {"exchange": "TSE", "name": "TOPIX Index", "currency": "JPY"},
"HSI": {"exchange": "HKFE", "name": "Hang Seng Index", "currency": "HKD"},
"HSCEI": {"exchange": "HKFE", "name": "Hang Seng China Enterprises", "currency": "HKD"},
"KOSPI2": {"exchange": "KSE", "name": "KOSPI 200 Index", "currency": "KRW"},
"TWSE": {"exchange": "TWSE", "name": "Taiwan Weighted Index", "currency": "TWD"},
"STI": {"exchange": "SGX", "name": "Straits Times Index", "currency": "SGD"},
"AS51": {"exchange": "ASX", "name": "S&P/ASX 200 Index", "currency": "AUD"},
"NIFTY50": {"exchange": "NSE", "name": "Nifty 50 Index", "currency": "INR"},
"SENSEX": {"exchange": "BSE", "name": "BSE Sensex Index", "currency": "INR"},
"SHCOMP": {"exchange": "SEHK", "name": "Shanghai Composite", "currency": "CNY"},
"SZCOMP": {"exchange": "SEHK", "name": "Shenzhen Composite", "currency": "CNY"},
"CSI300": {"exchange": "SEHK", "name": "CSI 300 Index", "currency": "CNY"},
"KLCI": {"exchange": "MYX", "name": "FTSE Bursa Malaysia KLCI", "currency": "MYR"},
"SET": {"exchange": "BKK", "name": "SET Index", "currency": "THB"},
"JCI": {"exchange": "JSX", "name": "Jakarta Composite", "currency": "IDR"},
"PSEI": {"exchange": "PSE", "name": "PSEi Index", "currency": "PHP"},

# Other Indices
"BVSP": {"exchange": "BVMF", "name": "Bovespa Index", "currency": "BRL"},
"MERVAL": {"exchange": "BYMA", "name": "MERVAL Index", "currency": "ARS"},
"IPC": {"exchange": "MEXDER", "name": "IPC Index", "currency": "MXN"},
"TA35": {"exchange": "TASE", "name": "TA-35 Index", "currency": "ILS"},
"RTSI": {"exchange": "MOEX", "name": "RTS Index", "currency": "USD"},
}

# ============================================================================
# STOCK LISTS - HARDCODED MAJOR STOCKS BY COUNTRY
# ============================================================================

# Top US Stocks (will also download from NASDAQ FTP)
US_STOCKS_MAJOR = [
"AAPL", "MSFT", "GOOGL", "GOOG", "AMZN", "NVDA", "META", "TSLA", "BRK.B", "UNH",
"XOM", "JNJ", "JPM", "V", "PG", "MA", "HD", "CVX", "MRK", "ABBV",
"LLY", "PEP", "KO", "COST", "AVGO", "WMT", "MCD", "CSCO", "TMO", "ACN",
"ABT", "DHR", "NEE", "CRM", "LIN", "NKE", "TXN", "PM", "BMY", "UNP",
"ORCL", "RTX", "QCOM", "HON", "INTC", "AMD", "IBM", "SPGI", "SBUX", "GS",
"CAT", "BA", "GE", "DE", "AXP", "MDLZ", "LOW", "ISRG", "BKNG", "AMGN",
"ADI", "GILD", "SYK", "VRTX", "PLD", "LRCX", "MMC", "ADP", "REGN", "ZTS",
"CB", "NOW", "PANW", "CME", "C", "BDX", "CI", "EQIX", "MO", "PGR",
"TJX", "DUK", "SO", "BSX", "ITW", "NOC", "ICE", "WM", "SHW", "FI",
"CL", "USB", "EOG", "SCHW", "HUM", "SLB", "MU", "MCO", "SNPS", "PNC",
"KLAC", "CDNS", "APD", "CSX", "CCI", "AON", "TGT", "MSI", "TT", "EMR",
"MAR", "BK", "ORLY", "GM", "F", "ADSK", "ROP", "FCX", "NSC", "AZO",
"OXY", "MET", "CHTR", "DXCM", "AJG", "FTNT", "NEM", "ECL", "SRE", "D",
"AFL", "TRV", "PSX", "CMG", "JCI", "KMB", "PEG", "AEP", "O", "PAYX",
"GWW", "AIG", "HLT", "SPG", "NUE", "MNST", "TEL", "YUM", "MSCI", "CTAS",
"VLO", "PCAR", "KDP", "ADM", "MCHP", "LHX", "RCL", "IDXX", "EW", "CMI",
"PRU", "FAST", "CARR", "COF", "STZ", "A", "KR", "ALL", "DLR", "AME",
"ROST", "AMP", "HCA", "OKE", "PSA", "CTSH", "BKR", "DOW", "GIS", "OTIS",
"WELL", "IQV", "PCG", "CPRT", "HAL", "PPG", "VRSK", "WBD", "GEHC", "DVN",
"EA", "DFS", "HSY", "EXC", "ODFL", "FTV", "KEYS", "XEL", "DLTR", "KHC",
"DD", "ALB", "MLM", "APTV", "SYY", "EFX", "ED", "CTVA", "CBRE", "MTD",
"ON", "GPN", "WEC", "WAB", "VMC", "BIIB", "ROK", "WTW", "GLW", "DHI",
"AWK", "ANSS", "TSCO", "CHD", "TROW", "BRO", "FANG", "CDW", "HPE", "EBAY",
"ZBH", "CAH", "WST", "IRM", "FE", "EIX", "IT", "PWR", "WY", "FLT",
"VRSN", "HPQ", "LEN", "SBAC", "FITB", "ULTA", "PPL", "TDY", "HBAN", "ES",
"MTB", "ETR", "CINF", "DTE", "STLD", "AVB", "BAX", "K", "RJF", "HIG",
"RF", "CLX", "CNC", "MOH", "TRMB", "GPC", "WAT", "DG", "PFG", "HOLX",
"EXPD", "DRI", "AES", "AEE", "NTRS", "AKAM", "LYB", "MAA", "CSGP", "VTR",
"INVH", "SWKS", "LUV", "TER", "LYV", "POOL", "EQR", "MPWR", "BBY", "DAL",
"PKI", "STT", "IEX", "BALL", "STE", "BF.B", "ILMN", "ALGN", "MKC", "EXPE",
"SNA", "DGX", "CF", "CPT", "NVR", "J", "ARE", "JBHT", "FDS", "TXT",
"ENPH", "SEDG", "PAYC", "NDAQ", "MAS", "VTRS", "IP", "CE", "WRB", "PHM",
"PTC", "UAL", "KIM", "BG", "INCY", "TECH", "OMC", "PEAK", "HST", "WRK",
"CHRW", "UDR", "EVRG", "ZBRA", "CRL", "REG", "JKHY", "WDC", "HWM", "LDOS",
"CBOE", "NDSN", "SWK", "ATO", "L", "NI", "TFX", "IPG", "FFIV", "BWA",
"AAL", "ROL", "MGM", "TPR", "RHI", "LKQ", "APA", "HRL", "CAG", "TAP",
"HII", "BXP", "QRVO", "CZR", "GL", "AIZ", "IVZ", "XRAY", "WYNN", "MTCH",
"WHR", "PNW", "ALLE", "SEE", "NWS", "NWSA", "DVA", "BBWI", "MOS", "PARA",
"LUMN", "VFC", "DISH", "ZION", "MHK", "FRT", "BEN", "NCLH", "CPB", "NRG",
"ETSY", "PNR", "CMA", "RNR", "FOXA", "FOX",
]

# India NSE Major Stocks
INDIA_NSE_STOCKS = [
"RELIANCE", "TCS", "HDFCBANK", "INFY", "ICICIBANK", "HINDUNILVR", "SBIN", "BHARTIARTL",
"ITC", "KOTAKBANK", "LT", "AXISBANK", "HCLTECH", "ASIANPAINT", "MARUTI", "BAJFINANCE",
"SUNPHARMA", "TITAN", "ULTRACEMCO", "NTPC", "WIPRO", "TATAMOTORS", "M&M", "ONGC",
"NESTLEIND", "JSWSTEEL", "POWERGRID", "TATASTEEL", "ADANIENT", "ADANIPORTS", "DIVISLAB",
"BAJAJFINSV", "COALINDIA", "TECHM", "HDFCLIFE", "SBILIFE", "DRREDDY", "EICHERMOT",
"HINDALCO", "INDUSINDBK", "APOLLOHOSP", "CIPLA", "GRASIM", "BRITANNIA", "TATACONSUM",
"HEROMOTOCO", "DABUR", "BPCL", "VEDL", "SHREECEM", "PIIND", "HAVELLS", "GODREJCP",
"MUTHOOTFIN", "BERGEPAINT", "ICICIPRULI", "SIEMENS", "GAIL", "BAJAJ-AUTO", "IOC",
"HDFC", "TATAPOWER", "ACC", "AMBUJACEM", "BIOCON", "BANDHANBNK", "PFC", "RECLTD",
"TORNTPHARM", "COLPAL", "AUROPHARMA", "CADILAHC", "MARICO", "LUPIN", "SRTRANSFIN",
"BOSCHLTD", "MINDTREE", "MPHASIS", "LTTS", "PERSISTENT", "COFORGE", "HAPPSTMNDS",
"BANKBARODA", "PNB", "CANBK", "UNIONBANK", "FEDERALBNK", "IDFCFIRSTB", "RBLBANK",
"AUBANK", "NAUKRI", "ZOMATO", "PAYTM", "POLICYBZR", "DELHIVERY", "NYKAA",
"ADANIGREEN", "ADANITRANS", "TRENT", "PAGEIND", "BATAINDIA", "JUBLFOOD", "INDIGO",
"IRCTC", "Dixon", "POLYCAB", "VOLTAS", "BLUEDART", "ASTRAL", "SUPREMEIND",
"AARTIIND", "ATUL", "DEEPAKNTR", "NAVINFLUOR", "SRF", "TATAELXSI", "LTIM",
"MCDOWELL-N", "UBL", "VARUNBEVER", "RADICO", "CROMPTON", "BLUESTARCO", "WHIRLPOOL",
"HONAUT", "JKCEMENT", "RAMCOCEM", "DALBHARAT", "INDIACEM", "HEIDELBERG", "OBEROIRLTY",
"DLF", "GODREJPROP", "PRESTIGE", "SOBHA", "PHOENIXLTD", "LODHA", "BRIGADE",
"BHARATFORG", "MOTHERSON", "EXIDEIND", "AMARAJABAT", "BALKRISIND", "MRF", "APOLLOTYRE",
"CEATLTD", "JKTYRE", "SUNDRMFAST", "ENDURANCE", "SUPRAJIT", "SCHAEFFLER", "TIINDIA",
"SKFINDIA", "TIMKEN", "FIVESTAR", "CHOLAFIN", "SHRIRAMFIN", "LICHSGFIN", "CANFINHOME",
"MANAPPURAM", "IIFL", "HDFCAMC", "NIPPONIND", "ICICIGI", "SBICARD", "STARHEALTH",
"MAXHEALTH", "METROPOLIS", "LALPATHLAB", "THYROCARE", "FORTIS", "NH", "KIMS",
"ALKEM", "GLENMARK", "NATCOPHARMA", "GRANULES", "LAURUSLABS", "SOLARA", "ABBOTINDIA",
"PFIZER", "SANOFI", "GLAXO", "AJANTPHARM", "IPCALAB", "TORNTPOWER", "TATACOMM",
"IDEA", "MTNL", "ROUTE", "GMRINFRA", "ADANIPOWER", "CESC", "NHPC", "SJVN",
"BEL", "HAL", "BDL", "GRINDWELL", "CARBORUNIV", "FINCABLES", "CENTURYPLY",
"APLAPOLLO", "JINDALSTEL", "SAIL", "NMDC", "MOIL", "COALINDIA", "HINDZINC",
"NATIONALUM", "WELCORP", "JINDALSAW", "RATNAMANI", "JSWENERGY", "TATAPOWER",
]

# Japan TSE Major Stocks (use stock codes)
JAPAN_TSE_STOCKS = [
# Auto
("7203", "TOYOTA"), ("7267", "HONDA"), ("7201", "NISSAN"), ("7270", "SUBARU"),
("7269", "SUZUKI"), ("7211", "MITSUBISHI-MOTORS"), ("7261", "MAZDA"), ("7272", "YAMAHA-MOTOR"),

# Tech/Electronics
("6758", "SONY"), ("6861", "KEYENCE"), ("6954", "FANUC"), ("6594", "NIDEC"),
("6501", "HITACHI"), ("6502", "TOSHIBA"), ("6752", "PANASONIC"), ("6503", "MITSUBISHI-ELEC"),
("6702", "FUJITSU"), ("6701", "NEC"), ("6762", "TDK"), ("6981", "MURATA"),
("6902", "DENSO"), ("6723", "RENESAS"), ("6857", "ADVANTEST"), ("7735", "SCREEN"),
("8035", "TOKYO-ELECTRON"), ("6146", "DISCO"), ("6273", "SMC"), ("6367", "DAIKIN"),

# Finance
("8306", "MITSUBISHI-UFJ"), ("8316", "SUMITOMO-MITSUI"), ("8411", "MIZUHO"),
("8766", "TOKIO-MARINE"), ("8725", "MS&AD"), ("8630", "SOMPO"), ("8750", "DAI-ICHI-LIFE"),
("8795", "T&D"), ("8697", "JPX"), ("8604", "NOMURA"), ("8601", "DAIWA"),

# Trading Companies
("8058", "MITSUBISHI-CORP"), ("8031", "MITSUI"), ("8001", "ITOCHU"),
("8053", "SUMITOMO-CORP"), ("8002", "MARUBENI"), ("8015", "TOYOTA-TSUSHO"),

# Retail/Consumer
("9983", "FAST-RETAILING"), ("7974", "NINTENDO"), ("4063", "SHIN-ETSU"),
("4502", "TAKEDA"), ("4503", "ASTELLAS"), ("4519", "CHUGAI"), ("4568", "DAIICHI-SANKYO"),
("4578", "OTSUKA"), ("4523", "EISAI"), ("6869", "SYSMEX"),

# Telecom/Tech Services
("9432", "NTT"), ("9433", "KDDI"), ("9434", "SOFTBANK-CORP"), ("9984", "SOFTBANK-GROUP"),
("4385", "MERCARI"), ("4755", "RAKUTEN"), ("2413", "M3"), ("3659", "NEXON"),

# Industrial
("7011", "MITSUBISHI-HEAVY"), ("7012", "KAWASAKI-HEAVY"), ("7013", "IHI"),
("6301", "KOMATSU"), ("6305", "HITACHI-CONSTR"), ("6326", "KUBOTA"),
("5401", "NIPPON-STEEL"), ("5411", "JFE"), ("5406", "KOBE-STEEL"),
("3407", "ASAHI-KASEI"), ("4005", "SUMITOMO-CHEM"), ("4188", "MITSUBISHI-CHEM"),

# Real Estate/Construction
("1925", "DAIWA-HOUSE"), ("1928", "SEKISUI-HOUSE"), ("8801", "MITSUI-FUDOSAN"),
("8802", "MITSUBISHI-ESTATE"), ("8830", "SUMITOMO-REALTY"),

# Transport/Logistics
("9020", "JR-EAST"), ("9021", "JR-WEST"), ("9022", "JR-CENTRAL"),
("9101", "NIPPON-YUSEN"), ("9104", "MITSUI-OSK"), ("9107", "KAWASAKI-KISEN"),
("9201", "JAL"), ("9202", "ANA"),

# Other Major
("4901", "FUJIFILM"), ("4452", "KAO"), ("4911", "SHISEIDO"), ("2802", "AJINOMOTO"),
("2914", "JT"), ("2503", "KIRIN"), ("2502", "ASAHI-GROUP"), ("2501", "SAPPORO"),
("7751", "CANON"), ("4543", "TERUMO"), ("7733", "OLYMPUS"), ("4151", "KYOWA-KIRIN"),
("9062", "NIPPON-EXPRESS"), ("9064", "YAMATO"), ("6098", "RECRUIT"),
]

# China/Hong Kong Stocks (HKEX listed)
CHINA_HK_STOCKS = [
# Tech Giants (Hong Kong Listed)
("0700", "TENCENT"), ("9988", "ALIBABA"), ("3690", "MEITUAN"), ("9618", "JD"),
("9999", "NETEASE"), ("1810", "XIAOMI"), ("9888", "BAIDU"), ("2015", "LI-AUTO"),
("9866", "NIO"), ("9868", "XPENG"), ("0981", "SMIC"), ("1024", "KUAISHOU"),
("2382", "SUNNY-OPTICAL"), ("0285", "BYD-ELEC"), ("1211", "BYD"),

# Finance
("1398", "ICBC"), ("0939", "CCB"), ("3988", "BOC"), ("1288", "ABC"),
("3968", "CMB"), ("0998", "CITIC-BANK"), ("1658", "PSBC"), ("6881", "CGS"),
("6030", "CITIC-SEC"), ("6886", "HTSC"), ("2318", "PING-AN"), ("1336", "NCI"),
("2628", "CHINA-LIFE"), ("1339", "PICC"), ("2601", "CPIC"), ("0966", "CITIC"),
("0388", "HKEX"), ("2388", "BOC-HK"),

# Energy/Resources
("0857", "PETROCHINA"), ("0386", "SINOPEC"), ("0883", "CNOOC"), ("1088", "CHINA-SHENHUA"),
("2899", "ZIJIN"), ("3993", "CMOC"), ("1898", "CHINA-COAL"), ("6883", "MEILAN"),

# Telecom
("0941", "CHINA-MOBILE"), ("0762", "CHINA-UNICOM"), ("0728", "CHINA-TELECOM"),

# Consumer
("0027", "GALAXY-ENT"), ("1928", "SANDS-CHINA"), ("0880", "SJM"), ("2282", "MGM-CHINA"),
("1169", "HAIER"), ("6060", "ZH-INTER"), ("2319", "MENGNIU"), ("0291", "CR-BEER"),
("0168", "TSINGTAO"), ("0220", "UNI-PRESIDENT"), ("0322", "TINGYI"), ("1044", "HENGAN"),
("0151", "WANT-WANT"), ("2020", "ANTA"), ("1361", "361-DEGREES"), ("2331", "LI-NING"),
("6110", "TOPSPORTS"),

# Real Estate
("1109", "CR-LAND"), ("0688", "COLI"), ("0960", "LONGFOR"), ("1918", "SUNAC"),
("2007", "COUNTRY-GARDEN"), ("3333", "EVERGRANDE"), ("0813", "SHIMAO"),
("0884", "CIFI"), ("0123", "YUEXIU"), ("0119", "POLY-PROP"),

# Industrial
("0914", "CONCH-CEMENT"), ("3323", "CNBM"), ("2600", "CHALCO"), ("0358", "JIANGXI-COPPER"),
("1800", "CHINA-COMMS"), ("0390", "CHINA-RAIL-GP"), ("0688", "COLI"),
("1186", "CHINA-RAIL-CONS"), ("3311", "CHINA-STATE"), ("1766", "CSR"), ("1668", "CCC"),

# Healthcare/Biotech
("1093", "CSPC"), ("1177", "SINO-BIOPHARMA"), ("2269", "WUXI-BIO"), ("6160", "BEIGENE"),
("1833", "INKE"), ("2196", "SHANGHAI-PHARMA"),

# Utilities
("0836", "CHINA-RES-POWER"), ("1816", "CGN-POWER"), ("0916", "CHINA-LONGYUAN"),
("0392", "BEIJING-ENT"),

# Others
("0489", "DONGFENG"), ("2238", "GAC"), ("1114", "BRILLIANCE"),
("0175", "GEELY"), ("0267", "CITIC-PACIFIC"),
]

# ============================================================================
# CFDs (Contracts for Difference)
# ============================================================================

# Index CFDs - IB offers these on SMART exchange
INDEX_CFDS = [
# US Index CFDs
("IBUS500", "US 500 Index CFD", "USD"),
("IBUS30", "US 30 Index CFD", "USD"),
("IBUST100", "US Tech 100 Index CFD", "USD"),
("IBUS2000", "US 2000 Index CFD", "USD"),

# European Index CFDs
("IBDE40", "Germany 40 Index CFD", "EUR"),
("IBEU50", "Europe 50 Index CFD", "EUR"),
("IBGB100", "UK 100 Index CFD", "GBP"),
("IBFR40", "France 40 Index CFD", "EUR"),
("IBES35", "Spain 35 Index CFD", "EUR"),
("IBNL25", "Netherlands 25 Index CFD", "EUR"),
("IBCH20", "Switzerland 20 Index CFD", "CHF"),

# Asian Index CFDs
("IBJP225", "Japan 225 Index CFD", "JPY"),
("IBHK50", "Hong Kong 50 Index CFD", "HKD"),
("IBAU200", "Australia 200 Index CFD", "AUD"),
]

# Forex CFDs
FOREX_CFDS = [
("EUR.USD", "EUR/USD CFD", "USD"),
("GBP.USD", "GBP/USD CFD", "USD"),
("USD.JPY", "USD/JPY CFD", "JPY"),
("USD.CHF", "USD/CHF CFD", "CHF"),
("AUD.USD", "AUD/USD CFD", "USD"),
("USD.CAD", "USD/CAD CFD", "CAD"),
("NZD.USD", "NZD/USD CFD", "USD"),
("EUR.GBP", "EUR/GBP CFD", "GBP"),
("EUR.JPY", "EUR/JPY CFD", "JPY"),
("GBP.JPY", "GBP/JPY CFD", "JPY"),
("EUR.CHF", "EUR/CHF CFD", "CHF"),
("AUD.JPY", "AUD/JPY CFD", "JPY"),
("EUR.AUD", "EUR/AUD CFD", "AUD"),
("GBP.CHF", "GBP/CHF CFD", "CHF"),
("CAD.JPY", "CAD/JPY CFD", "JPY"),
("NZD.JPY", "NZD/JPY CFD", "JPY"),
("CHF.JPY", "CHF/JPY CFD", "JPY"),
("EUR.CAD", "EUR/CAD CFD", "CAD"),
("AUD.CAD", "AUD/CAD CFD", "CAD"),
("AUD.NZD", "AUD/NZD CFD", "NZD"),
("EUR.NZD", "EUR/NZD CFD", "NZD"),
("GBP.AUD", "GBP/AUD CFD", "AUD"),
("GBP.CAD", "GBP/CAD CFD", "CAD"),
("GBP.NZD", "GBP/NZD CFD", "NZD"),
("USD.SGD", "USD/SGD CFD", "SGD"),
("USD.HKD", "USD/HKD CFD", "HKD"),
("USD.CNH", "USD/CNH CFD", "CNH"),
("USD.MXN", "USD/MXN CFD", "MXN"),
("USD.ZAR", "USD/ZAR CFD", "ZAR"),
("USD.TRY", "USD/TRY CFD", "TRY"),
("EUR.TRY", "EUR/TRY CFD", "TRY"),
("USD.SEK", "USD/SEK CFD", "SEK"),
("USD.NOK", "USD/NOK CFD", "NOK"),
("EUR.SEK", "EUR/SEK CFD", "SEK"),
("EUR.NOK", "EUR/NOK CFD", "NOK"),
]

# Commodity CFDs
COMMODITY_CFDS = [
("XAUUSD", "Gold CFD", "USD"),
("XAGUSD", "Silver CFD", "USD"),
("COIL", "Brent Crude Oil CFD", "USD"),
("NGAS", "Natural Gas CFD", "USD"),
("COPPER", "Copper CFD", "USD"),
]

# ============================================================================
# ETFs - Major ETFs by Region
# ============================================================================

# US ETFs - Most Popular
US_ETFS = [
# S&P 500 ETFs
"SPY", "IVV", "VOO", "SPLG",
# Nasdaq/Tech ETFs
"QQQ", "QQQM", "VGT", "XLK", "ARKK", "ARKG", "ARKW", "ARKF", "ARKQ",
# Dow Jones ETFs
"DIA",
# Russell 2000/Small Cap
"IWM", "VB", "IJR", "SCHA", "VBK", "VBR",
# Total Market
"VTI", "ITOT", "SCHB", "SPTM",
# Growth/Value
"VUG", "IWF", "VOOG", "VTV", "IWD", "VOOV", "SCHG", "SCHV",
# International
"VEA", "IEFA", "EFA", "VWO", "IEMG", "EEM", "VXUS", "IXUS",
# Sector ETFs
"XLF", "XLE", "XLV", "XLI", "XLY", "XLP", "XLB", "XLU", "XLRE", "XLC",
"VFH", "VDE", "VHT", "VIS", "VCR", "VDC", "VAW", "VPU", "VNQ",
# Bond ETFs
"BND", "AGG", "TLT", "IEF", "SHY", "LQD", "HYG", "JNK", "VCIT", "VCSH",
"TIP", "VTIP", "SCHZ", "BSV", "BIV", "BLV", "GOVT", "MUB", "SUB",
# Commodity ETFs
"GLD", "IAU", "SLV", "GDX", "GDXJ", "USO", "UNG", "DBC", "DBA", "PDBC",
# Dividend ETFs
"VIG", "SCHD", "DVY", "VYM", "HDV", "DGRO", "NOBL", "SDY", "SPHD", "SPYD",
# Leveraged/Inverse ETFs
"TQQQ", "SQQQ", "SPXL", "SPXS", "UPRO", "SDS", "SSO", "QLD", "PSQ", "SH",
"SOXL", "SOXS", "TNA", "TZA", "LABU", "LABD", "FNGU", "FNGD",
# Thematic ETFs
"SOXX", "SMH", "HACK", "CIBR", "BOTZ", "ROBO", "FINX", "IPAY", "BLOK",
"ICLN", "TAN", "QCLN", "PBW", "LIT", "DRIV", "IDRV",
"IBB", "XBI", "ARKG", "GNOM",
"JETS", "AWAY", "NERD", "BETZ", "SOCL", "SKYY", "CLOU", "WCLD",
# Real Estate/REITs
"VNQ", "VNQI", "IYR", "RWR", "SCHH", "REET", "REM", "MORT",
# Volatility
"VIXY", "SVXY", "VXX", "UVXY",
# Currency
"UUP", "FXE", "FXY", "FXB", "FXC", "FXA", "CYB",
# ESG
"ESGU", "ESGV", "ESGE", "SUSL", "SUSA", "DSI", "CRBN",
# Minimum Volatility
"USMV", "SPLV", "EFAV", "EEMV",
# Quality/Factor
"QUAL", "SPHQ", "DGRW", "FQAL", "JQUA",
# Momentum
"MTUM", "PDP", "DWAS",
# Multi-Factor
"LRGF", "SMLF", "INTF", "EMGF",
]

# Europe ETFs (traded on European exchanges via IB)
EUROPE_ETFS = [
# iShares Europe
("CSPX", "LN", "iShares Core S&P 500 UCITS", "USD"),
("EQQQ", "LN", "Invesco Nasdaq-100 UCITS", "USD"),
("SWDA", "LN", "iShares Core MSCI World UCITS", "USD"),
("IWDA", "AS", "iShares Core MSCI World UCITS", "EUR"),
("VWRL", "LN", "Vanguard FTSE All-World UCITS", "USD"),
("VWCE", "DE", "Vanguard FTSE All-World UCITS", "EUR"),
("VUAA", "LN", "Vanguard S&P 500 UCITS", "USD"),
("VUSA", "LN", "Vanguard S&P 500 UCITS", "GBP"),
("IEEM", "LN", "iShares MSCI EM UCITS", "USD"),
("EMIM", "AS", "iShares Core MSCI EM IMI UCITS", "EUR"),
("SXR8", "DE", "iShares Core S&P 500 UCITS", "EUR"),
("EUNL", "DE", "iShares Core MSCI World UCITS", "EUR"),
("EXXT", "DE", "iShares NASDAQ-100 UCITS", "EUR"),
("IUSA", "DE", "iShares S&P 500 UCITS", "EUR"),
("IBCX", "DE", "iShares Euro Corp Bond UCITS", "EUR"),
("IEAG", "DE", "iShares Core Euro Corp Bond UCITS", "EUR"),
("IGLN", "LN", "iShares Physical Gold ETC", "USD"),
("SGLD", "LN", "Invesco Physical Gold ETC", "USD"),
("PHAU", "LN", "WisdomTree Physical Gold", "USD"),
("SXRV", "DE", "iShares Automation & Robotics UCITS", "EUR"),
("2B76", "DE", "iShares Global Clean Energy UCITS", "EUR"),
("IQQH", "DE", "iShares Global Clean Energy UCITS", "EUR"),
("ECAR", "LN", "iShares Electric Vehicles UCITS", "USD"),
("DGTL", "LN", "iShares Digitalisation UCITS", "USD"),
("HEAL", "LN", "iShares Healthcare Innovation UCITS", "USD"),
("RBOT", "LN", "iShares Automation & Robotics UCITS", "USD"),
]

# Asia/Pacific ETFs
ASIA_ETFS = [
# Japan ETFs
("1306", "TSEJ", "TOPIX ETF", "JPY"),
("1321", "TSEJ", "Nikkei 225 ETF", "JPY"),
("1330", "TSEJ", "Nikkei 225 Index Fund", "JPY"),
("1320", "TSEJ", "Daiwa TOPIX ETF", "JPY"),
("1348", "TSEJ", "MAXIS TOPIX ETF", "JPY"),
("1557", "TSEJ", "SPDR S&P 500 ETF", "JPY"),
("1655", "TSEJ", "iShares S&P 500 ETF", "JPY"),
("2558", "TSEJ", "MAXIS S&P 500 ETF", "JPY"),
("2559", "TSEJ", "MAXIS All Country World ETF", "JPY"),
("1545", "TSEJ", "NEXT FUNDS NASDAQ-100", "JPY"),
("2631", "TSEJ", "MAXIS NASDAQ-100 ETF", "JPY"),

# Hong Kong ETFs
("2800", "SEHK", "Tracker Fund of Hong Kong", "HKD"),
("2801", "SEHK", "iShares China Large-Cap ETF", "HKD"),
("2823", "SEHK", "iShares FTSE A50 China ETF", "HKD"),
("2828", "SEHK", "Hang Seng Index ETF", "HKD"),
("2833", "SEHK", "Hang Seng Index ETF", "HKD"),
("3188", "SEHK", "ChinaAMC CSI 300 ETF", "HKD"),
("3033", "SEHK", "CSOP Hang Seng TECH ETF", "HKD"),
("3067", "SEHK", "iShares Hang Seng TECH ETF", "HKD"),
("9988", "SEHK", "Alibaba Group", "HKD"),

# Australia ETFs
("STW", "ASX", "SPDR S&P/ASX 200", "AUD"),
("VAS", "ASX", "Vanguard Australian Shares", "AUD"),
("IOZ", "ASX", "iShares Core S&P/ASX 200", "AUD"),
("A200", "ASX", "BetaShares Australia 200", "AUD"),
("VGS", "ASX", "Vanguard MSCI Intl Shares", "AUD"),
("IVV", "ASX", "iShares S&P 500", "AUD"),
("NDQ", "ASX", "BetaShares NASDAQ 100", "AUD"),
("ASIA", "ASX", "BetaShares Asia Tech Tigers", "AUD"),
("HACK", "ASX", "BetaShares Global Cybersecurity", "AUD"),
("ETHI", "ASX", "BetaShares Global Sustainability", "AUD"),
]

# India ETFs
INDIA_ETFS = [
("NIFTYBEES", "NSE", "Nippon India Nifty 50 BeES", "INR"),
("BANKBEES", "NSE", "Nippon India Bank BeES", "INR"),
("JUNIORBEES", "NSE", "Nippon India Junior BeES", "INR"),
("GOLDBEES", "NSE", "Nippon India Gold BeES", "INR"),
("LIQUIDBEES", "NSE", "Nippon India Liquid BeES", "INR"),
("SETFNIF50", "NSE", "SBI Nifty 50 ETF", "INR"),
("SETFNN50", "NSE", "SBI Nifty Next 50 ETF", "INR"),
("HNGSNGBEES", "NSE", "Nippon India Hang Seng BeES", "INR"),
("NETFIT", "NSE", "Nippon India ETF Nifty IT", "INR"),
("NETFPHARMA", "NSE", "Nippon India ETF Pharma", "INR"),
("MOM50", "NSE", "ICICI Nifty 50 Momentum ETF", "INR"),
("MOM100", "NSE", "ICICI Nifty 100 Momentum ETF", "INR"),
]

# Germany XETRA Stocks
GERMANY_XETRA_STOCKS = [
# DAX 40
("SAP", "SAP-SE"), ("SIE", "SIEMENS"), ("ALV", "ALLIANZ"), ("DTE", "DEUTSCHE-TELEKOM"),
("BAS", "BASF"), ("VOW3", "VOLKSWAGEN"), ("BMW", "BMW"), ("MBG", "MERCEDES-BENZ"),
("MRK", "MERCK"), ("DB1", "DEUTSCHE-BOERSE"), ("ADS", "ADIDAS"), ("BAYN", "BAYER"),
("IFX", "INFINEON"), ("MUV2", "MUNICH-RE"), ("DHL", "DHL-GROUP"), ("RWE", "RWE"),
("HEN3", "HENKEL"), ("VNA", "VONOVIA"), ("ENR", "SIEMENS-ENERGY"), ("SY1", "SYMRISE"),
("HEI", "HEIDELBERG-MAT"), ("CON", "CONTINENTAL"), ("FRE", "FRESENIUS"), ("DTG", "DAIMLER-TRUCK"),
("ZAL", "ZALANDO"), ("PUM", "PUMA"), ("HNR1", "HANNOVER-RE"), ("BEI", "BEIERSDORF"),
("1COV", "COVESTRO"), ("QIA", "QIAGEN"), ("SHL", "SIEMENS-HEALTH"), ("AIR", "AIRBUS"),
("FME", "FRESENIUS-MED"), ("MTX", "MTU-AERO"), ("SRT3", "SARTORIUS"), ("PAH3", "PORSCHE-AUTO"),
("P911", "PORSCHE"), ("RHM", "RHEINMETALL"), ("HFG", "HELLOFRESH"), ("CBK", "COMMERZBANK"),

# MDAX Selected
("EVK", "EVONIK"), ("LEG", "LEG-IMMOBILIEN"), ("FPE3", "FUCHS-PETROL"), ("BOSS", "HUGO-BOSS"),
("NDA", "AURUBIS"), ("AFX", "CARL-ZEISS"), ("HOT", "HOCHTIEF"), ("EVD", "CTS-EVENTIM"),
("LXS", "LANXESS"), ("O2D", "TELEFONICA-DE"), ("WAF", "SILTRONIC"), ("RAA", "RATIONAL"),
("GXI", "GERRESHEIMER"), ("AT1", "AROUNDTOWN"), ("TAG", "TAG-IMMOBILIEN"), ("DHER", "DELIVERY-HERO"),
("TLX", "TALANX"), ("KGX", "KION"), ("BNR", "BRENNTAG"), ("SDF", "K+S"),

# Tech/Growth
("WCH", "WACKER-CHEMIE"), ("DRW3", "DRAEGERWERK"), ("OSR", "OSRAM"), ("JEN", "JENOPTIK"),
("WAC", "WACKER-NEUSON"), ("NDX1", "NORDEX"), ("S92", "SMA-SOLAR"), ("SOW", "SOFTWARE-AG"),
("BC8", "BECHTLE"), ("AIXA", "AIXTRON"), ("UTDI", "UTD-INTERNET"), ("DLG", "DIALOG-SEMI"),

# Financial
("DBK", "DEUTSCHE-BANK"), ("LHA", "LUFTHANSA"), ("TKA", "THYSSENKRUPP"), ("G24", "SCOUT24"),
("AAD", "AMADEUS-FIRE"),
]

# ============================================================================
# HELPER FUNCTIONS
# ============================================================================

def get_contract_months(base_months: str, num_months: int = 12) -> List[str]:
"""Generate specific contract months for futures."""
contracts = []
now = datetime.now()

for i in range(num_months * 2): # Look ahead further to get enough contracts
target_date = now + timedelta(days=30 * i)
month = target_date.month
year = target_date.year

month_code = MONTH_CODES[month]
if month_code in base_months:
contract = f"{year}{month:02d}"
if contract not in contracts:
contracts.append(contract)
if len(contracts) >= num_months:
break

return contracts


def download_nasdaq_symbols() -> List[str]:
"""Download NASDAQ traded symbols from FTP."""
print(" Downloading NASDAQ symbols from FTP...")
symbols = []

try:
# Create SSL context that doesn't verify certificates (for FTP)
context = ssl.create_default_context()
context.check_hostname = False
context.verify_mode = ssl.CERT_NONE

url = "https://www.nasdaqtrader.com/dynamic/SymDir/nasdaqtraded.txt"

req = urllib.request.Request(url)
req.add_header('User-Agent', 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36')

with urllib.request.urlopen(req, timeout=30) as response:
data = response.read().decode('utf-8')

lines = data.strip().split('\n')

for line in lines[1:]: # Skip header
if '|' in line:
parts = line.split('|')
if len(parts) >= 2:
symbol = parts[1].strip()
# Filter out test symbols and warrants
if symbol and len(symbol) <= 5 and not symbol.endswith('W'):
if symbol.isalpha() or '.' in symbol:
# Skip symbols with special characters except .
if not any(c in symbol for c in ['$', '^', '*', '#']):
symbols.append(symbol)

print(f" Downloaded {len(symbols)} NASDAQ symbols")

except Exception as e:
print(f" Warning: Could not download NASDAQ symbols: {e}")
print(" Using hardcoded US stock list instead")

return symbols


def prettify_xml(elem) -> str:
"""Return a pretty-printed XML string for the Element."""
rough_string = ET.tostring(elem, 'unicode')
reparsed = minidom.parseString(rough_string)
return reparsed.toprettyxml(indent=" ")


# ============================================================================
# GLOBAL DEDUPLICATION TRACKING
# ============================================================================

# Global set to track all symbols and prevent duplicates
_generated_symbols: Set[str] = set()
_duplicates_skipped: int = 0


def reset_deduplication():
"""Reset deduplication tracking (call before generating symbols)."""
global _generated_symbols, _duplicates_skipped
_generated_symbols = set()
_duplicates_skipped = 0


def get_deduplication_stats() -> Tuple[int, int]:
"""Get deduplication stats: (unique_count, duplicates_skipped)."""
return len(_generated_symbols), _duplicates_skipped


# ============================================================================
# XML GENERATION FUNCTIONS
# ============================================================================

def create_symbol_element(
symbol: str,
description: str,
category: str,
tick_size: float,
price_format: str,
tick_value: float,
currency: str,
trade_symbol: str,
exchange: str,
security_type: str,
use_pattern: bool = False,
multiplier: float = None
) -> ET.Element:
"""Create a symbol XML element."""
sym = ET.Element("symbol")

ET.SubElement(sym, "symbol").text = symbol
ET.SubElement(sym, "description").text = description
ET.SubElement(sym, "category").text = category
ET.SubElement(sym, "tick-size").text = str(tick_size)
ET.SubElement(sym, "price-display-format").text = price_format
ET.SubElement(sym, "currency-value-per-tick").text = str(tick_value)
ET.SubElement(sym, "currency").text = currency
ET.SubElement(sym, "trade-quote-symbol").text = trade_symbol
ET.SubElement(sym, "exchange").text = exchange
ET.SubElement(sym, "security-type").text = security_type

if use_pattern:
ET.SubElement(sym, "use-pattern-matching").text = "true"

if multiplier:
ET.SubElement(sym, "multiplier").text = str(multiplier)

# Disable all visual alerts
ET.SubElement(sym, "alert-enabled").text = "0"
ET.SubElement(sym, "visual-alert-enabled").text = "0"
ET.SubElement(sym, "alert-sound-enabled").text = "0"
ET.SubElement(sym, "alert-sound-number").text = "0"

ET.SubElement(sym, "is-custom-symbol").text = "true"

return sym


def add_symbol_with_dedup(
root: ET.Element,
symbol: str,
description: str,
category: str,
tick_size: float,
price_format: str,
tick_value: float,
currency: str,
trade_symbol: str,
exchange: str,
security_type: str,
use_pattern: bool = False,
multiplier: float = None
) -> bool:
"""Add a symbol to root if not already added (global deduplication).

Returns True if symbol was added, False if it was a duplicate.
"""
global _generated_symbols, _duplicates_skipped

if symbol in _generated_symbols:
_duplicates_skipped += 1
return False

_generated_symbols.add(symbol)
elem = create_symbol_element(
symbol=symbol,
description=description,
category=category,
tick_size=tick_size,
price_format=price_format,
tick_value=tick_value,
currency=currency,
trade_symbol=trade_symbol,
exchange=exchange,
security_type=security_type,
use_pattern=use_pattern,
multiplier=multiplier
)
root.append(elem)
return True


def generate_futures_symbols(root: ET.Element) -> int:
"""Generate all futures symbols."""
print("\nGenerating FUTURES symbols...")
count = 0

for symbol, info in FUTURES.items():
exchange = info["exchange"]
name = info["name"]
tick = info["tick"]
value = info["value"]
mult = info["mult"]
months = info["months"]
currency = info["currency"]

# Determine price format based on tick size
if tick >= 1:
price_format = "0"
elif tick >= 0.1:
price_format = "1"
elif tick >= 0.01:
price_format = "2"
elif tick >= 0.001:
price_format = "3"
elif tick >= 0.0001:
price_format = "4"
elif tick >= 0.00001:
price_format = "5"
else:
price_format = "6"

# Pattern symbol
pattern_symbol = f"{symbol}-######-{exchange}"
trade_pattern = f"{symbol}-######-{exchange}-{currency}"

elem = create_symbol_element(
symbol=pattern_symbol,
description=f"{name} (Pattern)",
category=f"Futures - {exchange}",
tick_size=tick,
price_format=price_format,
tick_value=value,
currency=currency,
trade_symbol=trade_pattern,
exchange=exchange,
security_type="FUTURES",
use_pattern=True,
multiplier=mult
)
root.append(elem)
count += 1

# Generate specific contract months
contract_months = get_contract_months(months, 12)
for contract in contract_months:
specific_symbol = f"{symbol}-{contract}-{exchange}"
trade_symbol = f"{symbol}-{contract}-{exchange}-{currency}"

elem = create_symbol_element(
symbol=specific_symbol,
description=f"{name} {contract}",
category=f"Futures - {exchange}",
tick_size=tick,
price_format=price_format,
tick_value=value,
currency=currency,
trade_symbol=trade_symbol,
exchange=exchange,
security_type="FUTURES",
multiplier=mult
)
root.append(elem)
count += 1

print(f" Generated {count} futures symbols")
return count


def generate_forex_symbols(root: ET.Element) -> int:
"""Generate all forex pair symbols."""
print("\nGenerating FOREX symbols...")
count = 0

for base, quote in FOREX_PAIRS:
# Determine if it's a metal spot
if base in ["XAU", "XAG", "XPT", "XPD"]:
category = "Forex - Metals Spot"
else:
category = "Forex - Currency Pairs"

symbol = f"{base}.{quote}-CASH-IDEALPRO"
trade_symbol = f"{base}{quote}-CASH-IDEALPRO"

# Determine tick size
if base in ["XAU", "XAG", "XPT", "XPD"]:
tick = 0.01
price_format = "2"
elif "JPY" in (base, quote):
tick = 0.001
price_format = "3"
else:
tick = 0.00001
price_format = "5"

elem = create_symbol_element(
symbol=symbol,
description=f"{base}/{quote}",
category=category,
tick_size=tick,
price_format=price_format,
tick_value=tick,
currency=quote,
trade_symbol=trade_symbol,
exchange="IDEALPRO",
security_type="FOREX"
)
root.append(elem)
count += 1

print(f" Generated {count} forex symbols")
return count


def generate_index_symbols(root: ET.Element) -> int:
"""Generate all index symbols."""
print("\nGenerating INDEX symbols...")
count = 0

for symbol, info in INDICES.items():
exchange = info["exchange"]
name = info["name"]
currency = info["currency"]

sc_symbol = f"{symbol}-IND-{exchange}"
trade_symbol = f"{symbol}-IND-{exchange}"

elem = create_symbol_element(
symbol=sc_symbol,
description=name,
category=f"Indices - {exchange}",
tick_size=0.01,
price_format="2",
tick_value=0.01,
currency=currency,
trade_symbol=trade_symbol,
exchange=exchange,
security_type="INDEX"
)
root.append(elem)
count += 1

print(f" Generated {count} index symbols")
return count


def generate_stock_symbols(root: ET.Element, symbols: List[str], exchange: str,
currency: str, category: str, use_codes: bool = False) -> int:
"""Generate stock symbols for a specific exchange with deduplication."""
count = 0

for item in symbols:
if use_codes and isinstance(item, tuple):
code, name = item
symbol = code
description = name.replace("-", " ")
else:
symbol = item if isinstance(item, str) else item[0]
description = symbol

# Clean symbol
symbol = symbol.replace(".", "-") # For BRK.B -> BRK-B

sc_symbol = f"{symbol}-STK-{exchange}"
trade_symbol = f"{symbol}-STK-{exchange}-{currency}"

if add_symbol_with_dedup(
root=root,
symbol=sc_symbol,
description=description,
category=category,
tick_size=0.01,
price_format="2",
tick_value=0.01,
currency=currency,
trade_symbol=trade_symbol,
exchange=exchange,
security_type="STOCK"
):
count += 1

return count


def generate_us_stocks(root: ET.Element) -> int:
"""Generate US stock symbols."""
print("\nGenerating US STOCK symbols...")

# Try to download from NASDAQ
downloaded_symbols = download_nasdaq_symbols()

# Combine with hardcoded major stocks
all_symbols = list(set(downloaded_symbols + US_STOCKS_MAJOR))
all_symbols.sort()

count = generate_stock_symbols(root, all_symbols, "SMART", "USD", "Stocks - US (SMART)")
print(f" Generated {count} US stock symbols")
return count


def generate_india_stocks(root: ET.Element) -> int:
"""Generate India NSE stock symbols."""
print("\nGenerating INDIA STOCK symbols...")
count = generate_stock_symbols(root, INDIA_NSE_STOCKS, "NSE", "INR", "Stocks - India (NSE)")
print(f" Generated {count} India stock symbols")
return count


def generate_japan_stocks(root: ET.Element) -> int:
"""Generate Japan TSE stock symbols."""
print("\nGenerating JAPAN STOCK symbols...")
count = generate_stock_symbols(root, JAPAN_TSE_STOCKS, "TSEJ", "JPY",
"Stocks - Japan (TSE)", use_codes=True)
print(f" Generated {count} Japan stock symbols")
return count


def generate_china_stocks(root: ET.Element) -> int:
"""Generate China/Hong Kong stock symbols."""
print("\nGenerating CHINA/HK STOCK symbols...")
count = generate_stock_symbols(root, CHINA_HK_STOCKS, "SEHK", "HKD",
"Stocks - China/HK (SEHK)", use_codes=True)
print(f" Generated {count} China/HK stock symbols")
return count


def generate_germany_stocks(root: ET.Element) -> int:
"""Generate Germany XETRA stock symbols."""
print("\nGenerating GERMANY STOCK symbols...")
count = generate_stock_symbols(root, GERMANY_XETRA_STOCKS, "IBIS", "EUR",
"Stocks - Germany (XETRA)", use_codes=True)
print(f" Generated {count} Germany stock symbols")
return count


def generate_cfd_symbols(root: ET.Element) -> int:
"""Generate CFD symbols with deduplication."""
print("\nGenerating CFD symbols...")
count = 0

# Index CFDs
for symbol, name, currency in INDEX_CFDS:
sc_symbol = f"{symbol}-CFD-SMART"
trade_symbol = f"{symbol}-CFD-SMART-{currency}"

if add_symbol_with_dedup(
root=root,
symbol=sc_symbol,
description=name,
category="CFDs - Index",
tick_size=0.01,
price_format="2",
tick_value=1.00,
currency=currency,
trade_symbol=trade_symbol,
exchange="SMART",
security_type="CFD"
):
count += 1

# Forex CFDs
for symbol, name, currency in FOREX_CFDS:
sc_symbol = f"{symbol}-CFD-SMART"
trade_symbol = f"{symbol}-CFD-SMART-{currency}"

# Determine tick size
if "JPY" in symbol:
tick = 0.001
price_format = "3"
else:
tick = 0.00001
price_format = "5"

if add_symbol_with_dedup(
root=root,
symbol=sc_symbol,
description=name,
category="CFDs - Forex",
tick_size=tick,
price_format=price_format,
tick_value=tick,
currency=currency,
trade_symbol=trade_symbol,
exchange="SMART",
security_type="CFD"
):
count += 1

# Commodity CFDs
for symbol, name, currency in COMMODITY_CFDS:
sc_symbol = f"{symbol}-CFD-SMART"
trade_symbol = f"{symbol}-CFD-SMART-{currency}"

if add_symbol_with_dedup(
root=root,
symbol=sc_symbol,
description=name,
category="CFDs - Commodity",
tick_size=0.01,
price_format="2",
tick_value=0.01,
currency=currency,
trade_symbol=trade_symbol,
exchange="SMART",
security_type="CFD"
):
count += 1

print(f" Generated {count} CFD symbols")
return count


def generate_etf_symbols(root: ET.Element) -> int:
"""Generate ETF symbols with deduplication."""
print("\nGenerating ETF symbols...")
count = 0

# US ETFs
for symbol in US_ETFS:
sc_symbol = f"{symbol}-ETF-SMART"
trade_symbol = f"{symbol}-STK-SMART-USD"

if add_symbol_with_dedup(
root=root,
symbol=sc_symbol,
description=f"{symbol} ETF",
category="ETFs - US",
tick_size=0.01,
price_format="2",
tick_value=0.01,
currency="USD",
trade_symbol=trade_symbol,
exchange="SMART",
security_type="ETF"
):
count += 1

# Europe ETFs
for item in EUROPE_ETFS:
symbol, exchange, name, currency = item
# Map exchange codes to IB exchange names
exchange_map = {"LN": "LSE", "AS": "AEB", "DE": "IBIS"}
ib_exchange = exchange_map.get(exchange, exchange)

sc_symbol = f"{symbol}-ETF-{ib_exchange}"
trade_symbol = f"{symbol}-STK-{ib_exchange}-{currency}"

if add_symbol_with_dedup(
root=root,
symbol=sc_symbol,
description=name,
category="ETFs - Europe",
tick_size=0.01,
price_format="2",
tick_value=0.01,
currency=currency,
trade_symbol=trade_symbol,
exchange=ib_exchange,
security_type="ETF"
):
count += 1

# Asia ETFs
for item in ASIA_ETFS:
symbol, exchange, name, currency = item

sc_symbol = f"{symbol}-ETF-{exchange}"
trade_symbol = f"{symbol}-STK-{exchange}-{currency}"

if add_symbol_with_dedup(
root=root,
symbol=sc_symbol,
description=name,
category="ETFs - Asia",
tick_size=0.01 if currency != "JPY" else 1.0,
price_format="2" if currency != "JPY" else "0",
tick_value=0.01 if currency != "JPY" else 1.0,
currency=currency,
trade_symbol=trade_symbol,
exchange=exchange,
security_type="ETF"
):
count += 1

# India ETFs
for item in INDIA_ETFS:
symbol, exchange, name, currency = item

sc_symbol = f"{symbol}-ETF-{exchange}"
trade_symbol = f"{symbol}-STK-{exchange}-{currency}"

if add_symbol_with_dedup(
root=root,
symbol=sc_symbol,
description=name,
category="ETFs - India",
tick_size=0.05,
price_format="2",
tick_value=0.05,
currency=currency,
trade_symbol=trade_symbol,
exchange=exchange,
security_type="ETF"
):
count += 1

print(f" Generated {count} ETF symbols")
return count


# ============================================================================
# MAIN FUNCTION
# ============================================================================

def main():
print("=" * 70)
print("ULTIMATE IB TO SIERRA CHART SYMBOL MAPPER")
print("=" * 70)
print(f"Started at: {datetime.now().strftime('%Y-%m-%d %H:%M:%S')}")
print()

# Reset deduplication tracking
reset_deduplication()

# Create root element
root = ET.Element("symbol-settings")
root.set("version", "1.0")

# Generate all symbols (with automatic deduplication)
counts = {}
counts["futures"] = generate_futures_symbols(root)
counts["forex"] = generate_forex_symbols(root)
counts["indices"] = generate_index_symbols(root)
counts["cfds"] = generate_cfd_symbols(root)
counts["etfs"] = generate_etf_symbols(root)
counts["us_stocks"] = generate_us_stocks(root)
counts["india_stocks"] = generate_india_stocks(root)
counts["japan_stocks"] = generate_japan_stocks(root)
counts["china_stocks"] = generate_china_stocks(root)
counts["germany_stocks"] = generate_germany_stocks(root)

total = sum(counts.values())

# Write XML file
print("\n" + "=" * 70)
print("WRITING XML FILE...")
print("=" * 70)

# Get output path
script_dir = os.path.dirname(os.path.abspath(__file__))
output_path = os.path.join(script_dir, OUTPUT_FILE)

# Write pretty XML
xml_string = prettify_xml(root)

# Remove extra blank lines
lines = xml_string.split('\n')
cleaned_lines = [line for line in lines if line.strip()]
xml_string = '\n'.join(cleaned_lines)

with open(output_path, 'w', encoding='utf-8') as f:
f.write(xml_string)

print(f"\nOutput file: {output_path}")
print(f"File size: {os.path.getsize(output_path) / 1024 / 1024:.2f} MB")

# Try to copy to Sierra Chart folder (Windows only)
if sys.platform == 'win32' and os.path.exists(SIERRA_CHART_PATH):
try:
dest_path = os.path.join(SIERRA_CHART_PATH, OUTPUT_FILE)
shutil.copy2(output_path, dest_path)
print(f"\nAuto-installed to: {dest_path}")
except Exception as e:
print(f"\nCould not auto-install: {e}")

# Get deduplication statistics
unique_count, duplicates_skipped = get_deduplication_stats()

# Print summary
print("\n" + "=" * 70)
print("SUMMARY")
print("=" * 70)
print(f"{'Category':<30} {'Count':>10}")
print("-" * 42)
print(f"{'Futures':<30} {counts['futures']:>10,}")
print(f"{'Forex':<30} {counts['forex']:>10,}")
print(f"{'Indices':<30} {counts['indices']:>10,}")
print(f"{'CFDs':<30} {counts['cfds']:>10,}")
print(f"{'ETFs':<30} {counts['etfs']:>10,}")
print(f"{'US Stocks':<30} {counts['us_stocks']:>10,}")
print(f"{'India Stocks':<30} {counts['india_stocks']:>10,}")
print(f"{'Japan Stocks':<30} {counts['japan_stocks']:>10,}")
print(f"{'China/HK Stocks':<30} {counts['china_stocks']:>10,}")
print(f"{'Germany Stocks':<30} {counts['germany_stocks']:>10,}")
print("-" * 42)
print(f"{'TOTAL UNIQUE':<30} {unique_count:>10,}")
if duplicates_skipped > 0:
print(f"{'Duplicates Skipped':<30} {duplicates_skipped:>10,}")

# Print installation instructions
print("\n" + "=" * 70)
print("INSTALLATION INSTRUCTIONS")
print("=" * 70)
print("""
1. EXIT Sierra Chart completely (File >> Exit)

2. COPY the generated XML file to:
C:\\SierraChart\\SymbolSettings\\

3. START Sierra Chart

4. Go to: Global Settings >> Symbol Settings

5. CHECK the box: "Use Custom Symbol Settings Values"

6. Click OK

7. RECONNECT to IB (File >> Reconnect)

DONE! All symbols should now work with Interactive Brokers.
""")

print("=" * 70)
print(f"Completed at: {datetime.now().strftime('%Y-%m-%d %H:%M:%S')}")
print("=" * 70)

return 0


if __name__ == "__main__":
sys.exit(main())

========================================================================================

To post a message in this thread, you need to log in with your Sierra Chart account:

Login

Login Page - Create Account