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}")