Browse Source

Increment BOT_VERSION to 2.2.138 and update stop loss order implementation in HyperliquidClient.

- Updated BOT_VERSION for the upcoming release.
- Refined the place_stop_loss_order method to implement stop-market orders instead of limit orders, enhancing order execution logic.
- Adjusted parameters and logging for clarity in order placement and error handling.
- Updated place_take_profit_order method to reflect current functionality and added a TODO for future improvements.
Carles Sentis 2 ngày trước cách đây
mục cha
commit
234fd9f873

+ 50 - 30
src/clients/hyperliquid_client.py

@@ -448,76 +448,96 @@ class HyperliquidClient:
             logger.error(f"❌ Error loading markets: {error_message} (Full exception: {e})")
             return None
     
-    def place_stop_loss_order(self, symbol: str, side: str, amount: float, price: float, params: Optional[Dict] = None) -> Tuple[Optional[Dict[str, Any]], Optional[str]]:
+    def place_stop_loss_order(self, symbol: str, side: str, amount: float, stop_price_arg: float, params: Optional[Dict] = None) -> Tuple[Optional[Dict[str, Any]], Optional[str]]:
         """
-        Place a stop loss order (implemented as a limit order).
+        Place a stop loss order (as a stop-market order).
         Returns a tuple: (order_object, error_message_string).
         Error_message_string is None on success. Order_object is None on failure.
 
         Args:
             symbol: Trading symbol (e.g., 'BTC/USDC:USDC')
-            side: 'buy' or 'sell'
+            side: 'buy' or 'sell' (side of the order to be placed when stop is triggered)
             amount: Order amount
-            price: Stop loss price
-            params: Additional parameters for CCXT compatibility
+            stop_price_arg: The price at which the stop loss triggers
+            params: Additional parameters (mostly unused now, but kept for signature compatibility)
         """
         try:
             if not self.sync_client:
                 logger.error("❌ Client not initialized")
                 return None, "Client not initialized"
+
+            # Construct parameters for a trigger order (stop-market)
+            # The main order type is 'market', triggered at stop_price_arg.
+            # The 'price' for the create_order call will be None.
+            trigger_params = {
+                'trigger': {
+                    'triggerPx': str(stop_price_arg),  # The stop price
+                    'isMarket': True,               # Execute as a market order when triggered
+                    'tpsl': 'sl'                    # Indicate it's a stop loss
+                }
+            }
             
-            # Stop loss orders are implemented as limit orders
-            # They will be filled when the market price reaches the stop price
-            order_params = params or {}
-            
-            # Add order type information for clarity in logs
-            logger.info(f"🛑 Placing stop loss order: {side} {amount} {symbol} @ ${price}")
-            
-            order = self.sync_client.create_limit_order(symbol, side, amount, price, params=order_params)
-            
-            logger.info(f"✅ Successfully placed stop loss order for {amount} {symbol} at ${price}")
-            logger.debug(f"📄 Stop loss order details: {order}")
+            # Merge with any incoming params, though trigger_params should take precedence for SL logic
+            if params:
+                trigger_params.update(params)
+
+            logger.info(f"🛑 Placing STOP-MARKET order: {side} {amount} {symbol} with trigger @ ${stop_price_arg:.4f}")
+            
+            # For a stop-market order, the 'price' argument to create_order is None.
+            # The 'type' argument to create_order is 'market'.
+            order = self.sync_client.create_order(
+                symbol, 
+                'market',  # Order type to execute when triggered
+                side, 
+                amount, 
+                None,      # Price is None for market orders
+                params=trigger_params
+            )
+            
+            logger.info(f"✅ Successfully placed stop-market order for {amount} {symbol}, trigger @ ${stop_price_arg:.4f}")
+            logger.debug(f"📄 Stop-market order details: {order}")
             
             return order, None
         except Exception as e:
             error_message = self._extract_error_message(e)
-            logger.error(f"❌ Error placing stop loss order: {error_message} (Full exception: {e})")
+            logger.error(f"❌ Error placing stop-market order: {error_message} (Full exception: {e})")
             return None, error_message
     
-    def place_take_profit_order(self, symbol: str, side: str, amount: float, price: float, params: Optional[Dict] = None) -> Tuple[Optional[Dict[str, Any]], Optional[str]]:
+    def place_take_profit_order(self, symbol: str, side: str, amount: float, take_profit_price_arg: float, params: Optional[Dict] = None) -> Tuple[Optional[Dict[str, Any]], Optional[str]]:
         """
-        Place a take profit order (implemented as a limit order).
+        Place a take profit order (as a limit order, or can be adapted to trigger like SL).
+        Currently places a limit order. For true TP trigger, needs similar logic to SL.
         Returns a tuple: (order_object, error_message_string).
-        Error_message_string is None on success. Order_object is None on failure.
 
         Args:
-            symbol: Trading symbol (e.g., 'BTC/USDC:USDC')
+            symbol: Trading symbol
             side: 'buy' or 'sell'
             amount: Order amount
-            price: Take profit price
-            params: Additional parameters for CCXT compatibility
+            take_profit_price_arg: The price for the take profit order
+            params: Additional parameters
         """
+        # TODO: Implement actual take profit trigger logic (similar to stop_loss_order if needed)
+        # For now, it remains a limit order as per previous implementation, 
+        # but could be changed to a trigger order: isMarket=False, tpsl='tp'.
+        logger.warning("⚠️ place_take_profit_order currently places a LIMIT order. For triggered TP, it needs updating similar to SL.")
         try:
             if not self.sync_client:
                 logger.error("❌ Client not initialized")
                 return None, "Client not initialized"
             
-            # Take profit orders are implemented as limit orders
-            # They will be filled when the market price reaches the target price
             order_params = params or {}
             
-            # Add order type information for clarity in logs
-            logger.info(f"🎯 Placing take profit order: {side} {amount} {symbol} @ ${price}")
+            logger.info(f"🎯 Placing take profit (LIMIT) order: {side} {amount} {symbol} @ ${take_profit_price_arg}")
             
-            order = self.sync_client.create_limit_order(symbol, side, amount, price, params=order_params)
+            order = self.sync_client.create_limit_order(symbol, side, amount, take_profit_price_arg, params=order_params)
             
-            logger.info(f"✅ Successfully placed take profit order for {amount} {symbol} at ${price}")
+            logger.info(f"✅ Successfully placed take profit (LIMIT) order for {amount} {symbol} at ${take_profit_price_arg}")
             logger.debug(f"📄 Take profit order details: {order}")
             
             return order, None
         except Exception as e:
             error_message = self._extract_error_message(e)
-            logger.error(f"❌ Error placing take profit order: {error_message} (Full exception: {e})")
+            logger.error(f"❌ Error placing take profit (LIMIT) order: {error_message} (Full exception: {e})")
             return None, error_message
 
     def get_recent_fills(self, limit: int = 100) -> Optional[List[Dict[str, Any]]]:

+ 1 - 1
src/commands/info_commands.py

@@ -583,7 +583,7 @@ class InfoCommands:
                      await reply_method(text=f"❌ Could not fetch detailed stats for {token_name_arg}.", parse_mode='HTML')
                      return
 
-                stats_message = self._format_token_specific_stats_message(token_stats_data, token_name_arg)
+                stats_message = await self._format_token_specific_stats_message(token_stats_data, token_name_arg) # ADDED await
                 await reply_method(text=stats_message, parse_mode='HTML')
             else:
                 # Overall stats

+ 7 - 5
src/trading/trading_engine.py

@@ -658,13 +658,14 @@ class TradingEngine:
             elif position_type == "SHORT" and stop_price <= entry_price:
                 return {"success": False, "error": "Stop loss price should be above entry price for short positions"}
             
-            order_type_for_stats = 'limit' # Stop loss is a limit order at stop_price
+            order_type_for_stats = 'stop_market' # Changed from 'limit'
 
             # 1. Generate bot_order_ref_id and record order placement intent
             bot_order_ref_id = uuid.uuid4().hex
+            # For stop_market, the 'price' recorded is the trigger price.
             order_db_id = self.stats.record_order_placed(
                 symbol=symbol, side=exit_side, order_type=order_type_for_stats,
-                amount_requested=contracts, price=stop_price, 
+                amount_requested=contracts, price=stop_price, # price here is the trigger price
                 bot_order_ref_id=bot_order_ref_id, status='pending_submission'
             )
 
@@ -672,9 +673,10 @@ class TradingEngine:
                 logger.error(f"Failed to record SL order intent in DB for {symbol} with bot_ref_id {bot_order_ref_id}")
                 return {"success": False, "error": "Failed to record SL order intent in database."}
 
-            # 2. Place limit order at stop loss price
-            logger.info(f"Placing STOP LOSS (LIMIT {exit_side.upper()}) order ({bot_order_ref_id}) for {formatter.format_amount(contracts, token)} {symbol} at {formatter.format_price_with_symbol(stop_price, token)}")
-            exchange_order_data, error_msg = self.client.place_limit_order(symbol, exit_side, contracts, stop_price)
+            # 2. Place stop-market order using the updated client method
+            logger.info(f"Placing STOP LOSS (STOP MARKET {exit_side.upper()}) order ({bot_order_ref_id}) for {formatter.format_amount(contracts, token)} {symbol} at trigger {formatter.format_price_with_symbol(stop_price, token)}")
+            # The client method now expects 'stop_price_arg' for the trigger price.
+            exchange_order_data, error_msg = self.client.place_stop_loss_order(symbol, exit_side, contracts, stop_price_arg=stop_price)
             
             if error_msg:
                 logger.error(f"Stop loss order placement failed for {symbol} ({bot_order_ref_id}): {error_msg}")

+ 1 - 1
trading_bot.py

@@ -14,7 +14,7 @@ from datetime import datetime
 from pathlib import Path
 
 # Bot version
-BOT_VERSION = "2.2.137"
+BOT_VERSION = "2.2.138"
 
 # Add src directory to Python path
 sys.path.insert(0, str(Path(__file__).parent / "src"))