#!/usr/bin/env python3 """ Core Telegram Bot - Handles only bot setup, authentication, and basic messaging. """ import asyncio import logging from datetime import datetime from telegram import Update, InlineKeyboardButton, InlineKeyboardMarkup from telegram.ext import Application, ContextTypes, CommandHandler, CallbackQueryHandler, MessageHandler, filters from src.config.config import Config from src.trading.trading_engine import TradingEngine from src.monitoring.market_monitor import MarketMonitor from src.notifications.notification_manager import NotificationManager from src.commands.trading_commands import TradingCommands from src.commands.info_commands import InfoCommands from src.commands.management_commands import ManagementCommands logger = logging.getLogger(__name__) class TelegramTradingBot: """Core Telegram bot handling only authentication, messaging, and command routing.""" def __init__(self): """Initialize the core bot with minimal responsibilities.""" # Core bot attributes self.application = None self.version = "Unknown" # Initialize subsystems self.trading_engine = TradingEngine() self.market_monitor = MarketMonitor(self.trading_engine) self.notification_manager = NotificationManager() # Connect notification manager to market monitor self.market_monitor.set_notification_manager(self.notification_manager) # Initialize command handlers self.trading_commands = TradingCommands(self.trading_engine, self.notification_manager) self.info_commands = InfoCommands(self.trading_engine) self.management_commands = ManagementCommands(self.trading_engine, self.market_monitor) def is_authorized(self, chat_id: str) -> bool: """Check if the chat ID is authorized to use the bot.""" return str(chat_id) == str(Config.TELEGRAM_CHAT_ID) async def send_message(self, text: str, parse_mode: str = 'HTML') -> None: """Send a message to the authorized chat.""" if self.application and Config.TELEGRAM_CHAT_ID: try: await self.application.bot.send_message( chat_id=Config.TELEGRAM_CHAT_ID, text=text, parse_mode=parse_mode ) except Exception as e: logger.error(f"Failed to send message: {e}") def setup_handlers(self): """Set up command handlers for the bot.""" if not self.application: return # Basic bot commands self.application.add_handler(CommandHandler("start", self.start_command)) self.application.add_handler(CommandHandler("help", self.help_command)) # Trading commands self.application.add_handler(CommandHandler("long", self.trading_commands.long_command)) self.application.add_handler(CommandHandler("short", self.trading_commands.short_command)) self.application.add_handler(CommandHandler("exit", self.trading_commands.exit_command)) self.application.add_handler(CommandHandler("sl", self.trading_commands.sl_command)) self.application.add_handler(CommandHandler("tp", self.trading_commands.tp_command)) self.application.add_handler(CommandHandler("coo", self.trading_commands.coo_command)) # Info commands self.application.add_handler(CommandHandler("balance", self.info_commands.balance_command)) self.application.add_handler(CommandHandler("positions", self.info_commands.positions_command)) self.application.add_handler(CommandHandler("orders", self.info_commands.orders_command)) self.application.add_handler(CommandHandler("stats", self.info_commands.stats_command)) self.application.add_handler(CommandHandler("trades", self.info_commands.trades_command)) self.application.add_handler(CommandHandler("market", self.info_commands.market_command)) self.application.add_handler(CommandHandler("price", self.info_commands.price_command)) self.application.add_handler(CommandHandler("performance", self.info_commands.performance_command)) self.application.add_handler(CommandHandler("daily", self.info_commands.daily_command)) self.application.add_handler(CommandHandler("weekly", self.info_commands.weekly_command)) self.application.add_handler(CommandHandler("monthly", self.info_commands.monthly_command)) self.application.add_handler(CommandHandler("risk", self.info_commands.risk_command)) self.application.add_handler(CommandHandler("balance_adjustments", self.info_commands.balance_adjustments_command)) self.application.add_handler(CommandHandler("commands", self.info_commands.commands_command)) self.application.add_handler(CommandHandler("c", self.info_commands.commands_command)) # Alias # Management commands self.application.add_handler(CommandHandler("monitoring", self.management_commands.monitoring_command)) self.application.add_handler(CommandHandler("alarm", self.management_commands.alarm_command)) self.application.add_handler(CommandHandler("logs", self.management_commands.logs_command)) self.application.add_handler(CommandHandler("debug", self.management_commands.debug_command)) self.application.add_handler(CommandHandler("version", self.management_commands.version_command)) self.application.add_handler(CommandHandler("keyboard", self.management_commands.keyboard_command)) # Callback and message handlers self.application.add_handler(CallbackQueryHandler(self.trading_commands.button_callback)) async def start_command(self, update: Update, context: ContextTypes.DEFAULT_TYPE) -> None: """Handle the /start command.""" if not self.is_authorized(update.effective_chat.id): await update.message.reply_text("❌ Unauthorized access.") return # Determine risk management and stop loss details from Config risk_enabled = getattr(Config, 'RISK_MANAGEMENT_ENABLED', False) stop_loss_percentage = getattr(Config, 'STOP_LOSS_PERCENTAGE', 0) bot_heartbeat = getattr(Config, 'BOT_HEARTBEAT_SECONDS', 10) welcome_text = f""" 🤖 Welcome to Hyperliquid Trading Bot v{self.version} 📱 Quick Actions: • Trading: /long {Config.DEFAULT_TRADING_TOKEN} 100 or /short {Config.DEFAULT_TRADING_TOKEN} 50 • Exit: /exit {Config.DEFAULT_TRADING_TOKEN} (closes position) • Info: /balance, /positions, /orders 📊 Market Data: • /market - Detailed market overview • /price - Quick price check ⚡ Quick Commands: • /balance - Account balance • /positions - Open positions • /orders - Active orders • /market - Market data & prices 🚀 Trading: • /long {Config.DEFAULT_TRADING_TOKEN} 100 - Long position • /long {Config.DEFAULT_TRADING_TOKEN} 100 45000 - Limit order • /long {Config.DEFAULT_TRADING_TOKEN} 100 sl:44000 - With stop loss • /short {Config.DEFAULT_TRADING_TOKEN} 50 - Short position • /short {Config.DEFAULT_TRADING_TOKEN} 50 3500 sl:3600 - With stop loss • /exit {Config.DEFAULT_TRADING_TOKEN} - Close position • /coo {Config.DEFAULT_TRADING_TOKEN} - Cancel open orders 🛡️ Risk Management: • Enabled: {'✅ Yes' if risk_enabled else '❌ No'} • Auto Stop Loss: {stop_loss_percentage}% • Order Stop Loss: Use sl:price parameter • /sl {Config.DEFAULT_TRADING_TOKEN} 44000 - Manual stop loss • /tp {Config.DEFAULT_TRADING_TOKEN} 50000 - Take profit order 📈 Performance & Analytics: • /stats - Complete trading statistics • /performance - Token performance ranking & detailed stats • /daily - Daily performance (last 10 days) • /weekly - Weekly performance (last 10 weeks) • /monthly - Monthly performance (last 10 months) • /risk - Sharpe ratio, drawdown, VaR • /version - Bot version & system information • /trades - Recent trade history 🔔 Price Alerts: • /alarm - List all active alarms • /alarm {Config.DEFAULT_TRADING_TOKEN} 50000 - Set alarm for {Config.DEFAULT_TRADING_TOKEN} at $50,000 • /alarm {Config.DEFAULT_TRADING_TOKEN} - Show all {Config.DEFAULT_TRADING_TOKEN} alarms • /alarm 3 - Remove alarm ID 3 🔄 Automatic Monitoring: • Real-time order fill alerts • Position opened/closed notifications • P&L calculations on trade closure • Price alarm triggers • External trade detection & sync • Auto stats synchronization • Automatic stop loss placement • {bot_heartbeat}-second monitoring interval 📊 Universal Trade Tracking: • Bot trades: Full logging & notifications • Platform trades: Auto-detected & synced • Mobile app trades: Monitored & recorded • API trades: Tracked & included in stats Type /help for detailed command information. 🔄 Order Monitoring: • /monitoring - View monitoring status • /logs - View log file statistics and cleanup ⚙️ Configuration: • Default Token: {Config.DEFAULT_TRADING_TOKEN} • Network: {'Testnet' if Config.HYPERLIQUID_TESTNET else 'Mainnet'} 🛡️ Safety Features: • All trades logged automatically • Comprehensive performance tracking • Real-time balance monitoring • Risk metrics calculation • Automatic stop loss protection 📱 Mobile Optimized: • Quick action buttons via /commands • Instant notifications • Clean, readable layout 💡 Quick Access: • /commands or /c - One-tap button menu for all commands For support, contact your bot administrator. """ await update.message.reply_text(welcome_text, parse_mode='HTML') async def help_command(self, update: Update, context: ContextTypes.DEFAULT_TYPE) -> None: """Handle the /help command.""" if not self.is_authorized(update.effective_chat.id): await update.message.reply_text("❌ Unauthorized access.") return help_text = """ 📖 Complete Command Reference 🔄 Trading Commands: • /long [token] [USDC] [price] [sl:price] - Open long position • /short [token] [USDC] [price] [sl:price] - Open short position • /exit [token] - Close position (market order) • /sl [token] [price] - Set stop loss order • /tp [token] [price] - Set take profit order • /coo [token] - Cancel all open orders 📊 Account Info: • /balance - Account balance and equity • /positions - Open positions with P&L • /orders - Active orders • /trades - Recent trade history • /stats - Comprehensive trading statistics 📈 Market Data: • /market [token] - Market data and orderbook • /price [token] - Quick price check ⚙️ Management: • /monitoring - View/toggle order monitoring • /alarm [token] [price] - Set price alerts • /logs - View recent bot logs • /debug - Bot internal state (troubleshooting) For support or issues, check the logs or contact the administrator. """ await update.message.reply_text(help_text, parse_mode='HTML') async def run(self): """Run the Telegram bot.""" if not Config.TELEGRAM_BOT_TOKEN: logger.error("❌ TELEGRAM_BOT_TOKEN not configured") return if not Config.TELEGRAM_CHAT_ID: logger.error("❌ TELEGRAM_CHAT_ID not configured") return try: # Create application self.application = Application.builder().token(Config.TELEGRAM_BOT_TOKEN).build() # Connect notification manager to the bot application self.notification_manager.set_bot_application(self.application) # Set up handlers self.setup_handlers() logger.info("🚀 Starting Telegram trading bot...") # Send startup notification await self.send_message( f"🤖 Manual Trading Bot v{self.version} Started\n\n" f"✅ Connected to Hyperliquid {'Testnet' if Config.HYPERLIQUID_TESTNET else 'Mainnet'}\n" f"📊 Default Symbol: {Config.DEFAULT_TRADING_TOKEN}\n" f"🔄 All systems ready!\n\n" "Use /start for quick actions or /help for all commands." ) # Start subsystems await self.market_monitor.start() # Start polling for updates using the modern approach logger.info("🔄 Starting bot polling...") await self.application.run_polling( drop_pending_updates=True # By default, run_polling handles SIGINT, SIGTERM, SIGABRT. # No need to specify stop_signals=None if we want this default behavior. ) except asyncio.CancelledError: logger.info("🛑 Bot polling cancelled") raise except Exception as e: logger.error(f"❌ Error in telegram bot: {e}") raise finally: # Clean shutdown try: await self.market_monitor.stop() if self.application: await self.application.stop() await self.application.shutdown() except Exception as e: logger.error(f"Error during shutdown: {e}")