Skip to content

Conversation

@faysou
Copy link
Collaborator

@faysou faysou commented Jan 16, 2026

Pull Request

NautilusTrader prioritizes correctness and reliability, please follow existing patterns for validation and testing.

  • I have reviewed the CONTRIBUTING.md and followed the established practices

Summary

Summary

This PR introduces a refined market exit process for strategies in nautilus_trader. It provides a mechanism to gracefully stop a strategy by cancelling all open orders and closing all open positions, waiting for all in-flight actions to resolve before finalising the stop.

Changes

  • StrategyConfig: Added inflight_check_interval_ms (defaulting to 100ms) to control the frequency of checks during the market exit process.

  • Strategy:

    • Added market_exit() method which initiates the cancellation of all orders and closing of all positions.
    • Added on_market_exit() and after_market_exit() lifecycle hooks for custom logic.
    • Implemented _check_market_exit() which uses a timer to iteratively ensure all orders are resolved and positions are closed before stopping the strategy.
  • Controller & Trader: Added support for the MarketExitStrategy command and corresponding methods to trigger the exit process from the controller/trader levels.

  • MarketExitStrategy: New command message to trigger the exit process.

  • Tests: Added comprehensive unit tests in test_strategy.py covering standard market exit and market exit with open positions.

Implementation Details

The market exit process is iterative. Upon initiation, it immediately attempts to cancel all open orders and close all open positions for all instruments the strategy is involved with. A periodic timer is then started to monitor the state. The strategy only transitions to the STOPPED state once the cache reports no open or in-flight orders, and no open positions. If open positions remain without active orders, the process will re-attempt to close them.

Related Issues/PRs

#2876

Type of change

  • Bug fix (non-breaking)
  • New feature (non-breaking)
  • Improvement (non-breaking)
  • Breaking change (impacts existing behavior)
  • Documentation update
  • Maintenance / chore

Breaking change details (if applicable)

Documentation

  • Documentation changes follow the style guide (docs/developer_guide/docs.md)

Release notes

  • I added a concise entry to RELEASES.md that follows the existing conventions (when applicable)

Testing

Ensure new or changed logic is covered by tests.

  • Affected code paths are already covered by the test suite
  • I added/updated tests to cover new or changed logic

@faysou faysou force-pushed the exit-process branch 2 times, most recently from 41032cd to 71ce2ab Compare January 19, 2026 09:20
@cuberone
Copy link

cuberone commented Jan 24, 2026

Thanks! Please consider few comments:

  1. strategy.pyx move
    self.stop()
    to
    self.after_market_exit()
    for better customisation.

  2. inflight_check_interval_ms parameter is already exists. Just reuse it.

  3. For safety reason better to cancel only non-inflight orders, so consider to filter it first:
    cdef list[Order] non_inflight_orders = [order for idx, order in enumerate(open_orders) if not order.is_inflight]

  4. Also for safety reason perform close positions only if no inflight orders found for an instrument. Otherwise existing inflight market order could trigger an error.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants