Sfoglia il codice sorgente

Refactor info_commands to streamline position data handling and improve display.

- Removed unused AUTO-SYNC logic and related comments to simplify the code.
- Updated P&L percentage calculations to prioritize database data over exchange data for accuracy.
- Cleaned up position display logic by removing redundant checks and comments, enhancing readability.
- Adjusted stop loss and take profit display formatting for clarity.
- Ensured consistent handling of margin calculations and removed deprecated ROE references.
Carles Sentis 1 settimana fa
parent
commit
3670f99cb0
2 ha cambiato i file con 21 aggiunte e 131 eliminazioni
  1. 20 130
      src/commands/info_commands.py
  2. 1 1
      trading_bot.py

+ 20 - 130
src/commands/info_commands.py

@@ -155,29 +155,17 @@ class InfoCommands:
                 await reply_method(text="❌ Trading statistics not available.", parse_mode='HTML')
                 return
             
-            # 🆕 AUTO-SYNC logic removed as per user request.
-            # Assuming heartbeat updates the DB sufficiently.
-            sync_msg = "" 
-            
             # Get open positions from unified trades table
             open_positions = stats.get_open_positions()
             
             # Add position count to header
             position_count = len(open_positions) if open_positions else 0
-            positions_text = f"📈 <b>Open Positions ({position_count})</b>\n\n{sync_msg}" # sync_msg will be empty
+            positions_text = f"📈 <b>Open Positions ({position_count})</b>\n\n"
             
             if open_positions:
                 total_unrealized = 0
                 total_position_value = 0
-                total_margin_used = 0
-                total_equity_used = 0  # For ROE calculation
-                
-                # Fetch exchange data once for the entire command
-                exchange_positions_data = self.trading_engine.get_positions() or []
-                
-                # Fetch all exchange orders once to use throughout the command if needed by other parts
-                # For this specific change, we'll use it inside the loop, but good practice to fetch once.
-                # self._cached_all_exchange_orders = self.trading_engine.get_orders() or [] 
+                total_margin_used = 0 
                 
                 for position_trade in open_positions:
                     symbol = position_trade['symbol']
@@ -229,56 +217,18 @@ class InfoCommands:
                             unrealized_pnl = (entry_price - mark_price) * abs_current_amount
                     unrealized_pnl = unrealized_pnl or 0.0 # Ensure it's not None for calculations
 
-                    # Tiered P&L Percentage Calculation (prioritize exchange percentage)
+                    # P&L Percentage Calculation (use database data)
                     pnl_percentage = 0.0
-                    exchange_pnl_percentage = position_trade.get('unrealized_pnl_percentage') # From exchange, e.g., 50.5 for 50.5%
+                    db_pnl_percentage = position_trade.get('unrealized_pnl_percentage')
                     margin_used = position_trade.get('margin_used')
 
-                    if exchange_pnl_percentage is not None:
-                        pnl_percentage = exchange_pnl_percentage 
-                        logger.debug(f"Using exchange percentage for {symbol}: {exchange_pnl_percentage}%")
+                    if db_pnl_percentage is not None:
+                        pnl_percentage = db_pnl_percentage 
                     elif margin_used is not None and margin_used > 0 and unrealized_pnl != 0:
                         pnl_percentage = (unrealized_pnl / margin_used) * 100
-                        logger.debug(f"Using margin-based calculation for {symbol}: {pnl_percentage}%")
                     elif entry_price != 0 and abs_current_amount != 0 and unrealized_pnl != 0:
                         initial_value = entry_price * abs_current_amount
                         pnl_percentage = (unrealized_pnl / initial_value) * 100
-                        logger.debug(f"Using position value calculation for {symbol}: {pnl_percentage}%")
-                    # else pnl_percentage remains 0.0
-
-                    # Get ROE (Return on Equity) and margin info from exchange data
-                    roe_percentage = None
-                    live_margin_used = None
-                    # Use the exchange data we fetched once at the beginning
-                    if exchange_positions_data:
-                        for pos_data in exchange_positions_data:
-                            if pos_data.get('symbol') == symbol:
-                                # Get margin from CCXT data
-                                live_margin_used = pos_data.get('initialMargin')
-                                
-                                # Get ROE from raw exchange info
-                                info_data = pos_data.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
-                                        logger.debug(f"Found ROE for {symbol}: {roe_percentage}%")
-                                    except (ValueError, TypeError):
-                                        logger.warning(f"Could not parse ROE value: {roe_raw} for {symbol}")
-                                
-                                # Also try to get margin from raw exchange info if CCXT doesn't have it
-                                if live_margin_used is None:
-                                    live_margin_used = position_info.get('marginUsed')
-                                    if live_margin_used is not None:
-                                        try:
-                                            live_margin_used = float(live_margin_used)
-                                        except (ValueError, TypeError):
-                                            live_margin_used = None
-                                break
-                    
-                    # Use live margin data if available, otherwise fall back to database
-                    margin_used = live_margin_used if live_margin_used is not None else position_trade.get('margin_used')
 
                     # Add to totals
                     individual_position_value = position_trade.get('position_value')
@@ -292,12 +242,6 @@ class InfoCommands:
                     if margin_used is not None:
                         total_margin_used += margin_used
                     
-                    # Add equity used for ROE calculation
-                    if roe_percentage is not None and roe_percentage != 0:
-                        # Calculate equity used from: unrealized_pnl = equity_used * (roe_percentage/100)
-                        equity_used = abs(unrealized_pnl / (roe_percentage / 100)) if roe_percentage != 0 else 0
-                        total_equity_used += equity_used
-                    
                     # --- Position Header Formatting (Emoji, Direction, Leverage) ---
                     pos_emoji = ""
                     direction_text = ""
@@ -351,10 +295,7 @@ class InfoCommands:
                         positions_text += f"   📈 Mark: {mark_price_str}\n"
                     
                     pnl_line_emoji = "🟢" if unrealized_pnl >= 0 else "🔴"
-                    if roe_percentage is not None:
-                        positions_text += f"   {pnl_line_emoji} P&L: ${unrealized_pnl:,.2f} ({pnl_percentage:+.2f}% | ROE: {roe_percentage:+.2f}%)\n"
-                    else:
-                        positions_text += f"   {pnl_line_emoji} P&L: ${unrealized_pnl:,.2f} ({pnl_percentage:+.2f}%)\n"
+                    positions_text += f"   {pnl_line_emoji} P&L: ${unrealized_pnl:,.2f} ({pnl_percentage:+.2f}%)\n"
                     
                     # Show exchange-provided risk data if available
                     if margin_used is not None:
@@ -366,69 +307,14 @@ class InfoCommands:
                     # Show stop loss if linked
                     if position_trade.get('stop_loss_price'):
                         sl_price = position_trade['stop_loss_price']
-                        sl_status = "Pending" if not position_trade.get('stop_loss_order_id') else "Active"
-                        positions_text += f"   🛑 Stop Loss: {formatter.format_price_with_symbol(sl_price, base_asset)} ({sl_status})\n"
+                        positions_text += f"   🛑 Stop Loss: {formatter.format_price_with_symbol(sl_price, base_asset)}\n"
                     
                     # Show take profit if linked
                     if position_trade.get('take_profit_price'):
                         tp_price = position_trade['take_profit_price']
-                        tp_status = "Pending" if not position_trade.get('take_profit_order_id') else "Active"
-                        positions_text += f"   🎯 Take Profit: {formatter.format_price_with_symbol(tp_price, base_asset)} ({tp_status})\n"
+                        positions_text += f"   🎯 Take Profit: {formatter.format_price_with_symbol(tp_price, base_asset)}\n"
                     
-                    # --- NEW: Display potential unlinked SL/TP orders from exchange ---
-                    # Fetch all open orders from the exchange once per /positions call if not already done
-                    # For this example, assuming self.all_exchange_orders is populated at the start of positions_command
-                    # If not, it should be: all_exchange_orders = self.trading_engine.get_orders() or []
-                    
-                    # Ensure all_exchange_orders is fetched (ideally once at the start of the command)
-                    # For this isolated edit, let's assume it's passed or fetched.
-                    # A better place would be at the start of the `positions_command` method.
-                    # e.g., all_exchange_orders = self.trading_engine.get_orders() or []
-                    
-                    # The following line is a placeholder for where all_exchange_orders would be fetched
-                    # This should be done *once* at the beginning of the `positions_command` method.
-                    # all_exchange_orders = getattr(self, '_cached_all_exchange_orders', []) # Example of how it might be cached
-
-                    # To make this edit apply, we'll add a temporary fetch here.
-                    # THIS IS INEFFICIENT and should be done ONCE at the start of the command.
-                    temp_all_exchange_orders = self.trading_engine.get_orders() or []
-
-
-                    if temp_all_exchange_orders: # Check if fetching was successful
-                        unlinked_sl_display = []
-                        unlinked_tp_display = []
-
-                        # Using entry_price of the current bot position for comparison.
-                        # Could also use current_market_price if fetched, but entry_price is simpler here.
-                        # current_market_price_for_pos = mark_price # mark_price is already available
-
-                        for ex_order in temp_all_exchange_orders:
-                            if ex_order.get('symbol') == symbol: # Match symbol
-                                ex_order_id = ex_order.get('id')
-                                # Skip if this order is already formally linked as SL or TP to the current position_trade
-                                if ex_order_id == position_trade.get('stop_loss_order_id') or \
-                                   ex_order_id == position_trade.get('take_profit_order_id'):
-                                    continue
-
-                                order_price = float(ex_order.get('price', 0))
-                                order_side = ex_order.get('side', '').lower()
-
-                                if position_side == 'long' and order_side == 'sell':
-                                    if order_price < entry_price: # Potential SL
-                                        unlinked_sl_display.append(f"SELL @ {formatter.format_price_with_symbol(order_price, base_asset)}")
-                                    elif order_price > entry_price: # Potential TP
-                                        unlinked_tp_display.append(f"SELL @ {formatter.format_price_with_symbol(order_price, base_asset)}")
-                                elif position_side == 'short' and order_side == 'buy':
-                                    if order_price > entry_price: # Potential SL
-                                        unlinked_sl_display.append(f"BUY @ {formatter.format_price_with_symbol(order_price, base_asset)}")
-                                    elif order_price < entry_price: # Potential TP
-                                        unlinked_tp_display.append(f"BUY @ {formatter.format_price_with_symbol(order_price, base_asset)}")
-                        
-                        if unlinked_sl_display:
-                            positions_text += f"   ⚠️ Unlinked SL(s)?: {', '.join(unlinked_sl_display)}\n"
-                        if unlinked_tp_display:
-                            positions_text += f"   🎯 Unlinked TP(s)?: {', '.join(unlinked_tp_display)}\n"
-                    # --- END: Display potential unlinked SL/TP orders ---
+
 
                     positions_text += f"   🆔 Lifecycle ID: {position_trade['trade_lifecycle_id'][:8]}\n\n"
                 
@@ -443,14 +329,9 @@ class InfoCommands:
                 positions_text += f"   {portfolio_emoji} Total Unrealized P&L: ${total_unrealized:,.2f}\n"
                 if total_margin_used > 0:
                     margin_pnl_percentage = (total_unrealized / total_margin_used) * 100
-                    positions_text += f"   📊 Portfolio Return: {margin_pnl_percentage:+.2f}% (on margin)"
-                    if total_equity_used > 0:
-                        equity_pnl_percentage = (total_unrealized / total_equity_used) * 100
-                        positions_text += f" | ROE: {equity_pnl_percentage:+.2f}%"
-                    positions_text += "\n"
+                    positions_text += f"   📊 Portfolio Return: {margin_pnl_percentage:+.2f}% (on margin)\n"
                 positions_text += "\n"
                 positions_text += f"🤖 <b>Legend:</b> 🤖 Bot-created • 🔄 External/synced\n"
-                positions_text += f"📊 <b>Percentages:</b> Standard % (margin-based) | ROE % (equity-based)\n"
                 positions_text += f"💡 Use /sl [token] [price] or /tp [token] [price] to set risk management"
                 
             else:
@@ -509,6 +390,15 @@ class InfoCommands:
                             side = order_data.get('side', '').upper()
                             amount = float(order_data.get('amount', 0))
                             price = float(order_data.get('price', 0))
+                            
+                            # Check for trigger price (for stop loss orders)
+                            trigger_price = order_data.get('info', {}).get('triggerPrice')
+                            if trigger_price:
+                                try:
+                                    price = float(trigger_price)
+                                except (ValueError, TypeError):
+                                    pass  # Keep original price if trigger price can't be parsed
+                            
                             order_type = order_data.get('type', 'unknown').title()
                             exchange_order_id = order_data.get('id', 'N/A') # Renamed for clarity
                             

+ 1 - 1
trading_bot.py

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