Monaco Protocol SDK v1.0.82
Order history now says what placed each order, so a liquidation no longer reads as a reduce-only IOC limit the user placed.sdk.ws reports which channels the server has confirmed, a batch close can target several trading pairs and one side, user trades return a bounded count again, and a TWAP order’s slices can be listed. TraderCodes is now published under its own name, and the PitPass paths and SDK names stay as deprecated aliases. Action items: send only tradingPairId, tradingPairIds, side and slippageToleranceBps to batchCloseAllPositions, since any other key is now rejected; move from sdk.pitpass and /api/v1/pitpass/... to sdk.tradercodes and /api/v1/tradercodes/...; read the order fee fields applicationTakerFee, monacoTakerFee and monacoMakerRebate as quote amounts, not basis points.
Added
What placed an order: origin
sdk.trading.getOrder (GET /api/v1/orders/{orderId}; gRPC OrdersService.GetOrder) and every row of sdk.trading.getPaginatedOrders (GET /api/v1/orders; gRPC OrdersService.ListOrders) return origin: "USER" (the account itself, including its TP/SL triggers and TWAP slices), "LIQUIDATION", "ADL", "COPY" or "SYSTEM". The order the liquidation engine places is a reduce-only IOC limit, so orderType, timeInForce and reduceOnly read the same as an order the user placed; origin === "LIQUIDATION" is the only tell. It is present on every book order and absent on a TWAP parent row and a CONDITIONAL row. Absent means not reported, never "USER". @0xmonaco/types types it as OrderOrigin | (string & {}), so a value this SDK version does not know still reaches you.
Order WebSocket events now report it for forced closes too: a liquidation slice’s OrderPlaced and fill events carry origin: "LIQUIDATION", and an ADL close’s fill event carries origin: "ADL", beside the "COPY" copy orders already reported. An order the account placed still leaves the key absent on events. useUserOrders rows carry origin from the OrderPlaced acknowledgement and from every REST row. See Trade and useUserOrders.
Server-confirmed WebSocket subscriptions
sdk.ws.getSubscriptionState(channel) returns "idle", "pending", "subscribed", "unconfirmed" or "unsubscribing", and sdk.ws.onSubscriptionChange(listener) reports each channel as the server confirms or releases it. A channel helper only requests a channel; it reads "subscribed" once a Subscribed acknowledgement names it. A reconnect starts every channel unconfirmed and re-sends the wanted ones, and a late or duplicate acknowledgement never revives a released channel. @0xmonaco/types adds WebSocketSubscriptionState, WebSocketSubscriptionChange and SubscriptionChangeHandler. See Subscription acknowledgements.
Batch close on several pairs, and on one side
sdk.positions.batchCloseAllPositions (POST /api/v1/positions/batch-close-all; gRPC PositionsService.BatchCloseAll) accepts tradingPairIds (1 to 256 trading-pair UUIDs, repeats ignored) to close the positions on any of those pairs in one request, and side ("LONG" or "SHORT") to close only one side, on top of either pair filter. tradingPairIds cannot be combined with tradingPairId. On the wire an empty tradingPairIds means no pair filter, which closes every open position, so the SDK rejects tradingPairIds: [] with a ValidationError before sending. Delegated sessions must pass tradingPairId or a non-empty tradingPairIds; side alone is refused with 403 / PERMISSION_DENIED. Over REST or gRPC, a malformed id, more than 256 ids or an unknown side is 400 / INVALID_ARGUMENT; the SDK throws a ValidationError for them before sending. The MCP batch_close_all_positions tool takes the same inputs. See Positions.
A bounded count on user trades
sdk.profile.getUserTrades (GET /api/v1/accounts/trades; gRPC AccountsService.GetUserTrades) returns total, totalPages and totalCapped on an unfiltered walk. total counts every trade you were maker or taker on, recent and archived, with a self-trade counted once. It is exact up to 10,000; past that it reads 10,000 with totalCapped: true, a lower bound. The count reads a replica, so for a moment it can disagree with the walk: a trade executed moments ago can be on a page before it is counted. With tradingPairId set, total and totalPages are absent. They are also absent when the count runs out of its server time budget, so treat an absent total as unknown, not zero, and keep driving the paging loop off nextPageToken. The MCP get_user_trades tool returns the same fields. See Profile.
List a TWAP order’s slices
sdk.trading.listTwapOrderSlices(twapOrderId, params?) (GET /api/v1/orders/twap/{twapOrderId}/slices; gRPC OrdersService.ListTwapOrderSlices) lists the child orders a TWAP parent placed, resting and finished alike. Each one is an Order exactly as getOrder returns it, with parentOrderId set. params takes orderBy ("ASC", oldest first and the default, or "DESC"), pageSize (1 to 1000, default 20) and pageToken; follow nextPageToken until it comes back empty. A passive parent’s reprices and deadline sweeps are slices too, so a parent can have more slices than slicesPlaced. The SDK checks twapOrderId, orderBy and pageSize before sending and throws a ValidationError. Over REST or gRPC the endpoint returns 400 / INVALID_ARGUMENT for a malformed twapOrderId, pageSize, orderBy or pageToken, 404 / NOT_FOUND for an unknown parent or another user’s, 401 for a delegated-agent session and 429 when the read budget is spent. See TWAP Orders.
estimatedNextFundingRate on screener rows
Each sdk.market.getScreener row (GET /api/v1/market/screener; gRPC MarketService.GetScreener) carries estimatedNextFundingRate, the same estimate getFundingState returns for the in-progress funding window, as a decimal fraction per window. It is null for spot pairs and for perps whose current window has no samples yet. A window that ends with too few accepted samples settles at the previous settled rate, so the settled rate can differ from the estimate. See Markets.
Changed
batchCloseAllPositions rejects unknown keys
POST /api/v1/positions/batch-close-all now rejects a JSON body with a field it does not know with 400 and closes nothing. Before, an unknown or misspelled field such as tradingPairIDs was ignored, which widened the close to every open position. sdk.positions.batchCloseAllPositions throws a ValidationError before sending for the same reason, BatchCloseAllSchema is strict, and the MCP batch_close_all_positions tool’s input schema refuses an unknown key before the SDK is called. With the serde feature, the Rust gRPC SDK’s BatchCloseAllRequest refuses unknown fields when deserializing JSON. gRPC itself decodes protobuf, which skips unknown fields, so a gRPC request is not refused for one.
Open interest is documented as gross
Every published open-interest figure is gross: longs plus shorts, both sides counted. The field descriptions ongetOpenInterest, getPerpMarketSummary, the screener and the market_stats frames now say so. Only the descriptions changed; the values were always gross.
Deprecated
PitPass names for TraderCodes
sdk.tradercodes calls the new /api/v1/tradercodes/... paths (gRPC monaco.api.tradercodes.TraderCodeService). sdk.pitpass is now a deprecated alias of the same object, and PitpassAPI of TraderCodesAPI. The /api/v1/pitpass/... paths and monaco.api.pitpass.TraderCodeService stay as deprecated aliases, served by the same handlers, so both return identical responses. The TraderCodes React hooks and MCP tools call sdk.tradercodes with unchanged names and signatures. The Rust REST SDK’s TraderCodes methods call the new paths and keep the old ones as pitpass_-prefixed methods, and the Rust gRPC SDK adds monaco::api::tradercodes, with monaco::api::pitpass as a deprecated alias. See TraderCodes.
Fixed
- Order fee fields are amounts —
applicationTakerFee,monacoTakerFeeandmonacoMakerRebateonOrderare fee amounts in quote currency summed over the order’s fills (negativemonacoMakerRebate= rebate), not basis points as they were documented. For a user-placed order, the two taker fees now equal the sum of the order’s trades’ fees exactly; before, they could exceed it by up to one smallest quote unit per extra fill.takerTotalPaymentandmakerTotalReceiptno longer carry a fraction of a smallest unit. This applies to orders that fill from this release on. For the rates, usefees.simulateFees. - The next funding time is always ahead —
getFundingStateandgetPerpMarketSummaryreturn anextFundingTimein the future, and themarket_statsframe’s funding timestamp is always after the frame was built. Before, both kept a passed boundary until the new window was recorded, so a countdown sat at 00:00:00. AUTH_REQUIREDchannels recover after login — a channel the server rejected because it was subscribed before the connection authenticated stays registered, reads"unconfirmed", and is re-sent once the connection authenticates, for example afterloginorsetSessionKeypair. Releasing and re-creating the subscription is no longer needed.- An agent’s orders carry the owner’s wallet — an order a delegated agent places now carries the owner’s wallet, so self-trade prevention uses the owner’s wallets and default mode, and TraderCodes referral rewards on it go to the owner’s referral chain. Before, it carried the agent’s wallet. See Delegated Agents.
Upgrade
batchCloseAllPositions call for keys other than tradingPairId, tradingPairIds, side and slippageToleranceBps.
