Add option for immediate execution with quotes in backtesting #3428
+689
−161
Add this suggestion to a batch that can be applied as a single commit.
This suggestion is invalid because no changes were made to the code.
Suggestions cannot be applied while the pull request is closed.
Suggestions cannot be applied while viewing a subset of changes.
Only one suggestion per line can be applied in a batch.
Add this suggestion to a batch that can be applied as a single commit.
Applying suggestions on deleted lines is not supported.
You must change the existing code in this line in order to create a valid suggestion.
Outdated suggestions cannot be applied.
This suggestion has been applied or marked resolved.
Suggestions cannot be applied from pending reviews.
Suggestions cannot be applied on multi-line comments.
Suggestions cannot be applied while the pull request is queued to merge.
Suggestion cannot be applied right now. Please check back later.
Pull Request
NautilusTrader prioritizes correctness and reliability, please follow existing patterns for validation and testing.
CONTRIBUTING.mdand followed the established practicesSummary
Add option for immediate execution with quotes inside bid ask spread
This PR adds a new
allow_immediate_quote_executionconfiguration option to the backtest engine that enables strategies to receive quotes and submit orders before the exchange processes the quote data. This feature allows orders to be filled on the same tick in discrete-time backtests (e.g., minute, hourly, or daily data) and extendsBestPriceFillModelto support execution within the bid-ask spread.Problem
In discrete-time backtests with sparse data (e.g., quotes every minute or hour), the default execution flow processes quotes through the exchange first, then notifies strategies. When a strategy submits an order in response to a quote tick, the order is queued and only processed after the exchange has already processed that quote. This means the order cannot be filled until the next quote arrives, causing a one-tick delay that may not accurately reflect realistic trading behavior where orders can be filled immediately when market conditions are favorable.
Additionally,
BestPriceFillModelpreviously only matched orders at the best bid or ask prices, which prevented orders with prices inside the spread from being filled, even though such fills are realistic in many market scenarios.Solution
Introduced a new
allow_immediate_quote_executionconfiguration flag that changes the execution order when enabled: strategies receive quotes first (through the data engine), can submit orders immediately, and these orders are then available when the exchange processes the quote. This allows orders to be filled on the same tick when the fill model determines they are matchable. The feature also extendsBestPriceFillModelto allow execution within the bid-ask spread for limit orders.Implementation details
Backtest engine configuration
Added
allow_immediate_quote_execution: boolparameter toBacktestEngineConfig(defaultFalse) that controls whether strategies can receive quotes and submit orders before exchange processing.When
allow_immediate_quote_execution=True, the backtest engine automatically disables the message queue (use_message_queue=False) to allow immediate order acceptance and processing.Updated
BacktestEngine.add_venue()to computeeffective_use_message_queuebased on the config flag, ensuring that immediate execution takes precedence over the explicituse_message_queueparameter.Data engine changes
Added
allow_immediate_quote_execution: boolparameter toDataEngine.__init__()to receive the configuration flag.Modified
DataEngine._handle_spread_quote()to process quotes through the data engine first (notifying strategies) before sending to the exchange when immediate execution is enabled. This ensures strategies can submit orders that will be available when the exchange processes the quote.When immediate execution is disabled (default), quotes are sent to the exchange first, then processed by the data engine, maintaining backward-compatible behavior.
Fill model enhancements
Extended
BestPriceFillModel.fill()to allow execution within the bid-ask spread for limit orders.For BUY limit orders: if the order price is within or above the spread (
bid <= price <= ask), the order is matchable. Orders at or above the ask are filled at the ask (aggressive), while orders within the spread are filled at the order price (optimistic).For SELL limit orders: if the order price is within or below the spread (
bid <= price <= ask), the order is matchable. Orders at or below the bid are filled at the bid (aggressive), while orders within the spread are filled at the order price (optimistic).Orders outside the spread remain passive and are not immediately matchable, consistent with realistic market behavior.
Execution engine improvements
Refactored code formatting and variable declarations for consistency and readability.
Improved conditional logic with
elifchains where appropriate to reduce nesting and improve code clarity.Testing infrastructure
Added comprehensive test suite
tests/unit_tests/backtest/test_immediate_quote_execution.py(353 lines) that validates immediate execution behavior.Tests verify that with
allow_immediate_quote_execution=True, orders fill on the same quote tick they are submitted.Tests verify that with
allow_immediate_quote_execution=False(default), orders fill on the next quote tick, maintaining backward compatibility.Added comparison test that demonstrates the timestamp difference between enabled and disabled modes.
Enhanced
tests/unit_tests/backtest/test_fill_models.py(109 new lines) with additional tests for fill model behavior.Example notebook updates
examples/backtest/notebooks/databento_option_greeks.pyto demonstrate the new immediate execution feature (49 lines modified).System kernel integration
nautilus_trader/system/kernel.pyto pass theallow_immediate_quote_executionconfiguration to the data engine (6 lines modified).Benefits
Discrete-time backtest accuracy: Enables more realistic backtesting on sparse data (minute, hourly, daily quotes) where strategies can react immediately to market data.
Reduced execution delay: Orders can be filled on the same tick when market conditions are favorable, eliminating artificial one-tick delays.
Spread execution support:
BestPriceFillModelnow supports execution within the bid-ask spread, providing more realistic fill behavior for limit orders.Backward compatibility: Default behavior remains unchanged (
allow_immediate_quote_execution=False), ensuring existing backtests continue to work as expected.Configurable behavior: Users can opt-in to immediate execution when needed without affecting existing workflows.
Comprehensive testing: New test suite ensures correct behavior and prevents regressions.
Files changed
examples/backtest/notebooks/databento_option_greeks.py: Updated example to demonstrate immediate execution feature (49 lines modified).nautilus_trader/backtest/config.py: Addedallow_immediate_quote_executionconfiguration parameter (5 lines added).nautilus_trader/backtest/engine.pyx: Modified execution flow to support immediate quote execution (240 lines modified).nautilus_trader/backtest/models/fill.pyx: ExtendedBestPriceFillModelto allow execution within bid-ask spread (28 lines modified).nautilus_trader/data/engine.pxd: Added parameter declaration to data engine interface (1 line added).nautilus_trader/data/engine.pyx: Modified quote processing order when immediate execution is enabled (14 lines modified).nautilus_trader/execution/engine.pyx: Code formatting and consistency improvements (51 lines modified).nautilus_trader/system/kernel.py: Updated to pass configuration to data engine (6 lines modified).tests/unit_tests/backtest/test_fill_models.py: Added new fill model tests (109 lines added).tests/unit_tests/backtest/test_immediate_quote_execution.py: New comprehensive test suite for immediate execution feature (353 lines added).Related Issues/PRs
Type of change
Breaking change details (if applicable)
Documentation
docs/developer_guide/docs.md)Release notes
RELEASES.mdthat follows the existing conventions (when applicable)Testing
Ensure new or changed logic is covered by tests.