#!/usr/bin/env python3 """ Hyperliquid Manual Trading Bot - Main Launcher A simple launcher for systemd deployment. This is the ONLY file you need to run - it handles everything. """ import sys import os import asyncio import logging from datetime import datetime from pathlib import Path # Bot version BOT_VERSION = "2.3.160" # Add src directory to Python path sys.path.insert(0, str(Path(__file__).parent / "src")) try: from src.config.config import Config from src.bot.core import TelegramTradingBot from src.stats import TradingStats except ImportError as e: print(f"āŒ Import error: {e}") print("šŸ’” Make sure you're in the correct directory and dependencies are installed") sys.exit(1) # Global variables for graceful shutdown bot_instance = None class BotManager: """Manages the trading bot with simple startup and shutdown.""" def __init__(self): self.bot = None self.setup_logging() # Ensure logs directory exists os.makedirs("logs", exist_ok=True) def setup_logging(self): """Set up comprehensive logging.""" # Create logs directory os.makedirs("logs", exist_ok=True) # Set up file logging log_file = f"logs/trading_bot_{datetime.now().strftime('%Y%m%d')}.log" try: from src.config.logging_config import setup_logging setup_logging() self.logger = logging.getLogger(__name__) self.logger.info(f"Logging initialized - Log file: {log_file}") except Exception as e: # Fallback logging setup logging.basicConfig( level=logging.INFO, format='%(asctime)s - %(name)s - %(levelname)s - %(message)s', handlers=[ logging.FileHandler(log_file), logging.StreamHandler() ] ) self.logger = logging.getLogger(__name__) self.logger.warning(f"Failed to setup advanced logging, using basic setup: {e}") def print_banner(self): """Print startup banner.""" banner = f""" ╔══════════════════════════════════════════════════════════════╗ ā•‘ šŸ“± HYPERLIQUID TRADING BOT ā•‘ ā•‘ Version {BOT_VERSION} ā•‘ ╠══════════════════════════════════════════════════════════════╣ ā•‘ ā•‘ ā•‘ šŸ¤– Manual phone control via Telegram ā•‘ ā•‘ šŸ“Š Comprehensive trading statistics ā•‘ ā•‘ šŸ›”ļø Systemd managed service ā•‘ ā•‘ šŸ’¾ Persistent data between restarts ā•‘ ā•‘ šŸ“± Professional mobile interface ā•‘ ā•‘ ā•‘ ā•šā•ā•ā•ā•ā•ā•ā•ā•ā•ā•ā•ā•ā•ā•ā•ā•ā•ā•ā•ā•ā•ā•ā•ā•ā•ā•ā•ā•ā•ā•ā•ā•ā•ā•ā•ā•ā•ā•ā•ā•ā•ā•ā•ā•ā•ā•ā•ā•ā•ā•ā•ā•ā•ā•ā•ā•ā•ā•ā•ā•ā•ā•ā• šŸš€ Starting at: {datetime.now().strftime('%Y-%m-%d %H:%M:%S')} šŸ“ Working directory: {os.getcwd()} 🌐 Network: {'Testnet' if Config.HYPERLIQUID_TESTNET else '🚨 MAINNET 🚨'} """ print(banner) def validate_configuration(self): """Validate bot configuration.""" self.logger.info("šŸ” Validating configuration...") missing_config = [] if not hasattr(Config, 'HYPERLIQUID_WALLET_ADDRESS') or not Config.HYPERLIQUID_WALLET_ADDRESS: missing_config.append("HYPERLIQUID_WALLET_ADDRESS") if not hasattr(Config, 'TELEGRAM_BOT_TOKEN') or not Config.TELEGRAM_BOT_TOKEN: missing_config.append("TELEGRAM_BOT_TOKEN") if not hasattr(Config, 'TELEGRAM_CHAT_ID') or not Config.TELEGRAM_CHAT_ID: missing_config.append("TELEGRAM_CHAT_ID") if not hasattr(Config, 'TELEGRAM_ENABLED') or not Config.TELEGRAM_ENABLED: missing_config.append("TELEGRAM_ENABLED (must be true)") if missing_config: error_msg = f"āŒ Missing configuration: {', '.join(missing_config)}" self.logger.error(error_msg) print(f"\n{error_msg}") print("\nšŸ’” Setup steps:") print("1. Copy config: cp config/env.example .env") print("2. Get Telegram setup: python utils/get_telegram_chat_id.py") print("3. Edit .env with your details") print("4. See: SETUP_GUIDE.md for detailed instructions") return False self.logger.info("āœ… Configuration validation passed") return True def check_stats_persistence(self): """Check and report on statistics persistence.""" # Check if we have persistent statistics self.logger.info("šŸ“Š Checking statistics persistence...") sqlite_file = "data/trading_stats.sqlite" if os.path.exists(sqlite_file): try: # Quick check to see if database has data stats = TradingStats() basic_stats = stats.get_basic_stats() total_trades = basic_stats.get('total_trades', 0) start_date = basic_stats.get('start_date', 'unknown') self.logger.info(f"šŸ“Š Existing SQLite database found - {total_trades} trades since {start_date}") return True except Exception as e: self.logger.warning(f"āš ļø SQLite database {sqlite_file} exists but couldn't load: {e}") return False else: self.logger.info(f"šŸ“Š No existing SQLite database at {sqlite_file} - will create new tracking from launch") return False async def run_bot(self): """Run the main bot.""" 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...") # Run the bot await self.bot.run() except KeyboardInterrupt: self.logger.info("šŸ‘‹ Bot stopped by user (Ctrl+C in BotManager.run_bot)") except Exception as e: self.logger.error(f"āŒ Bot error in BotManager.run_bot: {e}", exc_info=True) # Ensure cleanup is attempted if self.bot and self.bot.application and self.bot.application._running: self.logger.info("Attempting to stop application due to error in run_bot...") await self.bot.application.stop() raise # Re-raise the exception to be caught by main() async def start(self): """Main entry point for BotManager.""" try: self.print_banner() if not self.validate_configuration(): return False self.logger.info("šŸŽÆ All systems ready - starting trading bot...") print("šŸ’” Send /start to your Telegram bot for quick actions") print("šŸ›‘ Press Ctrl+C to stop\n") await self.run_bot() return True except Exception as e: self.logger.error(f"āŒ Fatal error in BotManager.start: {e}", exc_info=True) print(f"\nšŸ’„ Fatal error during bot start: {e}") return False def main(): """Main function.""" global bot_instance bot_manager = BotManager() bot_instance = bot_manager success = False try: success = asyncio.run(bot_manager.start()) except KeyboardInterrupt: logging.getLogger(__name__).info("šŸ‘‹ Bot stopped by user (Ctrl+C in main).") print("\nšŸ‘‹ Bot stopped by user.") except Exception as e: logging.getLogger(__name__).critical(f"šŸ’„ Unexpected critical error in main: {e}", exc_info=True) print(f"\nšŸ’„ Unexpected critical error: {e}") finally: logging.getLogger(__name__).info("BotManager main function finished.") print("šŸ“Š Your trading statistics should have been saved by the bot's internal shutdown.") if bot_manager and bot_manager.bot and bot_manager.bot.application: if hasattr(bot_manager.bot.application, '_running') and bot_manager.bot.application._running: logging.getLogger(__name__).warning("Application was still marked as running in main finally block. Attempting forced synchronous stop. This may not work if loop is closed.") if not success: print("\nāŒ Bot session ended with errors.") if __name__ == "__main__": main()