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

# Monaco Protocol SDK v1.0.47

This release is mostly TWAP. Parents gain a second execution style — `PASSIVE`, which rests each slice as a post-only maker quote at the touch and takes only what the window forces — and a conditional mode that keeps a parent dormant until the market reaches a price you name. Alongside those, a `postOnly` batch-replace item that would cross the book no longer costs you the original order, funding payments now appear in the movements feed, and the `account` WebSocket channel carries a tie-breaking `sequence`. Two things deserve reading before you upgrade: the corrected `clientOrderId` guidance, which previously told you to do something unsafe, and the exact scope of the batch-replace change.

## Added

### `PASSIVE` TWAP execution style

`sdk.trading.createTwapOrder()` accepts an optional `executionStyle` of `"TAKER"` or `"PASSIVE"`. `TAKER` is the default and is the existing v1 behavior, unchanged — banded `MARKET` slices fired at each slot.

`PASSIVE` works each slice as a maker instead. The engine rests a post-only `LIMIT` `GTC` child at the same-side touch, chases the touch with throttled cancel/replace reprices, and at the slot deadline sweeps whatever the child did not earn as a banded `MARKET` child. The result is maker where the book allows it and taker only for what the window forces. Two bounds hold while chasing: the parent's own `limitPrice`, which is never crossed, and the parent's `slippageToleranceBps` band around the **protective reference price** — the oracle mark on a margin market when one is available, otherwise a robust book-derived venue price (from the best bid/ask, when the spread is not degenerate) falling back to the last trade. Spot parents always use that book-derived chain, since spot has no oracle mark. There is one exception worth knowing if you lean on the band for protection: when the engine can resolve **no** reference at all — a one-sided book on a pair that has not traded in this process, so no mark, no trustworthy two-sided venue price and no last trade — the band check is skipped rather than failing closed, and `limitPrice` becomes the only bound on the resting quote. Set one on a passive parent if you need a hard ceiling in a market that thin. A passive placement whose target price would cross executes immediately as a banded taker instead — the book is already offering at or better than the passive target, so resting would be pointless.

Passive parents plan on their own cadence: a **60-second** target slice interval by default, against the taker default of \~5 seconds, because a child has to rest long enough to earn queue position. The 2,880-slice cap is the same for both styles, so a long window stretches its interval rather than adding slices. Both the cadence and the reprice throttle (1 second minimum between attempts) are operator-tunable.

Cancelling a passive parent is **cancel-only and atomic**: a resting child is removed in the same step that terminalizes the parent, fills it already earned stand, and nothing is left on the book. There is no final sweep on a user cancel.

Fees follow each fill's own side. Passive maker fills carry the pair's maker fee and **no application fee** — the application fee is a taker-side fee — while the deadline sweeps and any crossing take pay application taker fees as usual.

`executionStyle` is on the TWAP read model from `getTwapOrder()` / `listTwapOrders()`, and the `twap_orders` WebSocket channel gains three reasons: `passive_child_placed` (a post-only child now rests at the touch), `child_repriced` (the reprice step ran), and `passive_fill` (maker fills on the resting child folded into the parent). Deadline sweeps and crossing takes still surface as `slice_placed`.

Two of those three carry a case worth reading before you build on them. `child_repriced` is also what the cancel-only fallback emits when the old child was cancelled but the repost did not happen — so it does not imply a child is resting. The channel gives you no way to tell the two apart either: `TwapOrderEventData` carries no active-child field, so resting-child status is simply not observable from `twap_orders`. Track the child through the ordinary order channels if you need it. And the maker fill that *completes* the parent reports `completed`, not `passive_fill`, so a client waiting on a final `passive_fill` waits forever. See [TWAP Orders](/trading-mechanics/twap-orders#passive-execution) and [Trading](/sdk/typescript/trades#twap-orders).

### Conditional (price-triggered) TWAP parents

A TWAP parent can now wait for a price instead of only for a clock. Pass `triggerPrice` together with an explicit `triggerDirection` (`"ABOVE"` or `"BELOW"`) — both or neither — and the parent is created `PENDING`, watching rather than executing, and activates when the reference price crosses your level. `triggeredAt` records when it fired, and activation is one-way: once trading, the trigger is never re-evaluated.

The window floats. A conditional parent takes `durationSeconds` only — `startTime` and `endTime` are rejected — because its window is `[trigger instant, trigger instant + durationSeconds]` and cannot be known at creation. Read the armed window back off the parent rather than scheduling on the create response, whose `firstSliceAt` and `endTime` are provisional placeholders until the trigger fires.

Optional `conditionalExpiry` bounds the wait: an RFC 3339 UTC deadline (`Z` suffix only) that is valid only alongside a trigger, must be in the future, and both defaults to and is capped at 30 days from creation. If the trigger has not fired by then the parent goes `CANCELLED` having executed nothing, with the terminal reason `trigger expired`. It fires on the clock alone, so a halted or unpriceable market cannot leave a parent waiting past its deadline.

Two refusals to know about. A trigger **already satisfied** at creation returns `400` rather than starting immediately — omit `triggerPrice` if immediate execution is what you want. And on a margin market the reference is the oracle mark, so a create is rejected `400` while the market has no mark yet; an already-waiting parent keeps evaluating against the venue reference price if the mark later goes away.

`triggerPrice`, `triggerDirection`, `conditionalExpiry`, and `triggeredAt` are all on the read model and on the `twap_orders` WebSocket payload, and the channel gains the `triggered` and `trigger_expired` reasons. See [TWAP Orders](/trading-mechanics/twap-orders#conditional-twap-price-triggered-activation).

### `sequence` on the `account` WebSocket channel

Frames on the authenticated `account` channel now carry `sequence`, a monotonic per-margin-account publish counter within one producer run. It is a tie-breaker, not a stream cursor: dedupe by `updatedAt` with newest winning, and reach for `sequence` only to order two frames carrying the same `updatedAt`. It may skip values, and it resets to zero when the producer restarts — which can happen while your socket stays connected — so it is never a gap detector and never a cross-restart ordering mechanism. Every frame still carries the account's full current health, so a skipped value costs you nothing. Note that the producer emitting these frames is still gated off by default and not enabled in the deployed environments, so this is a contract addition to build against rather than a live stream change. See [WebSockets](/sdk/typescript/websockets#account-channel).

## Changed

### A crossing `postOnly` batch-replace item no longer costs you the original

`batchReplace` used to run its cancel pass first, so a `postOnly` replacement that would cross the book was refused *and* left you without the order it was replacing. The crossing check now runs in the pre-cancel preflight, so a refused item normally leaves its original resting — same order ID, same queue position. The item still reports `error.code === "POST_ONLY_WOULD_CROSS"`; only the fate of the original changed. This brings `batchReplace` into line with single `replaceOrder`, which already validated before cancelling. The check is evaluated against the book as it will stand once the batch's own cancels are applied, so a two-sided ladder shifting across its own resting prices is unaffected.

It is not a guarantee, because the preflight cannot see everything. Three narrow cases still report `POST_ONLY_WOULD_CROSS` with the original cancelled: your item crosses a replacement that an earlier item in the same batch just rested; it crosses another item's original that failed to cancel and stayed on the book; or you listed the same original twice with different replacements, where a refused occurrence's sibling still cancels the shared original. A fourth goes the other way — an earlier non-`postOnly` item that would have taken the touch is not visible either, so a quote that would have rested is refused and its original simply stays put. Confirm against your open orders rather than treating the error code alone as a guarantee.

Unchanged: every other per-item placement failure — insufficient balance, a self-trade against an order outside the batch, a risk rejection — still leaves the original cancelled. See [Batch Replace](/developers/trade#batch-replace).

### Funding payments appear in the movements feed

`GET /api/v1/accounts/movements` (`sdk.profile.getPaginatedUserMovements()`) now includes funding payments as `funding` transactions, unioned in at read time from the funding ledger. Filter to them with `transactionType=FUNDING`: the SDK method validates the value against an upper-case-only Zod enum and throws client-side on a lower-case one, while raw REST also accepts `funding` since the server matches case-insensitively. The `amount` is a magnitude, with `entryType` carrying the direction: `DEBIT` when the account paid funding, `CREDIT` when it received it. Like every other REST movement row, both enum values come back upper-case.

A funding movement is a collateral USD delta rather than a token transfer, so it reports `decimals` of `0` and an `amountRaw` equal to its `amount`. It carries the same `id` as its live `movements` WebSocket frame, so a client consuming both feeds deduplicates by `id` — but the two payloads are **not** identical, so do not diff them field for field. The REST row keeps the upper-case enum values and resolves `assetId`, `symbol`, and `decimals`; the WebSocket frame uses the lower-case forms and carries no resolved asset id. Zero-amount funding windows are excluded, and funding is scoped to the requesting application like every other movement. See [Account Balances](/developers/account-balances#movements).

### `MonacoWebSocket` and `MessageHandler` now live in `@0xmonaco/types`

Both are re-exported from `@0xmonaco/core` rather than defined there, making `@0xmonaco/types` the single source of truth for the channel surface. The public surface is unchanged and existing imports from `@0xmonaco/core` keep working.

## Fixed

### `MonacoSDK.ws` types the full channel surface

`createMonacoSDK()` instances carried a stale inline copy of the WebSocket type, so `positions`, `liquidations`, `account`, `instruments`, `marketStats`, and `marketStatsAll` were invisible on `sdk.ws` even though the runtime had them, and `ws.ohlcv` did not accept its optional `priceType` argument. `MonacoSDK.ws` is now typed as the full `MonacoWebSocket` interface, so all six channels and the `priceType` argument type-check. Runtime behavior is unchanged — this only unblocks calls TypeScript was rejecting.

### Corrected `clientOrderId` guidance — never retry on a lost response

The `clientOrderId` documentation was wrong in two ways, one of them dangerous. It described the handle as "not de-duplicated and not unique", and it told callers to reconcile a lost response against order history before resubmitting.

Uniqueness **is** enforced, but only among your orders that are **resting on the book**. An order that never rests — `MARKET`, `IOC`/`FOK`, or a `LIMIT` that fills completely on arrival — never claims the handle, so no conflict can be raised for it and a resubmission is accepted as a new order that can execute again. The handle is also released on any terminal state. It is a correlation handle, not an idempotency key, and it cannot make a lost-response retry safe.

Reconciliation belongs on the order WebSocket stream, not on order history. The stream carries the handle even for orders that never rested, and a matching event there is positive proof the order landed — while silence is never evidence that it did not. Order history is replica-backed and can omit an order that did land, and it never contains an order rejected before acceptance at all.

The same correction landed on the MCP `place_limit_order`, `place_market_order`, and `replace_order` tool descriptions. MCP exposes no order-stream tool, so an agent cannot resolve an ambiguous outcome from inside MCP: report it rather than retrying. No wire, schema, or behavior change — descriptions and documentation only.

## Upgrade

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

The MCP server ships at `1.0.47` too; it runs via `npx -y @0xmonaco/mcp-server`, so restarting your assistant picks up the new version. Every SDK bump is a patch release.

Nothing here is breaking: the TWAP additions are optional fields on an existing create, `sequence` and the `funding` movements are additive, and the batch-replace change only makes a rejection less costly. Two action items, though. First, if any code path retries an order after a lost or ambiguous response, keyed on `clientOrderId` for safety, change it — the handle does not protect a `MARKET`, `IOC`/`FOK`, or fully-filling `LIMIT` order, and a retry can execute twice. Reconcile on the order stream instead. Second, if you handle `POST_ONLY_WOULD_CROSS` in a batch-replace ladder by re-placing the original from scratch, that will now double it in the common case: re-price rather than re-place, and confirm against your open orders for the narrow cases that still cancel.


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