feat(notify): Telegram alerts on trade open and close
CI/CD / build-and-push (push) Successful in 7s
CI/CD / build-and-push (push) Successful in 7s
New module bot/notify/telegram.py — httpx async, fire-and-forget via asyncio.create_task, swallows all errors so notifications never affect trade execution. Three alert types: 📈/📉 TRADE ABIERTO — direction, size, edge_net (in execute()) ✅/❌ GANADO/PERDIDO — approx PnL (in close_position()) 🔒 LEGACY CLOSE — recovered capital + reason (in close_legacy_position()) close_position() and close_legacy_position() gain an optional question="" param so the message shows the market name instead of market_id. bot/main.py updated to pass question= to close_legacy_position(). Credentials (TELEGRAM_BOT_TOKEN, TELEGRAM_CHAT_ID) read from env vars injected via bot-secrets k8s secret. Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
This commit is contained in:
@@ -0,0 +1,53 @@
|
||||
"""Telegram notifications — fire-and-forget, never blocks trade execution."""
|
||||
import logging
|
||||
import os
|
||||
|
||||
import httpx
|
||||
|
||||
log = logging.getLogger(__name__)
|
||||
|
||||
_TOKEN = os.getenv("TELEGRAM_BOT_TOKEN", "")
|
||||
_CHAT_ID = os.getenv("TELEGRAM_CHAT_ID", "")
|
||||
|
||||
|
||||
async def _send(text: str) -> None:
|
||||
"""POST a message to Telegram. Swallows all errors silently."""
|
||||
if not _TOKEN or not _CHAT_ID:
|
||||
return
|
||||
try:
|
||||
async with httpx.AsyncClient(timeout=10) as client:
|
||||
resp = await client.post(
|
||||
f"https://api.telegram.org/bot{_TOKEN}/sendMessage",
|
||||
json={"chat_id": _CHAT_ID, "text": text},
|
||||
)
|
||||
if resp.status_code != 200:
|
||||
log.warning("Telegram %d: %s", resp.status_code, resp.text[:200])
|
||||
except Exception as exc:
|
||||
log.warning("Telegram notification failed: %s", exc)
|
||||
|
||||
|
||||
async def trade_opened(question: str, direction: str, size_usdc: float, edge_net: float) -> None:
|
||||
emoji = "📈" if direction == "BUY_YES" else "📉"
|
||||
await _send(
|
||||
f"{emoji} TRADE ABIERTO\n"
|
||||
f"{question[:80]}\n"
|
||||
f"Dir: {direction} Size: ${size_usdc:.2f} Edge: {edge_net:+.3f}"
|
||||
)
|
||||
|
||||
|
||||
async def trade_closed(question: str, pnl: float) -> None:
|
||||
emoji = "✅" if pnl > 0 else "❌"
|
||||
result = "GANADO" if pnl > 0 else "PERDIDO"
|
||||
await _send(
|
||||
f"{emoji} {result}\n"
|
||||
f"{question[:80]}\n"
|
||||
f"PnL: {pnl:+.2f} USDC"
|
||||
)
|
||||
|
||||
|
||||
async def trade_legacy_closed(question: str, recovered: float, reason: str) -> None:
|
||||
await _send(
|
||||
f"🔒 LEGACY CLOSE\n"
|
||||
f"{question[:80]}\n"
|
||||
f"Recuperado: ${recovered:.2f} | {reason[:60]}"
|
||||
)
|
||||
Reference in New Issue
Block a user