|
@@ -441,15 +441,16 @@ class PositionMonitor:
|
|
return False
|
|
return False
|
|
|
|
|
|
async def _check_external_trades(self):
|
|
async def _check_external_trades(self):
|
|
- """Check for trades made outside the Telegram bot and update stats."""
|
|
|
|
- try:
|
|
|
|
- stats = self.trading_engine.get_stats()
|
|
|
|
- if not stats:
|
|
|
|
- logger.warning("TradingStats not available in _check_external_trades. Skipping.")
|
|
|
|
- return
|
|
|
|
|
|
+ """Check for external trades and update internal tracking."""
|
|
|
|
+ stats = self.trading_engine.stats
|
|
|
|
+ if not stats:
|
|
|
|
+ logger.warning("No stats manager available for external trade checking")
|
|
|
|
+ return
|
|
|
|
|
|
|
|
+ try:
|
|
external_trades_processed = 0
|
|
external_trades_processed = 0
|
|
symbols_with_fills = set()
|
|
symbols_with_fills = set()
|
|
|
|
+ processed_fills_this_cycle = set() # Track fills processed in this cycle
|
|
|
|
|
|
recent_fills = self.trading_engine.get_recent_fills()
|
|
recent_fills = self.trading_engine.get_recent_fills()
|
|
if not recent_fills:
|
|
if not recent_fills:
|
|
@@ -486,6 +487,11 @@ class PositionMonitor:
|
|
logger.debug(f"Skipping already processed fill: {trade_id}")
|
|
logger.debug(f"Skipping already processed fill: {trade_id}")
|
|
continue
|
|
continue
|
|
|
|
|
|
|
|
+ # Check if this fill was already processed in this cycle
|
|
|
|
+ if trade_id and trade_id in processed_fills_this_cycle:
|
|
|
|
+ logger.debug(f"Skipping fill already processed in this cycle: {trade_id}")
|
|
|
|
+ continue
|
|
|
|
+
|
|
fill_processed_this_iteration = False
|
|
fill_processed_this_iteration = False
|
|
|
|
|
|
if not (symbol_from_fill and side_from_fill and amount_from_fill > 0 and price_from_fill > 0):
|
|
if not (symbol_from_fill and side_from_fill and amount_from_fill > 0 and price_from_fill > 0):
|
|
@@ -519,6 +525,10 @@ class PositionMonitor:
|
|
full_symbol, side_from_fill, amount_from_fill, price_from_fill,
|
|
full_symbol, side_from_fill, amount_from_fill, price_from_fill,
|
|
'position_opened', timestamp_dt
|
|
'position_opened', timestamp_dt
|
|
)
|
|
)
|
|
|
|
+
|
|
|
|
+ # Mark fill as processed in this cycle
|
|
|
|
+ if trade_id:
|
|
|
|
+ processed_fills_this_cycle.add(trade_id)
|
|
fill_processed_this_iteration = True
|
|
fill_processed_this_iteration = True
|
|
|
|
|
|
# Check if this is a bot order to increase an existing position
|
|
# Check if this is a bot order to increase an existing position
|
|
@@ -560,6 +570,10 @@ class PositionMonitor:
|
|
|
|
|
|
logger.info(f"📈 Position INCREASED: {full_symbol} by {amount_from_fill} for lifecycle {existing_lc['trade_lifecycle_id'][:8]}...")
|
|
logger.info(f"📈 Position INCREASED: {full_symbol} by {amount_from_fill} for lifecycle {existing_lc['trade_lifecycle_id'][:8]}...")
|
|
symbols_with_fills.add(token)
|
|
symbols_with_fills.add(token)
|
|
|
|
+
|
|
|
|
+ # Mark fill as processed in this cycle
|
|
|
|
+ if trade_id:
|
|
|
|
+ processed_fills_this_cycle.add(trade_id)
|
|
fill_processed_this_iteration = True
|
|
fill_processed_this_iteration = True
|
|
|
|
|
|
# Check if this is a known bot order (SL/TP/exit)
|
|
# Check if this is a known bot order (SL/TP/exit)
|
|
@@ -630,6 +644,10 @@ class PositionMonitor:
|
|
stats.migrate_trade_to_aggregated_stats(lc_id)
|
|
stats.migrate_trade_to_aggregated_stats(lc_id)
|
|
if bot_order_db_id_to_update:
|
|
if bot_order_db_id_to_update:
|
|
stats.update_order_status(order_db_id=bot_order_db_id_to_update, new_status='filled', amount_filled_increment=amount_from_fill)
|
|
stats.update_order_status(order_db_id=bot_order_db_id_to_update, new_status='filled', amount_filled_increment=amount_from_fill)
|
|
|
|
+
|
|
|
|
+ # Mark fill as processed in this cycle
|
|
|
|
+ if trade_id:
|
|
|
|
+ processed_fills_this_cycle.add(trade_id)
|
|
fill_processed_this_iteration = True
|
|
fill_processed_this_iteration = True
|
|
|
|
|
|
# Check for external stop losses
|
|
# Check for external stop losses
|
|
@@ -661,6 +679,10 @@ class PositionMonitor:
|
|
# Modify shared state carefully
|
|
# Modify shared state carefully
|
|
if exchange_order_id_from_fill in self.shared_state['external_stop_losses']:
|
|
if exchange_order_id_from_fill in self.shared_state['external_stop_losses']:
|
|
del self.shared_state['external_stop_losses'][exchange_order_id_from_fill]
|
|
del self.shared_state['external_stop_losses'][exchange_order_id_from_fill]
|
|
|
|
+
|
|
|
|
+ # Mark fill as processed in this cycle
|
|
|
|
+ if trade_id:
|
|
|
|
+ processed_fills_this_cycle.add(trade_id)
|
|
fill_processed_this_iteration = True
|
|
fill_processed_this_iteration = True
|
|
else:
|
|
else:
|
|
logger.warning(f"⚠️ External SL (MM) {exchange_order_id_from_fill} for {full_symbol}, but no active lifecycle found.")
|
|
logger.warning(f"⚠️ External SL (MM) {exchange_order_id_from_fill} for {full_symbol}, but no active lifecycle found.")
|
|
@@ -766,6 +788,10 @@ class PositionMonitor:
|
|
full_symbol, side_from_fill, amount_from_fill, price_from_fill,
|
|
full_symbol, side_from_fill, amount_from_fill, price_from_fill,
|
|
action_type, timestamp_dt
|
|
action_type, timestamp_dt
|
|
)
|
|
)
|
|
|
|
+
|
|
|
|
+ # Mark fill as processed in this cycle
|
|
|
|
+ if trade_id:
|
|
|
|
+ processed_fills_this_cycle.add(trade_id)
|
|
fill_processed_this_iteration = True
|
|
fill_processed_this_iteration = True
|
|
|
|
|
|
elif action_type == 'position_closed' and existing_lc:
|
|
elif action_type == 'position_closed' and existing_lc:
|
|
@@ -799,6 +825,10 @@ class PositionMonitor:
|
|
)
|
|
)
|
|
|
|
|
|
stats.migrate_trade_to_aggregated_stats(lc_id)
|
|
stats.migrate_trade_to_aggregated_stats(lc_id)
|
|
|
|
+
|
|
|
|
+ # Mark fill as processed in this cycle
|
|
|
|
+ if trade_id:
|
|
|
|
+ processed_fills_this_cycle.add(trade_id)
|
|
fill_processed_this_iteration = True
|
|
fill_processed_this_iteration = True
|
|
|
|
|
|
elif action_type in ['position_increased', 'position_decreased'] and existing_lc:
|
|
elif action_type in ['position_increased', 'position_decreased'] and existing_lc:
|
|
@@ -823,6 +853,10 @@ class PositionMonitor:
|
|
)
|
|
)
|
|
|
|
|
|
symbols_with_fills.add(token)
|
|
symbols_with_fills.add(token)
|
|
|
|
+
|
|
|
|
+ # Mark fill as processed in this cycle
|
|
|
|
+ if trade_id:
|
|
|
|
+ processed_fills_this_cycle.add(trade_id)
|
|
fill_processed_this_iteration = True
|
|
fill_processed_this_iteration = True
|
|
logger.info(f"📊 Position {action_type}: {full_symbol} new size: {new_size}")
|
|
logger.info(f"📊 Position {action_type}: {full_symbol} new size: {new_size}")
|
|
|
|
|
|
@@ -849,6 +883,10 @@ class PositionMonitor:
|
|
)
|
|
)
|
|
logger.info(f"📋 Recorded trade via FALLBACK: {trade_id} (Unmatched External Fill)")
|
|
logger.info(f"📋 Recorded trade via FALLBACK: {trade_id} (Unmatched External Fill)")
|
|
|
|
|
|
|
|
+ # Mark fill as processed in this cycle
|
|
|
|
+ if trade_id:
|
|
|
|
+ processed_fills_this_cycle.add(trade_id)
|
|
|
|
+
|
|
# No notification sent for unmatched external trades per user preference
|
|
# No notification sent for unmatched external trades per user preference
|
|
fill_processed_this_iteration = True
|
|
fill_processed_this_iteration = True
|
|
|
|
|