Monaco Protocol SDK v1.0.67
This release separates an isolated position’s committed backing from the funds held for its unfilled orders:isolatedMargin and liquidationPrice on position reads now reflect only what the filled position has committed, and riskBucketCollateral on an order funds order capacity rather than liquidation protection. The SDK’s conditional-order and TWAP listings move to cursor pagination by default, the market screener gains open-interest averages, and the liquidations channel gains a subscribe-time snapshot and a terminal alert when an episode recovers. The SDK also corrects for a skewed device clock when it signs requests, conditionalOrders frames carry createdAt, batch-cancel responses report the engine’s millisecond cancel stamp, each funding premium sample is bounded before it is averaged, and the WebSocket limits are raised. Action items: if you read isolatedMargin or liquidationPrice, expect a tighter value for an isolated position with large resting orders; if you read total / page from listConditionalOrders or listTwapOrders, follow nextPageToken instead or pass page explicitly; and if you consume ws.liquidations, treat a COMPLETED alert as the end of the episode — it closes a recovered episode as well as one liquidated to completion, so it is not by itself a sign that anything was liquidated.
Changed
Isolated position backing excludes open-order funds
Isolated positions are now backed by filled-position backing: committed principal, realized PnL, signed trading fees (a taker fee reduces it, a maker rebate adds to it) and funding.isolatedMargin on GET /api/v1/positions and GET /api/v1/positions/{position_id} (gRPC PositionsService.ListPositions, GetPosition) reports that backing, and liquidationPrice on those two and on GET /api/v1/positions/{position_id}/risk (gRPC GetPositionRisk, which carries no isolatedMargin) is computed from it. Funds reserved or parked for unfilled orders are excluded, and live and persisted reads agree. Previously a small partial fill could inherit protection from its whole order’s risk bucket and show a liquidation price far from the mark. Field names and wire types are unchanged.
riskBucketCollateral on POST /api/v1/orders and POST /api/v1/orders/batch-create (gRPC OrdersService.CreateOrder, BatchCreateOrders) still seeds a new isolated risk bucket, but the amount is an order budget: fills commit their required margin and paid-fee funding to the position’s backing, and unfilled or unused order funds do not protect it from liquidation. An automatically funded isolated LIMIT order returns unused taker-fee funding once its execution and resting-order fee liabilities are known. Explicit position-margin additions stay committed. See Positions and Trade.
Conditional-order and TWAP listings gain cursor pagination
GET /api/v1/orders/conditional and GET /api/v1/orders/twap (gRPC OrdersService.ListConditionalOrders and ListTwapOrders) gain keyset cursor pagination: send pageToken (page_token on gRPC) — "" to start — to walk (createdAt, id) newest-first, and follow each response’s nextPageToken until it comes back empty, with a pageSize up to 1000. The server selects cursor mode only when pageToken is present, so a raw request that omits both pageToken and page still gets page-number mode; send an explicit empty pageToken to start a walk. sdk.trading.listConditionalOrders and sdk.trading.listTwapOrders default to cursor mode: omitting page (or passing pageToken) sends the empty token for you. Cursor responses return total and page as 0 and run no count, and a token pins its request filters — replaying it against different filters is rejected. Passing the deprecated page still selects page-number mode, with its bounded total and 100 page-size cap. @0xmonaco/types adds pageToken to ListConditionalOrdersParams and ListTwapOrdersParams and nextPageToken to both responses. See Trades.
Added
Screener open-interest averages
sdk.market.getScreener() (GET /api/v1/market/screener; gRPC MarketService.GetScreener) rows gain nullable decimal-string openInterestNotionalAvg1h, openInterestNotionalAvg24h and openInterestNotionalAvg7d, and each ScreenerSnapshotPoint gains openInterestNotionalAvg. Each is the mean of one valued gross open-interest notional per completed UTC minute, at the latest trade price, over 60, 1440 or 10080 minutes or a completed UTC day. A value is published once at least 95% of the window’s minutes carry a sample (57, 1368 or 9576; 1368 for a daily point). Spot pairs and under-covered windows are null, and a known zero is "0". There is no historical backfill, so the windows fill in after deployment. See Market Screener.
createdAt on conditional-order frames
sdk.ws.conditionalOrders() live conditional_order_update frames and subscribe-time snapshot rows carry createdAt, the same ISO 8601 string REST returns for that conditional order. ConditionalOrderEventData.createdAt is optional only so a frame from an older deployment during a rolling release still parses. See WebSockets.
Fixed
The liquidations channel has a baseline and reports recoveries
sdk.ws.liquidations(handler, tradingPairId?, onSnapshot) now receives a subscribe-time snapshot: one LiquidationEventData row per in-flight (UNRESOLVED) liquidation record you own, with accountState: "Liquidating". A record is account-scoped, so each row’s positionId, tradingPairId, liquidationPrice and markPrice are absent (null on the raw wire; the SDK parses them to undefined), and a pair-scoped liquidations:{pair} subscription baselines empty — subscribe to the bare liquidations channel for the account-wide baseline. An empty baseline arrives as onSnapshot([]); an unreadable read returns SNAPSHOT_UNAVAILABLE and leaves the subscription active.
A liquidation episode that recovers — its risk bucket heals back over maintenance on a liquidation step, an oracle mark, a funding settlement, or an auto-deleveraging close that lifts it — now closes its record as COMPLETED and sends one terminal liquidation_alert with status: "COMPLETED", accountState: "Normal" and the episode’s liquidationId. Previously a recovered episode ended silently and its record stayed UNRESOLVED. See Liquidations.
Signing survives a skewed device clock
Signed REST requests and the WebSocketAuthenticate handshake no longer fail with 401 Request timestamp outside allowed skew when the device clock is more than 30 seconds off. The SDK reads the server’s time from each response’s Date header, signs with the corrected time once the offset reaches 5 seconds, and retries a signed request once after a 401 that corrected the clock. A WebSocket handshake rejected for skew reconnects and re-signs, up to 3 times, with whatever the clock has learned since — so it recovers once a REST response has calibrated the clock, for example when a restored session authenticates the socket before its first REST call returns. An AUTH_FAILED frame carries no server time, so a socket-only client whose clock was never calibrated by a REST response still ends in onReauthenticationRequired. Responses a browser may have served from cache do not calibrate the clock, and browser clients need an API gateway that exposes Date over CORS. See Error Handling.
Batch-cancel cancelledAt is the engine’s stamp
POST /api/v1/orders/batch-cancel, POST /api/v1/orders/batch-cancel-all and POST /api/v1/orders/batch-cancel-all/{trading_pair_id} (gRPC OrdersService.BatchCancelOrders, BatchCancelAll, BatchCancelAllByPair) render cancelledAt from the matching engine’s per-order cancel stamp at millisecond precision, matching, to the millisecond, the cancelledAt a later read of the order returns. Previously it came from the API’s own clock truncated to whole seconds, which could render a cancel earlier than the order’s createdAt.
Funding premium samples are bounded before averaging
Each premium sample is now bounded to the market’s maximum funding rate before it enters the window, and every accepted sample in the hour carries equal weight. A brief burst of resting depth can no longer move the settled rate by more than its share of the window. The window, sample cadence and cap are unchanged. See Window Aggregation.Higher WebSocket limits
Each connection now allows 1,024 subscriptions (was 256) and a message burst of 1,200 (was 300); each account may hold 50 authenticated connections per server instance (was 20). The sustained rate stays 20 messages per second. A per-connection byte budget also refuses a burst of very large frames withMESSAGE_RATE_LIMIT, and a channel string longer than 128 bytes, or one that needs JSON escaping, is rejected with INVALID_SUBSCRIPTION. See WebSocket connections.
Upgrade
ScreenerItem gains openInterestNotionalAvg1h, openInterestNotionalAvg24h and openInterestNotionalAvg7d, and ScreenerSnapshotPoint gains openInterestNotionalAvg (all string | null); ListConditionalOrdersResponse and ListTwapOrdersResponse gain nextPageToken: string. Three behaviours differ: isolatedMargin and liquidationPrice exclude open-order funds, listConditionalOrders / listTwapOrders return cursor pages unless you pass page, and a recovered liquidation episode sends a terminal COMPLETED alert.
