Initial commit: multi-symbol bot with backtest engine and RSI trend strategy

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
This commit is contained in:
2026-04-28 21:09:12 +02:00
commit ad8dfa27d7
32 changed files with 2644 additions and 0 deletions
+77
View File
@@ -0,0 +1,77 @@
"""
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"<b>[{mode}] {arrow} {symbol}</b>\n"
f"Entry : <code>{entry:.5f}</code>\n"
f"SL : <code>{sl:.5f}</code>\n"
f"TP : <code>{tp:.5f}</code>\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"<b>{sign} CLOSED {symbol}</b>\n"
f"Dir : {direction.upper()}\n"
f"Entry : <code>{entry:.5f}</code> → <code>{exit_price:.5f}</code>\n"
f"P&L : <b>{pnl_pips:+.1f} pips</b>\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"🚨 <b>ERROR</b>: {message}")