diff --git a/bots/mcp_server.py b/bots/mcp_server.py index 9f927096..0111d640 100644 --- a/bots/mcp_server.py +++ b/bots/mcp_server.py @@ -132,8 +132,8 @@ class MockBridge(MT5Bridge): "trade_mode": "DEMO", } - async def symbols(self) -> List[str]: - return self.SYMBOLS + async def symbols(self, search: Optional[str] = None) -> List[str]: + return [x for x in self.SYMBOLS if not search or search.upper() in x] async def symbol_info(self, symbol: str) -> Dict[str, Any]: if symbol not in self.BASE_PRICES: @@ -367,15 +367,53 @@ class LiveBridge(MT5Bridge): "trade_mode": "DEMO" if info.trade_mode == 0 else "REAL", } - async def symbols(self) -> List[str]: + async def symbols(self, search: Optional[str] = None) -> List[str]: await self.ensure() + if search: + syms = self.mt5.symbols_get(group=f"*{search}*") + return [s.name for s in syms] if syms else [] syms = self.mt5.symbols_get() if syms is None: return [] return [s.name for s in syms if s.visible] + def _resolve(self, symbol: str) -> str: + """Broker-Symbolnamen finden und in der Marktübersicht aktivieren. + PU Prime u.a. hängen Suffixe an (z.B. XAUUSD -> XAUUSD.s), und Symbole + ausserhalb der Marktübersicht liefern keine Ticks. + Reihenfolge: SYMBOL_MAP (bots.env) -> exakter Name -> Name mit Suffix.""" + cache = getattr(self, "_symbol_cache", None) + if cache is None: + cache = self._symbol_cache = {} + for pair in os.getenv("SYMBOL_MAP", "").split(","): + if "=" in pair: + k, v = pair.split("=", 1) + cache[k.strip().upper()] = v.strip() # z.B. XAUUSD=XAUUSD.s + key = symbol.upper() + name = cache.get(key) + if name is None: + # Endpunkte schreiben Symbole gross ("XAUUSD.S"), der Broker-Name ist aber + # case-sensitiv ("XAUUSD.s") -> über den Basisnamen suchen, Gross/Klein egal + base = key.split(".")[0] + cands = [c.name for c in (self.mt5.symbols_get(group=f"*{base}*") or [])] + exact = [n for n in cands if n.upper() == key] + prefixed = sorted((n for n in cands if n.upper().startswith(base)), key=len) + if exact: + name = exact[0] + elif prefixed: + name = prefixed[0] + else: + raise HTTPException(404, f"Symbol {symbol} not found at broker") + if name != symbol: + log.info(f"Symbol {symbol} -> {name} (broker name)") + cache[key] = name + if not self.mt5.symbol_select(name, True): + log.warning(f"symbol_select({name}) failed: {self.mt5.last_error()}") + return name + async def symbol_info(self, symbol: str) -> Dict[str, Any]: await self.ensure() + symbol = self._resolve(symbol) info = self.mt5.symbol_info(symbol) if info is None: raise HTTPException(404, f"Symbol {symbol} not found") @@ -395,6 +433,7 @@ class LiveBridge(MT5Bridge): async def ohlc(self, symbol: str, timeframe: str, bars: int, start_pos: int = 0) -> List[Dict]: await self.ensure() + symbol = self._resolve(symbol) import pandas as pd # Check if seconds timeframe @@ -440,6 +479,7 @@ class LiveBridge(MT5Bridge): to_ts: Unix timestamp Ende """ await self.ensure() + symbol = self._resolve(symbol) import pandas as pd from datetime import datetime, timezone @@ -575,6 +615,7 @@ class LiveBridge(MT5Bridge): comment: str = "") -> Dict[str, Any]: """Open a market order.""" await self.ensure() + symbol = self._resolve(symbol) # Get symbol info for filling info = self.mt5.symbol_info(symbol) @@ -688,6 +729,7 @@ class LiveBridge(MT5Bridge): async def tick(self, symbol: str) -> Dict[str, Any]: await self.ensure() + symbol = self._resolve(symbol) tick = self.mt5.symbol_info_tick(symbol) if tick is None: raise HTTPException(404, f"No tick for {symbol}") @@ -839,8 +881,8 @@ async def account(): @app.get("/symbols") -async def symbols(): - syms = await bridge.symbols() +async def symbols(search: Optional[str] = Query(None, description="z.B. XAU – sucht in ALLEN Broker-Symbolen")): + syms = await bridge.symbols(search) if search else await bridge.symbols() return {"count": len(syms), "symbols": syms} diff --git a/mt5-stack.zip b/mt5-stack.zip index 5a00175f..31a522bb 100644 Binary files a/mt5-stack.zip and b/mt5-stack.zip differ