Skip to content
All library documents

Interpreting PositionChanged Events and Their PnL Fields

Article NautilusTrader

Summary

This reference explains when an execution engine emits a PositionChanged event: a fill or fill correction updates a position while leaving it open. Strategies can receive the event through the on_position_changed handler. The document highlights fields beyond the opening snapshot, including the largest directional quantity reached, average close price so far, realized return and PnL, and the position’s opening timestamp. It also notes a compatibility alias for peak quantity.

A key semantic caveat is that unrealized PnL is set to zero by the engine and is not a mark-to-market calculation. Realized PnL may be unavailable, while average close price is only present if some quantity has closed. The example shows logging the instrument, signed quantity, and realized PnL. This is an event and data-model reference, not a trading strategy or performance analysis; the linked lifecycle and field references contain broader context.

Key ideas

  • A PositionChanged event reports a fill-related update that leaves the position open.
  • The event is dispatched to the strategy’s on_position_changed handler.
  • Peak quantity, realized PnL, and average close price describe aspects of the position’s history.
  • Unrealized PnL is set to zero and does not represent a mark-to-market value.
  • Realized PnL can be unavailable, and average close price requires some closed quantity.

Tags

Full text
# PositionChanged


# PositionChanged

`PositionChanged` records an update that leaves a position open. The `ExecutionEngine` emits it when
a fill changes an open position without closing it, or when a fill correction leaves the corrected
position open. See [From fill to position](index.md#from-fill-to-position-the-causal-chain).
Handler: `on_position_changed`.

## Fields

See [Position event fields](index.md#position-event-fields) for the complete field matrix. In
addition to the opening snapshot fields, `PositionChanged` exposes:

| Field             | Python type       | Description                                                  |
| ----------------- | ----------------- | ------------------------------------------------------------ |
| `peak_quantity`   | `Quantity`        | The largest directional quantity reached by the position.    |
| `peak_qty`        | `Quantity`        | Compatibility alias for `peak_quantity`.                     |
| `avg_px_close`    | `float` or `None` | The average close price so far, if any quantity has closed.  |
| `realized_return` | `float`           | The realized return for the position.                        |
| `realized_pnl`    | `Money` or `None` | The realized PnL, if available.                              |
| `unrealized_pnl`  | `Money`           | Set to zero by the engine, not a mark-to-market calculation. |
| `ts_opened`       | `int`             | UNIX timestamp (nanoseconds) when the position opened.       |

## Example

Reading the event in a strategy handler:

```python
def on_position_changed(self, event: PositionChanged) -> None:
    self.log.info(
        f"Changed {event.instrument_id} to {event.signed_qty} (realized={event.realized_pnl})",
    )
```

## Related guides

- [Events](index.md) - Event categories, dispatch, and the fill-to-position chain.
- [Positions](../positions.md) - Position lifecycle, aggregation, and PnL.
- [Orders](../orders/) - Orders whose fills open and close positions.

Shown in full with attribution under the source's licence. Licence: LGPL-3.0

This summary was written by Stratmill's research agent from the original; it is not a copy of the source.