GET /positions
List open positions
List open positions.
operationId: fetchPositions · CCXT method: fetchPositions
Declared server:
https://api.testnet.nexus.xyz/v1. See Networks.
Authentication: agentAuth, hmacAuth.
HMAC API key authentication. Send three headers:
X-API-Key: your key ID (e.g.nx_a1b2c3...)X-Timestamp: current time in unix millisecondsX-Signature:hex(hmac_sha256(secret, timestamp + "\n" + METHOD + "\n" + path + "\n" + query + "\n" + sha256_hex(body)))
Timestamp must be within 30 seconds of server time.
The key is network-scoped; the signature is not. The canonical string above has no network component, so the same signed request is byte-identical on every host. What stops a testnet key from authenticating against mainnet is that the key does not exist in that host's key store — sign only for the host that minted the key, and never replay a signed payload against another network. Presented to the wrong host, a key is refused with the same opaque 401 as an unknown key and must not be retried there. See “API keys are bound to one network” in the API description.
The account's open positions.
An open position with per-position risk detail. Enriched risk fields are derived strictly from indexer-mirrored state (no engine round-trip, to stay on the low-latency read path): when an input is not mirrored, the field is null and its companion <field>_error carries a machine-readable reason rather than a fabricated number. Monetary fields are lossless decimal strings; leverage fields are JSON numbers.
Field names follow CCXT's unified vocabulary wherever CCXT defines one and stay snake_case for the Nexus extensions it does not — the same rule Order and Trade follow (ENG-15117, which renamed thirteen fields and carried eight <field>_error twins with them to <ccxtName>_error). size, roe, max_leverage and funding_paid keep the venue's spelling: CCXT defines no counterpart for a position's base-asset quantity (its contracts is size / contractSize, a different number), for a bare return ratio (its percentage is per hundred), for a market's leverage ceiling, or for a paid-positive cumulative funding figure.
GET /account does not answer this schema. That route relays the matching engine's own position response — a narrower object, under the engine's names, carrying funding_accrued, fee_pnl and entry_pnl which are absent here. A client reading both surfaces reads two vocabularies. PositionPnl (GET /positions/pnl) is likewise a separate schema that this rename did not reach. ClosedPosition (GET /positions/closed) WAS one until ENG-15258, which carried the same five renames onto it — it now serves symbol, entryPrice, realizedPnl, lastPrice and lastUpdateTimestamp, and the fields it does not serve are the open-only ones a closed position cannot state.
Arbitrary-precision decimal serialized as a string (lossless). Parse with a decimal type, never a float.
Arbitrary-precision decimal serialized as a string (lossless). Parse with a decimal type, never a float.
The mark price this position's unrealizedPnl, notional and initialMargin were computed against. This is the real blended mark that margin and liquidations use — not the trade reference that FundingSample.mark_price carries under the same name. Mirrored from the engine's GET /markets/{market_id}/mark-price by the indexer's per-cycle poll, and treated as unavailable when it is absent OR staler than the freshness bound, so a frozen last-good mark is never served as live (ENG-5909).
null when the mark is unavailable — see markPrice_error. It used to be served as the string "0" in that case, a sentinel a client could not tell apart from a genuine zero; the other mark-dependent fields on this object (notional, initialMargin, roe) already reported that absence as null + *_error: "mark_price_unavailable", and this field now follows the same rule (ENG-13921).
Machine-readable reason markPrice is null (mark_price_unavailable), or null when populated.
Arbitrary-precision decimal serialized as a string (lossless). Parse with a decimal type, never a float.
Arbitrary-precision decimal serialized as a string (lossless). Parse with a decimal type, never a float.
Price at which the position is liquidated, as a decimal string. null when the venue has not computed one — see liquidationPrice_error. A zero is a real price and never a stand-in for absence: this field used to carry the string "0" to mean "not computed", which made a position that liquidates only at zero — i.e. effectively cannot be liquidated — indistinguishable from one whose liquidation price is simply unknown.
Machine-readable reason liquidationPrice is null. Absent, or null, when liquidationPrice is populated — the indexer omits the sibling rather than nulling it, and /account (relayed from the engine) does not carry it at all. On the indexer-served reads it is always margin_state_not_mirrored: liquidation price needs margin-module state the indexer does not mirror.
Position leverage (the account's leverage multiplier for this position). Currently always null: deriving it needs the user's leverage setting or account equity/allocated margin, which the indexer does not mirror; when null, leverage_error carries the reason. Do not infer leverage from initialMargin — that collapses to 1/initial_margin_rate, a per-market constant, not the real leverage.
Machine-readable reason leverage is null, or null when leverage is populated. Currently always margin_state_not_mirrored.
Position notional value (|size| × mark price), as a decimal string. null when the mark price is unavailable — see notional_error.
Machine-readable reason notional is null (e.g. mark_price_unavailable), or null when populated.
Return on equity: unrealizedPnl / initialMargin (return on initial margin), as a decimal string. null when a required input is unavailable or margin is zero — see roe_error.
Machine-readable reason roe is null (e.g. mark_price_unavailable, margin_rate_unavailable, margin_used_zero), or null when populated.
Initial-margin requirement held against this position (notional × initial_margin_rate, under the engine's cross-margin model), as a decimal string. Isolated/custom margin allocations are not mirrored by the indexer. null when a required input is unavailable — see initialMargin_error.
Machine-readable reason initialMargin is null (e.g. mark_price_unavailable, margin_rate_unavailable), or null when populated.
Maximum leverage allowed for this market (from market risk params), as an integer matching max_leverage on /markets/{market_id}/risk-params. null when market params are unavailable — see max_leverage_error.
Machine-readable reason max_leverage is null (e.g. market_params_unavailable), or null when populated.
Arbitrary-precision decimal serialized as a string (lossless). Parse with a decimal type, never a float.
Base-asset quantity one contract represents, mirrored from the market's parameters — the same value GET /markets publishes. "1" on every market listed today. null while market parameters have not yet loaded — see contractSize_error.
Machine-readable reason contractSize is null (market_params_unavailable), or null when populated.
The market's last traded price, as a decimal string. null before the market's first fill — see lastPrice_error.
Distinct from markPrice, which is oracle-derived and is what margin and liquidation are computed against. This is the last execution. They differ, and substituting one for the other is wrong in a way that is hard to notice.
Machine-readable reason lastPrice is null (no_trades_yet), or null when populated.
When this position was opened, in epoch milliseconds. null when no engine position snapshot has been folded for it yet — see timestamp_error.
Distinct from lastUpdateTimestamp: one is when the position began, the other when it was last written to. A position that has traded since opening has two different values here, and the open instant is the one that orders a position list by age.
Machine-readable reason timestamp is null (position_snapshot_not_seen), or null when populated.
When this position was last written to, in epoch milliseconds. Always present — every path that creates or refreshes a position entry stamps it, so there is no absence to report and no companion _error field.
Margin mode for this position. Per-position, not per-account: a market whose risk class forces isolated margin holds that mode while the same account's other positions may be cross. null when no engine position snapshot has been folded for it yet — see marginMode_error.
Machine-readable reason marginMode is null (position_snapshot_not_seen), or null when populated.
Margin allocated to this position specifically, as a decimal string. Meaningful only for an isolated position. null — with a reason in collateral_error — in three cases that are deliberately kept apart: a cross position has no per-position allocation at all because the account backs it (cross_margin_no_allocation); an isolated position may have none posted yet (isolated_margin_not_allocated); and a position with no folded snapshot has one we cannot state (position_snapshot_not_seen). The first is an answer, not a gap.
Machine-readable reason collateral is null (cross_margin_no_allocation, isolated_margin_not_allocated, or position_snapshot_not_seen), or null when populated.
Authentication failed. All 401 responses return the same opaque body to prevent information leakage. A credential minted on a different network is one of the causes it hides — by design, so that a key id cannot be probed for existence on another network. Do not retry it against another host.
Rate limit exceeded. Retryable — honour retry-after and back off; this is what separates a 429 from the permanent 403 jurisdiction refusals. Branch on the body's code, which is always RATE_LIMIT_EXCEEDED, and on bucket to learn WHICH budget bottlenecked — the buckets are independent, so a caller at its read ceiling can still submit orders and should not back off the whole client. Do not parse message: it is a diagnostic whose wording is not stable. Requests are charged by weight, so exhausting a budget takes fewer heavy calls than x-ratelimit-limit suggests. See “Rate limits” in the API description.
GET /v1/positions HTTP/1.1
Host: api.testnet.nexus.xyz
X-API-Key: YOUR_API_KEY
Accept: */*
[
{
"symbol": "BTC-USDX-PERP",
"side": "Long",
"size": "0.5",
"entryPrice": "49500.00",
"unrealizedPnl": "250.50",
"realizedPnl": "0.00",
"liquidationPrice": null,
"liquidationPrice_error": "margin_state_not_mirrored",
"notional": "25000.50",
"notional_error": null,
"roe": "0.2004",
"roe_error": null,
"initialMargin": "1250.03",
"initialMargin_error": null,
"max_leverage": 20,
"max_leverage_error": null,
"funding_paid": "12.50",
"leverage": null,
"leverage_error": "margin_state_not_mirrored"
}
]Test it cannot sign this request. X-Signature is an HMAC computed per call: see Authentication, or use an SDK.
API version 0.9.122
Last updated

