Forráskód Böngészése

Refactor trading bot launcher and update Telegram bot features - Simplify the trading bot launcher for systemd deployment, removing error handling and auto-restart features. Introduce a versioning system for the bot and enhance the Telegram bot with a new /version command to display bot and system information. Update welcome messages and command descriptions for clarity and improved user experience.

Carles Sentis 6 napja
szülő
commit
5f4d851d65
2 módosított fájl, 125 hozzáadás és 179 törlés
  1. 109 53
      src/telegram_bot.py
  2. 16 126
      trading_bot.py

+ 109 - 53
src/telegram_bot.py

@@ -23,27 +23,25 @@ from logging_config import setup_logging, cleanup_logs, format_log_stats
 logger = setup_logging().getChild(__name__)
 
 class TelegramTradingBot:
-    """Telegram bot for manual trading with comprehensive statistics."""
+    """Telegram trading bot for manual trading operations."""
     
     def __init__(self):
         """Initialize the Telegram trading bot."""
-        self.client = HyperliquidClient(use_testnet=Config.HYPERLIQUID_TESTNET)
-        self.stats = TradingStats()
-        self.alarm_manager = AlarmManager()
-        self.authorized_chat_id = Config.TELEGRAM_CHAT_ID
+        self.client = HyperliquidClient()
         self.application = None
-        
-        # Order monitoring
-        self.monitoring_active = False
-        self.last_known_orders = set()  # Track order IDs we've seen
-        self.last_known_positions = {}  # Track position sizes for P&L calculation
-        
-        # External trade monitoring
-        self.last_processed_trade_time = None  # Track last processed external trade
-        
-        # Initialize stats with current balance
+        self.order_monitoring_task = None
+        self.last_filled_orders = set()
+        self.alarms = []  # List to store price alarms
+        self.bot_heartbeat_seconds = getattr(Config, 'BOT_HEARTBEAT_SECONDS', 10)
+        self.external_trade_timestamps = set()  # Track external trade timestamps to avoid duplicates
+        self.last_position_check = {}  # Track last position state for comparison
+        self._position_tracker = {}  # For enhanced position tracking
+        self.stats = None
+        self.version = "Unknown"  # Will be set by launcher
+        
+        # Initialize stats
         self._initialize_stats()
-        
+    
     def _initialize_stats(self):
         """Initialize stats with current balance."""
         try:
@@ -54,10 +52,10 @@ class TelegramTradingBot:
                 self.stats.set_initial_balance(usdc_balance)
         except Exception as e:
             logger.error(f"Could not initialize stats: {e}")
-        
+    
     def is_authorized(self, chat_id: str) -> bool:
         """Check if the chat ID is authorized to use the bot."""
-        return str(chat_id) == str(self.authorized_chat_id)
+        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."""
@@ -77,54 +75,44 @@ class TelegramTradingBot:
             await update.message.reply_text("❌ Unauthorized access.")
             return
         
-        welcome_text = """
-🤖 <b>Hyperliquid Manual Trading Bot</b>
+        welcome_text = f"""
+🤖 <b>Welcome to Hyperliquid Trading Bot</b>
 
-Welcome to your personal trading assistant! Control your Hyperliquid account directly from your phone.
+📱 <b>Quick Actions:</b>
+• Trading: /long BTC 100 or /short ETH 50
+• Exit: /exit BTC (closes position)
+• Info: /balance, /positions, /orders
 
-<b>📱 Quick Actions:</b>
-Tap the buttons below for instant access to key functions.
+📊 <b>Market Data:</b>
+• /market - Detailed market overview
+• /price - Quick price check
 
-<b>💼 Account Commands:</b>
-/balance - Account balance
-/positions - Open positions
-/orders - Open orders
-/stats - Trading statistics
+<b>⚡ Quick Commands:</b>
+/balance - Account balance  
+/positions - Open positions
+• /orders - Active orders
+• /market - Market data & prices
 
-<b>📊 Market Commands:</b>
-/market - Market data (default token)
-/market SOL - Market data for SOL
-/price - Current price (default token)
-/price BTC - Price for BTC
-
-<b>🚀 Perps Trading:</b>
-• /long BTC 100 - Long BTC with $100 USDC (Market Order)
-• /long BTC 100 45000 - Long BTC with $100 USDC at $45,000 (Limit Order)
-• /short ETH 50 - Short ETH with $50 USDC (Market Order)
-• /short ETH 50 3500 - Short ETH with $50 USDC at $3,500 (Limit Order)
-• /exit BTC - Close BTC position with Market Order
+<b>🚀 Trading:</b>
+• /long BTC 100 - Long position
+• /short ETH 50 - Short position  
+• /exit BTC - Close position
+• /coo BTC - Cancel open orders
 
 <b>🛡️ Risk Management:</b>
-• /sl BTC 44000 - Set stop loss for BTC at $44,000
-• /tp BTC 50000 - Set take profit for BTC at $50,000
-
-<b>🚨 Automatic Stop Loss:</b>
 • Enabled: {risk_enabled}
-• Stop Loss: {stop_loss}% (automatic execution)
-• Monitoring: Every {heartbeat} seconds
+• Auto Stop Loss: {stop_loss}%
+• /sl BTC 44000 - Manual stop loss
+• /tp BTC 50000 - Take profit order
 
-<b>📋 Order Management:</b>
-• /orders - Show all open orders
-• /orders BTC - Show open orders for BTC only
-• /coo BTC - Cancel all open orders for BTC
-
-<b>📈 Statistics & Analytics:</b>
+<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>
@@ -251,6 +239,7 @@ For support, contact your bot administrator.
 • /stats - Complete trading statistics
 • /performance - Win rate, profit factor, etc.
 • /risk - Sharpe ratio, drawdown, VaR
+• /version - Bot version & system information
 • /trades - Recent trade history
 
 <b>🔔 Price Alerts:</b>
@@ -1167,6 +1156,7 @@ Tap any button below for instant access to bot functions:
         self.application.add_handler(CommandHandler("weekly", self.weekly_command))
         self.application.add_handler(CommandHandler("monthly", self.monthly_command))
         self.application.add_handler(CommandHandler("risk", self.risk_command))
+        self.application.add_handler(CommandHandler("version", self.version_command))
         
         # Callback query handler for inline keyboards
         self.application.add_handler(CallbackQueryHandler(self.button_callback))
@@ -1198,7 +1188,7 @@ Tap any button below for instant access to bot functions:
             
             # Send startup notification
             await self.send_message(
-                "🤖 <b>Manual Trading Bot Started</b>\n\n"
+                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"📱 Manual trading ready!\n"
@@ -2982,6 +2972,72 @@ Will trigger when {token} price moves {alarm['direction']} ${target_price:,.2f}
             error_message = f"❌ Error processing risk command: {str(e)}"
             await update.message.reply_text(error_message)
             logger.error(f"Error in risk command: {e}")
+    
+    async def version_command(self, update: Update, context: ContextTypes.DEFAULT_TYPE) -> None:
+        """Handle the /version command to show bot version and system info."""
+        if not self.is_authorized(update.effective_chat.id):
+            await update.message.reply_text("❌ Unauthorized access.")
+            return
+        
+        try:
+            # Get system info
+            import platform
+            import sys
+            from datetime import datetime
+            
+            uptime_info = "Unknown"
+            try:
+                # Try to get process uptime if available
+                import psutil
+                process = psutil.Process()
+                create_time = datetime.fromtimestamp(process.create_time())
+                uptime = datetime.now() - create_time
+                days = uptime.days
+                hours, remainder = divmod(uptime.seconds, 3600)
+                minutes, _ = divmod(remainder, 60)
+                uptime_info = f"{days}d {hours}h {minutes}m"
+            except ImportError:
+                # psutil not available, skip uptime
+                pass
+            
+            # Get stats info
+            basic_stats = self.stats.get_basic_stats()
+            
+            version_text = f"""
+🤖 <b>Trading Bot Version & System Info</b>
+
+📱 <b>Bot Information:</b>
+• Version: <code>{self.version}</code>
+• Network: {'Testnet' if Config.HYPERLIQUID_TESTNET else 'Mainnet'}
+• Uptime: {uptime_info}
+• Default Token: {Config.DEFAULT_TRADING_TOKEN}
+
+💻 <b>System Information:</b>
+• Python: {sys.version.split()[0]}
+• Platform: {platform.system()} {platform.release()}
+• Architecture: {platform.machine()}
+
+📊 <b>Trading Stats:</b>
+• Total Orders: {basic_stats['total_trades']}
+• Completed Trades: {basic_stats['completed_trades']}
+• Days Active: {basic_stats['days_active']}
+• Start Date: {basic_stats['start_date']}
+
+🔄 <b>Monitoring Status:</b>
+• Order Monitoring: {'✅ Active' if self.order_monitoring_task and not self.order_monitoring_task.done() else '❌ Inactive'}
+• External Trades: ✅ Active
+• Price Alarms: ✅ Active ({len(self.alarms)} active)
+• Risk Management: {'✅ Enabled' if Config.RISK_MANAGEMENT_ENABLED else '❌ Disabled'}
+
+⏰ <b>Current Time:</b> {datetime.now().strftime('%Y-%m-%d %H:%M:%S')}
+            """
+            
+            await update.message.reply_text(version_text.strip(), parse_mode='HTML')
+            
+        except Exception as e:
+            error_message = f"❌ Error processing version command: {str(e)}"
+            await update.message.reply_text(error_message)
+            logger.error(f"Error in version command: {e}")
 
     def _get_position_state(self, symbol: str) -> Dict[str, Any]:
         """Get current position state for a symbol."""

+ 16 - 126
trading_bot.py

@@ -2,7 +2,7 @@
 """
 Hyperliquid Manual Trading Bot - Main Launcher
 
-A robust launcher with error handling, auto-restart, and telegram notifications.
+A simple launcher for systemd deployment.
 This is the ONLY file you need to run - it handles everything.
 """
 
@@ -10,12 +10,13 @@ import sys
 import os
 import asyncio
 import logging
-import traceback
 import signal
-import time
 from datetime import datetime
 from pathlib import Path
 
+# Bot version
+BOT_VERSION = "2.1.0"
+
 # Add src directory to Python path
 sys.path.insert(0, str(Path(__file__).parent / "src"))
 
@@ -33,14 +34,10 @@ bot_instance = None
 is_shutting_down = False
 
 class BotManager:
-    """Manages the trading bot with error handling and auto-restart."""
+    """Manages the trading bot with simple startup and shutdown."""
     
     def __init__(self):
         self.bot = None
-        self.restart_count = 0
-        self.max_restarts = 10
-        self.restart_delay = 5  # seconds
-        self.error_log_file = "logs/bot_errors.log"
         self.setup_logging()
         
         # Ensure logs directory exists
@@ -71,12 +68,12 @@ class BotManager:
         banner = f"""
 ╔══════════════════════════════════════════════════════════════╗
 ║                    📱 HYPERLIQUID TRADING BOT                ║
-║                     Single Command Launcher
+║                       Version {BOT_VERSION}    
 ╠══════════════════════════════════════════════════════════════╣
 ║                                                              ║
 ║  🤖 Manual phone control via Telegram                       ║
 ║  📊 Comprehensive trading statistics                        ║
-║  🛡️ Auto-restart & error notifications
+║  🛡️ Systemd managed service           
 ║  💾 Persistent data between restarts                        ║
 ║  📱 Professional mobile interface                            ║
 ║                                                              ║
@@ -136,132 +133,32 @@ class BotManager:
             self.logger.info("📊 No existing stats - will create new tracking from launch")
             return False
     
-    async def send_error_notification(self, error_message: str, error_details: str = None):
-        """Send error notification via Telegram."""
-        try:
-            if hasattr(Config, 'TELEGRAM_BOT_TOKEN') and hasattr(Config, 'TELEGRAM_CHAT_ID'):
-                from telegram import Bot
-                
-                bot = Bot(token=Config.TELEGRAM_BOT_TOKEN)
-                
-                notification = f"""
-🚨 <b>Trading Bot Error</b>
-
-⏰ <b>Time:</b> {datetime.now().strftime('%Y-%m-%d %H:%M:%S')}
-❌ <b>Error:</b> {error_message}
-
-🔄 <b>Status:</b> Attempting restart...
-📊 <b>Restart Count:</b> {self.restart_count}/{self.max_restarts}
-
-{f'<b>Details:</b> <code>{error_details[:500]}...</code>' if error_details else ''}
-                """
-                
-                await bot.send_message(
-                    chat_id=Config.TELEGRAM_CHAT_ID,
-                    text=notification.strip(),
-                    parse_mode='HTML'
-                )
-                
-                self.logger.info("📱 Error notification sent via Telegram")
-                
-        except Exception as e:
-            self.logger.error(f"Failed to send error notification: {e}")
-    
-    async def send_startup_notification(self):
-        """Send startup notification via Telegram."""
-        try:
-            if self.bot and hasattr(self.bot, 'send_message'):
-                message = f"""
-🟢 <b>Trading Bot Started Successfully</b>
-
-⏰ <b>Started:</b> {datetime.now().strftime('%Y-%m-%d %H:%M:%S')}
-🌐 <b>Network:</b> {'Testnet' if Config.HYPERLIQUID_TESTNET else 'Mainnet'}
-📊 <b>Symbol:</b> {Config.DEFAULT_TRADING_SYMBOL}
-💰 <b>Default Amount:</b> {Config.DEFAULT_TRADE_AMOUNT}
-🔄 <b>Restart #:</b> {self.restart_count}
-
-📱 Bot is ready for manual trading!
-Send /start for quick actions.
-                """
-                await self.bot.send_message(message.strip())
-                
-        except Exception as e:
-            self.logger.error(f"Failed to send startup notification: {e}")
-    
     async def run_bot(self):
-        """Run the main bot with error handling."""
+        """Run the main bot."""
+        global is_shutting_down
+        
         try:
             self.logger.info("🤖 Creating TelegramTradingBot instance...")
             self.bot = TelegramTradingBot()
             
+            # Set version for bot to use in messages
+            self.bot.version = BOT_VERSION
+            
             self.logger.info("📊 Checking statistics persistence...")
             self.check_stats_persistence()
             
             self.logger.info("🚀 Starting Telegram bot...")
             
-            # Send startup notification
-            await self.send_startup_notification()
-            
             # Run the bot
             await self.bot.run()
             
         except KeyboardInterrupt:
             self.logger.info("👋 Bot stopped by user (Ctrl+C)")
-            raise  # Re-raise to handle gracefully
+            is_shutting_down = True
             
         except Exception as e:
             self.logger.error(f"❌ Bot error: {e}")
-            
-            # Get detailed error info
-            error_details = traceback.format_exc()
-            self.logger.error(f"Error details:\n{error_details}")
-            
-            # Log to error file
-            with open(self.error_log_file, 'a') as f:
-                f.write(f"\n{datetime.now().isoformat()} - {e}\n{error_details}\n{'='*50}\n")
-            
-            # Send error notification
-            await self.send_error_notification(str(e), error_details)
-            
-            raise  # Re-raise for restart logic
-    
-    async def run_with_restart(self):
-        """Run bot with auto-restart capability."""
-        global is_shutting_down
-        
-        while not is_shutting_down and self.restart_count < self.max_restarts:
-            try:
-                if self.restart_count > 0:
-                    self.logger.info(f"🔄 Restarting bot (attempt {self.restart_count + 1}/{self.max_restarts})")
-                    self.logger.info(f"⏳ Waiting {self.restart_delay} seconds before restart...")
-                    await asyncio.sleep(self.restart_delay)
-                
-                await self.run_bot()
-                
-                # If we get here, bot exited normally
-                break
-                
-            except KeyboardInterrupt:
-                self.logger.info("👋 Graceful shutdown requested")
-                is_shutting_down = True
-                break
-                
-            except Exception as e:
-                self.restart_count += 1
-                self.logger.error(f"💥 Bot crashed: {e}")
-                
-                if self.restart_count >= self.max_restarts:
-                    self.logger.error(f"❌ Max restart attempts ({self.max_restarts}) reached. Giving up.")
-                    
-                    # Send final error notification
-                    await self.send_error_notification(
-                        f"Bot failed permanently after {self.max_restarts} restart attempts",
-                        f"Last error: {str(e)}"
-                    )
-                    break
-                
-                # Exponential backoff for restart delay
-                self.restart_delay = min(self.restart_delay * 1.5, 60)
+            raise
     
     async def start(self):
         """Main entry point."""
@@ -275,7 +172,7 @@ Send /start for quick actions.
             print("💡 Send /start to your Telegram bot for quick actions")
             print("🛑 Press Ctrl+C to stop\n")
             
-            await self.run_with_restart()
+            await self.run_bot()
             
             return True
             
@@ -290,12 +187,6 @@ def signal_handler(signum, frame):
     
     print("\n🛑 Shutdown signal received...")
     is_shutting_down = True
-    
-    if bot_instance:
-        try:
-            asyncio.create_task(bot_instance.send_error_notification("Bot shutdown requested", "Graceful shutdown"))
-        except:
-            pass
 
 def main():
     """Main function."""
@@ -325,7 +216,6 @@ def main():
         sys.exit(1)
     finally:
         print("📊 Your trading statistics have been saved")
-        print("🔄 Run again anytime to continue tracking!")
 
 if __name__ == "__main__":
     main()