Portfolio, positions and risk¶
Account state, positions, orders and fills are Nautilus's own domain (portfolio, accounting, and risk engine) — ONIX transports them, it does not recompute them. This page covers the surfaces that expose that state, their per-node availability, what happens to that state across a process restart, and the one arithmetic-fidelity limit worth stating precisely.
Read surfaces, and why "empty" is the wrong word for them¶
| Route | Returns |
|---|---|
GET /v2/account |
official Nautilus account state, balances as decimal strings |
GET /v2/accounts |
multi-account-safe list form, each with its own AccountStateSummary and provenance envelope |
GET /v2/portfolio |
official portfolio snapshot: net positions, unrealized PnL |
GET /v2/positions |
paginated PositionSummary rows, filterable by OPEN/CLOSED state |
GET /v2/orders |
paginated OrderSummary rows, filterable by OPEN (every non-terminal status) or HISTORY |
GET /v2/fills |
paginated FillSummary rows |
Every row carries decimal strings for money/quantity and nanosecond-string timestamps — never
floats on the wire — and, where applicable, an onix.execution_provenance envelope naming the
exact environment, account, venue, OMS type, and strategy/revision/job/run identity that produced
it. Position rows additionally carry state (OPEN/CLOSED), mark_px/ts_mark (the price and
time unrealized PnL was computed at — always stated per-position, never claimed as a portfolio-
level mark), and duration_ns. An unknown future enum value (an order status Nautilus has not yet
had at the time a client was built, for example) is required to be preserved and treated as
non-terminal, not silently coerced to a known value or dropped.
Availability is per node, not a single build-wide answer, and none of these six routes serve
"empty" state on a deferring node. onix_engine.spine.deferred.DEFERRED_OPERATIONS covers this
whole lane (spine.account.get, spine.accounts.list, spine.portfolio.get,
spine.orders.list, spine.fills.list, spine.positions.list, plus the execution SSE stream and
the three sandbox-lifecycle operations):
| Operation | Core spine | Binance node | IBKR node |
|---|---|---|---|
account, accounts, portfolio |
UNAVAILABLE (501) |
UNAVAILABLE (501) |
READY |
orders, fills, positions |
UNAVAILABLE (501) |
UNAVAILABLE (501) |
READY |
execution SSE stream |
UNAVAILABLE (501) |
UNSUPPORTED (404) |
UNSUPPORTED (404) |
The IBKR node implements this read surface against a live execution-projection (see
execution-runtime.md) and serves it through the same shared gateway_v2
handlers every other node uses — it is a working operator surface, not a deferred one, and marking
it unavailable merely because the paper-execution lane is deferred elsewhere would take that
surface off any consumer. Read availability from the node you are talking to.
On the nodes that defer this lane, the previous behavior was not to serve genuinely empty state:
the routes stayed wired and answered 200 with a zeroed portfolio and 200 [] for orders/fills/
positions, while the capability registry reported all of them READY. That was corrected
specifically because "200, no orders" is indistinguishable from "this account has never traded" to
a caller. The corrected behavior is a named, typed HTTP 501 refusal
(capability.status: "UNAVAILABLE", reason: "DEFERRED_PAPER_PRODUCT") — distinct from the HTTP
404 NOT_SUPPORTED_ON_THIS_NODE a node without the concept at all would answer. This documentation
therefore does not describe the core-spine/Binance state as "empty" anywhere: it is a deliberately
refused capability, not a successfully served absence.
Persistence and recovery across a process restart¶
Because no market-spine process registers any execution client (see
execution-runtime.md) and the paper-execution lane above is deferred on
every node that would otherwise use it, there is currently no live order/position state on the
core spine or Binance node for a restart to lose. Stated for completeness anyway, because the fact
generalizes: Nautilus's own Cache save/load is a proven no-op at this pin (no backing-store
field exists on CacheConfig; see ../runtime/cache-message-bus.md),
so if a future release attaches a real execution client to a Nautilus node in this fleet, that
node's Portfolio/Cache state would not currently survive a process restart through any
Nautilus-provided persistence mechanism — ONIX would need to add its own, the same way it already
does for hydration scheduling state and consolidation watermarks (see
../data/catalog.md), not assume Nautilus does this for free. On the IBKR
node specifically, "recovery" after a reconnect is not a persistence question at all: orders/
fills/positions/portfolio are read live off the official IBKR execution client's own
account/order state each call, so a reconnect re-populates them from the broker's own reconciliation,
not from anything ONIX stored — an unconfigured or never-connected exec client has simply never
been able to produce that state, reported honestly by name (ACCOUNT_NOT_CONFIGURED /
EXEC_CLIENT_NOT_CONNECTED) rather than as an empty-but-successful read.
Nautilus's own PnL arithmetic — stated honestly¶
Nautilus computes position PnL in f64 (IEEE-754 double), then stores and transports the result as
an exact decimal at the account currency's precision. This is a real, upstream property of the
engine ONIX depends on, and ONIX states it rather than concealing it: values ONIX itself computes
(detector/signal arithmetic) are fixed-point decimal end to end, with float arithmetic denied at
the lint level — but a realized_pnl or unrealized_pnl field originating from Nautilus's own
Position accounting is exact as transported, not exact as computed: the underlying arithmetic
that produced it was IEEE-754, not decimal. ONIX does not attempt to "fix" this by reimplementing
position accounting — doing so would replace a well-tested upstream component with a less-tested
local one, and would violate the boundary described in
../nautilus-boundary.md. What ONIX does instead: it constructs prices
and quantities from decimal strings (Nautilus's own Price.from_str/Quantity.from_str) rather
than float constructors on its own side of the boundary, so no additional float error is
introduced by ONIX code — the remaining, small float-rounding characteristic is Nautilus's own and
is named as such.
Risk¶
The risk engine attached to each Nautilus node runs with Nautilus's own default configuration; no
ONIX-authored risk engine, position sizing, or exposure system exists anywhere in this backend —
per ../nautilus-boundary.md, risk is explicitly Nautilus's
responsibility, not ONIX's. Because no market-spine process registers any execution client (see
execution-runtime.md), those risk-engine instances currently have no
order flow to evaluate at all in the tracked production and development-activation states. A
backtest run's risk behavior is Nautilus's own simulated-execution risk handling, exercised
against a sealed strategy revision, never against live capital. A sandbox run would exercise the
same mechanism if activated, but sandbox execution is itself deferred (UNAVAILABLE/501) at this
pin — see execution-runtime.md.
What this page does not claim¶
No account, position, or order surface documented here is evidence of live trading activity by
itself — these are read contracts over whatever Nautilus state a given node actually holds. On the
core spine and Binance node this whole lane is a deliberately refused capability
(UNAVAILABLE/501, not a served empty state — see above); on the IBKR node it is a genuine,
READY read surface that is nonetheless not evidence of live trading by itself, since it is
disabled in the deployment activation roster and absent from the tracked production record (see
execution-runtime.md). A backtest run's account/position/order state is
Nautilus's own simulated-execution accounting, never live capital. See
execution-runtime.md for the full account of what execution capability
does and does not exist at this pin.
Evidence and source pins for this page
Verified. Current behaviour, confirmed in source at the pinned commit.
Verified on against the following immutable sources:
backend@efa38e04:docs/contracts/market-spine-v2.mdbackend@efa38e04:docs/contracts/capability-registry-v1.mdbackend@efa38e04:python/src/onix_engine/spine/deferred.pybackend@efa38e04:python/src/onix_engine/ibkr/execution.py
Status tokens are defined on the documentation and status model page. Every pin on this site is listed under versions and source pins.