risk.py 5.2 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111
  1. import logging
  2. from telegram import Update
  3. from telegram.ext import ContextTypes
  4. from .base import InfoCommandsBase
  5. from src.utils.token_display_formatter import get_formatter
  6. logger = logging.getLogger(__name__)
  7. class RiskCommands(InfoCommandsBase):
  8. """Handles all risk management-related commands."""
  9. async def risk_command(self, update: Update, context: ContextTypes.DEFAULT_TYPE) -> None:
  10. """Handle the /risk command to show risk management information."""
  11. if not self._is_authorized(update):
  12. return
  13. try:
  14. stats = self.trading_engine.get_stats()
  15. if not stats:
  16. await self._reply(update, "❌ Could not load trading statistics")
  17. return
  18. # Get current positions
  19. positions = stats.get_open_positions()
  20. if not positions:
  21. await self._reply(update, "📭 No open positions to analyze risk")
  22. return
  23. # Get current orders for stop loss analysis
  24. orders = self.trading_engine.get_orders() or []
  25. # Get formatter for consistent display
  26. formatter = get_formatter()
  27. # Build risk analysis message
  28. risk_text_parts = ["🎯 <b>Risk Management Overview</b>"]
  29. # Analyze each position
  30. for position in positions:
  31. symbol = position['symbol']
  32. token = symbol.split('/')[0] if '/' in symbol else symbol.split(':')[0]
  33. side = position['position_side']
  34. size = position['current_position_size']
  35. entry_price = position['entry_price']
  36. current_price = position.get('current_price', entry_price)
  37. # Calculate unrealized P&L
  38. if side == 'long':
  39. unrealized_pnl = size * (current_price - entry_price)
  40. else: # short
  41. unrealized_pnl = size * (entry_price - current_price)
  42. # Find stop loss orders for this position
  43. stop_loss_orders = [
  44. order for order in orders
  45. if order.get('symbol') == symbol and
  46. order.get('type') == 'stop_loss'
  47. ]
  48. # Get the most relevant stop loss price
  49. stop_loss_price = None
  50. if stop_loss_orders:
  51. # Sort by trigger price to find the most conservative stop loss
  52. stop_loss_orders.sort(key=lambda x: float(x.get('triggerPrice', 0)))
  53. if side == 'long':
  54. stop_loss_price = float(stop_loss_orders[0].get('triggerPrice', 0))
  55. else: # short
  56. stop_loss_price = float(stop_loss_orders[-1].get('triggerPrice', 0))
  57. # Calculate risk metrics
  58. risk_text_parts.append(f"\n📊 <b>{token}</b>")
  59. risk_text_parts.append(f"• Position: {side.upper()} {formatter.format_amount(size, token)} @ {formatter.format_price_with_symbol(entry_price, token)}")
  60. risk_text_parts.append(f"• Current: {formatter.format_price_with_symbol(current_price, token)}")
  61. # Show P&L
  62. pnl_emoji = "🟢" if unrealized_pnl >= 0 else "🔴"
  63. risk_text_parts.append(f"• P&L: {pnl_emoji} {formatter.format_price_with_symbol(unrealized_pnl)}")
  64. # Show stop loss info
  65. if stop_loss_price:
  66. risk_amount = abs(size * (stop_loss_price - entry_price))
  67. risk_text_parts.append(f"• Stop Loss: {formatter.format_price_with_symbol(stop_loss_price, token)}")
  68. risk_text_parts.append(f"• Risk Amount: {formatter.format_price_with_symbol(risk_amount)}")
  69. else:
  70. risk_text_parts.append("• Stop Loss: ❌ Not set")
  71. # Add separator between positions
  72. risk_text_parts.append("")
  73. # Add portfolio-level risk metrics
  74. total_position_value = sum(
  75. abs(float(pos.get('current_position_size', 0)) * float(pos.get('current_price', 0)))
  76. for pos in positions
  77. )
  78. # Get account balance
  79. balance = self.trading_engine.get_balance()
  80. if balance:
  81. total_balance = float(balance.get('total', 0))
  82. if total_balance > 0:
  83. portfolio_risk = (total_position_value / total_balance) * 100
  84. risk_text_parts.append(f"📈 <b>Portfolio Risk</b>")
  85. risk_text_parts.append(f"• Total Position Value: {formatter.format_price_with_symbol(total_position_value)}")
  86. risk_text_parts.append(f"• Portfolio Risk: {portfolio_risk:.1f}% of balance")
  87. await self._reply(update, "\n".join(risk_text_parts).strip())
  88. except Exception as e:
  89. error_message = f"❌ Error processing risk command: {str(e)}"
  90. await self._reply(update, error_message)
  91. logger.error(f"Error in risk command: {e}")