balance.py 3.8 KB

1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950515253545556575859606162636465666768697071727374757677787980818283848586
  1. import logging
  2. from typing import Dict, Any, Optional
  3. from telegram import Update
  4. from telegram.ext import ContextTypes
  5. from .base import InfoCommandsBase
  6. from datetime import datetime
  7. logger = logging.getLogger(__name__)
  8. class BalanceCommands(InfoCommandsBase):
  9. """Handles all balance-related commands."""
  10. async def balance_command(self, update: Update, context: ContextTypes.DEFAULT_TYPE) -> None:
  11. """Handle the /balance command."""
  12. try:
  13. if not self._is_authorized(update):
  14. await self._reply(update, "❌ Unauthorized access.")
  15. return
  16. balance = self.trading_engine.get_balance()
  17. if not balance:
  18. await self._reply(update, "❌ Could not fetch balance information")
  19. return
  20. # Get USDC balances
  21. usdc_total = 0.0
  22. usdc_free = 0.0
  23. usdc_used = 0.0
  24. if 'USDC' in balance.get('total', {}):
  25. usdc_total = float(balance['total']['USDC'])
  26. usdc_free = float(balance.get('free', {}).get('USDC', 0))
  27. usdc_used = float(balance.get('used', {}).get('USDC', 0))
  28. balance_text_parts = [
  29. f"💰 <b>Account Balance</b>\n",
  30. f" 💵 Total USDC: ${usdc_total:,.2f}",
  31. f" ✅ Available USDC: ${usdc_free:,.2f}",
  32. f" 🔒 USDC In Use: ${usdc_used:,.2f}"
  33. ]
  34. # Add other assets
  35. other_assets_text = []
  36. for asset, amount_val in balance.get('total', {}).items():
  37. if asset != 'USDC' and float(amount_val) > 0:
  38. free_amount = float(balance.get('free', {}).get(asset, 0))
  39. other_assets_text.append(f" 🪙 {asset}: {float(amount_val):.6f} (Free: {free_amount:.6f})")
  40. if other_assets_text:
  41. balance_text_parts.append("\n📊 <b>Other Assets:</b>")
  42. balance_text_parts.extend(other_assets_text)
  43. # Performance Metrics
  44. stats = self.trading_engine.get_stats()
  45. initial_balance = 0.0
  46. pnl = 0.0
  47. pnl_percent = 0.0
  48. pnl_emoji = "⚪"
  49. if stats:
  50. basic_stats = stats.get_basic_stats()
  51. initial_balance = basic_stats.get('initial_balance', usdc_total) # Fallback to current total if no initial
  52. if initial_balance is None: # Should not happen if basic_stats is fetched
  53. initial_balance = usdc_total
  54. pnl = usdc_total - initial_balance
  55. pnl_percent = (pnl / initial_balance * 100) if initial_balance > 0 else 0
  56. pnl_emoji = "🟢" if pnl >= 0 else "🔴"
  57. balance_text_parts.append("\n📈 <b>Performance:</b>")
  58. balance_text_parts.append(f" 💵 Initial Balance: ${initial_balance:,.2f}")
  59. balance_text_parts.append(f" {pnl_emoji} Overall P&L: ${pnl:,.2f} ({pnl_percent:+.2f}%)")
  60. # System Status
  61. trading_engine_active = "✅ Active" if self.trading_engine else "❌ Inactive (Error)"
  62. balance_text_parts.append("\n⚙️ <b>System Status:</b>")
  63. balance_text_parts.append(f"• Trading Engine: {trading_engine_active}")
  64. balance_text_parts.append(f"• Data Source: Exchange (Live)") # Balance is usually live
  65. balance_text_parts.append(f"• Last Update: {datetime.now().strftime('%H:%M:%S')}")
  66. final_message = "\n".join(balance_text_parts)
  67. await self._reply(update, final_message.strip())
  68. except Exception as e:
  69. logger.error(f"Error in balance command: {e}", exc_info=True)
  70. await self._reply(update, "❌ Error retrieving balance information.")