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

# Monaco Protocol SDK v1.0.66

This release adds **trailing stops** for perp positions and fixes two WebSocket client gaps. A trailing stop is a reduce-only market close whose trigger follows the best mark reached since it armed: attach one to an open position with `sdk.positions.attachPositionTpSl(positionId, { trailingStop })`, or to a margin entry order with `trailingStop` on `placeLimitOrder` / `placeMarketOrder`. Position TP/SL attach now draws from the order-creation rate-limit budget, and `Position.version` now advances only on a position whose content actually changed. On the socket, the client stops reconnecting in a loop after the server ends a connection for lost authentication — it calls the new `onReauthenticationRequired` option instead — and `onError` now names the `channel` a per-channel `Error` frame concerns. **Action items:** pass `onReauthenticationRequired` if your app holds authenticated WebSocket subscriptions, so it can supply a fresh session when one is revoked or expires; and if you attach TP/SL at high frequency, budget each attach as one order-creation item.

## Added

### Trailing stops

`sdk.positions.attachPositionTpSl` (`POST /api/v1/positions/{position_id}/tp-sl`; gRPC `PositionsService.AttachPositionTpSl`) accepts a `trailingStop: TrailingStopLeg`, alone or alongside `takeProfit` / `stopLoss`, and the response gains `trailingStopOrderId`. `sdk.trading.placeLimitOrder` / `placeMarketOrder` (`POST /api/v1/orders`; gRPC `OrdersService.CreateOrder`) accept the same leg as `options.trailingStop` (`ParentTrailingStopLegParams`) on margin entry orders that are not reduce-only, and `CreateOrderResponse` gains `trailingStopOrderId` — present only when the trailing stop materializes: the order fully fills on arrival or leaves a resting remainder. A partial fill whose remainder is cancelled (an `IOC` or `MARKET` order) creates no trailing stop and omits the id.

The leg takes:

* `trailBps` — integer, 10 to 2000 basis points behind the watermark
* `activationPrice?` — the mark level that starts tracking and seeds the watermark; rejected if the mark has already reached it. Omitted, tracking starts at the current mark
* `quantity?` / `closePosition?` — the same sizing contract as a TP/SL leg, mutually exclusive
* `slippageToleranceBps?` — 0 to 10000
* `expiresAt?` — ISO 8601

For a long, trigger = watermark × (1 − trailBps/10000) and it fires when the mark falls to or below it; a short mirrors it. The watermark is the best mark since arming, so the trigger only ever moves in the position's favour. It fires through the standard conditional market close (1,200 bps band, optional slippage cap).

* **One per position.** A second attach is rejected with `409` (gRPC `ALREADY_EXISTS`) — as is an attach while a resting entry order on the same trading pair and risk bucket carries a pending trailing stop — never replaced, and a trailing stop never supersedes or is superseded by a full-close TP/SL leg. It coexists with fixed TP/SL and never joins `oco`.
* **No modify.** Cancel it and attach a new one.
* **Entry-order attach** arms only when the order **fully** fills, and is cancelled with the order otherwise.
* **Reads.** `ConditionalOrder` (`getConditionalOrder`, `listConditionalOrders`, and the conditional detail of order history) reads `conditionType: "TRAILING_STOP"` and gains `trailBps`, `activationPrice`, `watermarkPrice` and `trailArmedAt`. An entry-attached stop waits as `PENDING_PARENT` until its order fully fills; from then on (and from the start for a position attach) the row stays `state: "ACTIVE"` while waiting for activation and while tracking; `trailArmedAt` is absent until tracking begins, and `triggerPrice` is provisional until the first mark after arming.
* **Streaming.** `sdk.ws.conditionalOrders` frames (snapshot and live) carry the same four fields, read leniently. Live frames carry `reason: "armed"` when tracking begins and `reason: "ratcheted"` on every trigger move, with the new `triggerPrice` and `watermarkPrice`.

The SDK rejects a `trailBps` outside 10–2000 or not an integer, `quantity` together with `closePosition`, and a trailing stop on a SPOT or reduce-only order before any request is made. `@0xmonaco/types` adds `TrailingStopLeg`, `ParentTrailingStopLegParams`, `"TRAILING_STOP"` on `ConditionalOrderConditionType`, `"armed"` / `"ratcheted"` on `ConditionalOrderEventReason`, and the matching Zod schemas.

See [Trailing Stop](/developers/trade#trailing-stop) and [Positions](/sdk/typescript/positions).

## Changed

### Position TP/SL attach is metered by the order-creation rate limit

`attachPositionTpSl` (`POST /api/v1/positions/{positionId}/tp-sl`; gRPC `PositionsService.AttachPositionTpSl`) now draws **one** item per call from the order-creation budget it shares with order creates, replaces, batch items and TWAP creation — whatever legs the call carries. The account-family tier, the PrivateLink column and the `X-RateLimit-*` headers apply exactly as on the order endpoints. Over budget it returns `429` `RATE_LIMIT_EXCEEDED` with `Retry-After` on REST and `RESOURCE_EXHAUSTED` with `RetryInfo` on gRPC. Position closes stay unmetered. Like the other order budgets, enforcement is warn-only until it is turned on for the environment, so handle `429` / `RESOURCE_EXHAUSTED` now and back off on the server's retry hint rather than waiting for a rejection. See [Rate Limits](/developers/rate-limits).

### `Position.version` advances only when the position changed

`version` on position reads now advances exactly when that row's versioned content changed at a sequence (including a change undone within one persistence batch), instead of on every open position of the margin account whenever a sequenced command touched it. An unchanged position keeps its `version`. A ranked live `position_update` frame for an untouched sibling of a filled position can therefore carry a higher version than the REST row while describing identical content — rank it as newer; nothing changes. The per-field-group merge rule is unchanged, and there is no field or type change. See [Positions](/developers/positions).

## Fixed

### The WebSocket client stops reconnecting after authentication is lost

After a `SESSION_INVALID` `Error` frame followed by a `1008` close (the session was revoked or expired), or an `AUTH_FAILED` frame after the connection sent `Authenticate`, the client now stops reconnecting instead of retrying in a loop. It drops the rejected session keypair, keeps your subscription handlers, reports `"disconnected"`, and calls the new `onReauthenticationRequired({ code, message, closeCode })` option once. Supplying a fresh session (`sdk.login`, or `setSessionKeypair` on a `createMonacoWebSocket` client) reconnects, authenticates, resubscribes every channel you still hold, and fires `onResync`. Every other abnormal close — including a `1008` for the inbound message-rate cap and a `1013` slow-client shed — keeps the backoff reconnect. `@0xmonaco/types` exports `ReauthenticationRequiredHandler` and `WebSocketReauthenticationInfo`. See [Session loss](/sdk/typescript/websockets#session-loss-close-code-1008).

### `onError` names the channel a per-channel failure concerns

`onError` now delivers `{ code, message, channel }` for a per-channel `Error` frame (`INVALID_SUBSCRIPTION`, `AUTH_REQUIRED`, `SUBSCRIPTION_LIMIT`, `SNAPSHOT_UNAVAILABLE`), with `channel` echoed exactly as you sent it — so an app can tell which channel of a batched or reconnect `Subscribe` failed, and refetch only that one, without matching the human-readable `message`. A connection-wide failure is still delivered as exactly `{ code, message }`. `WebSocketErrorInfo` gains the optional `channel?: string`. See [Server error frames](/sdk/typescript/websockets#server-error-frames).

## Upgrade

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

Every change is additive for TypeScript callers — no signature changes to adopt. Two behaviours differ: an authenticated WebSocket connection whose session is revoked or expired now stays disconnected until you supply a fresh session (handle `onReauthenticationRequired`), and each `attachPositionTpSl` call now counts against the order-creation budget.
