|
@@ -402,10 +402,6 @@ class InfoCommands:
|
|
if not self._is_authorized(update):
|
|
if not self._is_authorized(update):
|
|
return
|
|
return
|
|
|
|
|
|
- # 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.
|
|
|
|
- # self._cached_all_exchange_orders = self.trading_engine.get_orders() or []
|
|
|
|
-
|
|
|
|
reply_method = None
|
|
reply_method = None
|
|
if update.callback_query:
|
|
if update.callback_query:
|
|
reply_method = update.callback_query.message.reply_text
|
|
reply_method = update.callback_query.message.reply_text
|
|
@@ -418,6 +414,7 @@ class InfoCommands:
|
|
|
|
|
|
try:
|
|
try:
|
|
orders = self.trading_engine.get_orders()
|
|
orders = self.trading_engine.get_orders()
|
|
|
|
+ stats = self.trading_engine.get_stats()
|
|
|
|
|
|
if orders is not None:
|
|
if orders is not None:
|
|
if len(orders) > 0:
|
|
if len(orders) > 0:
|
|
@@ -460,8 +457,9 @@ class InfoCommands:
|
|
orders_text += f" {side_emoji} {side} {formatter.format_amount(amount, symbol)} @ {formatter.format_price_with_symbol(price, symbol)}\n"
|
|
orders_text += f" {side_emoji} {side} {formatter.format_amount(amount, symbol)} @ {formatter.format_price_with_symbol(price, symbol)}\n"
|
|
orders_text += f" 📋 Type: {order_type} | ID: {exchange_order_id}\n"
|
|
orders_text += f" 📋 Type: {order_type} | ID: {exchange_order_id}\n"
|
|
|
|
|
|
- stats = self.trading_engine.get_stats()
|
|
|
|
|
|
+ # Check for pending SL in the trade lifecycle system
|
|
if stats:
|
|
if stats:
|
|
|
|
+ # First check the old system for conceptual pending SLs
|
|
order_in_db = stats.get_order_by_exchange_id(exchange_order_id)
|
|
order_in_db = stats.get_order_by_exchange_id(exchange_order_id)
|
|
if order_in_db:
|
|
if order_in_db:
|
|
bot_ref_id = order_in_db.get('bot_order_ref_id')
|
|
bot_ref_id = order_in_db.get('bot_order_ref_id')
|
|
@@ -480,15 +478,26 @@ class InfoCommands:
|
|
orders_text += f" ⏳ Pending SL Activation: {sl_conceptual_side} at {formatter.format_price_with_symbol(sl_price, symbol)}\n"
|
|
orders_text += f" ⏳ Pending SL Activation: {sl_conceptual_side} at {formatter.format_price_with_symbol(sl_price, symbol)}\n"
|
|
orders_text += f" (Activates after main order fills)\n"
|
|
orders_text += f" (Activates after main order fills)\n"
|
|
displayed_sl_parent_refs.add(bot_ref_id)
|
|
displayed_sl_parent_refs.add(bot_ref_id)
|
|
|
|
+
|
|
|
|
+ # Also check for pending SL in trade lifecycle (new system)
|
|
|
|
+ lifecycle_manager = stats.trade_lifecycle_manager
|
|
|
|
+ if lifecycle_manager:
|
|
|
|
+ pending_trade = lifecycle_manager.get_lifecycle_by_entry_order_id(exchange_order_id, status='pending')
|
|
|
|
+ if pending_trade and pending_trade.get('stop_loss_price'):
|
|
|
|
+ sl_price = pending_trade['stop_loss_price']
|
|
|
|
+ entry_side = pending_trade.get('side', '').lower()
|
|
|
|
+ sl_side = 'SELL' if entry_side == 'buy' else 'BUY'
|
|
|
|
+
|
|
|
|
+ orders_text += f" ⏳ Pending SL: {sl_side} at {formatter.format_price_with_symbol(sl_price, symbol)}\n"
|
|
|
|
+ orders_text += f" (Activates when order fills)\n"
|
|
|
|
|
|
orders_text += "\n"
|
|
orders_text += "\n"
|
|
|
|
|
|
orders_text += f"💼 <b>Total Open Exchange Orders:</b> {len(orders)}\n"
|
|
orders_text += f"💼 <b>Total Open Exchange Orders:</b> {len(orders)}\n"
|
|
|
|
|
|
- # Now, check for any other pending_sl_activation orders whose parents are not in the open list
|
|
|
|
- stats_for_orphan_check = self.trading_engine.get_stats() # Get stats again if not already available
|
|
|
|
- if stats_for_orphan_check:
|
|
|
|
- all_pending_sl_activations = stats_for_orphan_check.get_orders_by_status(
|
|
|
|
|
|
+ # Check for orphaned pending SLs from old system
|
|
|
|
+ if stats:
|
|
|
|
+ all_pending_sl_activations = stats.get_orders_by_status(
|
|
status='pending_activation',
|
|
status='pending_activation',
|
|
order_type_filter='pending_sl_activation'
|
|
order_type_filter='pending_sl_activation'
|
|
)
|
|
)
|
|
@@ -526,36 +535,57 @@ class InfoCommands:
|
|
else:
|
|
else:
|
|
orders_text = "📋 <b>Open Orders (0)</b>\n\n"
|
|
orders_text = "📋 <b>Open Orders (0)</b>\n\n"
|
|
orders_text += "📭 No open orders\n\n"
|
|
orders_text += "📭 No open orders\n\n"
|
|
- # Check for purely conceptual pending SLs even if no exchange orders are open
|
|
|
|
- stats_for_empty_check = self.trading_engine.get_stats()
|
|
|
|
- if stats_for_empty_check:
|
|
|
|
- all_pending_sl_activations_empty = stats_for_empty_check.get_orders_by_status(
|
|
|
|
- status='pending_activation',
|
|
|
|
- order_type_filter='pending_sl_activation'
|
|
|
|
- )
|
|
|
|
- if all_pending_sl_activations_empty:
|
|
|
|
- orders_text += "\n"
|
|
|
|
- orders_text += "⏳ <b>Pending SL Activations (Entry Order Assumed Filled/Closed)</b>\n\n"
|
|
|
|
- formatter_for_empty = get_formatter() # Ensure formatter is available
|
|
|
|
-
|
|
|
|
- orphaned_sls_by_symbol_group_empty = {}
|
|
|
|
- for sl_data_empty in all_pending_sl_activations_empty:
|
|
|
|
- sl_symbol_raw_empty = sl_data_empty.get('symbol', '')
|
|
|
|
- sl_symbol_display_key_empty = sl_symbol_raw_empty.replace('/USDC:USDC', '')
|
|
|
|
- if sl_symbol_display_key_empty not in orphaned_sls_by_symbol_group_empty:
|
|
|
|
- orphaned_sls_by_symbol_group_empty[sl_symbol_display_key_empty] = []
|
|
|
|
- orphaned_sls_by_symbol_group_empty[sl_symbol_display_key_empty].append(sl_data_empty)
|
|
|
|
|
|
+
|
|
|
|
+ # Check for pending SLs from trade lifecycle even if no exchange orders
|
|
|
|
+ if stats and stats.trade_lifecycle_manager:
|
|
|
|
+ pending_sl_trades = stats.trade_lifecycle_manager.get_pending_stop_loss_activations()
|
|
|
|
+
|
|
|
|
+ if pending_sl_trades:
|
|
|
|
+ orders_text += "\n⏳ <b>Pending Stop Loss Activations</b>\n\n"
|
|
|
|
+ formatter_for_empty = get_formatter()
|
|
|
|
|
|
- for sl_sym_key_empty, sl_list_items_empty in orphaned_sls_by_symbol_group_empty.items():
|
|
|
|
- orders_text += f"📊 <b>{sl_sym_key_empty}</b>\n"
|
|
|
|
- for sl_item_empty in sl_list_items_empty:
|
|
|
|
- sl_price_val_empty = sl_item_empty.get('price', 0)
|
|
|
|
- sl_side_val_empty = sl_item_empty.get('side', '').upper()
|
|
|
|
- orders_text += f" ⏳ Pending SL: {sl_side_val_empty} at {formatter_for_empty.format_price_with_symbol(sl_price_val_empty, sl_sym_key_empty)}\n"
|
|
|
|
- orders_text += f" (Awaiting activation by bot)\n\n"
|
|
|
|
- orders_text += f"📦 <b>Total Pending Activations (Entry Filled):</b> {len(all_pending_sl_activations_empty)}\n"
|
|
|
|
|
|
+ for trade in pending_sl_trades:
|
|
|
|
+ symbol_raw = trade.get('symbol', '')
|
|
|
|
+ symbol_display = symbol_raw.replace('/USDC:USDC', '')
|
|
|
|
+ sl_price = trade.get('stop_loss_price', 0)
|
|
|
|
+ entry_side = trade.get('side', '').lower()
|
|
|
|
+ sl_side = 'SELL' if entry_side == 'buy' else 'BUY'
|
|
|
|
+ lifecycle_id = trade.get('trade_lifecycle_id', '')[:8]
|
|
|
|
+
|
|
|
|
+ orders_text += f"📊 <b>{symbol_display}</b>\n"
|
|
|
|
+ orders_text += f" ⏳ Pending SL: {sl_side} at {formatter_for_empty.format_price_with_symbol(sl_price, symbol_display)}\n"
|
|
|
|
+ orders_text += f" (Position opened, awaiting SL activation)\n"
|
|
|
|
+ orders_text += f" Lifecycle: {lifecycle_id}\n\n"
|
|
|
|
+
|
|
|
|
+ orders_text += f"📦 <b>Total Pending SL Activations:</b> {len(pending_sl_trades)}\n"
|
|
else:
|
|
else:
|
|
- orders_text += "💡 Use /long, /short, /sl, or /tp to create orders" # Original message if no pending SLs either
|
|
|
|
|
|
+ # Check for purely conceptual pending SLs from old system
|
|
|
|
+ all_pending_sl_activations_empty = stats.get_orders_by_status(
|
|
|
|
+ status='pending_activation',
|
|
|
|
+ order_type_filter='pending_sl_activation'
|
|
|
|
+ )
|
|
|
|
+ if all_pending_sl_activations_empty:
|
|
|
|
+ orders_text += "\n⏳ <b>Pending SL Activations (Entry Order Assumed Filled/Closed)</b>\n\n"
|
|
|
|
+ formatter_for_empty = get_formatter()
|
|
|
|
+
|
|
|
|
+ orphaned_sls_by_symbol_group_empty = {}
|
|
|
|
+ for sl_data_empty in all_pending_sl_activations_empty:
|
|
|
|
+ sl_symbol_raw_empty = sl_data_empty.get('symbol', '')
|
|
|
|
+ sl_symbol_display_key_empty = sl_symbol_raw_empty.replace('/USDC:USDC', '')
|
|
|
|
+ if sl_symbol_display_key_empty not in orphaned_sls_by_symbol_group_empty:
|
|
|
|
+ orphaned_sls_by_symbol_group_empty[sl_symbol_display_key_empty] = []
|
|
|
|
+ orphaned_sls_by_symbol_group_empty[sl_symbol_display_key_empty].append(sl_data_empty)
|
|
|
|
+
|
|
|
|
+ for sl_sym_key_empty, sl_list_items_empty in orphaned_sls_by_symbol_group_empty.items():
|
|
|
|
+ orders_text += f"📊 <b>{sl_sym_key_empty}</b>\n"
|
|
|
|
+ for sl_item_empty in sl_list_items_empty:
|
|
|
|
+ sl_price_val_empty = sl_item_empty.get('price', 0)
|
|
|
|
+ sl_side_val_empty = sl_item_empty.get('side', '').upper()
|
|
|
|
+ orders_text += f" ⏳ Pending SL: {sl_side_val_empty} at {formatter_for_empty.format_price_with_symbol(sl_price_val_empty, sl_sym_key_empty)}\n"
|
|
|
|
+ orders_text += f" (Awaiting activation by bot)\n\n"
|
|
|
|
+ orders_text += f"📦 <b>Total Pending Activations (Entry Filled):</b> {len(all_pending_sl_activations_empty)}\n"
|
|
|
|
+ else:
|
|
|
|
+ orders_text += "💡 Use /long, /short, /sl, or /tp to create orders"
|
|
else:
|
|
else:
|
|
orders_text += "💡 Use /long, /short, /sl, or /tp to create orders"
|
|
orders_text += "💡 Use /long, /short, /sl, or /tp to create orders"
|
|
|
|
|