|
@@ -3,6 +3,7 @@ from typing import Dict, Any, Optional
|
|
from telegram import Update
|
|
from telegram import Update
|
|
from telegram.ext import ContextTypes
|
|
from telegram.ext import ContextTypes
|
|
from .base import InfoCommandsBase
|
|
from .base import InfoCommandsBase
|
|
|
|
+from datetime import datetime
|
|
|
|
|
|
logger = logging.getLogger(__name__)
|
|
logger = logging.getLogger(__name__)
|
|
|
|
|
|
@@ -16,58 +17,70 @@ class BalanceCommands(InfoCommandsBase):
|
|
await self._reply(update, "❌ Unauthorized access.")
|
|
await self._reply(update, "❌ Unauthorized access.")
|
|
return
|
|
return
|
|
|
|
|
|
- stats = self.trading_engine.get_stats()
|
|
|
|
- if not stats:
|
|
|
|
- await self._reply(update, "❌ Trading stats not available.")
|
|
|
|
- return
|
|
|
|
-
|
|
|
|
- # Get balance info
|
|
|
|
- balance_info = stats.get_basic_stats()
|
|
|
|
- if not balance_info:
|
|
|
|
- await self._reply(update, "❌ Balance information not available.")
|
|
|
|
|
|
+ balance = self.trading_engine.get_balance()
|
|
|
|
+ if not balance:
|
|
|
|
+ await self._reply(update, "❌ Could not fetch balance information")
|
|
return
|
|
return
|
|
|
|
|
|
- # Format balance text
|
|
|
|
- balance_text = "💰 <b>Account Balance</b>\n\n"
|
|
|
|
|
|
+ # Get USDC balances
|
|
|
|
+ usdc_total = 0.0
|
|
|
|
+ usdc_free = 0.0
|
|
|
|
+ usdc_used = 0.0
|
|
|
|
|
|
- # Add total balance
|
|
|
|
- total_balance = balance_info.get('total_balance', 0.0)
|
|
|
|
- balance_text += f"💵 Total Balance: ${total_balance:,.2f}\n"
|
|
|
|
|
|
+ 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))
|
|
|
|
|
|
- # Add available balance
|
|
|
|
- available_balance = balance_info.get('available_balance', 0.0)
|
|
|
|
- balance_text += f"💳 Available Balance: ${available_balance:,.2f}\n"
|
|
|
|
|
|
+ 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}"
|
|
|
|
+ ]
|
|
|
|
|
|
- # Add margin used
|
|
|
|
- margin_used = balance_info.get('margin_used', 0.0)
|
|
|
|
- if margin_used > 0:
|
|
|
|
- balance_text += f"📊 Margin Used: ${margin_used:,.2f}\n"
|
|
|
|
- margin_ratio = (margin_used / total_balance) * 100 if total_balance > 0 else 0
|
|
|
|
- balance_text += f"⚖️ Margin Ratio: {margin_ratio:.2f}%\n"
|
|
|
|
|
|
+ # Add other assets
|
|
|
|
+ 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})")
|
|
|
|
+
|
|
|
|
+ if other_assets_text:
|
|
|
|
+ balance_text_parts.append("\n📊 <b>Other Assets:</b>")
|
|
|
|
+ balance_text_parts.extend(other_assets_text)
|
|
|
|
|
|
- # Add unrealized P&L
|
|
|
|
- unrealized_pnl = balance_info.get('unrealized_pnl', 0.0)
|
|
|
|
- pnl_emoji = "🟢" if unrealized_pnl >= 0 else "🔴"
|
|
|
|
- balance_text += f"{pnl_emoji} Unrealized P&L: ${unrealized_pnl:,.2f}\n"
|
|
|
|
-
|
|
|
|
- # Add realized P&L if available
|
|
|
|
- realized_pnl = balance_info.get('realized_pnl', 0.0)
|
|
|
|
- if realized_pnl != 0:
|
|
|
|
- realized_emoji = "🟢" if realized_pnl >= 0 else "🔴"
|
|
|
|
- balance_text += f"{realized_emoji} Realized P&L: ${realized_pnl:,.2f}\n"
|
|
|
|
-
|
|
|
|
- # Add total P&L
|
|
|
|
- total_pnl = unrealized_pnl + realized_pnl
|
|
|
|
- total_pnl_emoji = "🟢" if total_pnl >= 0 else "🔴"
|
|
|
|
- balance_text += f"{total_pnl_emoji} Total P&L: ${total_pnl:,.2f}\n"
|
|
|
|
|
|
+ # Performance Metrics
|
|
|
|
+ stats = self.trading_engine.get_stats()
|
|
|
|
+ initial_balance = 0.0
|
|
|
|
+ pnl = 0.0
|
|
|
|
+ pnl_percent = 0.0
|
|
|
|
+ pnl_emoji = "⚪"
|
|
|
|
|
|
- # Add P&L percentage if margin is used
|
|
|
|
- if margin_used > 0:
|
|
|
|
- pnl_percentage = (total_pnl / margin_used) * 100
|
|
|
|
- balance_text += f"📈 Return on Margin: {pnl_percentage:+.2f}%\n"
|
|
|
|
|
|
+ if stats:
|
|
|
|
+ basic_stats = stats.get_basic_stats()
|
|
|
|
+ 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_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}%)")
|
|
|
|
|
|
- await self._reply(update, balance_text.strip())
|
|
|
|
|
|
+ # 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')}")
|
|
|
|
+
|
|
|
|
+ final_message = "\n".join(balance_text_parts)
|
|
|
|
+ await self._reply(update, final_message.strip())
|
|
|
|
|
|
except Exception as e:
|
|
except Exception as e:
|
|
- logger.error(f"Error in balance command: {e}")
|
|
|
|
|
|
+ logger.error(f"Error in balance command: {e}", exc_info=True)
|
|
await self._reply(update, "❌ Error retrieving balance information.")
|
|
await self._reply(update, "❌ Error retrieving balance information.")
|