|
@@ -1222,7 +1222,6 @@ class MarketMonitor:
|
|
|
if not stats:
|
|
|
return
|
|
|
|
|
|
-
|
|
|
pending_stop_losses = stats.get_orders_by_status('pending_trigger', 'stop_limit_trigger')
|
|
|
|
|
|
if not pending_stop_losses:
|
|
@@ -1230,18 +1229,16 @@ class MarketMonitor:
|
|
|
|
|
|
logger.debug(f"Checking {len(pending_stop_losses)} pending stop losses for orphaned orders")
|
|
|
|
|
|
-
|
|
|
- current_positions = self.cached_positions or []
|
|
|
+ current_positions = self.cached_positions or []
|
|
|
position_symbols = set()
|
|
|
|
|
|
- if current_positions:
|
|
|
+ if current_positions:
|
|
|
for pos in current_positions:
|
|
|
symbol = pos.get('symbol')
|
|
|
contracts = float(pos.get('contracts', 0))
|
|
|
if symbol and contracts != 0:
|
|
|
position_symbols.add(symbol)
|
|
|
|
|
|
-
|
|
|
orphaned_count = 0
|
|
|
for sl_order in pending_stop_losses:
|
|
|
symbol = sl_order.get('symbol')
|
|
@@ -1251,12 +1248,10 @@ class MarketMonitor:
|
|
|
should_cancel = False
|
|
|
cancel_reason = ""
|
|
|
|
|
|
-
|
|
|
if parent_bot_ref_id:
|
|
|
parent_order = stats.get_order_by_bot_ref_id(parent_bot_ref_id)
|
|
|
if parent_order:
|
|
|
parent_status = parent_order.get('status', '').lower()
|
|
|
- parent_order_id = parent_order.get('id')
|
|
|
|
|
|
if parent_order.get('exchange_order_id'):
|
|
|
entry_oid = parent_order['exchange_order_id']
|
|
@@ -1269,7 +1264,6 @@ class MarketMonitor:
|
|
|
should_cancel = True
|
|
|
cancel_reason = f"parent order ({entry_oid[:6]}...) {parent_status} and lifecycle explicitly cancelled"
|
|
|
elif not lc_pending and not lc_opened:
|
|
|
-
|
|
|
should_cancel = True
|
|
|
cancel_reason = f"parent order ({entry_oid[:6]}...) {parent_status} and no active/pending/cancelled lifecycle found"
|
|
|
else:
|
|
@@ -1280,35 +1274,44 @@ class MarketMonitor:
|
|
|
should_cancel = False
|
|
|
|
|
|
elif parent_status in ['failed_submission', 'failed_submission_no_data', 'cancelled_manually', 'disappeared_from_exchange']:
|
|
|
- if not lc_opened:
|
|
|
+ if not lc_opened:
|
|
|
should_cancel = True
|
|
|
cancel_reason = f"parent order ({entry_oid[:6]}...) {parent_status} and no 'position_opened' lifecycle"
|
|
|
else:
|
|
|
logger.info(f"SL {order_db_id} for parent {parent_bot_ref_id} (status {parent_status}) - lifecycle is 'position_opened'. SL not cancelled by this rule.")
|
|
|
should_cancel = False
|
|
|
elif parent_status == 'filled':
|
|
|
- if symbol not in position_symbols:
|
|
|
+ if symbol not in position_symbols:
|
|
|
should_cancel = True
|
|
|
cancel_reason = "parent filled but actual position no longer exists"
|
|
|
-
|
|
|
- else:
|
|
|
+
|
|
|
+
|
|
|
+ else:
|
|
|
+
|
|
|
+ if parent_status in ['open', 'pending_submission', 'submitted']:
|
|
|
+ logger.info(f"SL Cleanup: Parent order {parent_order.get('id')} (status '{parent_status}') is missing exchange_id in DB record. SL {sl_order.get('id')} will NOT be cancelled by this rule, assuming parent is still active or pending.")
|
|
|
+ should_cancel = False
|
|
|
+ if parent_status == 'open':
|
|
|
+ logger.warning(f"SL Cleanup: DATA ANOMALY - Parent order {parent_order.get('id')} status is 'open' but fetched DB record shows no exchange_id. Investigate DB state for order {parent_order.get('id')}.")
|
|
|
+ else:
|
|
|
+
|
|
|
+
|
|
|
should_cancel = True
|
|
|
- cancel_reason = f"parent order {parent_status} but no exchange_order_id to check lifecycle"
|
|
|
- else:
|
|
|
- should_cancel = True
|
|
|
- cancel_reason = "parent order not found in database"
|
|
|
+ cancel_reason = f"parent order {parent_status} (no exch_id) and status indicates it's not live/pending."
|
|
|
+ else:
|
|
|
+ should_cancel = True
|
|
|
+ cancel_reason = "parent order not found in database"
|
|
|
else:
|
|
|
-
|
|
|
+
|
|
|
|
|
|
if symbol not in position_symbols:
|
|
|
should_cancel = True
|
|
|
cancel_reason = "no position exists and no parent reference"
|
|
|
|
|
|
if should_cancel:
|
|
|
-
|
|
|
success = stats.update_order_status(
|
|
|
order_db_id=order_db_id,
|
|
|
- new_status='cancelled_orphaned'
|
|
|
+ new_status='cancelled_orphaned'
|
|
|
)
|
|
|
|
|
|
if success:
|
|
@@ -1331,6 +1334,7 @@ class MarketMonitor:
|
|
|
except Exception as e:
|
|
|
logger.error(f"❌ Error cleaning up orphaned stop losses: {e}", exc_info=True)
|
|
|
|
|
|
+
|
|
|
async def _activate_pending_stop_losses_from_trades(self):
|
|
|
"""🆕 PHASE 4: Check trades table for pending stop loss activation first (highest priority)"""
|
|
|
try:
|