Workflow:Nautechsystems Nautilus trader Strategy development
| Knowledge Sources | |
|---|---|
| Domains | Algorithmic_Trading, Strategy_Development, Quantitative_Finance |
| Last Updated | 2026-02-10 09:00 GMT |
Overview
End-to-end process for developing a custom trading strategy in NautilusTrader by inheriting from the Strategy base class and implementing the event-driven lifecycle hooks.
Description
This workflow covers the complete procedure for authoring a trading strategy within the NautilusTrader framework. Strategies are Python classes that inherit from the Strategy base class and implement event handler methods (on_start, on_bar, on_trade_tick, on_quote_tick, on_order_filled, on_position_opened, on_position_closed, on_stop). The strategy communicates with the platform through a well-defined API for data subscriptions, order submission, position management, indicator registration, and portfolio queries. The same strategy code runs identically in backtesting, sandbox (paper trading), and live environments.
Usage
Execute this workflow when you want to implement a new trading strategy for the NautilusTrader platform. This applies whether you are building a simple moving average crossover, a complex multi-instrument arbitrage system, an order book market maker, or any other algorithmic trading strategy. The workflow produces a reusable strategy class and configuration that can be deployed in any NautilusTrader runtime context.
Execution Steps
Step 1: Define Strategy Configuration
Create a StrategyConfig subclass that declares all configurable parameters for the strategy. This includes the instrument ID, bar type specification, trade sizing parameters, indicator periods, and any strategy-specific settings. The configuration class supports serialization for reproducible experiment tracking and factory-based instantiation.
Key considerations:
- Inherit from StrategyConfig and use standard Python type annotations
- Include instrument_id as an InstrumentId for instrument binding
- Define bar_type as a string that will be parsed into a BarType
- All parameters should have sensible defaults where possible
- The config is hashable and JSON-serializable for experiment tracking
Step 2: Implement the Strategy Class
Create a class inheriting from Strategy. In the constructor, parse the configuration and initialize instance variables for indicators, state tracking, and instrument references. Register indicators so the platform automatically updates them with incoming data.
Key considerations:
- Call super().__init__(config) in the constructor
- Create indicator instances (EMA, RSI, Bollinger Bands, etc.) in the constructor
- Use self.register_indicator_for_bars(bar_type, indicator) for automatic updates
- Initialize order tracking variables and position state flags
Step 3: Implement on_start Lifecycle Hook
Implement the on_start() method, which executes when the strategy is started by the trader. Subscribe to the data feeds needed by the strategy (bars, ticks, order book data). Load the instrument definition from the cache for price/quantity precision. Set up any timers for periodic logic.
Key considerations:
- Use self.subscribe_bars(bar_type) for bar data
- Use self.subscribe_trade_ticks(instrument_id) for trade data
- Use self.subscribe_quote_ticks(instrument_id) for quote data
- Use self.subscribe_order_book_deltas(instrument_id) for order book data
- Retrieve the instrument via self.cache.instrument(instrument_id)
- Use self.clock.set_timer() for time-based periodic actions
Step 4: Implement Data Event Handlers
Implement the core trading logic in the data event handlers. The on_bar() method receives each new bar and is the primary location for signal generation. Compute indicators, evaluate entry/exit conditions, and submit orders when signals trigger. Additional handlers (on_trade_tick, on_quote_tick, on_order_book_delta) provide finer-grained data access.
Key considerations:
- Check indicator readiness before trading (e.g., enough bars for EMA warmup)
- Use self.portfolio.is_flat(instrument_id) to check current position state
- Use self.order_factory.market() or self.order_factory.limit() to create orders
- Submit orders via self.submit_order(order)
- Use self.submit_order_list() for bracket orders (entry + stop loss + take profit)
- Access current positions via self.cache.positions_open()
Step 5: Implement Order and Position Event Handlers
Implement handlers for order lifecycle and position events. The on_order_filled() method fires when an order receives a fill. The on_position_opened(), on_position_changed(), and on_position_closed() methods track position state transitions. These handlers enable reactive logic such as trailing stop adjustments, scale-in/scale-out, and risk management actions.
Key considerations:
- on_order_filled receives an OrderFilled event with fill price, quantity, and commission
- on_position_opened fires when a new position is established
- on_position_closed fires when a position is fully closed, providing realized PnL
- Use these handlers for position management logic separate from signal generation
Step 6: Implement on_stop Lifecycle Hook
Implement the on_stop() method to handle strategy shutdown. Cancel all open orders and optionally close all open positions. Clean up any resources, timers, or external connections. This method is called during both backtest completion and live trading shutdown.
Key considerations:
- Use self.cancel_all_orders(instrument_id) to cancel pending orders
- Use self.close_all_positions(instrument_id) to flatten positions
- Unsubscribe from data feeds if needed
- Cancel any active timers
Step 7: Validate with Backtest
Test the strategy by running it through the BacktestEngine with historical data. Verify correct behavior including order submission, fill handling, position tracking, indicator calculations, and edge cases. Review the generated reports and performance statistics to validate the strategy logic before considering live deployment.
Key considerations:
- Start with a small dataset to verify basic correctness
- Check order fill reports for expected entry/exit behavior
- Verify position reports match expected trade count and direction
- Run acceptance tests comparing results across multiple data sets
- Use DEBUG logging to trace event flow through the strategy