Skip to main content

Monaco Protocol SDK v1.0.81

A margin account now holds each market in one margin mode at a time, and an order into the other mode is refused with the structured code MARGIN_MODE_CONFLICT. Order-risk previews return that code as rejectCode, open positions report their lifetime fundingPaid, and the ohlcv channel sends the newest 350 candles on subscribe. Action items: handle MARGIN_MODE_CONFLICT on order placement, replace, batch and TWAP creation by closing or cancelling the other side first or sending a reduce-only order; read rejectCode rather than parsing rejectReason; treat an absent fundingPaid on an open position as unknown, never zero.

Breaking

One margin mode per market and margin account

While a margin account holds a trading pair in one risk bucket (its cross bucket, or the pair’s isolated bucket) with an open position or a resting order that is not reduce-only, an order that is not reduce-only into the other bucket is rejected with 400 / gRPC INVALID_ARGUMENT and the structured error code MARGIN_MODE_CONFLICT. The message names the holding risk bucket and its mode. This applies to sdk.trading.placeLimitOrder / placeMarketOrder (POST /api/v1/orders; gRPC OrdersService.CreateOrder), order replace, the batch create and replace items, and POST /api/v1/orders/twap (gRPC OrdersService.CreateTwapOrder), which is refused at creation when the pair is already held in the other mode; a TWAP slice whose pair becomes held later is skipped. Reduce-only orders always pass, so an account that already held a pair in both modes can reduce either side until one closes. The per-pair position cap and the margin tiers therefore bind per margin account and pair. A copy-trading fill skipped for this reason reports the new CopySkipReason MARGIN_MODE_CONFLICT, with the detail naming the holding mode, instead of MARKET_STATE. It does not count toward consecutiveSkips. See One margin mode per market and Copy Trading.

Added

rejectCode on order-risk previews

sdk.marginAccounts.simulateOrderRisk (POST /api/v1/margin/accounts/{margin_account_id}/simulate-order-risk; gRPC MarginAccountsService.SimulateOrderRisk), simulateParentMarginOrderRisk (POST /api/v1/margin/parent-margin-account/simulate-order-risk; gRPC SimulateParentMarginOrderRisk) and simulateRiskBucketOrderRisk (POST /api/v1/margin/risk-buckets/simulate-order-risk; gRPC SimulateRiskBucketOrderRisk) return an optional rejectCode beside rejectReason: the structured code placing the order would return in the error envelope, such as MARGIN_MODE_CONFLICT or INVALID_ORDER. It is present on a client-class refusal and absent on an accepted preview and on a server-side fault, where rejectReason is a generic retry message. See Margin Accounts.

Lifetime fundingPaid on open positions

sdk.positions.listPositions (GET /api/v1/positions; gRPC PositionsService.ListPositions) and sdk.positions.getPosition (GET /api/v1/positions/{positionId}; gRPC PositionsService.GetPosition) now return fundingPaid on OPEN and LIQUIDATING rows as well as terminal rows: the funding accrued on the size still open plus the funding its partial closes settled, signed (negative means received), so the last open value equals the terminal value. On an open row it is absent whenever lifetimeTotalsStatus is not LIFETIME_TOTALS_STATUS_COMPLETE, never an understated partial; never read an absent fundingPaid as zero. Terminal rows are unchanged. sdk.ws.positions carries the same value as fundingPaid on every position_update frame, terminal frames included, and on every subscribe-time snapshot row. A terminal frame carries the closed position’s whole-life total. The field is absent, never zero, when the value cannot be served whole, including on frames from an older server; keep the last value you held. position_valuation frames do not carry it. @0xmonaco/types adds PositionEventData.fundingPaid. See Positions.

A subscribe-time snapshot on ohlcv

sdk.ws.ohlcv(tradingPairId, tradingMode, interval, handler, priceType?, onSnapshot?) takes an optional onSnapshot that receives the subscribed series’ newest 350 candles, oldest first, as OHLCVSnapshotCandle rows (a Candlestick without s). These are the same candles as sdk.market.getCandlesticks with its default limit. The snapshot is a recent-history baseline: its newest candle can be partial or missing, so upsert live candles by T. Pass undefined for priceType to keep the trade series. See WebSockets.

Changed

1013 when events are lost before the WebSocket server

The WebSocket server also closes connections with code 1013 and reason slow consumer: stream gapped, resync required when events may have been lost before they reached it: an engine drop under load, a failed publish, or a NATS connection that dropped, reconnected or fell behind. Every connection on that server holding a subscription is closed, at most about once per second, and the SDK reconnects, resubscribes and calls onResync with slowClient: true as for any 1013. Lost market_stats and position valuation frames never cause one, since the next frame restates them. No SDK code changed. See WebSockets.

Fixed

  • Spot win rate counts the buy-side fee — the winners, losers and winRatePct of sdk.portfolio.getRealizedPnl and the copy-trading leader winRatePct classify a spot close by its realized PnL net of both the buy and the sell fee. Before, a spot close counted only its sell fee, so a trade that lost money after fees could count as a winner. Closes counted before this release are not reclassified.
  • Margin balance frames always name a token — a margin collateral balance_update frame carries the margin account’s collateral token, or the market’s quote token when none is bound. Before, some frames after a perp fill sent an empty tokenAddress, which the SDK rejected, so the balance did not update live until a refresh.
  • TraderCodes naming — the SDK JSDoc, the MCP get_rewards_balance and transfer_rewards tool descriptions, and the proto comments say TraderCodes where they meant only the referral program. PitPass stays the umbrella over TraderCodes and BuilderCodes. No method, type, tool or wire name changed.

Upgrade

Handle MARGIN_MODE_CONFLICT wherever you place, replace or batch margin orders.