> ## 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.81

# 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](/trading-mechanics/perps#one-margin-mode-per-market) and [Copy Trading](/developers/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](/sdk/typescript/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](/developers/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](/sdk/typescript/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](/sdk/typescript/websockets#slow-client-disconnect-close-code-1013).

## 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

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

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


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