|
@@ -28,8 +28,16 @@ class PositionsCommands(InfoCommandsBase):
|
|
await self._reply(update, "📭 No open positions\n\n💡 Use /long or /short to open a position")
|
|
await self._reply(update, "📭 No open positions\n\n💡 Use /long or /short to open a position")
|
|
return
|
|
return
|
|
|
|
|
|
- # Get current exchange orders for stop loss detection
|
|
|
|
|
|
+ # Get current exchange data for live ROE and mark prices
|
|
|
|
+ exchange_positions = self.trading_engine.get_positions() or []
|
|
exchange_orders = self.trading_engine.get_orders() or []
|
|
exchange_orders = self.trading_engine.get_orders() or []
|
|
|
|
+
|
|
|
|
+ # Create lookup for exchange data by symbol
|
|
|
|
+ exchange_data_by_symbol = {}
|
|
|
|
+ for ex_pos in exchange_positions:
|
|
|
|
+ symbol_key = ex_pos.get('coin', '')
|
|
|
|
+ if symbol_key:
|
|
|
|
+ exchange_data_by_symbol[symbol_key] = ex_pos
|
|
|
|
|
|
# Initialize totals
|
|
# Initialize totals
|
|
total_position_value = 0.0
|
|
total_position_value = 0.0
|
|
@@ -91,13 +99,21 @@ class PositionsCommands(InfoCommandsBase):
|
|
logger.warning(f"Could not parse position_opened_at: {position_opened_at_str} for {symbol}")
|
|
logger.warning(f"Could not parse position_opened_at: {position_opened_at_str} for {symbol}")
|
|
duration_str = "Error"
|
|
duration_str = "Error"
|
|
|
|
|
|
- # Get price data with defaults
|
|
|
|
|
|
+ # Get price data with defaults - prioritize live exchange data
|
|
mark_price = entry_price # Default to entry price
|
|
mark_price = entry_price # Default to entry price
|
|
- if position_trade.get('mark_price') is not None:
|
|
|
|
|
|
+
|
|
|
|
+ # Try to get live mark price from exchange first
|
|
|
|
+ if exchange_data and exchange_data.get('markPrice') is not None:
|
|
|
|
+ try:
|
|
|
|
+ mark_price = float(exchange_data['markPrice'])
|
|
|
|
+ except (ValueError, TypeError):
|
|
|
|
+ logger.warning(f"Could not convert exchange mark_price for {symbol}")
|
|
|
|
+ # Fallback to database mark price
|
|
|
|
+ elif position_trade.get('mark_price') is not None:
|
|
try:
|
|
try:
|
|
mark_price = float(position_trade['mark_price'])
|
|
mark_price = float(position_trade['mark_price'])
|
|
except (ValueError, TypeError):
|
|
except (ValueError, TypeError):
|
|
- logger.warning(f"Could not convert mark_price for {symbol}")
|
|
|
|
|
|
+ logger.warning(f"Could not convert database mark_price for {symbol}")
|
|
|
|
|
|
# Calculate unrealized PnL
|
|
# Calculate unrealized PnL
|
|
unrealized_pnl = 0.0
|
|
unrealized_pnl = 0.0
|
|
@@ -107,13 +123,22 @@ class PositionsCommands(InfoCommandsBase):
|
|
except (ValueError, TypeError):
|
|
except (ValueError, TypeError):
|
|
logger.warning(f"Could not convert unrealized_pnl for {symbol}")
|
|
logger.warning(f"Could not convert unrealized_pnl for {symbol}")
|
|
|
|
|
|
- # Get ROE from database
|
|
|
|
|
|
+ # Get ROE from live exchange data (much more accurate)
|
|
roe_percentage = 0.0
|
|
roe_percentage = 0.0
|
|
- if position_trade.get('roe_percentage') is not None:
|
|
|
|
|
|
+ exchange_data = exchange_data_by_symbol.get(base_asset)
|
|
|
|
+ if exchange_data and exchange_data.get('returnOnEquity') is not None:
|
|
|
|
+ try:
|
|
|
|
+ # Convert from decimal (0.118) to percentage (11.8%)
|
|
|
|
+ roe_percentage = float(exchange_data['returnOnEquity']) * 100
|
|
|
|
+ except (ValueError, TypeError):
|
|
|
|
+ logger.warning(f"Could not convert exchange ROE for {symbol}")
|
|
|
|
+
|
|
|
|
+ # Fallback to database ROE if exchange data not available
|
|
|
|
+ if roe_percentage == 0.0 and position_trade.get('roe_percentage') is not None:
|
|
try:
|
|
try:
|
|
roe_percentage = float(position_trade['roe_percentage'])
|
|
roe_percentage = float(position_trade['roe_percentage'])
|
|
except (ValueError, TypeError):
|
|
except (ValueError, TypeError):
|
|
- logger.warning(f"Could not convert roe_percentage for {symbol}")
|
|
|
|
|
|
+ logger.warning(f"Could not convert database roe_percentage for {symbol}")
|
|
|
|
|
|
# Add to totals
|
|
# Add to totals
|
|
individual_position_value = 0.0
|
|
individual_position_value = 0.0
|