Skip to main content

Monaco Protocol SDK v1.0.74

This release adds margin sub-accounts with per-account PnL and portfolio views, secured delegated-agent policies with IP allowlists and bounded expiry, and referral tree reads with the full upline. Withdrawable collateral now counts realized PnL, BuilderCodes payouts start crediting builder fees, and every engine write is now rate limited per account. Action items: replace accountState with riskBucketState on margin-account summaries and liquidation events; set an expiresAt (or accept the 14-day default) and use only the supported actions when you upsert delegated agents; handle 429 on position closes, copy-trading settings writes, follow stops and reward transfers; treat AUTO_DELEVERAGING as a cancel reason.

Breaking

accountState is replaced by riskBucketState

A margin account has no liquidation lifecycle of its own: every position, order and bad-debt record lives in a risk bucket. MarginAccountSummary.accountState (GET /api/v1/margin/accounts, GET /api/v1/margin/accounts/{marginAccountId}, GET /api/v1/margin/parent-margin-account; gRPC MarginAccountsService) is removed. The new optional riskBucketState (NORMAL, PENDING_LIQUIDATION, LIQUIDATING, BAD_DEBT, CLOSED) is set only on risk-bucket rows and absent on the parent row. The list state filter now matches riskBucketState, so any filter excludes the parent row. sdk.ws.liquidations events expose data.riskBucketState (wire key risk_bucket_state) in place of data.accountState. See Margin Accounts and WebSockets.

Added

Margin sub-accounts

A wallet can hold several margin accounts per application, one per sub-account key. The key default is the existing parent margin account: it keeps its id and is the only account deposits, withdrawals and wallet transfers touch. Every sub-account holds its own collateral and its own risk buckets.
  • sdk.marginAccounts.createMarginSubAccount({ subaccountKey, label? }) (POST /api/v1/margin/sub-accounts; gRPC MarginAccountsService.CreateSubAccount) opens a sub-account. Keys are up to 64 letters, digits, _, -, . or :; copy: keys are reserved and default is refused. The default margin account must exist first. Re-creating a closed key re-opens the same account. Open sub-accounts are capped per owner by weighted 14-day volume tier (10 at the first tier, up to 50) or an admin override; copy-trading sub-accounts do not count.
  • sdk.marginAccounts.transferMarginCollateral({ asset, amount, from, to }) (POST /api/v1/margin/collateral/transfer; gRPC MarginAccountsService.TransferCollateral) moves collateral between the wallet and the default margin account, or from one margin account to another in one atomic step. from and to are { wallet: true } or { marginAccountId }. Risk-bucket endpoints are refused on this route; fund a risk bucket with transferCollateralToRiskBucket.
  • sdk.marginAccounts.closeMarginSubAccount(marginAccountId) (POST /api/v1/margin/sub-accounts/{marginAccountId}/close; gRPC MarginAccountsService.CloseSubAccount) closes a flat sub-account in one call: its flat, solvent risk buckets close with it and all of its collateral moves to the default margin account. A risk bucket in liquidation or bad debt refuses the close, and nothing moves.
  • sdk.marginAccounts.getMarginAccountPnl(marginAccountId) (GET /api/v1/margin/accounts/{marginAccountId}/pnl; gRPC MarginAccountsService.GetMarginAccountPnl) returns one margin account’s own realized and unrealized PnL; an owner’s accounts sum to the owner-wide figure.
  • transferCollateralToRiskBucket and simulateRiskBucketOrderRisk take an optional marginAccountId naming any of the caller’s open sub-accounts, so a sub-account funds and previews its own risk buckets. A copy-trading sub-account’s risk buckets belong to its follow and are refused.
  • sdk.portfolio.getMargin({ marginAccountId, includeAccounts }) (GET /api/v1/accounts/me/portfolio/margin; gRPC AccountsService.GetPortfolioMargin) describes one margin account, and with includeAccounts also returns accounts (one row per open margin account) and ownerTotal. The response names the account it describes in marginAccountId.
  • MarginAccountSummary gains subaccountKey and marginAccountState (ACTIVE or CLOSED), and private order events carry marginAccountId (wire margin_account_id; a maker fill carries the maker’s account), so you can route events per sub-account.
Creating and closing a sub-account and account-to-account transfers spend the per-user movement budget. See Margin Sub-Accounts and Margin Accounts.

Secured delegated-agent policies

sdk.delegatedAgents.upsertDelegatedAgent (POST /api/v1/delegated-agents; gRPC DelegatedAgentsService.UpsertDelegatedAgent) policies now scope with AND: an agent listing a margin account (and markets) trades only that account (on those markets); a market-only agent still trades its markets in every account. expiresAt defaults to 14 days from now and may be at most 180 days ahead. allowedActions accepts CREATE_ORDER, CANCEL_ORDER, REPLACE_ORDER, READ and MANAGE_RISK_BUCKETS and rejects anything else; a READ-only policy is a read-only key. An owner holds at most 10 active, unexpired agents. The new ipAllowlist (up to 20 IP addresses or CIDR ranges) restricts where the agent may create its delegated session (POST /api/v1/delegated-agents/sessions returns 403 / PERMISSION_DENIED from any other address). MANAGE_RISK_BUCKETS lets an agent move a listed margin account’s free collateral into that account’s own risk buckets through transferCollateralToRiskBucket (POST /api/v1/margin/risk-buckets/collateral/transfer-in), only into an isolated risk bucket on a listed market when the policy lists markets. Every other collateral route still refuses delegated agents. See Delegated Agents.

Referral tree and full upline

sdk.pitpass.getMyReferralTree({ parent?, pageSize?, pageToken? }) (GET /api/v1/pitpass/referrals/me/tree; gRPC TraderCodeService.GetMyReferralTree) reads one level of your own referral tree a page at a time: the wallets a parent referred, each with its level, parent, referral time, referee count and what you earned from it. Omit parent for your L1 referees, or pass one of your L1 or L2 referees to expand it; any other wallet is 404 / NOT_FOUND. pageSize is 1 to 200, default 50. getMyReferralPosition also returns upline, your referral chain above you (L1, L2, L3) nearest first. React adds useMyReferralTree. See PitPass.

AUTO_DELEVERAGING cancel reason

A cancelled order can report the terminal reason AUTO_DELEVERAGING: auto-deleveraging fully closed the position your risk bucket held and this reduce-only order on that pair was cancelled with it. You were deleveraged as a counterparty, not liquidated, so it is not reported as LIQUIDATION. Code that switches on terminalReason should treat unrecognised values as an opaque cause. See WebSockets.

Changed

Withdrawable collateral counts realized PnL

withdrawableCollateral now counts realized PnL on every surface that publishes it: margin-account summaries, newWithdrawableCollateral on collateral transfer responses, the margin figure on GET /api/v1/margin/collateral/available, and the portfolio margin totals. On the parent margin account the bound is deposits plus settled realized PnL and funding, less risk bucket allocations, and never more than freeCollateral less any unrealized gain; a user who deposited 1,000 and realized 500 can now withdraw 1,500, and a settled loss reduces it. A risk bucket’s bound is its allocation plus realized PnL, less funding paid, unrealized losses and required margin; it is no longer capped at the allocation, and it is zero while the risk bucket is not NORMAL. A parent margin account is charged at most a risk bucket’s allocation: a loss past it is the insurance fund’s. A cross risk bucket that goes flat with no resting orders returns its allocation and realized PnL to the parent margin account and stays open, empty. Collateral a risk bucket draws automatically for an order returns to the parent margin account when that order is cancelled or released, less the share its fills used, and a replace to a smaller order returns only the draw it frees; collateral you moved in yourself stays. Transfers into or out of a risk bucket frozen as bad debt are refused. Field types are unchanged. See Modify Margin.

BuilderCodes payouts credit builder fees

sdk.buildercodes.listPayouts and getPayoutSummary now return BUILDER_FEE credits: each positive builder fee charged on an enrolled application’s taker fill accrues in full to the application payout wallet’s claimable balance, and appliedBps reports the charged builder-fee rate. See BuilderCodes.

Every engine write is rate limited

Every authenticated write that sends a command to the matching engine now draws a per-account limit before it reaches the engine. Eight previously unmetered operations can return 429 RATE_LIMIT_EXCEEDED with Retry-After and details.retryAfter (gRPC RESOURCE_EXHAUSTED with RetryInfo); a 429 applied nothing.
  • Settings writes share one budget (1 write/s, burst 10, family 4x): the lead trader profile, follow create/edit and the self-trade-prevention default.
  • Follow stops have their own budget (1 stop/s, burst 20), so closes and edits never delay a stop.
  • Position closes draw on the order limit’s risk-reduction class: closePosition one item, batchCloseAllPositions one per open position (the first 100 are charged up front; over budget the whole call is refused).
  • Reward transfers (sdk.pitpass.transferRewards, BuilderCodes claims) spend the per-user movement budget shared with withdrawals and collateral transfers.
Position closes and reward transfers follow each environment’s enforcement setting. See Rate Limits.

Position frames rank funding and terminal updates

Position WebSocket updates for non-zero funding settlements and terminal closes, liquidations and ADL are ranked with the durable position version. Terminal frames keep the historical position size and exact CLOSED or LIQUIDATED status; detect closure from status, not a zero size. Zero funding payments emit no position frame. See Positions.

Liquidation, deleveraging and bad debt

  • Forced closes ignore self-trade prevention. Liquidation slices can match the account’s own resting orders, and an account’s other risk buckets’ positions can be ADL counterparties; ADL closes positions directly and fills no resting orders, though a position it fully closes has its resting reduce-only orders on that pair cancelled with AUTO_DELEVERAGING.
  • ADL is single-price. ADL starts when the book refuses the position below three quarters of maintenance margin, fills every chunk at the liquidated position’s pinned price, and reaches counterparties in two stages by where the fill leaves their account.
  • The insurance fund’s role follows the liquidation mode. In the default Unbounded mode the terminal slice carries no limit and the insurance fund absorbs fills past the pinned price. When the fund is empty, the venue switches to the Zero mode, where anything the book declines at that price goes straight to ADL. It stays there until it is switched back after the fund is replenished.
  • Bad debt drains from the insurance fund. A risk bucket in bad debt stays frozen with an immutable allocation; the fund credits it as it pays, possibly in parts, and it unfreezes once covered. There is no manual write-off.
  • A risk bucket’s loss stops at its allocation, for cross and isolated alike, and the engine’s live account equity never counts an open loss past it (persisted portfolio and fallback summary reads still sum unrealized PnL uncapped).
See Liquidations and Auto-Deleveraging.

Order margin and margin release

  • A margin limit order’s initial margin is priced at its limit, never the average of its fills; a market order uses its pre-trade estimate.
  • Automatic top-up returns only the unused draw. An order draws only its shortfall from the parent margin account at admission. Cancelling or releasing it returns the part of that draw its fills did not use, and a replace to a smaller order returns only the draw it frees, for cross and isolated risk buckets alike.
  • Margin release is floored at initial margin. Reducing a position’s margin cannot take it below its initial margin requirement.
See Margin.

Fixed

Position reads report index-price outages as unavailable

listPositions, getPosition and getPositionRisk (GET /api/v1/positions, /positions/{positionId}, /positions/{positionId}/risk) return 503 SERVICE_UNAVAILABLE / gRPC UNAVAILABLE when a transient index-price read fails, so you can retry; other failures stay 500 / INTERNAL. See Positions.

Funding publication resumes after epoch gaps

Funding publication resumes when old history occupies newer settlement epochs. Epoch numbers may jump; skipped identifiers are not retroactive charges. The funding endpoints’ fields are unchanged. See Funding Rates.

Copy-position margin errors

sdk.copyTrading.addFollowPositionMargin refuses with 400 COPY_TRADING_REJECTED when there is no isolated copy position on the pair, the copy position is being liquidated or is in bad debt, or the amount exceeds what the copy account can put in; 409 covers only a stopping or stopped follow. See Copy Trading.

Upgrade

Rename accountState to riskBucketState where you read margin-account summaries or liquidation events, and stop expecting a state on the parent row. Review delegated-agent policies for AND scoping and expiry, and retry 429s after Retry-After. Sub-accounts are opt-in; the default margin account and the existing transfer methods are unchanged.