Prometheus, Grafana y alertas por Telegram/Discord ” visibilidad total 24/7
from prometheus_client import ( Counter, Gauge, Histogram, Summary, start_http_server ) import time # === MÉTRICAS DEL BOT === # Contadores (solo suben) TRADES_TOTAL = Counter( 'bot_trades_total', 'Número total de trades', ['symbol', 'side', 'strategy'] ) ERRORS_TOTAL = Counter('bot_errors_total', 'Errores acumulados', ['type']) # Gauges (suben y bajan) PORTFOLIO_VALUE = Gauge('bot_portfolio_value_usd', 'Valor total del portfolio') OPEN_POSITIONS = Gauge('bot_open_positions', 'Posiciones abiertas actualmente') DAILY_PNL = Gauge('bot_daily_pnl_pct', 'PNL del día en %') LAST_PRICE = Gauge('bot_last_price', 'Último precio', ['symbol']) # Histogramas (distribución de valores) TRADE_DURATION = Histogram( 'bot_trade_duration_seconds', 'Duración de trades', buckets=[60, 300, 900, 3600, 86400] ) API_LATENCY = Histogram( 'bot_api_latency_ms', 'Latencia de llamadas al exchange' ) # === USO EN EL BOT === def on_trade_executed(symbol, side, pnl): TRADES_TOTAL.labels(symbol=symbol, side=side, strategy='rsi_mean').inc() PORTFOLIO_VALUE.set(portfolio.total_value()) DAILY_PNL.set(pnl) with API_LATENCY.time(): # Context manager para latencia response = exchange.fetch_ticker('BTC/USDT') # Exponer métricas en puerto 8080 start_http_server(8080) # Prometheus scrapeará http://bot:8080/metrics cada 15s
global: scrape_interval: 15s evaluation_interval: 15s scrape_configs: - job_name: 'trading_bot' static_configs: - targets: ['bot:8080'] # nombre del servicio Docker relabel_configs: - source_labels: [__address__] target_label: instance replacement: 'bot-prod-1' - job_name: 'postgres' static_configs: - targets: ['postgres_exporter:9187'] alerting: alertmanagers: - static_configs: - targets: ['alertmanager:9093'] rule_files: - '/etc/prometheus/alerts.yml'
import asyncio from telegram import Bot from telegram.constants import ParseMode import os TELEGRAM_TOKEN = os.getenv("TELEGRAM_TOKEN") TELEGRAM_CHAT = os.getenv("TELEGRAM_CHAT_ID") bot = Bot(token=TELEGRAM_TOKEN) async def send_alert(msg: str, level="INFO"): icons = {"INFO":"¹ï¸", "WARN":" ï¸", "ERROR":"🚨", "TRADE":"💹"} text = f"{icons.get(level,'¢')} *[{level}]* {msg}" await bot.send_message( chat_id=TELEGRAM_CHAT, text=text, parse_mode=ParseMode.MARKDOWN ) # Alerta de trade ejecutado async def notify_trade(side, symbol, price, pnl): emoji = "🟢" if pnl > 0 else "🔴" msg = ( f"{emoji} *TRADE {side.upper()}*\n" f"Par: `{symbol}`\n" f"Precio: `{price:,.2f} USDT`\n" f"PNL: `{pnl:+.2f}%`" ) await send_alert(msg, level="TRADE") # Alerta de drawdown crítico async def check_drawdown(current_dd): if current_dd > 0.05: # 5% drawdown await send_alert( f"Drawdown crítico: {current_dd:.1%}", level="WARN" )
# Discord Webhook ” alternativa import requests DISCORD_WH = os.getenv("DISCORD_WEBHOOK") def discord_alert(title, desc, color=0x00dc64): payload = { "embeds": [{ "title": title, "description": desc, "color": color, "footer": { "text": f"TradingBot · {datetime.now():%Y-%m-%d %H:%M}" } }] } requests.post(DISCORD_WH, json=payload) # Uso: discord_alert( "💹 Trade Ejecutado", f"BTC/USDT BUY @ $65,420\nPNL: +2.3%", color=0x00dc64 # verde ) discord_alert( "🚨 ERROR CRÍTICO", "Exchange timeout - 3 reintentos fallidos", color=0xff4040 # rojo ) # prometheus alertmanager ” alerts.yml # groups: # - name: bot_alerts # rules: # - alert: HighDrawdown # expr: bot_daily_pnl_pct < -0.05 # for: 1m # labels: {severity: critical}
# grafana/datasources/prometheus.yml apiVersion: 1 datasources: - name: Prometheus type: prometheus url: http://prometheus:9090 isDefault: true access: proxy # Queries PromQL para el dashboard # PNL acumulado (línea) sum(bot_daily_pnl_pct) # Trades por hora rate(bot_trades_total[1h]) * 3600 # Latencia P95 de la API histogram_quantile(0.95, rate(bot_api_latency_ms_bucket[5m])) # Errores por tipo sum by(type) (bot_errors_total) # Uptime del bot up{job="trading_bot"}
# grafana/dashboards/bot.json (fragmento) { "panels": [ { "title": "💰 Portfolio Value", "type": "stat", "targets": [{ "expr": "bot_portfolio_value_usd" }], "fieldConfig": { "defaults": { "unit": "currencyUSD", "thresholds": { "steps": [ {"value": null, "color": "red"}, {"value": 9000, "color": "yellow"}, {"value": 10000, "color": "green"} ] } } } } ] }