Просмотр исходного кода

Update risk management to utilize Return on Equity (ROE) for stop loss calculations.

- Added a new configuration for stop loss percentage based on ROE in the environment example.
- Updated Telegram bot notifications to reflect ROE in stop loss metrics.
- Enhanced risk cleanup manager to calculate and log ROE for positions, providing a more accurate assessment of losses.
- Implemented fallback mechanisms for ROE calculation to ensure robustness in risk management.
Carles Sentis 1 неделя назад
Родитель
Сommit
481cafc27d
4 измененных файлов с 49 добавлено и 12 удалено
  1. 2 0
      config/env.example
  2. 1 1
      src/bot/core.py
  3. 45 10
      src/monitoring/risk_cleanup_manager.py
  4. 1 1
      trading_bot.py

+ 2 - 0
config/env.example

@@ -21,6 +21,8 @@ DEFAULT_TRADING_TOKEN=BTC
 
 # Risk management settings
 RISK_MANAGEMENT_ENABLED=true
+# Stop loss threshold based on ROE (Return on Equity) percentage - matches Hyperliquid UI
+# This is the percentage loss of your actual cash investment, not margin
 STOP_LOSS_PERCENTAGE=10.0
 
 # ========================================

+ 1 - 1
src/bot/core.py

@@ -156,7 +156,7 @@ class TelegramTradingBot:
 
 <b>🛡️ Risk Management:</b>
 • Enabled: {'✅ Yes' if risk_enabled else '❌ No'}
-• Auto Stop Loss: {stop_loss_percentage}%
+• Auto Stop Loss: {stop_loss_percentage}% ROE
 • Order Stop Loss: Use sl:price parameter
 • /sl {Config.DEFAULT_TRADING_TOKEN} 44000 - Manual stop loss
 • /tp {Config.DEFAULT_TRADING_TOKEN} 50000 - Take profit order

+ 45 - 10
src/monitoring/risk_cleanup_manager.py

@@ -143,11 +143,46 @@ class RiskCleanupManager:
                     if contracts == 0 or entry_price <= 0 or mark_price <= 0:
                         continue
 
-                    entry_value = abs(contracts) * entry_price
-                    if entry_value <= 0:
-                        continue
+                    # Calculate ROE (Return on Equity) percentage instead of standard PnL percentage
+                    # ROE is what the user sees in the Hyperliquid UI and represents actual return on cash invested
+                    roe_percentage = 0.0
+                    
+                    # Try to get ROE directly from exchange
+                    info_data = position.get('info', {})
+                    position_info = info_data.get('position', {})
+                    roe_raw = position_info.get('returnOnEquity')
+                    
+                    if roe_raw is not None:
+                        try:
+                            roe_percentage = float(roe_raw) * 100  # Convert to percentage
+                        except (ValueError, TypeError):
+                            logger.warning(f"Could not parse ROE value: {roe_raw} for {symbol}")
+                            roe_percentage = 0.0
+                    
+                    # Fallback to calculating ROE if not available from exchange
+                    if roe_percentage == 0.0 and unrealized_pnl != 0:
+                        # Calculate equity used: For leveraged positions, equity is less than margin
+                        # We can estimate it from the margin and leverage
+                        margin_used = position.get('marginUsed') or position.get('initialMargin')
+                        leverage = position.get('leverage', 1.0)
                         
-                    pnl_percentage = (unrealized_pnl / entry_value) * 100
+                        if margin_used and leverage and leverage > 0:
+                            try:
+                                equity_used = float(margin_used) / float(leverage)
+                                if equity_used > 0:
+                                    roe_percentage = (unrealized_pnl / equity_used) * 100
+                                    logger.debug(f"Calculated ROE for {symbol}: {roe_percentage:.2f}% (margin: ${margin_used}, leverage: {leverage}x, equity: ${equity_used})")
+                            except (ValueError, TypeError, ZeroDivisionError):
+                                pass
+                    
+                    # Final fallback to standard percentage if ROE calculation fails
+                    if roe_percentage == 0.0 and unrealized_pnl != 0:
+                        entry_value = abs(contracts) * entry_price
+                        if entry_value > 0:
+                            roe_percentage = (unrealized_pnl / entry_value) * 100
+                            logger.debug(f"Using standard PnL calculation for {symbol} as ROE fallback: {roe_percentage:.2f}%")
+                    
+                    pnl_percentage = roe_percentage  # Use ROE as the percentage for stop loss comparison
 
                     if pnl_percentage <= -Config.STOP_LOSS_PERCENTAGE:
                         token = symbol.split('/')[0] if '/' in symbol else symbol.split(':')[0]
@@ -159,7 +194,7 @@ class RiskCleanupManager:
                             if active_trade_lc:
                                 lifecycle_id_str = active_trade_lc.get('trade_lifecycle_id', "N/A")[:8] + "..."
 
-                        logger.warning(f"🚨 AUTOMATIC STOP LOSS TRIGGERED: {token} {position_side} position (Lifecycle: {lifecycle_id_str}) has {pnl_percentage:.2f}% loss (threshold: -{Config.STOP_LOSS_PERCENTAGE}%)")
+                        logger.warning(f"🚨 AUTOMATIC STOP LOSS TRIGGERED: {token} {position_side} position (Lifecycle: {lifecycle_id_str}) has {pnl_percentage:.2f}% ROE loss (threshold: -{Config.STOP_LOSS_PERCENTAGE}%)")
                         
                         if self.notification_manager:
                             await self.notification_manager.send_generic_notification(
@@ -169,8 +204,8 @@ Lifecycle ID: {lifecycle_id_str}\\n
 Position: {position_side} {abs(contracts):.6f}\\n
 Entry Price: ${entry_price:.4f}\\n
 Current Price: ${mark_price:.4f}\\n
-Unrealized P&L: ${unrealized_pnl:.2f} ({pnl_percentage:.2f}%)\\n
-Safety Threshold: -{Config.STOP_LOSS_PERCENTAGE}%\\n
+Unrealized P&L: ${unrealized_pnl:.2f} ({pnl_percentage:.2f}% ROE)\\n
+Safety Threshold: -{Config.STOP_LOSS_PERCENTAGE}% ROE\\n
 Action: Executing emergency exit order..."""
                             )
 
@@ -192,8 +227,8 @@ Action: Executing emergency exit order..."""
                                     f"""✅ <b>Emergency Exit Initiated</b>\\n\\n
 📊 <b>Position:</b> {token} {position_side}\\n
 🆔 <b>Lifecycle ID:</b> {lifecycle_id_str}\\n
-📉 <b>Loss at Trigger:</b> {pnl_percentage:.2f}% (${unrealized_pnl:.2f})\\n
-⚠️ <b>Threshold:</b> -{Config.STOP_LOSS_PERCENTAGE}%\\n
+📉 <b>Loss at Trigger:</b> {pnl_percentage:.2f}% ROE (${unrealized_pnl:.2f})\\n
+⚠️ <b>Threshold:</b> -{Config.STOP_LOSS_PERCENTAGE}% ROE\\n
 ✅ <b>Action:</b> Market exit order placed successfully\\n
 🆔 <b>Exit Order ID:</b> {placed_order_details.get('exchange_order_id', 'N/A')}\\n
 {f'🛑 <b>Cleanup:</b> Cancelled {cancelled_sl_count} other pending stop losses' if cancelled_sl_count > 0 else ''}
@@ -208,7 +243,7 @@ Action: Executing emergency exit order..."""
                                     f"""❌ <b>CRITICAL: Emergency Exit Failed!</b>\\n\\n
 📊 <b>Position:</b> {token} {position_side}\\n
 🆔 <b>Lifecycle ID:</b> {lifecycle_id_str}\\n
-📉 <b>Loss:</b> {pnl_percentage:.2f}%\\n
+📉 <b>Loss:</b> {pnl_percentage:.2f}% ROE\\n
 ❌ <b>Error Placing Order:</b> {error_msg}\\n\\n
 ⚠️ <b>MANUAL INTERVENTION REQUIRED</b>\\n
 Please close this position manually via /exit {token}"""

+ 1 - 1
trading_bot.py

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