risk.py 6.5 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126
  1. import logging
  2. import html
  3. from telegram import Update
  4. from telegram.ext import ContextTypes
  5. from .base import InfoCommandsBase
  6. from src.config.config import Config
  7. from src.utils.token_display_formatter import get_formatter
  8. logger = logging.getLogger(__name__)
  9. class RiskCommands(InfoCommandsBase):
  10. """Handles all risk management-related commands."""
  11. def __init__(self, trading_engine, notification_manager):
  12. super().__init__(trading_engine, notification_manager)
  13. self.formatter = get_formatter()
  14. async def risk_command(self, update: Update, context: ContextTypes.DEFAULT_TYPE) -> None:
  15. """Handle the /risk command to show risk management information."""
  16. try:
  17. # Get token from command arguments or use default
  18. token = context.args[0].upper() if context.args else Config.DEFAULT_TRADING_TOKEN
  19. # Get current positions and orders
  20. positions = self.trading_engine.get_positions()
  21. orders = self.trading_engine.get_orders()
  22. # Get trading statistics
  23. stats = self.trading_engine.get_stats()
  24. # Calculate unrealized P&L for open positions
  25. total_unrealized_pnl = 0.0
  26. position_risks = []
  27. for position in positions:
  28. try:
  29. # Get position details
  30. symbol = position.get('symbol', '')
  31. size = float(position.get('size', 0) or 0)
  32. entry_price = float(position.get('entryPrice', 0) or 0)
  33. mark_price = float(position.get('markPrice', 0) or 0)
  34. liquidation_price = float(position.get('liquidationPrice', 0) or 0)
  35. if size == 0 or entry_price == 0:
  36. continue
  37. # Calculate position value and P&L
  38. position_value = abs(size * entry_price)
  39. unrealized_pnl = size * (mark_price - entry_price)
  40. total_unrealized_pnl += unrealized_pnl
  41. # Calculate risk metrics
  42. price_risk = abs((mark_price - entry_price) / entry_price * 100)
  43. liquidation_risk = abs((liquidation_price - mark_price) / mark_price * 100) if liquidation_price > 0 else 0
  44. # Find stop loss orders for this position
  45. stop_loss_orders = [o for o in orders if o.get('symbol') == symbol and o.get('type') == 'stop']
  46. stop_loss_risk = 0
  47. if stop_loss_orders:
  48. stop_price = float(stop_loss_orders[0].get('price', 0) or 0)
  49. if stop_price > 0:
  50. stop_loss_risk = abs((stop_price - mark_price) / mark_price * 100)
  51. position_risks.append({
  52. 'symbol': symbol,
  53. 'size': size,
  54. 'entry_price': entry_price,
  55. 'mark_price': mark_price,
  56. 'position_value': position_value,
  57. 'unrealized_pnl': unrealized_pnl,
  58. 'price_risk': price_risk,
  59. 'liquidation_risk': liquidation_risk,
  60. 'stop_loss_risk': stop_loss_risk
  61. })
  62. except (ValueError, TypeError) as e:
  63. logger.error(f"Error processing position: {e}")
  64. continue
  65. # Get portfolio value
  66. balance_data = self.trading_engine.get_balance()
  67. portfolio_value = float(balance_data.get('total', {}).get('USDC', 0) or 0) if balance_data else 0.0
  68. # Calculate portfolio risk metrics
  69. portfolio_risk = (total_unrealized_pnl / portfolio_value * 100) if portfolio_value > 0 else 0
  70. # Format the message
  71. message = f"📊 <b>Risk Analysis for {token}</b>\n\n"
  72. # Add position risks
  73. if position_risks:
  74. message += "🔍 <b>Position Risks:</b>\n"
  75. for risk in position_risks:
  76. pnl_emoji = "🟢" if risk['unrealized_pnl'] >= 0 else "🔴"
  77. message += (
  78. f"\n<b>{risk['symbol']}</b>\n"
  79. f"Size: {await self.formatter.format_amount(risk['size'], risk['symbol'].split('/')[0])}\n"
  80. f"Entry: {await self.formatter.format_price_with_symbol(risk['entry_price'], risk['symbol'].split('/')[0])}\n"
  81. f"Mark: {await self.formatter.format_price_with_symbol(risk['mark_price'], risk['symbol'].split('/')[0])}\n"
  82. f"P&L: {pnl_emoji} {await self.formatter.format_price_with_symbol(risk['unrealized_pnl'], risk['symbol'].split('/')[0])}\n"
  83. f"Price Risk: {risk['price_risk']:.2f}%\n"
  84. f"Liquidation Risk: {risk['liquidation_risk']:.2f}%\n"
  85. f"Stop Loss Risk: {risk['stop_loss_risk']:.2f}%\n"
  86. )
  87. else:
  88. message += "No open positions\n"
  89. # Add portfolio summary
  90. message += f"\n📈 <b>Portfolio Summary:</b>\n"
  91. message += f"Total Value: {await self.formatter.format_price_with_symbol(portfolio_value)}\n"
  92. message += f"Unrealized P&L: {await self.formatter.format_price_with_symbol(total_unrealized_pnl)}\n"
  93. message += f"Portfolio Risk: {portfolio_risk:.2f}%\n"
  94. # Add trading statistics
  95. if stats:
  96. performance_stats = stats.get_performance_stats()
  97. message += f"\n📊 <b>Trading Statistics:</b>\n"
  98. message += f"Win Rate: {performance_stats.get('win_rate', 0):.2f}%\n"
  99. message += f"Profit Factor: {performance_stats.get('profit_factor', 0):.2f}\n"
  100. message += f"Average Win: {await self.formatter.format_price_with_symbol(performance_stats.get('average_win', 0))}\n"
  101. message += f"Average Loss: {await self.formatter.format_price_with_symbol(performance_stats.get('average_loss', 0))}\n"
  102. await self._reply(update, message)
  103. except Exception as e:
  104. logger.error(f"Error in risk command: {e}", exc_info=True)
  105. await self._reply(update, "❌ Error getting risk information. Please try again later.")