Implementation:Nautechsystems Nautilus trader Data Event Handlers
| Field | Value |
|---|---|
| sources | https://github.com/nautechsystems/nautilus_trader , https://nautilustrader.io/docs/ |
| domains | algorithmic trading, event handling, pattern documentation, NautilusTrader |
| type | Pattern Doc (user-defined overrides) |
| last_updated | 2026-02-10 12:00 GMT |
Overview
Concrete pattern for implementing data event handler methods that NautilusTrader invokes on a running strategy when subscribed market data arrives.
Description
NautilusTrader defines a set of data event handler methods on the Actor base class that strategies override to process incoming market data. These methods are not called by user code -- they are invoked by the framework's message bus when matching data events are published. The user's responsibility is to override these methods with custom trading logic.
The primary data event handlers are:
on_bar(self, bar: Bar): Called when a subscribed bar is received.on_trade_tick(self, tick: TradeTick): Called when a subscribed trade tick is received.on_quote_tick(self, tick: QuoteTick): Called when a subscribed quote tick is received.
Additional handlers exist for specialized data:
on_order_book(self, order_book: OrderBook): Called for order book snapshots.on_order_book_deltas(self, deltas): Called for order book delta updates.on_mark_price(self, mark_price: MarkPriceUpdate): Called for mark price updates.on_index_price(self, index_price: IndexPriceUpdate): Called for index price updates.on_funding_rate(self, funding_rate: FundingRateUpdate): Called for funding rate updates.on_data(self, data): Called for generic/custom data types.
All handlers have a default no-op implementation in the base class (commented as "Optionally override in subclass"), so strategies need only override the handlers relevant to their data subscriptions.
Usage
Override these methods in your Strategy subclass to implement your trading logic. You must have an active subscription (via subscribe_bars, subscribe_trade_ticks, etc.) for the corresponding handler to be invoked.
Code Reference
Source Location
nautilus_trader/common/actor.pyx, lines 458-552.
Interface Specification
on_bar:
cpdef void on_bar(self, Bar bar):
"""
Actions to be performed when running and receives a bar.
Parameters
----------
bar : Bar
The bar received.
"""
# Optionally override in subclass
on_trade_tick:
cpdef void on_trade_tick(self, TradeTick tick):
"""
Actions to be performed when running and receives a trade tick.
Parameters
----------
tick : TradeTick
The tick received.
"""
# Optionally override in subclass
on_quote_tick:
cpdef void on_quote_tick(self, QuoteTick tick):
"""
Actions to be performed when running and receives a quote tick.
Parameters
----------
tick : QuoteTick
The tick received.
"""
# Optionally override in subclass
Import
No separate import is needed. These methods are defined on Actor and inherited by Strategy. Override them in your subclass.
from nautilus_trader.trading.strategy import Strategy
from nautilus_trader.model.data import Bar, BarType, TradeTick, QuoteTick
I/O Contract
Inputs
| Handler | Parameter | Type | Description |
|---|---|---|---|
on_bar |
bar |
Bar |
Aggregated OHLCV bar with open, high, low, close, volume, and timestamp fields. |
on_trade_tick |
tick |
TradeTick |
Individual trade execution with price, size, aggressor side, and timestamp. |
on_quote_tick |
tick |
QuoteTick |
Best bid/ask update with bid price, ask price, bid size, ask size, and timestamp. |
Outputs
| Output | Type | Description |
|---|---|---|
| None | void |
Handlers return nothing. Side effects are produced by calling order submission methods, updating internal state, or logging. |
Usage Examples
Bar-based moving average crossover strategy:
from nautilus_trader.trading.strategy import Strategy
from nautilus_trader.model.data import Bar, BarType
from nautilus_trader.indicators.average.ema import ExponentialMovingAverage
class EMACrossStrategy(Strategy):
def __init__(self, config):
super().__init__(config)
self.fast_ema = ExponentialMovingAverage(config.fast_ema_period)
self.slow_ema = ExponentialMovingAverage(config.slow_ema_period)
def on_start(self):
bar_type = BarType.from_str(f"{self.config.instrument_id}-1-MINUTE-LAST-EXTERNAL")
self.subscribe_bars(bar_type)
def on_bar(self, bar: Bar):
self.fast_ema.handle_bar(bar)
self.slow_ema.handle_bar(bar)
if not self.fast_ema.initialized or not self.slow_ema.initialized:
return # Wait for indicators to warm up
if self.fast_ema.value > self.slow_ema.value:
if not self.portfolio.is_net_long(bar.bar_type.instrument_id):
order = self.order_factory.market(
instrument_id=bar.bar_type.instrument_id,
order_side=OrderSide.BUY,
quantity=Quantity.from_str("100"),
)
self.submit_order(order)
elif self.fast_ema.value < self.slow_ema.value:
if not self.portfolio.is_net_short(bar.bar_type.instrument_id):
self.close_all_positions(bar.bar_type.instrument_id)
Trade tick handler for volume analysis:
from nautilus_trader.model.data import TradeTick
class VolumeStrategy(Strategy):
def __init__(self, config):
super().__init__(config)
self._cumulative_volume = 0.0
self._tick_count = 0
def on_start(self):
instrument_id = InstrumentId.from_str(self.config.instrument_id)
self.subscribe_trade_ticks(instrument_id)
def on_trade_tick(self, tick: TradeTick):
self._cumulative_volume += float(tick.size)
self._tick_count += 1
self.log.info(
f"Trade: {tick.price} x {tick.size} | "
f"Cumulative volume: {self._cumulative_volume}"
)
Quote tick handler for spread monitoring:
from nautilus_trader.model.data import QuoteTick
class SpreadMonitorStrategy(Strategy):
def on_start(self):
instrument_id = InstrumentId.from_str("EURUSD.SIM")
self.subscribe_quote_ticks(instrument_id)
def on_quote_tick(self, tick: QuoteTick):
spread = float(tick.ask_price) - float(tick.bid_price)
self.log.info(
f"Bid: {tick.bid_price} | Ask: {tick.ask_price} | Spread: {spread:.5f}"
)