> ## Documentation Index
> Fetch the complete documentation index at: https://docs.0xmonaco.com/llms.txt
> Use this file to discover all available pages before exploring further.

# V1.0.80

# Monaco Protocol SDK v1.0.80

This release rolls up the consumer-facing changes shipped since v1.0.77. Positions report whether their lifetime totals are complete, closed and liquidated position lists sort newest-closed first, and a flat margin account now gets an `account` frame when its collateral moves. Perp `instruments` frames carry the whole margin ladder, the `trades` channel sends the newest 50 trades on subscribe, risk-bucket rows report `maxRemovableCollateral`, and the Rust gRPC SDK gains an optional `serde` feature. **Action items:** restart a `status=CLOSED` or `status=LIQUIDATED` position walk with an empty `pageToken`; read `lifetimeTotalsStatus` before trusting a position's lifetime totals, and never read an absent total as zero; handle `timeInForce: null` on MARKET order events; parse insufficient-balance amounts as asset units, not raw integers.

## Added

### `lifetimeTotalsStatus` on positions

`sdk.positions.listPositions` (`GET /api/v1/positions`; gRPC `PositionsService.ListPositions`) and `sdk.positions.getPosition` (`GET /api/v1/positions/{positionId}`; gRPC `PositionsService.GetPosition`) return `lifetimeTotalsStatus` on every position. It says whether the lifetime fields (`exitPrice`, `realizedRoe`, `feesPaid`, `fundingPaid`, `cumFees`, `netRealizedPnl`) can be trusted:

* `LIFETIME_TOTALS_STATUS_COMPLETE` — the position history covers the whole lifetime, and the lifetime fields follow their existing rules.
* `LIFETIME_TOTALS_STATUS_PENDING` — history is still being written, or the row changed while it was being read. Every lifetime field is absent. Retry the read after a short backoff (seconds).
* `LIFETIME_TOTALS_STATUS_INCOMPLETE` — the totals are not complete now, and a quick retry will not change that. A gap in legacy history is permanent; a missing or drifted totals row is repaired by a background reconcile, after which the position reads `COMPLETE`.

A lifetime field that is present is cross-checked against the others, so `exitPrice` can be present while `cumFees` is absent. Never read an absent total as zero. `OPEN` and `LIQUIDATING` positions are now reconciled against their `realizedPnl` like closed ones: one whose history does not account for it reports `INCOMPLETE` and no longer returns `cumFees`, which it used to return understated. Closed, liquidated and expired positions return the same lifetime fields as before. WebSocket position frames do not carry the field. `@0xmonaco/types` adds the `LifetimeTotalsStatus` type, `usePositions` exposes the field, the MCP `get_position` / `get_positions` tools return it, and the Rust REST and gRPC SDKs gain `lifetime_totals_status`. See [Positions](/developers/positions).

### The margin ladder on `instruments` frames

`sdk.ws.instruments` frames for a perp market carry `riskTiers`, the whole margin ladder ascending by `tierLevel`, with the same fields as the REST perp config's `riskTiers`, on both the subscribe-time snapshot and live frames. Any margin-ladder edit now sends a `config_change` frame whose `changedFields` names `risk_tiers` (plus `max_leverage`, `initial_margin_rate` and `maintenance_margin_rate` when tier 1 changed). Spot frames omit the field, and a frame from an older server arrives with `riskTiers` `undefined`. See [WebSockets](/sdk/typescript/websockets).

### A subscribe-time snapshot on `trades`

`sdk.ws.trades(tradingPairId, handler, onSnapshot?)` takes an optional `onSnapshot` that receives the market's newest 50 trades, newest first, when you subscribe. It is a recent-history baseline, not a gap-free replay: it can overlap the first live trades or miss one executed just before you subscribed, so dedupe on `tradeId`. See [WebSockets](/sdk/typescript/websockets).

### `maxRemovableCollateral` on risk-bucket rows

Risk-bucket rows of `sdk.marginAccounts.getMarginAccountSummary` and `getParentMarginAccountSummary` (`MarginAccountSummary`, `GetMarginAccountSummaryResponse`) and of `sdk.portfolio.getMargin` (`PortfolioRiskBucket`) carry `maxRemovableCollateral`: the largest removal the risk engine accepts when it may also lower the posted margin of an isolated bucket's one open position, down to its initial margin at the mark. The part past `withdrawableCollateral` comes out of the position's margin, raising its leverage and moving its liquidation price toward the mark. It equals `withdrawableCollateral` on a cross bucket and on an isolated one with no open position or several, and is present only on live-engine reads. `withdrawableCollateral` is unchanged. See [Margin Accounts](/sdk/typescript/margin-accounts).

### Optional `serde` feature in the Rust gRPC SDK

`monaco-grpc-sdk` gains an optional `serde` feature. With it on, every generated message and enum derives `Serialize` / `Deserialize` in the JSON shape the REST API uses: camelCase field names, the uint64 lease and quote fences as decimal strings, unset optional fields omitted where REST omits them, and the REST snake\_case aliases accepted. It is off by default and changes nothing on the gRPC wire. See [Rust gRPC SDK](/sdk/rust-grpc).

## Changed

### Closed and liquidated positions list newest-closed first

`sdk.positions.listPositions` (`GET /api/v1/positions`; gRPC `PositionsService.ListPositions`) with `status=CLOSED` or `status=LIQUIDATED` now orders by `(closedAt, positionId)` descending, so a long-held position closed today is on the first page. Every other filter keeps its order. A `status=CLOSED` or `status=LIQUIDATED` `pageToken` issued before this release is rejected with `400`; restart that walk with an empty `pageToken`. A position closed from this release on takes the instant the matching engine emitted the close as its close time, and a terminal position's `updatedAt` reports it. No request or response fields changed.

### Flat margin accounts get `account` frames

`sdk.ws.account` now sends a live `account_update` frame for a flat margin account (no open position and no resting order) after its collateral moves: a transfer between the spot wallet and margin, an on-chain deposit into margin or a withdrawal from it, a transfer between your margin accounts, closing a sub-account, a copy-trading allocation, or an insurance payout. Moves that land close together collapse into one frame carrying the latest state, normally within about 2 seconds. The frame uses the account's existing `sequence`, so the dedupe rule is unchanged. A flat account still sends no periodic keepalive. No wire-shape change.

## Fixed

* **Market orders report no time in force** — every WebSocket order event of a MARKET order carries `timeInForce: null`, and `Order.timeInForce` is absent (`null` on the orders snapshot), including for existing market orders. Before, `OrderPlaced` said `GTC`, and engine-placed market orders such as TWAP slices and copy orders said `IOC`. LIMIT orders keep their time in force.
* **Win rate counts fees** — the `winners`, `losers` and `winRatePct` of `sdk.portfolio.getRealizedPnl` and the copy-trading leader `winRatePct` classify a perp close by its realized PnL net of fees. Before, a close that was profitable before fees but lost money after them counted as a winner. Closes counted before this release are not reclassified.
* **Cross transfers into a closed risk bucket move the money** — a transfer into a cross risk bucket that had been emptied and closed now reopens it and moves the collateral. Before, it could report success without moving anything.
* **Isolated order previews work after the bucket closes** — `sdk.marginAccounts.simulateRiskBucketOrderRisk` on a pair whose isolated risk bucket was closed now previews like a first isolated order, instead of returning a permanent "still being provisioned" `400`.
* **Insufficient-balance errors show asset units** — a wallet shortfall on a spot order, a margin collateral transfer-in or a withdrawal reports `Available` and `Required` in the asset's units (`Required: 5000`, not `Required: 5000000000`). A transfer or withdrawal above the account's whole balance now returns the insufficient-balance error instead of an isolated-collateral message.
* **A risk bucket leaves bad debt flat** — once the insurance fund covers a bucket's shortfall, an isolated risk bucket closes and a cross risk bucket returns to normal, empty. See [Liquidations](/trading-mechanics/liquidations).
* **Referral and BuilderCodes reads are per client application** — the docs now say that `getMyReferralPosition().upline` lists the levels your trades on this client application pay, and can be empty while `referrer` is set; `getMyReferralTree()` goes as deep as any client application rewards (at most 10); and the BuilderCodes config fields describe this application's values. No wire change.

## Upgrade

```bash theme={null}
npm install @0xmonaco/core@1.0.80 @0xmonaco/types@1.0.80 @0xmonaco/react@1.0.80
# or bun
bun add @0xmonaco/core@1.0.80 @0xmonaco/types@1.0.80 @0xmonaco/react@1.0.80
```

Restart closed and liquidated position walks with an empty `pageToken`. Check `lifetimeTotalsStatus` before using a position's lifetime totals.


This documentation is built and hosted on [Mintlify](https://mintlify.com), a developer documentation platform.