123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309 |
- #!/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"""
- 🤖 <b>Welcome to Hyperliquid Trading Bot v{self.version}</b>
- 📱 <b>Quick Actions:</b>
- • 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
- 📊 <b>Market Data:</b>
- • /market - Detailed market overview
- • /price - Quick price check
- <b>⚡ Quick Commands:</b>
- • /balance - Account balance
- • /positions - Open positions
- • /orders - Active orders
- • /market - Market data & prices
- <b>🚀 Trading:</b>
- • /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
- <b>🛡️ Risk Management:</b>
- • 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
- <b>📈 Performance & Analytics:</b>
- • /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
- <b>🔔 Price Alerts:</b>
- • /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
- <b>🔄 Automatic Monitoring:</b>
- • 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
- <b>📊 Universal Trade Tracking:</b>
- • 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.
- <b>🔄 Order Monitoring:</b>
- • /monitoring - View monitoring status
- • /logs - View log file statistics and cleanup
- <b>⚙️ Configuration:</b>
- • Default Token: {Config.DEFAULT_TRADING_TOKEN}
- • Network: {'Testnet' if Config.HYPERLIQUID_TESTNET else 'Mainnet'}
- <b>🛡️ Safety Features:</b>
- • All trades logged automatically
- • Comprehensive performance tracking
- • Real-time balance monitoring
- • Risk metrics calculation
- • Automatic stop loss protection
- <b>📱 Mobile Optimized:</b>
- • Quick action buttons via /commands
- • Instant notifications
- • Clean, readable layout
- <b>💡 Quick Access:</b>
- • /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 = """
- 📖 <b>Complete Command Reference</b>
- 🔄 <b>Trading Commands:</b>
- • /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
- 📊 <b>Account Info:</b>
- • /balance - Account balance and equity
- • /positions - Open positions with P&L
- • /orders - Active orders
- • /trades - Recent trade history
- • /stats - Comprehensive trading statistics
- 📈 <b>Market Data:</b>
- • /market [token] - Market data and orderbook
- • /price [token] - Quick price check
- ⚙️ <b>Management:</b>
- • /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"🤖 <b>Manual Trading Bot v{self.version} Started</b>\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}")
|