"""
Telegram alert system. All sends are fire-and-forget â a send failure
never crashes the main loop.
"""
import logging
from . import config
logger = logging.getLogger(__name__)
_bot = None
_chat_id = None
def _get_bot():
global _bot, _chat_id
if _bot is None and config.TELEGRAM_BOT_TOKEN and config.TELEGRAM_CHAT_ID:
try:
from telegram import Bot
_bot = Bot(token=config.TELEGRAM_BOT_TOKEN)
_chat_id = config.TELEGRAM_CHAT_ID
except ImportError:
logger.warning("python-telegram-bot not installed â Telegram disabled")
return _bot
async def send(text: str) -> None:
bot = _get_bot()
if bot is None:
return
try:
await bot.send_message(chat_id=_chat_id, text=text, parse_mode="HTML")
except Exception as exc:
logger.warning("Telegram send failed: %s", exc)
async def notify_open(symbol: str, direction: str, entry: float,
sl: float, tp: float, lots: float) -> None:
arrow = "đĸ BUY" if direction == "long" else "đ´ SELL"
mode = "PAPER" if config.PAPER_TRADING else "LIVE"
text = (
f"[{mode}] {arrow} {symbol}\n"
f"Entry : {entry:.5f}\n"
f"SL : {sl:.5f}\n"
f"TP : {tp:.5f}\n"
f"Size : {lots:.2f} lots\n"
f"R:R : 1 : {config.TP_ATR / config.SL_ATR:.1f}"
)
logger.info("OPEN %s %s @ %.5f SL=%.5f TP=%.5f %.2f lots",
symbol, direction.upper(), entry, sl, tp, lots)
await send(text)
async def notify_close(symbol: str, direction: str, entry: float,
exit_price: float, pnl_pips: float, reason: str) -> None:
sign = "â
" if pnl_pips > 0 else "â"
text = (
f"{sign} CLOSED {symbol}\n"
f"Dir : {direction.upper()}\n"
f"Entry : {entry:.5f} â {exit_price:.5f}\n"
f"P&L : {pnl_pips:+.1f} pips\n"
f"Reason: {reason}"
)
logger.info("CLOSE %s %s %+.1f pips (%s)", symbol, direction.upper(),
pnl_pips, reason)
await send(text)
async def notify_status(message: str) -> None:
logger.info(message)
await send(f"âšī¸ {message}")
async def notify_error(message: str) -> None:
logger.error(message)
await send(f"đ¨ ERROR: {message}")