import logging from telegram import Update from telegram.ext import ContextTypes from .base import InfoCommandsBase from src.utils.token_display_formatter import get_formatter logger = logging.getLogger(__name__) class RiskCommands(InfoCommandsBase): """Handles all risk management-related commands.""" async def risk_command(self, update: Update, context: ContextTypes.DEFAULT_TYPE) -> None: """Handle the /risk command to show risk management information.""" if not self._is_authorized(update): return try: stats = self.trading_engine.get_stats() if not stats: await self._reply(update, "āŒ Could not load trading statistics") return # Get current positions positions = stats.get_open_positions() if not positions: await self._reply(update, "šŸ“­ No open positions to analyze risk") return # Get current orders for stop loss analysis orders = self.trading_engine.get_orders() or [] # Get formatter for consistent display formatter = get_formatter() # Build risk analysis message risk_text_parts = ["šŸŽÆ Risk Management Overview"] # Analyze each position for position in positions: symbol = position['symbol'] token = symbol.split('/')[0] if '/' in symbol else symbol.split(':')[0] side = position['position_side'] size = position['current_position_size'] entry_price = position['entry_price'] current_price = position.get('current_price', entry_price) # Calculate unrealized P&L if side == 'long': unrealized_pnl = size * (current_price - entry_price) else: # short unrealized_pnl = size * (entry_price - current_price) # Find stop loss orders for this position stop_loss_orders = [ order for order in orders if order.get('symbol') == symbol and order.get('type') == 'stop_loss' ] # Get the most relevant stop loss price stop_loss_price = None if stop_loss_orders: # Sort by trigger price to find the most conservative stop loss stop_loss_orders.sort(key=lambda x: float(x.get('triggerPrice', 0))) if side == 'long': stop_loss_price = float(stop_loss_orders[0].get('triggerPrice', 0)) else: # short stop_loss_price = float(stop_loss_orders[-1].get('triggerPrice', 0)) # Calculate risk metrics risk_text_parts.append(f"\nšŸ“Š {token}") risk_text_parts.append(f"• Position: {side.upper()} {formatter.format_amount(size, token)} @ {formatter.format_price_with_symbol(entry_price, token)}") risk_text_parts.append(f"• Current: {formatter.format_price_with_symbol(current_price, token)}") # Show P&L pnl_emoji = "🟢" if unrealized_pnl >= 0 else "šŸ”“" risk_text_parts.append(f"• P&L: {pnl_emoji} {formatter.format_price_with_symbol(unrealized_pnl)}") # Show stop loss info if stop_loss_price: risk_amount = abs(size * (stop_loss_price - entry_price)) risk_text_parts.append(f"• Stop Loss: {formatter.format_price_with_symbol(stop_loss_price, token)}") risk_text_parts.append(f"• Risk Amount: {formatter.format_price_with_symbol(risk_amount)}") else: risk_text_parts.append("• Stop Loss: āŒ Not set") # Add separator between positions risk_text_parts.append("") # Add portfolio-level risk metrics total_position_value = sum( abs(float(pos.get('current_position_size', 0)) * float(pos.get('current_price', 0))) for pos in positions ) # Get account balance balance = self.trading_engine.get_balance() if balance: total_balance = float(balance.get('total', 0)) if total_balance > 0: portfolio_risk = (total_position_value / total_balance) * 100 risk_text_parts.append(f"šŸ“ˆ Portfolio Risk") risk_text_parts.append(f"• Total Position Value: {formatter.format_price_with_symbol(total_position_value)}") risk_text_parts.append(f"• Portfolio Risk: {portfolio_risk:.1f}% of balance") await self._reply(update, "\n".join(risk_text_parts).strip()) except Exception as e: error_message = f"āŒ Error processing risk command: {str(e)}" await self._reply(update, error_message) logger.error(f"Error in risk command: {e}")