|
@@ -169,6 +169,8 @@ class InfoCommands:
|
|
|
if open_positions:
|
|
|
total_unrealized = 0
|
|
|
total_position_value = 0
|
|
|
+ total_margin_used = 0
|
|
|
+ total_equity_used = 0 # For ROE calculation
|
|
|
|
|
|
# Fetch all exchange orders once to use throughout the command if needed by other parts
|
|
|
# For this specific change, we'll use it inside the loop, but good practice to fetch once.
|
|
@@ -224,20 +226,41 @@ class InfoCommands:
|
|
|
unrealized_pnl = (entry_price - mark_price) * abs_current_amount
|
|
|
unrealized_pnl = unrealized_pnl or 0.0 # Ensure it's not None for calculations
|
|
|
|
|
|
- # Tiered P&L Percentage Calculation
|
|
|
+ # Tiered P&L Percentage Calculation (prioritize exchange percentage)
|
|
|
pnl_percentage = 0.0
|
|
|
exchange_pnl_percentage = position_trade.get('unrealized_pnl_percentage') # From exchange, e.g., 50.5 for 50.5%
|
|
|
margin_used = position_trade.get('margin_used')
|
|
|
|
|
|
if exchange_pnl_percentage is not None:
|
|
|
pnl_percentage = exchange_pnl_percentage
|
|
|
+ logger.debug(f"Using exchange percentage for {symbol}: {exchange_pnl_percentage}%")
|
|
|
elif margin_used is not None and margin_used > 0 and unrealized_pnl != 0:
|
|
|
pnl_percentage = (unrealized_pnl / margin_used) * 100
|
|
|
+ logger.debug(f"Using margin-based calculation for {symbol}: {pnl_percentage}%")
|
|
|
elif entry_price != 0 and abs_current_amount != 0 and unrealized_pnl != 0:
|
|
|
initial_value = entry_price * abs_current_amount
|
|
|
pnl_percentage = (unrealized_pnl / initial_value) * 100
|
|
|
+ logger.debug(f"Using position value calculation for {symbol}: {pnl_percentage}%")
|
|
|
# else pnl_percentage remains 0.0
|
|
|
|
|
|
+ # Get ROE (Return on Equity) from exchange data
|
|
|
+ roe_percentage = None
|
|
|
+ # Try to get ROE from the raw exchange data
|
|
|
+ exchange_data = self.trading_engine.get_positions()
|
|
|
+ if exchange_data:
|
|
|
+ for pos_data in exchange_data:
|
|
|
+ if pos_data.get('symbol') == symbol:
|
|
|
+ info_data = pos_data.get('info', {})
|
|
|
+ position_info = info_data.get('position', {})
|
|
|
+ roe_raw = position_info.get('returnOnEquity')
|
|
|
+ if roe_raw is not None:
|
|
|
+ try:
|
|
|
+ roe_percentage = float(roe_raw) * 100 # Convert to percentage
|
|
|
+ logger.debug(f"Found ROE for {symbol}: {roe_percentage}%")
|
|
|
+ except (ValueError, TypeError):
|
|
|
+ logger.warning(f"Could not parse ROE value: {roe_raw} for {symbol}")
|
|
|
+ break
|
|
|
+
|
|
|
# Add to totals
|
|
|
individual_position_value = position_trade.get('position_value')
|
|
|
if individual_position_value is None: # Fallback if not in DB
|
|
@@ -246,6 +269,16 @@ class InfoCommands:
|
|
|
total_position_value += individual_position_value
|
|
|
total_unrealized += unrealized_pnl
|
|
|
|
|
|
+ # Add margin to total
|
|
|
+ if margin_used is not None:
|
|
|
+ total_margin_used += margin_used
|
|
|
+
|
|
|
+ # Add equity used for ROE calculation
|
|
|
+ if roe_percentage is not None and roe_percentage != 0:
|
|
|
+ # Calculate equity used from: unrealized_pnl = equity_used * (roe_percentage/100)
|
|
|
+ equity_used = abs(unrealized_pnl / (roe_percentage / 100)) if roe_percentage != 0 else 0
|
|
|
+ total_equity_used += equity_used
|
|
|
+
|
|
|
# --- Position Header Formatting (Emoji, Direction, Leverage) ---
|
|
|
pos_emoji = ""
|
|
|
direction_text = ""
|
|
@@ -299,7 +332,10 @@ class InfoCommands:
|
|
|
positions_text += f" 📈 Mark: {mark_price_str}\n"
|
|
|
|
|
|
pnl_line_emoji = "🟢" if unrealized_pnl >= 0 else "🔴"
|
|
|
- positions_text += f" {pnl_line_emoji} P&L: ${unrealized_pnl:,.2f} ({pnl_percentage:+.2f}%)\n"
|
|
|
+ if roe_percentage is not None:
|
|
|
+ positions_text += f" {pnl_line_emoji} P&L: ${unrealized_pnl:,.2f} ({pnl_percentage:+.2f}% | ROE: {roe_percentage:+.2f}%)\n"
|
|
|
+ else:
|
|
|
+ positions_text += f" {pnl_line_emoji} P&L: ${unrealized_pnl:,.2f} ({pnl_percentage:+.2f}%)\n"
|
|
|
|
|
|
# Show exchange-provided risk data if available
|
|
|
if margin_used is not None:
|
|
@@ -381,8 +417,21 @@ class InfoCommands:
|
|
|
portfolio_emoji = "🟢" if total_unrealized >= 0 else "🔴"
|
|
|
positions_text += f"💼 <b>Total Portfolio:</b>\n"
|
|
|
positions_text += f" 🏦 Total Positions Value: ${total_position_value:,.2f}\n"
|
|
|
- positions_text += f" {portfolio_emoji} Total Unrealized P&L: ${total_unrealized:,.2f}\n\n"
|
|
|
+ if total_margin_used > 0:
|
|
|
+ positions_text += f" 💳 Total Margin Used: ${total_margin_used:,.2f}\n"
|
|
|
+ leverage_ratio = total_position_value / total_margin_used if total_margin_used > 0 else 1.0
|
|
|
+ positions_text += f" ⚖️ Portfolio Leverage: {leverage_ratio:.2f}x\n"
|
|
|
+ positions_text += f" {portfolio_emoji} Total Unrealized P&L: ${total_unrealized:,.2f}\n"
|
|
|
+ if total_margin_used > 0:
|
|
|
+ margin_pnl_percentage = (total_unrealized / total_margin_used) * 100
|
|
|
+ positions_text += f" 📊 Portfolio Return: {margin_pnl_percentage:+.2f}% (on margin)"
|
|
|
+ if total_equity_used > 0:
|
|
|
+ equity_pnl_percentage = (total_unrealized / total_equity_used) * 100
|
|
|
+ positions_text += f" | ROE: {equity_pnl_percentage:+.2f}%"
|
|
|
+ positions_text += "\n"
|
|
|
+ positions_text += "\n"
|
|
|
positions_text += f"🤖 <b>Legend:</b> 🤖 Bot-created • 🔄 External/synced\n"
|
|
|
+ positions_text += f"📊 <b>Percentages:</b> Standard % (margin-based) | ROE % (equity-based)\n"
|
|
|
positions_text += f"💡 Use /sl [token] [price] or /tp [token] [price] to set risk management"
|
|
|
|
|
|
else:
|