trading_bot.py 9.1 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223
  1. #!/usr/bin/env python3
  2. """
  3. Hyperliquid Manual Trading Bot - Main Launcher
  4. A simple launcher for systemd deployment.
  5. This is the ONLY file you need to run - it handles everything.
  6. """
  7. import sys
  8. import os
  9. import asyncio
  10. import logging
  11. from datetime import datetime
  12. from pathlib import Path
  13. # Bot version
  14. BOT_VERSION = "2.3.160"
  15. # Add src directory to Python path
  16. sys.path.insert(0, str(Path(__file__).parent / "src"))
  17. try:
  18. from src.config.config import Config
  19. from src.bot.core import TelegramTradingBot
  20. from src.stats import TradingStats
  21. except ImportError as e:
  22. print(f"❌ Import error: {e}")
  23. print("💡 Make sure you're in the correct directory and dependencies are installed")
  24. sys.exit(1)
  25. # Global variables for graceful shutdown
  26. bot_instance = None
  27. class BotManager:
  28. """Manages the trading bot with simple startup and shutdown."""
  29. def __init__(self):
  30. self.bot = None
  31. self.setup_logging()
  32. # Ensure logs directory exists
  33. os.makedirs("logs", exist_ok=True)
  34. def setup_logging(self):
  35. """Set up comprehensive logging."""
  36. # Create logs directory
  37. os.makedirs("logs", exist_ok=True)
  38. # Set up file logging
  39. log_file = f"logs/trading_bot_{datetime.now().strftime('%Y%m%d')}.log"
  40. try:
  41. from src.config.logging_config import setup_logging
  42. setup_logging()
  43. self.logger = logging.getLogger(__name__)
  44. self.logger.info(f"Logging initialized - Log file: {log_file}")
  45. except Exception as e:
  46. # Fallback logging setup
  47. logging.basicConfig(
  48. level=logging.INFO,
  49. format='%(asctime)s - %(name)s - %(levelname)s - %(message)s',
  50. handlers=[
  51. logging.FileHandler(log_file),
  52. logging.StreamHandler()
  53. ]
  54. )
  55. self.logger = logging.getLogger(__name__)
  56. self.logger.warning(f"Failed to setup advanced logging, using basic setup: {e}")
  57. def print_banner(self):
  58. """Print startup banner."""
  59. banner = f"""
  60. ╔══════════════════════════════════════════════════════════════╗
  61. ║ 📱 HYPERLIQUID TRADING BOT ║
  62. ║ Version {BOT_VERSION} ║
  63. ╠══════════════════════════════════════════════════════════════╣
  64. ║ ║
  65. ║ 🤖 Manual phone control via Telegram ║
  66. ║ 📊 Comprehensive trading statistics ║
  67. ║ 🛡️ Systemd managed service ║
  68. ║ 💾 Persistent data between restarts ║
  69. ║ 📱 Professional mobile interface ║
  70. ║ ║
  71. ╚══════════════════════════════════════════════════════════════╝
  72. 🚀 Starting at: {datetime.now().strftime('%Y-%m-%d %H:%M:%S')}
  73. 📁 Working directory: {os.getcwd()}
  74. 🌐 Network: {'Testnet' if Config.HYPERLIQUID_TESTNET else '🚨 MAINNET 🚨'}
  75. """
  76. print(banner)
  77. def validate_configuration(self):
  78. """Validate bot configuration."""
  79. self.logger.info("🔍 Validating configuration...")
  80. missing_config = []
  81. if not hasattr(Config, 'HYPERLIQUID_WALLET_ADDRESS') or not Config.HYPERLIQUID_WALLET_ADDRESS:
  82. missing_config.append("HYPERLIQUID_WALLET_ADDRESS")
  83. if not hasattr(Config, 'TELEGRAM_BOT_TOKEN') or not Config.TELEGRAM_BOT_TOKEN:
  84. missing_config.append("TELEGRAM_BOT_TOKEN")
  85. if not hasattr(Config, 'TELEGRAM_CHAT_ID') or not Config.TELEGRAM_CHAT_ID:
  86. missing_config.append("TELEGRAM_CHAT_ID")
  87. if not hasattr(Config, 'TELEGRAM_ENABLED') or not Config.TELEGRAM_ENABLED:
  88. missing_config.append("TELEGRAM_ENABLED (must be true)")
  89. if missing_config:
  90. error_msg = f"❌ Missing configuration: {', '.join(missing_config)}"
  91. self.logger.error(error_msg)
  92. print(f"\n{error_msg}")
  93. print("\n💡 Setup steps:")
  94. print("1. Copy config: cp config/env.example .env")
  95. print("2. Get Telegram setup: python utils/get_telegram_chat_id.py")
  96. print("3. Edit .env with your details")
  97. print("4. See: SETUP_GUIDE.md for detailed instructions")
  98. return False
  99. self.logger.info("✅ Configuration validation passed")
  100. return True
  101. def check_stats_persistence(self):
  102. """Check and report on statistics persistence."""
  103. # Check if we have persistent statistics
  104. self.logger.info("📊 Checking statistics persistence...")
  105. sqlite_file = "data/trading_stats.sqlite"
  106. if os.path.exists(sqlite_file):
  107. try:
  108. # Quick check to see if database has data
  109. stats = TradingStats()
  110. basic_stats = stats.get_basic_stats()
  111. total_trades = basic_stats.get('total_trades', 0)
  112. start_date = basic_stats.get('start_date', 'unknown')
  113. self.logger.info(f"📊 Existing SQLite database found - {total_trades} trades since {start_date}")
  114. return True
  115. except Exception as e:
  116. self.logger.warning(f"⚠️ SQLite database {sqlite_file} exists but couldn't load: {e}")
  117. return False
  118. else:
  119. self.logger.info(f"📊 No existing SQLite database at {sqlite_file} - will create new tracking from launch")
  120. return False
  121. async def run_bot(self):
  122. """Run the main bot."""
  123. try:
  124. self.logger.info("🤖 Creating TelegramTradingBot instance...")
  125. self.bot = TelegramTradingBot()
  126. # Set version for bot to use in messages
  127. self.bot.version = BOT_VERSION
  128. self.logger.info("📊 Checking statistics persistence...")
  129. self.check_stats_persistence()
  130. self.logger.info("🚀 Starting Telegram bot...")
  131. # Run the bot
  132. await self.bot.run()
  133. except KeyboardInterrupt:
  134. self.logger.info("👋 Bot stopped by user (Ctrl+C in BotManager.run_bot)")
  135. except Exception as e:
  136. self.logger.error(f"❌ Bot error in BotManager.run_bot: {e}", exc_info=True)
  137. # Ensure cleanup is attempted
  138. if self.bot and self.bot.application and self.bot.application._running:
  139. self.logger.info("Attempting to stop application due to error in run_bot...")
  140. await self.bot.application.stop()
  141. raise # Re-raise the exception to be caught by main()
  142. async def start(self):
  143. """Main entry point for BotManager."""
  144. try:
  145. self.print_banner()
  146. if not self.validate_configuration():
  147. return False
  148. self.logger.info("🎯 All systems ready - starting trading bot...")
  149. print("💡 Send /start to your Telegram bot for quick actions")
  150. print("🛑 Press Ctrl+C to stop\n")
  151. await self.run_bot()
  152. return True
  153. except Exception as e:
  154. self.logger.error(f"❌ Fatal error in BotManager.start: {e}", exc_info=True)
  155. print(f"\n💥 Fatal error during bot start: {e}")
  156. return False
  157. def main():
  158. """Main function."""
  159. global bot_instance
  160. bot_manager = BotManager()
  161. bot_instance = bot_manager
  162. success = False
  163. try:
  164. success = asyncio.run(bot_manager.start())
  165. except KeyboardInterrupt:
  166. logging.getLogger(__name__).info("👋 Bot stopped by user (Ctrl+C in main).")
  167. print("\n👋 Bot stopped by user.")
  168. except Exception as e:
  169. logging.getLogger(__name__).critical(f"💥 Unexpected critical error in main: {e}", exc_info=True)
  170. print(f"\n💥 Unexpected critical error: {e}")
  171. finally:
  172. logging.getLogger(__name__).info("BotManager main function finished.")
  173. print("📊 Your trading statistics should have been saved by the bot's internal shutdown.")
  174. if bot_manager and bot_manager.bot and bot_manager.bot.application:
  175. if hasattr(bot_manager.bot.application, '_running') and bot_manager.bot.application._running:
  176. 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.")
  177. if not success:
  178. print("\n❌ Bot session ended with errors.")
  179. if __name__ == "__main__":
  180. main()