@@ -707,7 +707,47 @@ def _sync_positions_best_effort(self, target_strategy_id: Optional[int] = None)
707707 if to_insert :
708708 logger .debug (f"position sync: inserted { len (to_insert )} new positions for strategy_id={ sid } " )
709709 except Exception as e :
710- logger .error (f"position sync: strategy_id={ sid } failed: { e } " , exc_info = True )
710+ # Any exchange auth/config/connectivity fatal errors should stop the strategy to avoid endless spam.
711+ msg = str (e )
712+ m = msg .lower ()
713+ is_fatal = any (tok in m for tok in (
714+ # Generic HTTP auth / permission failures
715+ " http 401" ,
716+ "unauthorized" ,
717+ "forbidden" ,
718+ "invalid api" ,
719+ "invalid api-key" ,
720+ "invalid api key" ,
721+ "invalid_key" ,
722+ "invalid key" ,
723+ "signature mismatch" ,
724+ "invalid_signature" ,
725+ "permission" ,
726+ # Common connection failures (IBKR/TWS, local gateways)
727+ "connection refused" ,
728+ "connect call failed" ,
729+ "errno 111" ,
730+ ))
731+ if is_fatal :
732+ logger .error (f"[PositionSync] Strategy { sid } fatal error; auto-stopping. error={ msg } " , exc_info = True )
733+ try :
734+ from app .utils .strategy_runtime_logs import append_strategy_log
735+ append_strategy_log (int (sid ), "error" , f"Auto-stopped: position sync fatal error: { msg } " )
736+ except Exception :
737+ pass
738+ try :
739+ with get_db_connection () as db :
740+ cur = db .cursor ()
741+ cur .execute (
742+ "UPDATE qd_strategies_trading SET status = 'stopped' WHERE id = %s AND status = 'running'" ,
743+ (int (sid ),),
744+ )
745+ db .commit ()
746+ cur .close ()
747+ except Exception :
748+ pass
749+ else :
750+ logger .error (f"position sync: strategy_id={ sid } failed: { e } " , exc_info = True )
711751
712752 def _fetch_pending_orders (self , limit : int = 50 ) -> List [Dict [str , Any ]]:
713753 try :
@@ -2286,6 +2326,34 @@ def _execute_ibkr_order(
22862326 _console_print (f"[worker] IBKR order exception: strategy_id={ strategy_id } pending_id={ order_id } err={ e } " )
22872327 _notify_live_best_effort (status = "failed" , error = str (e ))
22882328 append_strategy_log (strategy_id , "error" , f"IBKR order exception ({ symbol } { signal_type } ): { e } " )
2329+ # Auto-stop strategy on fatal connectivity issues (e.g. TWS/IBG not reachable).
2330+ try :
2331+ msg = str (e )
2332+ m = msg .lower ()
2333+ is_fatal = any (tok in m for tok in (
2334+ "connection refused" ,
2335+ "connect call failed" ,
2336+ "errno 111" ,
2337+ "make sure api port on tws" ,
2338+ ))
2339+ if is_fatal :
2340+ try :
2341+ append_strategy_log (strategy_id , "error" , f"Auto-stopped: IBKR connection failed: { msg } " )
2342+ except Exception :
2343+ pass
2344+ try :
2345+ with get_db_connection () as db :
2346+ cur = db .cursor ()
2347+ cur .execute (
2348+ "UPDATE qd_strategies_trading SET status = 'stopped' WHERE id = %s AND status = 'running'" ,
2349+ (int (strategy_id ),),
2350+ )
2351+ db .commit ()
2352+ cur .close ()
2353+ except Exception :
2354+ pass
2355+ except Exception :
2356+ pass
22892357
22902358 def _execute_mt5_order (
22912359 self ,
0 commit comments