strategy_bot.py 9.8 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255
  1. #!/usr/bin/env python3
  2. """
  3. Automated Trading Strategy Bot
  4. This bot runs continuously and implements automated trading strategies.
  5. It can run alongside the Telegram bot for monitoring and control.
  6. """
  7. import asyncio
  8. import time
  9. import logging
  10. from datetime import datetime, timedelta
  11. from typing import Optional, Dict, Any
  12. from hyperliquid_client import HyperliquidClient
  13. from config import Config
  14. # Set up logging
  15. logging.basicConfig(
  16. format='%(asctime)s - %(name)s - %(levelname)s - %(message)s',
  17. level=getattr(logging, Config.LOG_LEVEL)
  18. )
  19. logger = logging.getLogger(__name__)
  20. class TradingStrategy:
  21. """Base class for trading strategies."""
  22. def __init__(self, client: HyperliquidClient):
  23. self.client = client
  24. self.symbol = Config.DEFAULT_TRADING_SYMBOL
  25. self.trade_amount = Config.DEFAULT_TRADE_AMOUNT
  26. async def analyze_market(self) -> Dict[str, Any]:
  27. """Analyze market conditions. Override in subclasses."""
  28. market_data = self.client.get_market_data(self.symbol)
  29. return {
  30. 'signal': 'HOLD', # BUY, SELL, HOLD
  31. 'confidence': 0.0, # 0.0 to 1.0
  32. 'market_data': market_data
  33. }
  34. async def execute_strategy(self) -> bool:
  35. """Execute the trading strategy. Override in subclasses."""
  36. analysis = await self.analyze_market()
  37. logger.info(f"Strategy analysis: {analysis['signal']} (confidence: {analysis['confidence']:.2f})")
  38. return True
  39. class SimpleMovingAverageStrategy(TradingStrategy):
  40. """Example: Simple Moving Average Crossover Strategy."""
  41. def __init__(self, client: HyperliquidClient):
  42. super().__init__(client)
  43. self.price_history = []
  44. self.short_period = 5 # 5 minute MA
  45. self.long_period = 20 # 20 minute MA
  46. async def analyze_market(self) -> Dict[str, Any]:
  47. """Analyze using moving average crossover."""
  48. market_data = self.client.get_market_data(self.symbol)
  49. if not market_data:
  50. return {'signal': 'HOLD', 'confidence': 0.0, 'market_data': None}
  51. current_price = float(market_data['ticker'].get('last', 0))
  52. if current_price == 0:
  53. return {'signal': 'HOLD', 'confidence': 0.0, 'market_data': market_data}
  54. # Add current price to history
  55. self.price_history.append(current_price)
  56. # Keep only what we need
  57. max_history = max(self.short_period, self.long_period) + 5
  58. if len(self.price_history) > max_history:
  59. self.price_history = self.price_history[-max_history:]
  60. # Need enough data for analysis
  61. if len(self.price_history) < self.long_period:
  62. return {'signal': 'HOLD', 'confidence': 0.0, 'market_data': market_data}
  63. # Calculate moving averages
  64. short_ma = sum(self.price_history[-self.short_period:]) / self.short_period
  65. long_ma = sum(self.price_history[-self.long_period:]) / self.long_period
  66. # Previous MAs for crossover detection
  67. if len(self.price_history) >= self.long_period + 1:
  68. prev_short_ma = sum(self.price_history[-self.short_period-1:-1]) / self.short_period
  69. prev_long_ma = sum(self.price_history[-self.long_period-1:-1]) / self.long_period
  70. # Bullish crossover: short MA crosses above long MA
  71. if prev_short_ma <= prev_long_ma and short_ma > long_ma:
  72. return {'signal': 'BUY', 'confidence': 0.7, 'market_data': market_data}
  73. # Bearish crossover: short MA crosses below long MA
  74. if prev_short_ma >= prev_long_ma and short_ma < long_ma:
  75. return {'signal': 'SELL', 'confidence': 0.7, 'market_data': market_data}
  76. return {'signal': 'HOLD', 'confidence': 0.5, 'market_data': market_data}
  77. async def execute_strategy(self) -> bool:
  78. """Execute the moving average strategy."""
  79. analysis = await self.analyze_market()
  80. signal = analysis['signal']
  81. confidence = analysis['confidence']
  82. logger.info(f"MA Strategy: {signal} (confidence: {confidence:.2f})")
  83. # Only trade with high confidence
  84. if confidence < 0.6:
  85. return True
  86. # Get current positions to avoid overtrading
  87. positions = self.client.get_positions(self.symbol)
  88. current_position = 0
  89. if positions:
  90. for pos in positions:
  91. if pos.get('symbol') == self.symbol:
  92. current_position = float(pos.get('contracts', 0))
  93. break
  94. market_data = analysis['market_data']
  95. current_price = float(market_data['ticker'].get('last', 0))
  96. if signal == 'BUY' and current_position <= 0:
  97. # Place buy order slightly below market price
  98. buy_price = current_price * 0.999 # 0.1% below market
  99. logger.info(f"🟢 Placing BUY order: {self.trade_amount} {self.symbol} @ ${buy_price:.2f}")
  100. # Uncomment to enable real trading:
  101. # order = self.client.place_limit_order(self.symbol, 'buy', self.trade_amount, buy_price)
  102. # if order:
  103. # logger.info(f"✅ Buy order placed: {order}")
  104. # else:
  105. # logger.error("❌ Failed to place buy order")
  106. elif signal == 'SELL' and current_position >= 0:
  107. # Place sell order slightly above market price
  108. sell_price = current_price * 1.001 # 0.1% above market
  109. logger.info(f"🔴 Placing SELL order: {self.trade_amount} {self.symbol} @ ${sell_price:.2f}")
  110. # Uncomment to enable real trading:
  111. # order = self.client.place_limit_order(self.symbol, 'sell', self.trade_amount, sell_price)
  112. # if order:
  113. # logger.info(f"✅ Sell order placed: {order}")
  114. # else:
  115. # logger.error("❌ Failed to place sell order")
  116. return True
  117. class StrategyBot:
  118. """Main automated trading bot that runs strategies continuously."""
  119. def __init__(self):
  120. """Initialize the strategy bot."""
  121. self.client = HyperliquidClient(use_testnet=Config.HYPERLIQUID_TESTNET)
  122. self.strategies = []
  123. self.is_running = False
  124. self.loop_interval = 60 # Run every 60 seconds
  125. # Add strategies
  126. self.add_strategy(SimpleMovingAverageStrategy(self.client))
  127. def add_strategy(self, strategy: TradingStrategy):
  128. """Add a trading strategy to the bot."""
  129. self.strategies.append(strategy)
  130. logger.info(f"Added strategy: {strategy.__class__.__name__}")
  131. async def run_strategies(self):
  132. """Run all strategies once."""
  133. for strategy in self.strategies:
  134. try:
  135. await strategy.execute_strategy()
  136. except Exception as e:
  137. logger.error(f"Error in strategy {strategy.__class__.__name__}: {e}")
  138. async def health_check(self):
  139. """Check bot health and connection."""
  140. # Test connection
  141. market_data = self.client.get_market_data(Config.DEFAULT_TRADING_SYMBOL)
  142. if not market_data:
  143. logger.error("❌ Health check failed - cannot connect to Hyperliquid")
  144. return False
  145. # Check balance
  146. balance = self.client.get_balance()
  147. if not balance:
  148. logger.warning("⚠️ Could not fetch balance")
  149. logger.info("✅ Health check passed")
  150. return True
  151. async def run(self):
  152. """Main bot loop."""
  153. logger.info("🚀 Starting Strategy Bot...")
  154. logger.info(f"📊 Default Symbol: {Config.DEFAULT_TRADING_SYMBOL}")
  155. logger.info(f"💰 Trade Amount: {Config.DEFAULT_TRADE_AMOUNT}")
  156. logger.info(f"⏰ Loop Interval: {self.loop_interval} seconds")
  157. logger.info(f"🌐 Network: {'Testnet' if Config.HYPERLIQUID_TESTNET else 'Mainnet'}")
  158. # Initial health check
  159. if not await self.health_check():
  160. logger.error("❌ Initial health check failed. Exiting.")
  161. return
  162. self.is_running = True
  163. iteration = 0
  164. try:
  165. while self.is_running:
  166. iteration += 1
  167. start_time = time.time()
  168. logger.info(f"🔄 Strategy iteration #{iteration} started")
  169. # Run strategies
  170. await self.run_strategies()
  171. # Periodic health check (every 10 iterations)
  172. if iteration % 10 == 0:
  173. await self.health_check()
  174. # Calculate sleep time
  175. execution_time = time.time() - start_time
  176. sleep_time = max(0, self.loop_interval - execution_time)
  177. logger.info(f"✅ Iteration #{iteration} completed in {execution_time:.2f}s. Sleeping {sleep_time:.2f}s")
  178. if sleep_time > 0:
  179. await asyncio.sleep(sleep_time)
  180. except KeyboardInterrupt:
  181. logger.info("👋 Strategy bot stopped by user")
  182. except Exception as e:
  183. logger.error(f"❌ Unexpected error in strategy bot: {e}")
  184. finally:
  185. self.is_running = False
  186. logger.info("🛑 Strategy bot stopped")
  187. def main():
  188. """Main entry point."""
  189. try:
  190. # Validate configuration
  191. if not Config.validate():
  192. logger.error("❌ Configuration validation failed!")
  193. return
  194. # Create and run the bot
  195. bot = StrategyBot()
  196. asyncio.run(bot.run())
  197. except KeyboardInterrupt:
  198. logger.info("👋 Bot stopped by user")
  199. except Exception as e:
  200. logger.error(f"❌ Unexpected error: {e}")
  201. if __name__ == "__main__":
  202. main()