@@ -36,7 +36,6 @@ from libc.stdint cimport uint64_t
3636from nautilus_trader.cache.base cimport CacheFacade
3737from nautilus_trader.cache.cache cimport Cache
3838from nautilus_trader.common.actor cimport Actor
39- from nautilus_trader.common.component cimport CMD
4039from nautilus_trader.common.component cimport EVT
4140from nautilus_trader.common.component cimport RECV
4241from nautilus_trader.common.component cimport Clock
@@ -168,6 +167,7 @@ cdef class Strategy(Actor):
168167 self .manage_contingent_orders = config.manage_contingent_orders
169168 self .manage_gtd_expiry = config.manage_gtd_expiry
170169 self ._is_exiting = False
170+ self ._market_exit_attempts = 0
171171
172172 # Public components
173173 self .clock = self ._clock
@@ -432,6 +432,10 @@ cdef class Strategy(Actor):
432432 if self ._manager:
433433 self ._manager.reset()
434434
435+ # Reset market exit state
436+ self ._is_exiting = False
437+ self ._market_exit_attempts = 0
438+
435439 self .on_reset()
436440
437441# -- ABSTRACT METHODS -----------------------------------------------------------------------------
@@ -1698,12 +1702,13 @@ cdef class Strategy(Actor):
16981702
16991703 Will cancel all open orders and close all open positions, and wait for
17001704 all in-flight orders to resolve and positions to close before stopping
1701- the strategy.
1705+ the strategy.
17021706 """
17031707 if self ._is_exiting:
17041708 return
17051709
17061710 self ._is_exiting = True
1711+ self ._market_exit_attempts = 0
17071712
17081713 self ._log.info(" Initiating market exit..." , LogColor.BLUE)
17091714 self .on_market_exit()
@@ -1727,7 +1732,7 @@ cdef class Strategy(Actor):
17271732 self .close_all_positions(instrument_id)
17281733
17291734 # Start iterative check
1730- self ._log.warning (f" Setting market exit timer for {self.id}" )
1735+ self ._log.info (f" Setting market exit timer for {self.id}" )
17311736 self ._clock.set_timer(
17321737 f" MARKET-EXIT-CHECK:{self.id}" ,
17331738 pd.Timedelta(milliseconds = self .config.inflight_check_interval_ms),
@@ -1742,7 +1747,30 @@ cdef class Strategy(Actor):
17421747 if self .state != ComponentState.RUNNING:
17431748 return
17441749
1745- self ._log.warning(f" Timer triggered: {event.name}" )
1750+ self ._market_exit_attempts += 1
1751+ self ._log.debug(f" Market exit check triggered: {event.name} (attempt {self._market_exit_attempts})" )
1752+
1753+ # Check if max attempts reached
1754+ if self ._market_exit_attempts >= self .config.market_exit_max_attempts:
1755+ timer_name = f" MARKET-EXIT-CHECK:{self.id}"
1756+ if timer_name in self ._clock.timer_names:
1757+ self ._clock.cancel_timer(name = timer_name)
1758+
1759+ self ._log.warning(
1760+ f" Market exit max attempts ({self.config.market_exit_max_attempts}) reached. "
1761+ f" Forcing stop. Open orders: {len(self.cache.orders_open(None, None, self.id))}, "
1762+ f" inflight orders: {len(self.cache.orders_inflight(None, None, self.id))}, "
1763+ f" open positions: {len(self.cache.positions_open(None, None, self.id))}" ,
1764+ LogColor.YELLOW
1765+ )
1766+
1767+ # Reset before stopping
1768+ self ._is_exiting = False
1769+ self ._market_exit_attempts = 0
1770+ self .after_market_exit()
1771+ self .stop()
1772+ return
1773+
17461774 cdef list open_orders = self .cache.orders_open(None , None , self .id)
17471775 cdef list inflight_orders = self .cache.orders_inflight(None , None , self .id)
17481776
@@ -1758,9 +1786,13 @@ cdef class Strategy(Actor):
17581786 return
17591787
17601788 # All clear
1761- if f" MARKET-EXIT-CHECK:{self.id}" in self ._clock.timer_names:
1762- self ._clock.cancel_timer(name = f" MARKET-EXIT-CHECK:{self.id}" )
1789+ timer_name = f" MARKET-EXIT-CHECK:{self.id}"
1790+ if timer_name in self ._clock.timer_names:
1791+ self ._clock.cancel_timer(name = timer_name)
17631792
1793+ # Reset before stopping
1794+ self ._is_exiting = False
1795+ self ._market_exit_attempts = 0
17641796 self .after_market_exit()
17651797 self .stop()
17661798
0 commit comments