Kaynağa Gözat

Enhance InfoCommands to improve balance and performance reporting

- Refactored balance reporting to provide detailed breakdowns of USDC and other assets, including total, available, and in-use amounts.
- Updated performance metrics to display initial balance and overall P&L with improved formatting.
- Added system status information to the balance report, indicating trading engine activity and last update time.
- Introduced inline keyboard for quick actions related to trading commands, enhancing user interaction.
Carles Sentis 3 gün önce
ebeveyn
işleme
d7dcc87e53
1 değiştirilmiş dosya ile 69 ekleme ve 84 silme
  1. 69 84
      src/commands/info_commands.py

+ 69 - 84
src/commands/info_commands.py

@@ -6,7 +6,7 @@ Info Commands - Handles information-related Telegram commands.
 import logging
 from datetime import datetime, timezone, timedelta
 from typing import Optional, Dict, Any, List
-from telegram import Update
+from telegram import Update, InlineKeyboardButton, InlineKeyboardMarkup
 from telegram.ext import ContextTypes
 
 from src.config.config import Config
@@ -69,95 +69,75 @@ class InfoCommands:
         try:
             balance = self.trading_engine.get_balance()
             if balance:
-                balance_text = "💰 <b>Account Balance</b>\n\n"
-                
-                # Debug: Show raw balance structure (can be removed after debugging)
-                logger.debug(f"Raw balance data: {balance}")
-                
-                # CCXT balance structure includes 'free', 'used', and 'total'
-                total_balance = balance.get('total', {})
-                free_balance = balance.get('free', {})
-                used_balance = balance.get('used', {})
-                
-                # Get total portfolio value
-                total_portfolio_value = 0
-                
-                # Show USDC balance prominently
-                if 'USDC' in total_balance:
-                    usdc_total = float(total_balance['USDC'])
-                    usdc_free = float(free_balance.get('USDC', 0))
-                    usdc_used = float(used_balance.get('USDC', 0))
-                    
-                    balance_text += f"💵 <b>USDC:</b>\n"
-                    balance_text += f"   📊 Total: ${usdc_total:,.2f}\n"
-                    balance_text += f"   ✅ Available: ${usdc_free:,.2f}\n"
-                    balance_text += f"   🔒 In Use: ${usdc_used:,.2f}\n\n"
-                    
-                    total_portfolio_value += usdc_total
-                
-                # Show other non-zero balances
-                other_assets = []
-                for asset, amount in total_balance.items():
-                    if asset != 'USDC' and float(amount) > 0:
-                        other_assets.append((asset, float(amount)))
-                
-                if other_assets:
-                    balance_text += "📊 <b>Other Assets:</b>\n"
-                    for asset, amount in other_assets:
-                        free_amount = float(free_balance.get(asset, 0))
-                        used_amount = float(used_balance.get(asset, 0))
-                        
-                        balance_text += f"💵 <b>{asset}:</b>\n"
-                        balance_text += f"   📊 Total: {amount:.6f}\n"
-                        balance_text += f"   ✅ Available: {free_amount:.6f}\n"
-                        balance_text += f"   🔒 In Use: {used_amount:.6f}\n\n"
+                usdc_total = 0.0
+                usdc_free = 0.0
+                usdc_used = 0.0
+
+                if 'USDC' in balance.get('total', {}):
+                    usdc_total = float(balance['total']['USDC'])
+                    usdc_free = float(balance.get('free', {}).get('USDC', 0))
+                    usdc_used = float(balance.get('used', {}).get('USDC', 0))
+
+                balance_text_parts = [
+                    f"💰 <b>Account Balance</b>\n",
+                    f"   💵 Total USDC: ${usdc_total:,.2f}",
+                    f"   ✅ Available USDC: ${usdc_free:,.2f}",
+                    f"   🔒 USDC In Use: ${usdc_used:,.2f}"
+                ]
+
+                other_assets_text = []
+                for asset, amount_val in balance.get('total', {}).items():
+                    if asset != 'USDC' and float(amount_val) > 0:
+                        free_amount = float(balance.get('free', {}).get(asset, 0))
+                        other_assets_text.append(f"   🪙 {asset}: {float(amount_val):.6f} (Free: {free_amount:.6f})")
                 
-                # Portfolio summary
-                usdc_balance = float(total_balance.get('USDC', 0))
+                if other_assets_text:
+                    balance_text_parts.append("\n📊 <b>Other Assets:</b>")
+                    balance_text_parts.extend(other_assets_text)
+
+                # Performance Metrics
                 stats = self.trading_engine.get_stats()
+                initial_balance = 0.0
+                pnl = 0.0
+                pnl_percent = 0.0
+                pnl_emoji = "⚪"
+
                 if stats:
                     basic_stats = stats.get_basic_stats()
-                    initial_balance = basic_stats.get('initial_balance', usdc_balance)
-                    pnl = usdc_balance - initial_balance
+                    initial_balance = basic_stats.get('initial_balance', usdc_total) # Fallback to current total if no initial
+                    if initial_balance is None: # Should not happen if basic_stats is fetched
+                        initial_balance = usdc_total
+                    
+                    pnl = usdc_total - initial_balance
                     pnl_percent = (pnl / initial_balance * 100) if initial_balance > 0 else 0
                     pnl_emoji = "🟢" if pnl >= 0 else "🔴"
-                    
-                    balance_text += f"💼 <b>Portfolio Summary:</b>\n"
-                    balance_text += f"   💰 Total Value: ${total_portfolio_value:,.2f}\n"
-                    balance_text += f"   🚀 Available for Trading: ${float(free_balance.get('USDC', 0)):,.2f}\n"
-                    balance_text += f"   🔒 In Active Use: ${float(used_balance.get('USDC', 0)):,.2f}\n\n"
-                    balance_text += f"📊 <b>Performance:</b>\n"
-                    balance_text += f"   💵 Initial: ${initial_balance:,.2f}\n"
-                    balance_text += f"   {pnl_emoji} P&L: ${pnl:,.2f} ({pnl_percent:+.2f}%)\n"
                 
+                balance_text_parts.append("\n📈 <b>Performance:</b>")
+                balance_text_parts.append(f"   💵 Initial Balance: ${initial_balance:,.2f}")
+                balance_text_parts.append(f"   {pnl_emoji} Overall P&L: ${pnl:,.2f} ({pnl_percent:+.2f}%)")
+
+                # System Status
                 trading_engine_active = "✅ Active" if self.trading_engine else "❌ Inactive (Error)"
+                balance_text_parts.append("\n⚙️ <b>System Status:</b>")
+                balance_text_parts.append(f"• Trading Engine: {trading_engine_active}")
+                balance_text_parts.append(f"• Data Source: Exchange (Live)") # Balance is usually live
+                balance_text_parts.append(f"• Last Update: {datetime.now().strftime('%H:%M:%S')}")
                 
-                # Construct the balance message
-                balance_text = f"""
-💰 <b>Account Balance & Info</b>
-
-💰 <b>Account Balance:</b>
-   💵 Total: ${usdc_total:,.2f}
-   ✅ Available: ${usdc_free:,.2f}
-   🔒 In Use: ${usdc_used:,.2f}
-
-📊 <b>Portfolio Summary:</b>
-   💰 Total Value: ${total_portfolio_value:,.2f}
-   🚀 Available for Trading: ${float(free_balance.get('USDC', 0)):,.2f}
-   🔒 In Active Use: ${float(used_balance.get('USDC', 0)):,.2f}
-
-⚙️ <b>System Status:</b>
-• Trading Engine: {trading_engine_active}
-• Data Source: Cached (updated on heartbeat)
-
-⏰ Last Update: {datetime.now().strftime('%H:%M:%S')}
-                """
+                final_message = "\n".join(balance_text_parts)
+                
+                # Quick Actions Keyboard (same as before)
+                quick_actions = [
+                    [InlineKeyboardButton("Positions (/p)", callback_data="/positions"), InlineKeyboardButton("Orders (/o)", callback_data="/orders")],
+                    [InlineKeyboardButton("Trades (/tr)", callback_data="/trades"), InlineKeyboardButton("Stats (/st)", callback_data="/stats")],
+                    [InlineKeyboardButton("Performance (/pf)", callback_data="/performance"), InlineKeyboardButton("Commands (/c)", callback_data="/commands")]
+                ]
+                quick_action_markup = InlineKeyboardMarkup(quick_actions)
 
-                await reply_method(text=balance_text.strip(), parse_mode='HTML')
+                await reply_method(text=final_message.strip(), parse_mode='HTML', reply_markup=quick_action_markup)
             else:
                 await reply_method(text="❌ Could not fetch balance information", parse_mode='HTML')
         except Exception as e:
-            logger.error(f"Error in balance command: {e}")
+            logger.error(f"Error in balance command: {e}", exc_info=True)
             await reply_method(text="❌ Error retrieving balance information.", parse_mode='HTML')
     
     async def positions_command(self, update: Update, context: ContextTypes.DEFAULT_TYPE) -> None:
@@ -233,8 +213,11 @@ class InfoCommands:
                     # else pnl_percentage remains 0.0
 
                     # Add to totals
-                    current_pos_value_at_mark = abs_current_amount * mark_price
-                    total_position_value += current_pos_value_at_mark
+                    individual_position_value = position_trade.get('position_value')
+                    if individual_position_value is None: # Fallback if not in DB
+                        individual_position_value = abs_current_amount * mark_price
+                    
+                    total_position_value += individual_position_value
                     total_unrealized += unrealized_pnl
                     
                     # --- Position Header Formatting (Emoji, Direction, Leverage) ---
@@ -282,6 +265,9 @@ class InfoCommands:
                     positions_text += f"   📏 Size: {size_str} {base_asset}\n" # Use the formatted size_str
                     positions_text += f"   💰 Entry: {entry_price_str}\n"
                     
+                    # Display individual position value
+                    positions_text += f"   🏦 Value: ${individual_position_value:,.2f}\n"
+                    
                     if mark_price != 0 and abs(mark_price - entry_price) > 1e-9: # Only show mark if significantly different
                         positions_text += f"   📈 Mark: {mark_price_str}\n"
                     
@@ -312,8 +298,8 @@ class InfoCommands:
                 # Portfolio summary
                 portfolio_emoji = "🟢" if total_unrealized >= 0 else "🔴"
                 positions_text += f"💼 <b>Total Portfolio:</b>\n"
-                positions_text += f"   💵 Total Value: ${total_position_value:,.2f}\n"
-                positions_text += f"   {portfolio_emoji} Total P&L: ${total_unrealized:,.2f}\n\n"
+                positions_text += f"   🏦 Total Positions Value: ${total_position_value:,.2f}\n"
+                positions_text += f"   {portfolio_emoji} Total Unrealized P&L: ${total_unrealized:,.2f}\n\n"
                 positions_text += f"🤖 <b>Legend:</b> 🤖 Bot-created • 🔄 External/synced\n"
                 positions_text += f"💡 Use /sl [token] [price] or /tp [token] [price] to set risk management"
                 
@@ -624,7 +610,8 @@ class InfoCommands:
         
         if market_data:
             ticker = market_data.get('ticker', {})
-            
+            logger.debug(f"Market command: Ticker data for {symbol}: {ticker}") # Log the ticker data
+
             current_price = float(ticker.get('last', 0.0) or 0.0)
             bid_price = float(ticker.get('bid', 0.0) or 0.0)
             ask_price = float(ticker.get('ask', 0.0) or 0.0)
@@ -1316,8 +1303,6 @@ Tap any button below for instant access to bot functions:
 💡 <b>Pro Tip:</b> These buttons work the same as typing the commands manually, but faster!
         """
         
-        from telegram import InlineKeyboardButton, InlineKeyboardMarkup
-        
         keyboard = [
             [
                 InlineKeyboardButton("💰 Balance", callback_data="/balance"),