소스 검색

Refactor emergency exit handling in RiskCleanupManager and TradingEngine to improve error handling and logging. Enhanced notification formatting for exit failures and ensured cancellation of pending stop losses is robust and properly logged. This change streamlines the exit process and improves clarity in error reporting.

Carles Sentis 4 일 전
부모
커밋
72e24c79cf
4개의 변경된 파일47개의 추가작업 그리고 48개의 파일을 삭제
  1. 23 30
      src/monitoring/risk_cleanup_manager.py
  2. 1 3
      src/stats/aggregation_manager.py
  3. 22 14
      src/trading/trading_engine.py
  4. 1 1
      trading_bot.py

+ 23 - 30
src/monitoring/risk_cleanup_manager.py

@@ -148,7 +148,7 @@ class RiskCleanupManager:
                         logger.info(f"Skipping position {symbol}: contracts={contracts}, entry_price={entry_price}")
                         continue
 
-                    logger.info(f"[RiskMgmt] {symbol}: ROE={roe_percentage:+.2f}%, Threshold=-{Config.STOP_LOSS_PERCENTAGE}% (Trigger: {roe_percentage <= -Config.STOP_LOSS_PERCENTAGE})")
+                    logger.debug(f"[RiskMgmt] {symbol}: ROE={roe_percentage:+.2f}%, Threshold=-{Config.STOP_LOSS_PERCENTAGE}% (Trigger: {roe_percentage <= -Config.STOP_LOSS_PERCENTAGE})")
 
                     if roe_percentage <= -Config.STOP_LOSS_PERCENTAGE:
                         token = symbol.split('/')[0] if '/' in symbol else symbol.split(':')[0]
@@ -175,40 +175,33 @@ Safety Threshold: -{Config.STOP_LOSS_PERCENTAGE}% ROE
 Action: Executing emergency exit order..."""
                             )
 
+                        # Execute emergency exit order
                         exit_result = await self.trading_engine.execute_exit_order(token)
-                        
-                        if exit_result.get('success'):
-                            placed_order_details = exit_result.get('order_placed_details', {})
-                            logger.info(f"✅ Emergency exit order placed for {token} (Lifecycle: {lifecycle_id_str}). Order details: {placed_order_details}")
-                            
-                            if self.notification_manager:
-                                await self.notification_manager.send_generic_notification(
-                                    f"""✅ <b>Emergency Exit Initiated</b>
-
-📊 <b>Position:</b> {token} {position_side}
-🆔 <b>Lifecycle ID:</b> {lifecycle_id_str}
-📉 <b>Loss at Trigger:</b> {roe_percentage:+.2f}% ROE (${unrealized_pnl:+.2f})
-⚠️ <b>Threshold:</b> -{Config.STOP_LOSS_PERCENTAGE}% ROE
-✅ <b>Action:</b> Market exit order placed successfully
-🆔 <b>Exit Order ID:</b> {placed_order_details.get('exchange_order_id', 'N/A')}
-
-🛡️ The system will confirm closure and P&L once the exit order fill is processed."""
-                                )
-                        else:
+                        if not exit_result.get('success'):
                             error_msg = exit_result.get('error', 'Unknown error')
-                            logger.error(f"❌ Failed to execute emergency exit order for {token} (Lifecycle: {lifecycle_id_str}): {error_msg}")
+                            logger.error(f"❌  Failed to execute emergency exit order for {token} (Lifecycle: {lifecycle_id_str}): {error_msg}")
                             if self.notification_manager:
                                 await self.notification_manager.send_generic_notification(
-                                    f"""❌ <b>CRITICAL: Emergency Exit Failed!</b>
-
-📊 <b>Position:</b> {token} {position_side}
-🆔 <b>Lifecycle ID:</b> {lifecycle_id_str}
-📉 <b>Loss:</b> {roe_percentage:+.2f}% ROE
-❌ <b>Error Placing Order:</b> {error_msg}
-
-⚠️ <b>MANUAL INTERVENTION REQUIRED</b>
-Please close this position manually via /exit {token}"""
+                                    f"⚠️ <b>Emergency Exit Failed</b>\n\n"
+                                    f"Token: {token}\n"
+                                    f"Position: {position_side.upper()}\n"
+                                    f"ROE: {roe_percentage:.2f}%\n"
+                                    f"Error: {error_msg}\n\n"
+                                    f"Please check the position and close it manually if needed."
+                                )
+                            continue
+
+                        # Cancel any pending stop losses for this symbol
+                        if self.trading_engine.stats and hasattr(self.trading_engine.stats.order_manager, 'cancel_pending_stop_losses_by_symbol'):
+                            try:
+                                cancelled_sl_count = self.trading_engine.stats.order_manager.cancel_pending_stop_losses_by_symbol(
+                                    symbol=symbol,
+                                    new_status='cancelled_automatic_exit'
                                 )
+                                if cancelled_sl_count > 0:
+                                    logger.info(f"Cancelled {cancelled_sl_count} pending stop losses for {symbol} after automatic exit")
+                            except Exception as sl_error:
+                                logger.error(f"Error cancelling pending stop losses for {symbol}: {sl_error}")
                 except Exception as pos_error:
                     logger.error(f"Error processing position for automatic stop loss: {pos_error}")
                     continue

+ 1 - 3
src/stats/aggregation_manager.py

@@ -135,9 +135,7 @@ class AggregationManager:
                         WHEN ? > roe_percentage THEN ?
                         ELSE roe_percentage
                     END,
-                    updated_at = ?
-                )
-            """
+                    updated_at = ?"""
             
             # Execute the upsert
             self.db._execute_query(token_upsert_query, (

+ 22 - 14
src/trading/trading_engine.py

@@ -604,13 +604,17 @@ class TradingEngine:
             # action_type = self.stats.record_trade_with_enhanced_tracking(...)
             
             # Cancel any pending stop losses for this symbol since position will be closed
-            if self.stats:
-                cancelled_sl_count = self.stats.cancel_pending_stop_losses_by_symbol(
-                    symbol=symbol,
-                    new_status='cancelled_manual_exit'
-                )
-                if cancelled_sl_count > 0:
-                    logger.info(f"🛑 Cancelled {cancelled_sl_count} pending stop losses for {symbol} due to manual exit order")
+            cancelled_sl_count = 0
+            if self.stats and hasattr(self.stats.order_manager, 'cancel_pending_stop_losses_by_symbol'):
+                try:
+                    cancelled_sl_count = self.stats.order_manager.cancel_pending_stop_losses_by_symbol(
+                        symbol=symbol,
+                        new_status='cancelled_manual_exit'
+                    )
+                    if cancelled_sl_count > 0:
+                        logger.info(f"🛑 Cancelled {cancelled_sl_count} pending stop losses for {symbol} due to manual exit order")
+                except Exception as e:
+                    logger.warning(f"Could not cancel pending stop losses for {symbol}: {e}")
             
             # NOTE: Exit orders do not create new trade cycles - they close existing ones
             # The MarketMonitor will handle closing the trade cycle when the exit order fills
@@ -635,7 +639,7 @@ class TradingEngine:
                 },
                 "position_type_closed": position_type, # Info about the position it intends to close
                 "contracts_intended_to_close": contracts_to_close,
-                "cancelled_stop_losses": cancelled_sl_count if self.stats else 0,
+                "cancelled_stop_losses": cancelled_sl_count,
                 "trade_lifecycle_id": lifecycle_id_to_close # Return lifecycle_id of the closed position
             }
         except Exception as e:
@@ -881,12 +885,16 @@ class TradingEngine:
                             )
                 
                 # Cancel any linked pending stop losses for this symbol
-                cleanup_count = self.stats.cancel_pending_stop_losses_by_symbol(
-                    symbol, 
-                    'cancelled_manual_exit'
-                )
-                if cleanup_count > 0:
-                    logger.info(f"Cleaned up {cleanup_count} pending stop losses for {symbol}")
+                if hasattr(self.stats.order_manager, 'cancel_pending_stop_losses_by_symbol'):
+                    try:
+                        cleanup_count = self.stats.order_manager.cancel_pending_stop_losses_by_symbol(
+                            symbol, 
+                            'cancelled_manual_exit'
+                        )
+                        if cleanup_count > 0:
+                            logger.info(f"Cleaned up {cleanup_count} pending stop losses for {symbol}")
+                    except Exception as e:
+                        logger.error(f"Error cancelling pending stop losses for {symbol}: {e}")
             
             # Prepare result
             if failed_orders:

+ 1 - 1
trading_bot.py

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