Implementation:Nautechsystems Nautilus trader Order Position Handlers
| Field | Value |
|---|---|
| sources | https://github.com/nautechsystems/nautilus_trader , https://nautilustrader.io/docs/ |
| domains | algorithmic trading, order events, position events, pattern documentation, NautilusTrader |
| type | Pattern Doc (user-defined overrides) |
| last_updated | 2026-02-10 12:00 GMT |
Overview
Concrete pattern for implementing order and position event handler methods that NautilusTrader invokes on a running strategy when order state changes or position state changes occur.
Description
NautilusTrader defines a set of order and position event handler methods on the Strategy class that strategies override to react to execution feedback. These methods are not called by user code -- they are invoked by the framework when the execution engine processes order state transitions and the resulting position changes.
Order event handlers:
on_order_filled(self, event: OrderFilled): Called when an order is fully or partially filled.on_order_accepted(self, event: OrderAccepted): Called when an order is accepted by the venue.on_order_rejected(self, event: OrderRejected): Called when an order is rejected.on_order_canceled(self, event: OrderCanceled): Called when an order is canceled.on_order_expired(self, event: OrderExpired): Called when an order expires.on_order_updated(self, event: OrderUpdated): Called when an order is modified.on_order_triggered(self, event: OrderTriggered): Called when a stop order is triggered.on_order_pending_update(self, event: OrderPendingUpdate): Called when an order modify is pending.on_order_pending_cancel(self, event: OrderPendingCancel): Called when an order cancel is pending.on_order_modify_rejected(self, event: OrderModifyRejected): Called when an order modify is rejected.on_order_cancel_rejected(self, event: OrderCancelRejected): Called when an order cancel is rejected.
Position event handlers:
on_position_opened(self, event: PositionOpened): Called when a new position is opened.on_position_changed(self, event: PositionChanged): Called when an existing position is changed (increased or decreased).on_position_closed(self, event: PositionClosed): Called when a position is fully closed.on_position_event(self, event: PositionEvent): Called for any position event (generic handler).
All handlers have a default no-op implementation (commented as "Optionally override in subclass"), so strategies need only override the specific handlers relevant to their logic.
Usage
Override these methods in your Strategy subclass to implement execution feedback logic such as stop-loss placement after entry fills, P&L tracking on position close, or retry logic on order rejection.
Code Reference
Source Location
nautilus_trader/trading/strategy.pyx, lines 723-801.
Interface Specification
on_order_filled:
cpdef void on_order_filled(self, OrderFilled event):
"""
Actions to be performed when running and receives an order filled event.
Parameters
----------
event : OrderFilled
The event received.
"""
# Optionally override in subclass
on_position_opened:
cpdef void on_position_opened(self, PositionOpened event):
"""
Actions to be performed when running and receives a position opened event.
Parameters
----------
event : PositionOpened
The event received.
"""
# Optionally override in subclass
on_position_changed:
cpdef void on_position_changed(self, PositionChanged event):
"""
Actions to be performed when running and receives a position changed event.
Parameters
----------
event : PositionChanged
The event received.
"""
# Optionally override in subclass
on_position_closed:
cpdef void on_position_closed(self, PositionClosed event):
"""
Actions to be performed when running and receives a position closed event.
Parameters
----------
event : PositionClosed
The event received.
"""
# Optionally override in subclass
Import
No separate import is needed for the handlers themselves. Import the event types for type annotations:
from nautilus_trader.trading.strategy import Strategy
from nautilus_trader.model.events.order import OrderFilled
from nautilus_trader.model.events.position import PositionOpened, PositionChanged, PositionClosed
I/O Contract
Inputs
| Handler | Parameter | Type | Description |
|---|---|---|---|
on_order_filled |
event |
OrderFilled |
Contains instrument_id, order_side, last_qty, last_px, position_id, and other fill details. |
on_position_opened |
event |
PositionOpened |
Contains the full Position object with instrument_id, side, quantity, avg_open price. |
on_position_changed |
event |
PositionChanged |
Contains the updated Position object reflecting the change. |
on_position_closed |
event |
PositionClosed |
Contains the closed Position object with realized P&L, duration, and close details. |
Outputs
| Output | Type | Description |
|---|---|---|
| None | void |
Handlers return nothing. Side effects include placing follow-up orders, logging, and updating internal state. |
Usage Examples
Placing a stop-loss after entry fill:
from nautilus_trader.trading.strategy import Strategy
from nautilus_trader.model.events.order import OrderFilled
from nautilus_trader.model.enums import OrderSide
from nautilus_trader.model.objects import Price, Quantity
class StopLossStrategy(Strategy):
def on_order_filled(self, event: OrderFilled):
# Place a stop-loss after a BUY entry fill
if event.order_side == OrderSide.BUY:
stop_price = Price.from_str(
str(float(event.last_px) * 0.98) # 2% stop
)
stop_order = self.order_factory.stop_market(
instrument_id=event.instrument_id,
order_side=OrderSide.SELL,
quantity=event.last_qty,
trigger_price=stop_price,
)
self.submit_order(stop_order)
self.log.info(
f"Stop-loss placed at {stop_price} for fill at {event.last_px}"
)
Tracking position lifecycle:
from nautilus_trader.trading.strategy import Strategy
from nautilus_trader.model.events.position import (
PositionOpened,
PositionChanged,
PositionClosed,
)
class PositionTrackerStrategy(Strategy):
def __init__(self, config):
super().__init__(config)
self._trade_count = 0
def on_position_opened(self, event: PositionOpened):
self._trade_count += 1
position = event.position
self.log.info(
f"Trade #{self._trade_count} opened: "
f"{position.side} {position.quantity} {position.instrument_id} "
f"@ {position.avg_px_open}"
)
def on_position_changed(self, event: PositionChanged):
position = event.position
self.log.info(
f"Position changed: {position.side} {position.quantity} "
f"unrealized P&L: {position.unrealized_pnl}"
)
def on_position_closed(self, event: PositionClosed):
position = event.position
self.log.info(
f"Trade #{self._trade_count} closed: "
f"realized P&L: {position.realized_pnl}, "
f"duration: {position.duration}"
)
Multi-handler strategy with fill-driven bracket orders:
from nautilus_trader.trading.strategy import Strategy
from nautilus_trader.model.events.order import OrderFilled, OrderRejected
from nautilus_trader.model.enums import OrderSide
class BracketStrategy(Strategy):
def on_order_filled(self, event: OrderFilled):
if event.order_side == OrderSide.BUY:
# Place take-profit
tp_order = self.order_factory.limit(
instrument_id=event.instrument_id,
order_side=OrderSide.SELL,
quantity=event.last_qty,
price=Price.from_str(str(float(event.last_px) * 1.03)),
reduce_only=True,
)
self.submit_order(tp_order)
def on_order_rejected(self, event: OrderRejected):
self.log.warning(f"Order rejected: {event.reason}")