docs/concepts/events/index.md
Nautilus is event-driven: every state change in the system is represented
by an event object that flows through the MessageBus to strategy and actor
handlers. This guide covers the event types, how they are dispatched, and how
order fills produce position events.
| Category | Examples | Origin |
|---|---|---|
| Order | OrderAccepted, OrderFilled, OrderCanceled | ExecutionEngine (from venue) |
| Position | PositionOpened, PositionChanged | ExecutionEngine (from fills) |
| Account | AccountState | ExecutionClient / Portfolio |
| Time | TimeEvent | Clock (timers and alerts) |
When an event reaches a strategy, the system calls handlers in a fixed priority order. The first matching handler runs, then the next level, so you can handle events at whatever granularity you need.
on_order_filled)on_order_event (receives all order events)on_event (receives everything)on_position_opened)on_position_event (receives all position events)on_event (receives everything)Timers and alerts produce TimeEvent objects. Pass a callback when calling
set_timer or set_time_alert to direct events to your own method. If you
omit the callback, a callback previously registered under the same name is
used when present; otherwise the event is delivered to on_event instead.
Each order event corresponds to a state transition in the
order state machine. The ExecutionEngine
applies the event to the order, updates the Cache, and publishes it on the
MessageBus. The table below shows the primary transitions; partially filled
and triggered orders support additional transitions documented in the full
order state flow.
| Event | Primary transition | Handler |
|---|---|---|
OrderInitialized | (created locally) | on_order_initialized |
OrderDenied | Initialized -> Denied | on_order_denied |
OrderEmulated | Initialized -> Emulated | on_order_emulated |
OrderReleased | Emulated -> Released | on_order_released |
OrderSubmitted | Initialized/Released -> Submitted | on_order_submitted |
OrderAccepted | Submitted -> Accepted | on_order_accepted |
OrderRejected | Submitted -> Rejected | on_order_rejected |
OrderTriggered | Accepted -> Triggered | on_order_triggered |
OrderPendingUpdate | Accepted -> PendingUpdate | on_order_pending_update |
OrderPendingCancel | Accepted -> PendingCancel | on_order_pending_cancel |
OrderUpdated | PendingUpdate -> previous status | on_order_updated |
OrderModifyRejected | PendingUpdate -> previous status | on_order_modify_rejected |
OrderCancelRejected | PendingCancel -> previous status | on_order_cancel_rejected |
OrderCanceled | PendingCancel/Accepted -> Canceled | on_order_canceled |
OrderExpired | Accepted -> Expired | on_order_expired |
OrderFilled | Accepted -> Filled/PartiallyFilled | on_order_filled |
OrderFillVoided | Fill present -> derived; absent + false -> Voided | on_order_fill_voided |
All order events share these fields:
| Field | Description |
|---|---|
trader_id | Trader instance identifier. |
strategy_id | Strategy that submitted the order. |
instrument_id | Instrument for the order. |
client_order_id | Client‑assigned order identifier. |
venue_order_id | Venue‑assigned order identifier. |
account_id | Account the order belongs to. |
reconciliation | Whether generated during reconciliation. |
event_id | Unique event identifier. |
ts_event | Timestamp when the event occurred. |
ts_init | Timestamp when the event was created. |
Each order event's page lists the type-specific fields it adds beyond this
common set, plus which optional common fields are populated. For example,
OrderFilled adds last_qty, last_px, trade_id, and
commission. OrderFillVoided identifies the corrected trade and carries
its cumulative voided quantity.
:::tip
Override on_order_event to handle all order events in one place. The specific
handlers fire first, so you can combine both approaches.
:::
Position events are a direct consequence of fill events. The ExecutionEngine
processes each OrderFilled, updates or creates a position, and emits the
corresponding position event.
An OrderFillVoided rebuilds the cached position from its effective fill history. It does not emit
an opposite fill or synthesize a position event.
| Event | When it fires | Handler |
|---|---|---|
PositionOpened | First fill creates a new position. | on_position_opened |
PositionChanged | Subsequent fill changes quantity or side. | on_position_changed |
PositionClosed | Fill reduces quantity to zero. | on_position_closed |
The following diagram shows how a single OrderFilled event produces a
position event. This is the key link between order management and position
tracking.
sequenceDiagram
participant Venue as Venue
participant EE as ExecutionEngine
participant Cache as Cache
participant Strategy as Strategy
Venue-->>EE: OrderFilled
EE->>EE: apply fill to order
EE->>Cache: update order state
EE->>EE: determine position ID
alt No existing position
EE->>Cache: add new Position
EE->>Strategy: PositionOpened
else Position open, not closed by fill
EE->>Cache: update Position
EE->>Strategy: PositionChanged
else Fill closes the position
EE->>Cache: update Position
EE->>Strategy: PositionClosed
end
Step by step:
ExecutionEngine receives an OrderFilled event
from the venue adapter.Cache.Position from
the fill, adds it to the Cache, and emits PositionOpened.Cache, and emits
PositionChanged.Cache, and emits PositionClosed.PositionClosed) and one that opens the new position
(PositionOpened).Every position event exposes all of these fields (they are defined on the PositionEvent
base). A check mark means the field carries a meaningful value for that event; a dash means
it is left at its zero or default (for example avg_px_close and duration_ns before a
position closes).
| Field | Opened | Changed | Closed | Description |
|---|---|---|---|---|
trader_id | ✓ | ✓ | ✓ | Trader instance identifier. |
strategy_id | ✓ | ✓ | ✓ | Strategy that owns the position. |
instrument_id | ✓ | ✓ | ✓ | Instrument for the position. |
position_id | ✓ | ✓ | ✓ | Unique position identifier. |
account_id | ✓ | ✓ | ✓ | Account the position belongs to. |
opening_order_id | ✓ | ✓ | ✓ | Order that opened the position. |
closing_order_id | - | - | ✓ | Order that closed the position. |
entry | ✓ | ✓ | ✓ | Side of the opening fill. |
side | ✓ | ✓ | ✓ | Current position side. |
signed_qty | ✓ | ✓ | ✓ | Signed quantity (negative=short). |
quantity | ✓ | ✓ | ✓ | Unsigned position quantity. |
peak_qty | ✓ | ✓ | ✓ | Largest quantity held. |
last_qty | ✓ | ✓ | ✓ | Quantity of the last fill. |
last_px | ✓ | ✓ | ✓ | Price of the last fill. |
currency | ✓ | ✓ | ✓ | Settlement currency. |
avg_px_open | ✓ | ✓ | ✓ | Average entry price. |
avg_px_close | - | ✓ | ✓ | Average exit price. |
realized_return | - | ✓ | ✓ | Realized return as a ratio. |
realized_pnl | ✓ | ✓ | ✓ | Realized profit and loss. |
unrealized_pnl | - | ✓ | ✓ | Unrealized profit and loss. |
duration_ns | - | - | ✓ | Time held in nanoseconds. |
ts_opened | ✓ | ✓ | ✓ | Timestamp when position opened. |
ts_closed | - | - | ✓ | Timestamp when position closed. |
event_id | ✓ | ✓ | ✓ | Unique event identifier. |
ts_event | ✓ | ✓ | ✓ | Timestamp of the triggering fill. |
ts_init | ✓ | ✓ | ✓ | Timestamp when event was created. |
The Cache provides methods to navigate between orders and positions:
# From a position, find all orders that contributed fills
orders = self.cache.orders_for_position(position.id)
# From an order, find the position it belongs to
position = self.cache.position_for_order(order.client_order_id)
# The opening order is stored directly on the position
opening_order_id = position.opening_order_id
AccountState events represent balance and margin snapshots. They fire when:
Portfolio recalculates account state after a position update
(for margin accounts with calculate_account_state enabled).Account state contains balances, margins, account type, and base currency.
The Portfolio subscribes to these events internally to maintain exposure
and balance tracking. See AccountState for the full
field list.
Beyond strategy handlers, actors can subscribe to specific event streams for
instruments they do not trade. These subscriptions use the MessageBus
directly and do not involve the DataEngine.
| Topic pattern | Receives |
|---|---|
events.order_filled.{instrument_id} | Fill events for one instrument. |
events.order_canceled.{instrument_id} | Cancel events for one instrument. |
events.order.{strategy_id} | All order events routed to one strategy. |
events.order.* | All strategy‑routed order events. |
These are useful for monitoring actors that track execution quality or fill rates across strategies without participating in order management.
For details and examples, see Order event subscriptions.