"""Notifiche Telegram per il paper trader.""" from __future__ import annotations import os import urllib.request import urllib.parse import json from src.version import APP_VERSION BOT_TOKEN = os.environ.get("TELEGRAM_BOT_TOKEN", "") CHAT_ID = os.environ.get("TELEGRAM_CHAT_ID", "") NOTIFY_EVENTS = { "SIGNAL", "OPENED", "CLOSED", "OPEN_FAILED", "CLOSE_FAILED", "ERROR", "STARTUP", "SHUTDOWN", "TRAINING_FAILED", # esecuzione REALE (shadow su Deribit testnet) "REAL_EXEC_LIVE", # primo ordine reale verificato di un worker (conferma "e' vivo") "REAL_OPEN_FAIL", # un'apertura reale NON si e' verificata (problema da guardare) "STALE_FEED", # feed flat/fermo da >= N barre 1h (worker ciechi: il prossimo # prezzo reale puo' gappare, come ETH 2026-06-05 1655->1600) "PANEL_SHORT", # TSM01/ROT02: panel inner-join troncato sotto il lookback # richiesto -> tick() salterebbe in SILENZIO (worker inerte) "FEED_OUTAGE", # N poll consecutivi falliti/degradati nel runner: exit non # valutati, posizioni reali protette solo dal disaster-SL "REAL_DIVERGENCE", # |slippage| sim/reale anomalo a open/close (es. spike print # testnet: sim entra su un prezzo fantasma, il reale sul book) "REAL_DSL_CANCEL_FAIL", # cancel del disaster-SL fallita dopo retry: possibile # stop ORFANO sul book -> verificare a mano "REAL_CLOSE_FAILED", # chiusura reale a 2 gambe (pairs) non verificata su una gamba } def send_telegram(text: str) -> bool: if not BOT_TOKEN or not CHAT_ID: return False try: url = f"https://api.telegram.org/bot{BOT_TOKEN}/sendMessage" data = urllib.parse.urlencode({"chat_id": CHAT_ID, "text": text, "parse_mode": "HTML"}).encode() urllib.request.urlopen(url, data, timeout=10) return True except Exception: return False def notify_event(event: str, data: dict | None = None): if event not in NOTIFY_EVENTS: return lines = [f"📊 {event} v{APP_VERSION}"] if data: for k, v in data.items(): if k in ("signal",): continue lines.append(f" {k}: {v}") send_telegram("\n".join(lines))