For the complete documentation index, see llms.txt. This page is also available as Markdown.

Portfolio & Account State

Consolidated account state, withdrawable balance, fee schedule, enriched positions, and the portfolio time-series — across the SDKs and CLI.

The portfolio surface answers four questions about an account in one place: what is it worth right now, what can actually be withdrawn, what does it get charged, and how has it performed over time. It is exposed as a small set of authenticated REST endpoints plus enriched per-position risk fields, and every interface — the Rust, TypeScript and Python SDKs and the CLI — reaches the same routes on the gateway documented under APIs & Rates.

If you are building a portfolio view, read Reading these values safely before you render anything. Several fields are deliberately nullable, one sign convention is easy to invert, and there is one race condition that a naive two-call implementation will hit in production.

Getting it

npm install @nexus-xyz/exchange-ts    # TypeScript
cargo add nexus-exchange              # Rust

# Python is not on PyPI yet — install from source:
pip install git+https://github.com/nexus-xyz/nexus-exchange-py

The CLI is distributed through nexus-exchange-cli releases. See the Interfaces overview for all four clients.

Authentication

Every route on this page is account-scoped and requires an HMAC API key — there is no public or unauthenticated variant. Requests carry X-API-Key, X-Timestamp and X-Signature, and the timestamp must be within 30 seconds of server time. The SDKs and CLI sign for you. The full flow, with runnable cURL, is in the Quickstart.

Keep the API secret out of source and out of shell history — it is shown once at creation and cannot be retrieved later. The examples below read it from the environment.

Choosing a network

The Exchange runs on Nexus Testnet today; mainnet will follow. Select the network by pointing the interface at the matching gateway base URL (see APIs & Rates). Credentials are scoped to the network they were created on, so a testnet key will not authenticate against mainnet.

Reference

Method
Path
Auth
Description

GET

/account/state

HMAC

Portfolio summary and every open position, from one coherent read

GET

/account/summary

HMAC

Portfolio summary alone — the same object embedded in /account/state

GET

/account/fees

HMAC

Effective fee schedule for the account

GET

/account/portfolio-history?window=&limit=

HMAC

Equity, cumulative PnL and cumulative volume time-series

Per-SDK entry points:

Interface
Consolidated state
Fee schedule
Time-series

Rust

fetch_account_state()

fetch_account_fees()

fetch_portfolio_history(window, limit)

Python

fetch_account_state()

fetch_account_fees()

fetch_portfolio_history(window, limit)

TypeScript

getAccountState()

getAccountFees()

getPortfolioHistory({ window, limit })

CLI

nexus account state

nexus account fees

nexus account portfolio-history --window --limit

Consolidated account state

GET /account/state returns the portfolio aggregates and all open positions built from one server-side read. Because both halves come from the same snapshot, summary.open_positions_count always equals the length of positions.

The summary carries collateral, total_equity, total_unrealized_pnl, total_realized_pnl_24h, total_volume_24h, open_positions_count, open_orders_count, margin_used, available_margin and withdrawable.

withdrawable is the balance that can actually leave the account: engine-authoritative free margin floored at zero, max(0, available_margin). Free margin already nets each position's initial margin and every pre-trade order reservation out of equity, so this is the amount a withdrawal can draw on — not total_equity, and not collateral. An underwater account clamps to "0" and is never reported negative. It is derived from the authoritative margin view, so when that view is unavailable the endpoint fails closed with 502 rather than returning a locally estimated number.

Fee schedule

GET /account/fees reports what the venue charges the account today — the forward-looking schedule rate, not a realized average over past fills.

Field
Notes

maker_fee_bps

Basis points. May be negative, which means the maker is paid a rebate

taker_fee_bps

Basis points — 5 is 0.05%

tier

Currently always base; there are no per-account tiers yet

schedule

Scope of the reported rate. Currently always standard

volume_30d

Rolling 30-day traded notional, decimal string. Best-effort — see the flag below

volume_30d_estimated

true when volume_30d may undercount (the source fill buffer was at capacity)

discounts

Active discounts. Currently always empty

tier, schedule and discounts are provisional: the fee model is still being finalized, and discounts entries have no guaranteed properties yet. Treat the two _bps integers as the stable part of this response and do not branch on a discount shape that does not exist.

Portfolio time-series

GET /account/portfolio-history returns equity, cumulative trading PnL and cumulative traded notional over a window, downsampled server-side. Points are oldest first.

window

Cadence

Max points

Span

day

5 min

288

24 h

week

1 h

168

7 d

month

6 h

120

30 d

all

1 d

366

~1 y

Omitting window gives day. A value outside that set is rejected with 400 (invalid_window); if the parameter is repeated, the first value is used. limit must be between 1 and 366 — inside that range it narrows the result and is clamped to the window's capacity rather than rejected, so asking for 366 points of day returns 288 rather than an error; outside it, the request is rejected with 400.

The response echoes the window and the cadence_ms that were actually served. Read them back rather than assuming the values you sent: it is the difference between labelling an axis correctly and labelling it plausibly.

Each point carries timestamp_ms, equity, pnl and volume. pnl and volume are cumulative up to that sample, not per-interval — to chart per-interval activity, difference adjacent points yourself.

Enriched position fields

Positions returned by /account/state (and /positions) carry per-position risk detail alongside market_id, side, size, entry_price, unrealized_pnl and realized_pnl:

Field
Meaning

notional_value

abs(size) × mark price

margin_used

Initial margin held against the position, under the cross-margin model

roe

Return on initial margin: unrealized_pnl / margin_used

max_leverage

Maximum leverage the market allows, from its risk params

leverage

The account's leverage multiplier for this position

funding_paid

Cumulative funding on the position — see the sign convention below

These are computed on the low-latency read path rather than by round-tripping to the matching engine, which is what keeps the endpoint fast. The trade-off is that when one of their inputs is not available on that path, the field is null and a companion <field>_error carries a machine-readable reason instead of a fabricated number.

leverage is currently always null, with leverage_error set to margin_state_not_mirrored: deriving it needs the account's leverage setting or its allocated margin, and neither is available on the read path. Do not reconstruct it from margin_used — that expression collapses to 1 / initial_margin_rate, which is a per-market constant, not the position's real leverage. Showing that as leverage would be confidently wrong on every position in the market.

funding_paid is paid-positive. A positive value means the position has paid funding; a negative value means it has received funding. It is always present, "0" before any funding accrues, and bounded by the funding history the venue retains. Inverting this sign turns a cost into income on a P&L screen, so it is worth a test.

Reading these values safely

Monetary values are decimal strings, not numbers. equity, pnl, volume, withdrawable, notional_value and the rest are arbitrary-precision decimals serialized as strings so they are lossless. Parse them with a decimal type. Passing them through a float — parseFloat, float(), as f64 — reintroduces exactly the rounding error the string encoding exists to prevent. Leverage fields (leverage, max_leverage) are genuine JSON numbers.

Derived fields have three states, not two. Each of the five computed position fields — notional_value, margin_used, roe, leverage and max_leverage — can be:

  1. a value — computed and authoritative;

  2. null — reported, but not computable; the paired <field>_error says why;

  3. absent — the server predates the field.

Collapsing any of these into 0 invents data. "Not reported", "not computable" and "zero" are three different answers to a user asking what their position is worth, and only one of them is a number. Render the missing cases as an explicit gap — the CLI prints - — and surface the <field>_error when you have it. The same applies to withdrawable: it is optional in the schema, so an older deployment can omit it, and defaulting that to "0" would tell someone they have nothing available when the truth is that nobody asked.

Those five are exactly the fields carrying a companion <field>_error. funding_paid is not one of them — it is always present, so there is no funding_paid_error to branch on.

Prefer /account/state over two calls. Fetching /account/summary and /positions separately is two independent requests against a live account. A fill landing between them returns an aggregate that disagrees with the position list — open_positions_count says three, the array has four — and the window is real under any load. The consolidated endpoint exists so that one coherent read backs both halves.

Handle the failure codes distinctly. 401 is a credential or clock problem — check that X-Timestamp is within 30 seconds of server time before assuming the key is wrong. 429 means you exceeded the rate budget; see Rate & Connection Limits and back off rather than retrying immediately. 502 on /account/state and /account/summary means the authoritative margin view was unreachable and the server declined to guess — retry, and do not fall back to a locally computed withdrawable.

Example

The numbers above are self-consistent, which is worth tracing once: at a mark price of 86000, notional_value is 0.25 × 86000, unrealized_pnl is 0.25 × (86000 − 84000), margin_used is notional_value × 1/max_leverage, roe is unrealized_pnl / margin_used, and withdrawable equals total_equity − margin_used because there are no open orders reserving margin.

Runnable end-to-end examples live in the SDK repositories: examples/portfolio.ts and examples/portfolio.rs.

Status: development preview on testnet. These routes ship in OpenAPI spec v0.7.2; pin to a spec version in production and check each SDK's release notes before upgrading. tier, schedule and discounts on /account/fees are provisional and finalize with the fee model, and leverage reports null until the margin state it needs is mirrored. Testnet credentials and balances have no real-world value.

Last updated