@0xmonaco/types 1.0.45 → 1.0.47

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -1,11 +1,11 @@
1
1
  import type { Address, PublicClient, TransactionReceipt, WalletClient } from "viem";
2
2
  import type { ApplicationsAPI } from "../applications";
3
- import type { AuthAPI, AuthState, SessionCredentials } from "../auth/index";
3
+ import type { AuthAPI, AuthState } from "../auth/index";
4
4
  import type { DelegatedAgentsAPI } from "../delegated-agents";
5
5
  import type { FaucetAPI } from "../faucet";
6
6
  import type { FeesAPI } from "../fees";
7
7
  import type { MarginAccountsAPI } from "../margin-accounts";
8
- import type { Interval, MarketAPI } from "../market";
8
+ import type { MarketAPI } from "../market";
9
9
  import type { PitpassAPI } from "../pitpass";
10
10
  import type { PortfolioAPI } from "../portfolio";
11
11
  import type { PositionsAPI } from "../positions";
@@ -15,7 +15,7 @@ import type { SubAccountsAPI } from "../sub-accounts";
15
15
  import type { SweeperAPI } from "../sweeper";
16
16
  import type { TradingAPI, TradingMode } from "../trading";
17
17
  import type { VaultAPI } from "../vault";
18
- import type { ConditionalOrderEvent, MonacoWebSocketConfig, OHLCVEvent, OrderbookEvent, OrderbookQuotationMode, OrderEvent, TradeEvent, TwapOrderEvent, UserBalanceEvent, UserMovementEvent } from "../websocket";
18
+ import type { MonacoWebSocket, MonacoWebSocketConfig, OrderbookEvent, OrderbookQuotationMode, TradeEvent } from "../websocket";
19
19
  import type { WhitelistAPI } from "../whitelist";
20
20
  import type { WithdrawalsAPI } from "../withdrawals";
21
21
  import type { Network } from "./network";
@@ -139,23 +139,8 @@ export interface MonacoSDK {
139
139
  pageSize?: number;
140
140
  }) => Promise<TradeEvent[]>;
141
141
  };
142
- /** WebSocket client for real-time data (orders, orderbook, ohlcv) */
143
- ws: {
144
- connect: () => Promise<void>;
145
- disconnect: () => void;
146
- isConnected: () => boolean;
147
- getStatus: () => "connected" | "connecting" | "disconnected" | "reconnecting";
148
- setSessionKeypair: (credentials: SessionCredentials | undefined) => void;
149
- orders: (tradingPairId: string, tradingMode: TradingMode, handler: (event: OrderEvent) => void) => () => void;
150
- orderbook: (tradingPairId: string, tradingMode: TradingMode, magnitude: number, quotationMode: OrderbookQuotationMode, handler: (event: OrderbookEvent) => void) => () => void;
151
- ohlcv: (tradingPairId: string, tradingMode: TradingMode, interval: Interval, handler: (event: OHLCVEvent) => void) => () => void;
152
- trades: (tradingPairId: string, handler: (event: TradeEvent) => void) => () => void;
153
- movements: (handler: (event: UserMovementEvent) => void) => () => void;
154
- userOrders: (handler: (event: OrderEvent) => void) => () => void;
155
- balances: (handler: (event: UserBalanceEvent) => void) => () => void;
156
- conditionalOrders: (handler: (event: ConditionalOrderEvent) => void, tradingPairId?: string) => () => void;
157
- twapOrders: (handler: (event: TwapOrderEvent) => void, tradingPairId?: string) => () => void;
158
- };
142
+ /** WebSocket client for real-time data — the full typed channel surface of {@link MonacoWebSocket} */
143
+ ws: MonacoWebSocket;
159
144
  /** Wallet client for all blockchain operations (undefined until set) */
160
145
  walletClient: WalletClient | undefined;
161
146
  /** Public client for read-only blockchain operations */
@@ -6,7 +6,7 @@
6
6
  import type { BaseAPI } from "../api/index";
7
7
  import type { MarginMode } from "../margin-accounts";
8
8
  import type { Order, OrderSide, PositionSide, TimeInForce, TradingMode } from "./orders";
9
- import type { BatchCancelOrdersResponse, BatchCreateOrderParams, BatchCreateOrdersResponse, BatchReplaceOrderParams, BatchReplaceOrdersResponse, CancelConditionalOrderResponse, CancelOrderResponse, CancelTwapOrderResponse, ConditionalOrder, CreateOrderResponse, CreateTwapOrderOptions, CreateTwapOrderResponse, GetOrderResponse, GetPaginatedOrdersParams, GetPaginatedOrdersResponse, ListConditionalOrdersParams, ListConditionalOrdersResponse, ListTwapOrdersParams, ListTwapOrdersResponse, ParentTpSlLegParams, ReplaceOrderResponse, TwapOrder } from "./responses";
9
+ import type { BatchCancelOrdersResponse, BatchCreateOrderParams, BatchCreateOrdersResponse, BatchReplaceOrderParams, BatchReplaceOrdersResponse, CancelConditionalOrderResponse, CancelOrderResponse, CancelTwapOrderResponse, ConditionalOrder, CreateOrderResponse, CreateTwapOrderOptions, CreateTwapOrderResponse, GetOrderResponse, GetPaginatedOrdersParams, GetPaginatedOrdersResponse, GetTwapOrderTcaResponse, ListConditionalOrdersParams, ListConditionalOrdersResponse, ListTwapOrdersParams, ListTwapOrdersResponse, ParentTpSlLegParams, ReplaceOrderResponse, TwapOrder } from "./responses";
10
10
  /**
11
11
  * Trading API interface.
12
12
  * Provides methods for placing and managing orders.
@@ -48,8 +48,11 @@ export interface TradingAPI extends BaseAPI {
48
48
  /**
49
49
  * Client-assigned correlation handle, echoed back on order reads and
50
50
  * order WebSocket events. At most 64 characters from [A-Za-z0-9._:-].
51
- * Not de-duplicated and not unique — a replacement re-states it and
52
- * inherits nothing from the original order.
51
+ * Uniqueness is enforced only among your orders that are resting on the
52
+ * book; an order that never rests does not take the handle, and the value
53
+ * is released on any terminal state. It does not make a retry safe —
54
+ * resubmitting after a lost response can execute a second time. A
55
+ * replacement re-states it and inherits nothing from the original order.
53
56
  */
54
57
  clientOrderId?: string;
55
58
  takeProfit?: ParentTpSlLegParams;
@@ -80,8 +83,11 @@ export interface TradingAPI extends BaseAPI {
80
83
  /**
81
84
  * Client-assigned correlation handle, echoed back on order reads and
82
85
  * order WebSocket events. At most 64 characters from [A-Za-z0-9._:-].
83
- * Not de-duplicated and not unique — a replacement re-states it and
84
- * inherits nothing from the original order.
86
+ * Uniqueness is enforced only among your orders that are resting on the
87
+ * book; an order that never rests does not take the handle, and the value
88
+ * is released on any terminal state. It does not make a retry safe —
89
+ * resubmitting after a lost response can execute a second time. A
90
+ * replacement re-states it and inherits nothing from the original order.
85
91
  */
86
92
  clientOrderId?: string;
87
93
  takeProfit?: ParentTpSlLegParams;
@@ -142,8 +148,14 @@ export interface TradingAPI extends BaseAPI {
142
148
  /**
143
149
  * Client-assigned correlation handle, echoed back on order reads and
144
150
  * order WebSocket events. At most 64 characters from [A-Za-z0-9._:-].
145
- * Not de-duplicated and not unique — a replacement re-states it and
146
- * inherits nothing from the original order.
151
+ * Uniqueness is enforced only among your orders that are resting on the
152
+ * book; an order that never rests does not take the handle, and the value
153
+ * is released on any terminal state. A replacement re-states it and
154
+ * inherits nothing from the original order. A retry of this replace cannot
155
+ * double-execute — it names the original order id, which the engine
156
+ * revalidates at sequence time, so a second attempt returns
157
+ * ORDER_NOT_FOUND — but that code is ambiguous on its own, so reconcile to
158
+ * find the replacement's new order id rather than to rule out a duplicate.
147
159
  */
148
160
  clientOrderId?: string;
149
161
  }): Promise<ReplaceOrderResponse>;
@@ -208,12 +220,20 @@ export interface TradingAPI extends BaseAPI {
208
220
  * own TWAP orders.
209
221
  */
210
222
  getTwapOrder(twapOrderId: string): Promise<TwapOrder>;
223
+ /**
224
+ * Gets the post-execution TCA report for a TERMINAL (COMPLETED or
225
+ * CANCELLED) TWAP parent: realized average price vs the arrival price and
226
+ * the window-TWAP benchmark over the active window. Positive bps =
227
+ * execution beat the reference. Benchmark fields are absent when no price
228
+ * series covered the window sufficiently.
229
+ */
230
+ getTwapOrderTca(twapOrderId: string): Promise<GetTwapOrderTcaResponse>;
211
231
  /**
212
232
  * Lists TWAP parent orders for the authenticated user with pagination and
213
233
  * optional state / trading pair filters.
214
234
  */
215
235
  listTwapOrders(params?: ListTwapOrdersParams): Promise<ListTwapOrdersResponse>;
216
236
  }
217
- export type { ConditionalOrderConditionType, ConditionalOrderState, ConditionalOrderTriggerSource, Order, OrderRole, OrderSide, OrderStatus, OrderStatusFilter, OrderTerminalReason, OrderType, PositionSide, TimeInForce, TradingMode, TwapOrderState, } from "./orders";
237
+ export type { ConditionalOrderConditionType, ConditionalOrderState, ConditionalOrderTriggerSource, Order, OrderRole, OrderSide, OrderStatus, OrderStatusFilter, OrderTerminalReason, OrderType, PositionSide, TimeInForce, TradingMode, TwapExecutionStyle, TwapOrderState, } from "./orders";
218
238
  export { ORDER_STATUS_VALUES } from "./orders";
219
- export type { BatchCancelError, BatchCancelOrdersResponse, BatchCancelResult, BatchCreateOrderParams, BatchCreateOrdersResponse, BatchCreateResult, BatchError, BatchReplaceOrderParams, BatchReplaceOrdersResponse, BatchReplaceResult, CancelConditionalOrderResponse, CancelOrderResponse, CancelTwapOrderResponse, ConditionalOrder, CreateOrderResponse, CreateTwapOrderOptions, CreateTwapOrderResponse, GetOrderResponse, GetPaginatedOrdersParams, GetPaginatedOrdersResponse, ListConditionalOrdersParams, ListConditionalOrdersResponse, ListTwapOrdersParams, ListTwapOrdersResponse, MatchResult, ParentTpSlLegParams, ReplaceOrderResponse, TwapOrder, UpdatedFields, } from "./responses";
239
+ export type { BatchCancelError, BatchCancelOrdersResponse, BatchCancelResult, BatchCreateOrderParams, BatchCreateOrdersResponse, BatchCreateResult, BatchError, BatchReplaceOrderParams, BatchReplaceOrdersResponse, BatchReplaceResult, CancelConditionalOrderResponse, CancelOrderResponse, CancelTwapOrderResponse, ConditionalOrder, CreateOrderResponse, CreateTwapOrderOptions, CreateTwapOrderResponse, GetOrderResponse, GetPaginatedOrdersParams, GetPaginatedOrdersResponse, GetTwapOrderTcaResponse, ListConditionalOrdersParams, ListConditionalOrdersResponse, ListTwapOrdersParams, ListTwapOrdersResponse, MatchResult, ParentTpSlLegParams, ReplaceOrderResponse, TwapOrder, UpdatedFields, } from "./responses";
@@ -74,8 +74,10 @@ export interface Order {
74
74
  * Client-assigned correlation handle as stored: the value supplied on create
75
75
  * or replace, with surrounding whitespace trimmed. Absent when the order
76
76
  * carried none, including every order placed before the field was accepted on
77
- * write. Not unique — several orders may share one value, so a lookup by it
78
- * can be ambiguous.
77
+ * write. Uniqueness is enforced only among your orders that are resting on
78
+ * the book, so several of your orders may share one value over time and a
79
+ * lookup by it can be ambiguous: an order that never rests never takes the
80
+ * handle, and the value is released on any terminal state.
79
81
  */
80
82
  clientOrderId?: string;
81
83
  /** Position ID linked to the order, when available */
@@ -168,3 +170,10 @@ export type ConditionalOrderState = "PENDING_PARENT" | "ACTIVE" | "TRIGGERING" |
168
170
  * - CANCELLED: cancelled by the user before the window ended
169
171
  */
170
172
  export type TwapOrderState = "PENDING" | "ACTIVE" | "COMPLETED" | "CANCELLED";
173
+ /**
174
+ * How a TWAP parent executes each slice.
175
+ * - TAKER (default): banded MARKET slices fired at each slot
176
+ * - PASSIVE: post-only children rested at the touch, with a taker sweep at
177
+ * each slice deadline
178
+ */
179
+ export type TwapExecutionStyle = "TAKER" | "PASSIVE";
@@ -4,7 +4,7 @@
4
4
  * Response types for trading operations.
5
5
  */
6
6
  import type { MarginMode } from "../margin-accounts";
7
- import type { ConditionalOrderConditionType, ConditionalOrderState, ConditionalOrderTriggerSource, Order, OrderSide, OrderStatus, OrderStatusFilter, OrderType, PositionSide, TimeInForce, TradingMode, TwapOrderState } from "./orders";
7
+ import type { ConditionalOrderConditionType, ConditionalOrderState, ConditionalOrderTriggerSource, Order, OrderSide, OrderStatus, OrderStatusFilter, OrderType, PositionSide, TimeInForce, TradingMode, TwapExecutionStyle, TwapOrderState } from "./orders";
8
8
  /**
9
9
  * Match result information for order execution.
10
10
  * Contains detailed information about trades, fills, and execution quality.
@@ -378,8 +378,11 @@ export interface BatchCreateOrderParams {
378
378
  /**
379
379
  * Client-assigned correlation handle, echoed back on order reads and on
380
380
  * order WebSocket events. At most 64 characters from [A-Za-z0-9._:-].
381
- * Monaco does not de-duplicate on it and does not enforce uniqueness —
382
- * it correlates a response to a request, it does not make a retry safe.
381
+ * Uniqueness is enforced only among your orders that are resting on the
382
+ * book; an order that never rests does not take the handle, and the value
383
+ * is released on any terminal state. It correlates a response to a request
384
+ * and does not make a retry safe — resubmitting after a lost response can
385
+ * execute a second time.
383
386
  */
384
387
  clientOrderId?: string;
385
388
  }
@@ -403,8 +406,13 @@ export interface BatchReplaceOrderParams {
403
406
  /**
404
407
  * Client-assigned correlation handle, echoed back on order reads and on
405
408
  * order WebSocket events. At most 64 characters from [A-Za-z0-9._:-].
406
- * Monaco does not de-duplicate on it and does not enforce uniqueness —
407
- * it correlates a response to a request, it does not make a retry safe.
409
+ * Uniqueness is enforced only among your orders that are resting on the
410
+ * book; an order that never rests does not take the handle, and the value
411
+ * is released on any terminal state. It correlates a response to a request.
412
+ * Each item names its own original order id, which the engine revalidates at
413
+ * sequence time, so retrying a batch cannot replace an already-replaced
414
+ * original a second time; reconcile per item on the results and their new
415
+ * order ids rather than to rule out duplicate execution.
408
416
  */
409
417
  clientOrderId?: string;
410
418
  }
@@ -491,6 +499,15 @@ export interface CreateTwapOrderOptions {
491
499
  * reproducible to compliance but unpredictable to the market.
492
500
  */
493
501
  randomize?: boolean;
502
+ /**
503
+ * Child execution style. "TAKER" (default): each slice executes
504
+ * immediately as a banded MARKET order. "PASSIVE": each slice rests as a
505
+ * post-only limit at the same-side touch (maker where possible, repriced
506
+ * to follow the touch), and the leftover is swept as a banded MARKET order
507
+ * at the slice deadline; passive parents use a slower default slice
508
+ * cadence (~60s).
509
+ */
510
+ executionStyle?: "TAKER" | "PASSIVE";
494
511
  /** Whether the TWAP parent is reduce-only (margin only). Child orders inherit it and clamp to the live position. */
495
512
  reduceOnly?: boolean;
496
513
  /** Trading mode: "SPOT" or "MARGIN" (default: "SPOT"). */
@@ -507,6 +524,23 @@ export interface CreateTwapOrderOptions {
507
524
  strategyKey?: string;
508
525
  /** Use master account balance for sub-account orders. */
509
526
  useMasterBalance?: boolean;
527
+ /**
528
+ * Price-triggered activation: the parent waits (PENDING) until the
529
+ * reference price reaches or crosses this price in `triggerDirection`,
530
+ * then runs a floating window of `durationSeconds` from the trigger
531
+ * instant. Requires `triggerDirection` and `durationSeconds`; incompatible
532
+ * with `startTime`/`endTime`. Rejected if already met at creation.
533
+ */
534
+ triggerPrice?: string;
535
+ /** Crossing direction for `triggerPrice`: "ABOVE" or "BELOW". Required with `triggerPrice`. */
536
+ triggerDirection?: "ABOVE" | "BELOW";
537
+ /**
538
+ * Deadline for the trigger to fire (RFC 3339, UTC only — Z suffix). If it
539
+ * has not fired by then the parent cancels with terminal reason
540
+ * 'trigger expired'. Only valid with `triggerPrice`; defaults to creation
541
+ * + the maximum window (30 days).
542
+ */
543
+ conditionalExpiry?: string;
510
544
  /** Client-assigned idempotency key (max 36 chars), unique per user and application. A duplicate is rejected with 409. */
511
545
  clientTwapId?: string;
512
546
  }
@@ -589,6 +623,8 @@ export interface TwapOrder {
589
623
  clientTwapId?: string;
590
624
  /** Whether seeded size/timing randomization is applied to the schedule */
591
625
  randomize: boolean;
626
+ /** Child execution style: "TAKER" (banded MARKET slices) or "PASSIVE" (post-only children at the touch, taker sweep at each slice deadline) */
627
+ executionStyle: TwapExecutionStyle;
592
628
  /** Window start (ISO 8601) */
593
629
  startTime: string;
594
630
  /** Window end (ISO 8601). Execution stops hard at this time. */
@@ -611,6 +647,14 @@ export interface TwapOrder {
611
647
  state: TwapOrderState;
612
648
  /** Terminal cause detail, e.g. an end-of-window shortfall ('ended with shortfall <qty>'); absent for non-terminal parents */
613
649
  terminalReason?: string;
650
+ /** Price-triggered activation level, when this parent is conditional */
651
+ triggerPrice?: string;
652
+ /** Trigger crossing direction: "ABOVE" or "BELOW" */
653
+ triggerDirection?: string;
654
+ /** Deadline for the trigger to fire (ISO 8601 UTC); reaching it cancels the parent ('trigger expired') */
655
+ conditionalExpiry?: string;
656
+ /** When the trigger fired and the floating window started (ISO 8601 UTC); absent while waiting */
657
+ triggeredAt?: string;
614
658
  /** Creation timestamp (ISO 8601) */
615
659
  createdAt: string;
616
660
  /** Last update timestamp (ISO 8601) */
@@ -620,6 +664,51 @@ export interface TwapOrder {
620
664
  /** Completion timestamp (ISO 8601) */
621
665
  completedAt?: string;
622
666
  }
667
+ /**
668
+ * Post-execution TCA (transaction cost analysis) report for a TERMINAL
669
+ * (COMPLETED or CANCELLED) TWAP parent.
670
+ *
671
+ * The measurement window is the parent's ACTIVE window — start to the
672
+ * terminal time (never the planned end: a cancelled parent stopped trading
673
+ * at cancellation). Benchmark inputs are computed once, server-side, when
674
+ * the terminal row is projected; the basis-point metrics are derived per
675
+ * read. Sign convention: POSITIVE bps means execution BEAT the reference —
676
+ * a BUY that paid less, a SELL that received more. Benchmark fields are null
677
+ * when no price series covered the window sufficiently (an honest null, not
678
+ * an approximation) and when nothing executed.
679
+ */
680
+ export interface GetTwapOrderTcaResponse {
681
+ /** TWAP parent order UUID */
682
+ twapOrderId: string;
683
+ /** Trading pair the parent executed on — a report is self-describing */
684
+ tradingPairId: string;
685
+ /** Terminal state the report covers: COMPLETED or CANCELLED */
686
+ state: TwapOrderState;
687
+ /** Parent side (fixes the sign convention) */
688
+ side: OrderSide;
689
+ /** Parent order quantity (decimal string) */
690
+ totalQuantity: string;
691
+ /** Active-window start (ISO 8601, UTC) */
692
+ windowStartAt: string;
693
+ /** Terminal time — completion or cancellation (ISO 8601, UTC) */
694
+ windowTerminalAt: string;
695
+ /** Quantity filled over the window (decimal string) */
696
+ executedQuantity: string;
697
+ /** Unfilled remainder: totalQuantity - executedQuantity, floored at zero */
698
+ shortfallQuantity: string;
699
+ /** Volume-weighted realized price (executedNotional / executedQuantity); absent when nothing executed */
700
+ realizedAvgPrice?: string;
701
+ /** Reference price at (or immediately before) the window start; absent when history could not cover the window */
702
+ arrivalPrice?: string;
703
+ /** Average reference price over the active window; absent when history could not cover the window */
704
+ benchmarkTwapPrice?: string;
705
+ /** Price series both benchmark values came from: risk_mark_1m or ohlcv_1m_close; absent when no series qualified */
706
+ benchmarkSource?: string;
707
+ /** Signed bps vs arrival; positive = beat it; absent when realized or arrival is absent */
708
+ slippageVsArrivalBps?: string;
709
+ /** Signed bps vs the window-TWAP benchmark; positive = beat it; absent when realized or benchmark is absent */
710
+ performanceVsBenchmarkBps?: string;
711
+ }
623
712
  /**
624
713
  * Query parameters for listing TWAP parent orders.
625
714
  */
@@ -331,12 +331,6 @@ export declare const TwapOrderStateSchema: z.ZodEnum<{
331
331
  PENDING: "PENDING";
332
332
  COMPLETED: "COMPLETED";
333
333
  }>;
334
- /**
335
- * Create TWAP Order validation schema
336
- *
337
- * Validates parameters for creating a TWAP parent order. The execution
338
- * window is mandatory: exactly one of durationSeconds or endTime.
339
- */
340
334
  export declare const CreateTwapOrderSchema: z.ZodObject<{
341
335
  tradingPairId: z.ZodUUID;
342
336
  side: z.ZodEnum<{
@@ -351,6 +345,10 @@ export declare const CreateTwapOrderSchema: z.ZodObject<{
351
345
  limitPrice: z.ZodOptional<z.ZodString>;
352
346
  slippageToleranceBps: z.ZodOptional<z.ZodNumber>;
353
347
  randomize: z.ZodOptional<z.ZodBoolean>;
348
+ executionStyle: z.ZodOptional<z.ZodEnum<{
349
+ TAKER: "TAKER";
350
+ PASSIVE: "PASSIVE";
351
+ }>>;
354
352
  reduceOnly: z.ZodOptional<z.ZodBoolean>;
355
353
  tradingMode: z.ZodOptional<z.ZodEnum<{
356
354
  SPOT: "SPOT";
@@ -366,6 +364,12 @@ export declare const CreateTwapOrderSchema: z.ZodObject<{
366
364
  strategyKey: z.ZodOptional<z.ZodString>;
367
365
  useMasterBalance: z.ZodOptional<z.ZodBoolean>;
368
366
  clientTwapId: z.ZodOptional<z.ZodString>;
367
+ triggerPrice: z.ZodOptional<z.ZodString>;
368
+ triggerDirection: z.ZodOptional<z.ZodEnum<{
369
+ ABOVE: "ABOVE";
370
+ BELOW: "BELOW";
371
+ }>>;
372
+ conditionalExpiry: z.ZodOptional<z.ZodISODateTime>;
369
373
  }, z.core.$strip>;
370
374
  }, z.core.$strip>;
371
375
  export declare const CancelTwapOrderSchema: z.ZodObject<{
@@ -386,6 +386,8 @@ const TwapDurationSecondsSchema = z.union([
386
386
  * Validates parameters for creating a TWAP parent order. The execution
387
387
  * window is mandatory: exactly one of durationSeconds or endTime.
388
388
  */
389
+ /** Maximum TWAP window, and therefore the cap on `conditionalExpiry` (30 days). */
390
+ const MAX_TWAP_WINDOW_MS = 30 * 24 * 60 * 60 * 1000;
389
391
  export const CreateTwapOrderSchema = z
390
392
  .object({
391
393
  tradingPairId: UUIDSchema,
@@ -398,6 +400,7 @@ export const CreateTwapOrderSchema = z
398
400
  limitPrice: PositiveDecimalStringSchema.optional(),
399
401
  slippageToleranceBps: TwapSlippageToleranceBpsSchema.optional(),
400
402
  randomize: z.boolean().optional(),
403
+ executionStyle: z.enum(["TAKER", "PASSIVE"]).optional(),
401
404
  reduceOnly: z.boolean().optional(),
402
405
  tradingMode: TradingModeSchema.optional(),
403
406
  marginAccountId: UUIDSchema.optional(),
@@ -407,6 +410,9 @@ export const CreateTwapOrderSchema = z
407
410
  strategyKey: z.string().min(1).max(128).optional(),
408
411
  useMasterBalance: z.boolean().optional(),
409
412
  clientTwapId: z.string().min(1).max(36, "clientTwapId cannot exceed 36 characters").optional(),
413
+ triggerPrice: PositiveDecimalStringSchema.optional(),
414
+ triggerDirection: z.enum(["ABOVE", "BELOW"]).optional(),
415
+ conditionalExpiry: ISO8601DateSchema.optional(),
410
416
  }),
411
417
  })
412
418
  .refine((data) => (data.options.durationSeconds !== undefined) !== (data.options.endTime !== undefined), {
@@ -436,6 +442,48 @@ export const CreateTwapOrderSchema = z
436
442
  .refine((data) => data.options.tradingMode !== "MARGIN" || data.options.reduceOnly === true || data.options.leverage !== undefined, {
437
443
  message: "leverage is required for normal MARGIN TWAP orders",
438
444
  path: ["options", "leverage"],
445
+ })
446
+ .refine((data) => (data.options.triggerPrice !== undefined) === (data.options.triggerDirection !== undefined), {
447
+ message: "triggerPrice and triggerDirection are both-or-neither",
448
+ path: ["options", "triggerDirection"],
449
+ })
450
+ .refine((data) => data.options.triggerPrice === undefined || data.options.durationSeconds !== undefined, {
451
+ message: "conditional TWAP (triggerPrice) requires durationSeconds — the window floats from the trigger instant",
452
+ path: ["options", "durationSeconds"],
453
+ })
454
+ .refine((data) => data.options.triggerPrice === undefined || (data.options.startTime === undefined && data.options.endTime === undefined), {
455
+ message: "triggerPrice is incompatible with startTime/endTime",
456
+ path: ["options", "triggerPrice"],
457
+ })
458
+ .refine((data) => data.options.conditionalExpiry === undefined || data.options.triggerPrice !== undefined, {
459
+ message: "conditionalExpiry is only valid together with triggerPrice",
460
+ path: ["options", "conditionalExpiry"],
461
+ })
462
+ // The two VALUE rules on conditionalExpiry, mirroring the server. Without
463
+ // them the schema only checks the timestamp's shape and every deadline in
464
+ // the past — or beyond the maximum window — round-trips to a 400. The
465
+ // UTC-suffix rule stays server-side: ISO8601DateSchema deliberately accepts
466
+ // any valid offset, and narrowing it here would change an unrelated shared
467
+ // schema.
468
+ .refine((data) => {
469
+ const raw = data.options.conditionalExpiry;
470
+ if (raw === undefined)
471
+ return true;
472
+ const at = Date.parse(raw);
473
+ return Number.isNaN(at) || at > Date.now();
474
+ }, {
475
+ message: "conditionalExpiry must be in the future",
476
+ path: ["options", "conditionalExpiry"],
477
+ })
478
+ .refine((data) => {
479
+ const raw = data.options.conditionalExpiry;
480
+ if (raw === undefined)
481
+ return true;
482
+ const at = Date.parse(raw);
483
+ return Number.isNaN(at) || at - Date.now() <= MAX_TWAP_WINDOW_MS;
484
+ }, {
485
+ message: "conditionalExpiry cannot be more than 30 days after creation (the maximum window)",
486
+ path: ["options", "conditionalExpiry"],
439
487
  });
440
488
  export const CancelTwapOrderSchema = z.object({
441
489
  twapOrderId: UUIDSchema,
@@ -0,0 +1,59 @@
1
+ /**
2
+ * Monaco WebSocket client surface.
3
+ *
4
+ * The canonical, hand-written subscription interface implemented by
5
+ * `@0xmonaco/core`'s `createMonacoWebSocket` and exposed as `MonacoSDK.ws`.
6
+ * It lives here (not in `@0xmonaco/core`) so every package types the same
7
+ * surface — the channel-coverage lock in
8
+ * `tests/websocket/channel-coverage.test.ts` parses this file as the source
9
+ * of truth for the SDK's channel surface.
10
+ */
11
+ import type { SessionCredentials } from "../auth/index";
12
+ import type { Interval } from "../market";
13
+ import type { TradingMode } from "../trading";
14
+ import type { WebSocketStatus } from "./base";
15
+ import type { OrderbookQuotationMode } from "./clients/orderbook-client";
16
+ import type { AccountEvent, ConditionalOrderEvent, InstrumentEvent, LiquidationEvent, MarketStatsAllEvent, MarketStatsEvent, OHLCVEvent, OHLCVPriceType, OrderbookEvent, OrderEvent, PositionEvent, TradeEvent, TwapOrderEvent, UserBalanceEvent, UserMovementEvent } from "./events";
17
+ export type MessageHandler<T> = (data: T) => void;
18
+ export interface MonacoWebSocket {
19
+ /** Connect to the WebSocket server */
20
+ connect: () => Promise<void>;
21
+ /** Disconnect from the WebSocket server */
22
+ disconnect: () => void;
23
+ /** Check if currently connected */
24
+ isConnected: () => boolean;
25
+ /** Get current websocket connection status */
26
+ getStatus: () => WebSocketStatus;
27
+ /** Set (or clear) the session keypair used to authenticate the connection */
28
+ setSessionKeypair: (credentials: SessionCredentials | undefined) => void;
29
+ /** Subscribe to order events (requires authentication) */
30
+ orders: (tradingPairId: string, tradingMode: TradingMode, handler: MessageHandler<OrderEvent>) => () => void;
31
+ /** Subscribe to orderbook events (public) */
32
+ orderbook: (tradingPairId: string, tradingMode: TradingMode, magnitude: number, quotationMode: OrderbookQuotationMode, handler: MessageHandler<OrderbookEvent>) => () => void;
33
+ /** Subscribe to OHLCV candlestick events (public) */
34
+ ohlcv: (tradingPairId: string, tradingMode: TradingMode, interval: Interval, handler: MessageHandler<OHLCVEvent>, priceType?: OHLCVPriceType) => () => void;
35
+ /** Subscribe to trade events (public) */
36
+ trades: (tradingPairId: string, handler: MessageHandler<TradeEvent>) => () => void;
37
+ /** Subscribe to user movement events (requires authentication) */
38
+ movements: (handler: MessageHandler<UserMovementEvent>) => () => void;
39
+ /** Subscribe to user order events for all trading pairs (requires authentication) */
40
+ userOrders: (handler: MessageHandler<OrderEvent>) => () => void;
41
+ /** Subscribe to user balance events (requires authentication) */
42
+ balances: (handler: MessageHandler<UserBalanceEvent>) => () => void;
43
+ /** Subscribe to conditional TP/SL lifecycle events (requires authentication) */
44
+ conditionalOrders: (handler: MessageHandler<ConditionalOrderEvent>, tradingPairId?: string) => () => void;
45
+ /** Subscribe to TWAP parent-order lifecycle events (requires authentication) */
46
+ twapOrders: (handler: MessageHandler<TwapOrderEvent>, tradingPairId?: string) => () => void;
47
+ /** Subscribe to position state updates (requires authentication); omit `tradingPairId` for all pairs */
48
+ positions: (handler: MessageHandler<PositionEvent>, tradingPairId?: string) => () => void;
49
+ /** Subscribe to liquidation alerts (requires authentication); omit `tradingPairId` for all pairs */
50
+ liquidations: (handler: MessageHandler<LiquidationEvent>, tradingPairId?: string) => () => void;
51
+ /** Subscribe to account-level margin health updates for the authenticated user (requires authentication) */
52
+ account: (handler: MessageHandler<AccountEvent>) => () => void;
53
+ /** Subscribe to market lifecycle + config changes (public); omit `tradingPairId` for all markets */
54
+ instruments: (handler: MessageHandler<InstrumentEvent>, tradingPairId?: string) => () => void;
55
+ /** Subscribe to per-market statistics for one trading pair (public) */
56
+ marketStats: (tradingPairId: string, handler: MessageHandler<MarketStatsEvent>) => () => void;
57
+ /** Subscribe to the all-markets statistics summary (public) */
58
+ marketStatsAll: (handler: MessageHandler<MarketStatsAllEvent>) => () => void;
59
+ }
@@ -0,0 +1,10 @@
1
+ /**
2
+ * Monaco WebSocket client surface.
3
+ *
4
+ * The canonical, hand-written subscription interface implemented by
5
+ * `@0xmonaco/core`'s `createMonacoWebSocket` and exposed as `MonacoSDK.ws`.
6
+ * It lives here (not in `@0xmonaco/core`) so every package types the same
7
+ * surface — the channel-coverage lock in
8
+ * `tests/websocket/channel-coverage.test.ts` parses this file as the source
9
+ * of truth for the SDK's channel surface.
10
+ */
@@ -16,6 +16,24 @@
16
16
  export interface AccountEventData {
17
17
  /** Margin account this update describes (UUID) */
18
18
  marginAccountId: string;
19
+ /**
20
+ * Monotonic per-account counter within one producer run. It advances only on
21
+ * published frames, may SKIP values (at-most-once delivery, conflation), and
22
+ * resets to zero when the producer restarts — which can happen while your
23
+ * WebSocket stays connected, because the socket is owned by a different
24
+ * service. It is therefore never a gap detector and never a cross-restart
25
+ * total order.
26
+ *
27
+ * Dedupe rule: order frames per account by `updatedAt`, newest wins; use
28
+ * `sequence` only to break ties between frames carrying the SAME `updatedAt`
29
+ * (those come from the same producer run).
30
+ *
31
+ * There is no subscribe-time snapshot: an account with active exposure
32
+ * delivers a baseline within ~30s (keepalive republish), but a FLAT account
33
+ * is silent until it re-activates — fetch the margin-accounts REST endpoint
34
+ * when an immediate flat baseline is needed.
35
+ */
36
+ sequence: number;
19
37
  /** Mark-inclusive account equity: collateral + unrealized PnL + funding (decimal string) */
20
38
  equity: string;
21
39
  /** Raw deposited collateral in USD (decimal string) */
@@ -62,10 +62,21 @@ export interface CommonOrderEventData {
62
62
  /**
63
63
  * Client-assigned correlation handle, echoed so an event can be matched to
64
64
  * the request that placed the order without a round trip to `getOrder`.
65
- * Carried on every order event that inherits these fields — placed, taker and
66
- * maker fills, cancelled, rejected and expired — so a submit whose HTTP
67
- * response was lost can be resolved from the stream alone. Omitted when the
68
- * order carried none. Wire: `client_order_id`.
65
+ * Carried on every order event that inherits these fields — the `OrderPlaced`
66
+ * acknowledgement, taker and maker fills, cancellation, expiry, and the
67
+ * `OrderRejected` raised after acceptance — an IOC/FOK that cannot fill, or a
68
+ * MARKET order whose apparent liquidity is invalidated by maker-risk
69
+ * revalidation — so a matching event is positive proof that a submit whose HTTP
70
+ * response was lost did land. Two separate caveats: a create rejected *before* acceptance
71
+ * (malformed, post-only would cross, handle conflict, a plain MARKET submit
72
+ * with no liquidity) emits no event at all; and the stream is not replayable, so a subscription opened
73
+ * late or gapped misses events it would otherwise have carried — and because
74
+ * the fan-out is non-persistent Core NATS, the service can re-establish an
75
+ * ended upstream subscription without closing your socket, so events can be
76
+ * missed while the connection still looks healthy. A matching event is
77
+ * positive proof the order was accepted; silence is NEVER evidence it was
78
+ * not, and no client-observable condition makes it so. Never resubmit on
79
+ * silence. Omitted when the order carried none. Wire: `client_order_id`.
69
80
  */
70
81
  clientOrderId?: string;
71
82
  }
@@ -1,8 +1,14 @@
1
- import type { OrderSide, TradingMode, TwapOrderState } from "../../trading";
1
+ import type { OrderSide, TradingMode, TwapExecutionStyle, TwapOrderState } from "../../trading";
2
2
  /**
3
3
  * Mutation that produced a TWAP parent-order snapshot.
4
+ *
5
+ * PASSIVE-style parents add three reasons: `passive_child_placed` (a
6
+ * post-only child now rests at the touch), `child_repriced` (the resting
7
+ * child atomically moved to the new touch), and `passive_fill` (maker fills
8
+ * on the resting child folded into the parent). Taker sweeps and crossing
9
+ * takes surface as the existing `slice_placed`.
4
10
  */
5
- export type TwapOrderEventReason = "created" | "slice_placed" | "slice_skipped" | "cancelled" | "completed";
11
+ export type TwapOrderEventReason = "created" | "slice_placed" | "slice_skipped" | "passive_child_placed" | "child_repriced" | "passive_fill" | "cancelled" | "completed" | "triggered" | "trigger_expired";
6
12
  /**
7
13
  * TWAP parent-order snapshot carried by a `twap_orders` WebSocket event.
8
14
  * The server publishes the full read model on every lifecycle transition
@@ -34,6 +40,8 @@ export interface TwapOrderEventData {
34
40
  strategyKey?: string;
35
41
  clientTwapId?: string;
36
42
  randomize: boolean;
43
+ /** Child execution style: "TAKER" (banded MARKET slices) or "PASSIVE" (post-only children at the touch, taker sweep at each slice deadline) */
44
+ executionStyle: TwapExecutionStyle;
37
45
  /** Window start (ISO 8601) */
38
46
  startAt: string;
39
47
  /** Window end (ISO 8601). Execution stops hard at this time. */
@@ -50,6 +58,12 @@ export interface TwapOrderEventData {
50
58
  state: TwapOrderState;
51
59
  /** Terminal annotation, e.g. "ended with shortfall 1.5"; absent for non-terminal parents */
52
60
  terminalReason?: string;
61
+ /** Conditional configuration + activation stamp (parity with the REST read
62
+ * model). All absent on unconditional parents. */
63
+ triggerPrice?: string;
64
+ triggerDirection?: "ABOVE" | "BELOW";
65
+ conditionalExpiry?: string;
66
+ triggeredAt?: string;
53
67
  createdAt: string;
54
68
  updatedAt: string;
55
69
  cancelledAt?: string;
@@ -4,5 +4,6 @@
4
4
  * Fundamental definitions for Websocket connections, errors and events.
5
5
  */
6
6
  export * from "./base";
7
+ export * from "./client";
7
8
  export * from "./clients/orderbook-client";
8
9
  export * from "./events";
@@ -4,5 +4,6 @@
4
4
  * Fundamental definitions for Websocket connections, errors and events.
5
5
  */
6
6
  export * from "./base";
7
+ export * from "./client";
7
8
  export * from "./clients/orderbook-client";
8
9
  export * from "./events";
@@ -10,6 +10,6 @@
10
10
  * hand-written SDK surface. CI regenerates it and fails on a diff, identical to
11
11
  * the `schema.ts` wire-types tripwire (MON-1476), so it can never go stale.
12
12
  */
13
- export declare const OPERATION_IDS: readonly ["attach_position_tp_sl", "batch_cancel_all", "batch_cancel_all_by_pair", "batch_cancel_orders", "batch_close_all_positions", "batch_create_orders", "batch_replace_orders", "cancel_conditional_order", "cancel_order", "cancel_twap_order", "check_trader_code_availability", "clear_trader_code", "close_position", "create_challenge", "create_delegated_session", "create_order", "create_sub_account_limit", "create_twap_order", "delete_sub_account_limit", "get_application_config", "get_application_stats", "get_available_collateral", "get_candles", "get_conditional_order", "get_faucet_info", "get_funding_state", "get_index_price", "get_margin_account_movements", "get_margin_account_summary", "get_mark_price", "get_market_metadata", "get_market_stats", "get_my_fee_tier", "get_my_trader_code", "get_open_interest", "get_order_by_id", "get_orderbook_snapshot", "get_orders", "get_parent_margin_account_movements", "get_parent_margin_account_summary", "get_perp_market_config", "get_perp_market_summary", "get_portfolio_assets", "get_portfolio_chart", "get_portfolio_margin", "get_portfolio_performance", "get_portfolio_realized_pnl", "get_portfolio_stats", "get_portfolio_summary", "get_position", "get_position_pnl_history", "get_position_risk", "get_pulse", "get_rewards_balance", "get_screener", "get_sub_account_limits", "get_trade_by_id", "get_trader_code_info", "get_trades", "get_trading_pair_by_id", "get_twap_order", "get_user_balance_by_asset", "get_user_balances", "get_user_movements", "get_user_profile", "get_user_trades", "get_withdrawal", "health_check", "initiate_withdrawal", "list_all_funding_history", "list_application_balances", "list_application_movements", "list_application_orders", "list_application_users", "list_conditional_orders", "list_delegated_agent_owners", "list_delegated_agents", "list_funding_history", "list_funding_payments", "list_margin_accounts", "list_pending_withdrawals", "list_position_history", "list_positions", "list_sub_accounts_with_balances", "list_trading_pairs", "list_twap_orders", "mint_tokens", "refresh_session", "register_deposit_address", "replace_order", "revoke_delegated_agent", "revoke_session", "set_trader_code", "simulate_fees", "simulate_order_risk", "simulate_parent_margin_order_risk", "simulate_risk_bucket_order_risk", "submit_whitelist", "transfer_collateral_from_margin_account", "transfer_collateral_from_parent_margin_account", "transfer_collateral_to_margin_account", "transfer_collateral_to_parent_margin_account", "transfer_collateral_to_risk_bucket", "transfer_rewards", "update_sub_account_limit", "upsert_delegated_agent", "verify_signature"];
13
+ export declare const OPERATION_IDS: readonly ["attach_position_tp_sl", "batch_cancel_all", "batch_cancel_all_by_pair", "batch_cancel_orders", "batch_close_all_positions", "batch_create_orders", "batch_replace_orders", "cancel_conditional_order", "cancel_order", "cancel_twap_order", "check_trader_code_availability", "clear_trader_code", "close_position", "create_challenge", "create_delegated_session", "create_order", "create_sub_account_limit", "create_twap_order", "delete_sub_account_limit", "get_application_config", "get_application_stats", "get_available_collateral", "get_candles", "get_conditional_order", "get_faucet_info", "get_funding_state", "get_index_price", "get_margin_account_movements", "get_margin_account_summary", "get_mark_price", "get_market_metadata", "get_market_stats", "get_my_fee_tier", "get_my_trader_code", "get_open_interest", "get_order_by_id", "get_orderbook_snapshot", "get_orders", "get_parent_margin_account_movements", "get_parent_margin_account_summary", "get_perp_market_config", "get_perp_market_summary", "get_portfolio_assets", "get_portfolio_chart", "get_portfolio_margin", "get_portfolio_performance", "get_portfolio_realized_pnl", "get_portfolio_stats", "get_portfolio_summary", "get_position", "get_position_pnl_history", "get_position_risk", "get_pulse", "get_rewards_balance", "get_screener", "get_sub_account_limits", "get_trade_by_id", "get_trader_code_info", "get_trades", "get_trading_pair_by_id", "get_twap_order", "get_twap_order_tca", "get_user_balance_by_asset", "get_user_balances", "get_user_movements", "get_user_profile", "get_user_trades", "get_withdrawal", "health_check", "initiate_withdrawal", "list_all_funding_history", "list_application_balances", "list_application_movements", "list_application_orders", "list_application_users", "list_conditional_orders", "list_delegated_agent_owners", "list_delegated_agents", "list_funding_history", "list_funding_payments", "list_margin_accounts", "list_pending_withdrawals", "list_position_history", "list_positions", "list_sub_accounts_with_balances", "list_trading_pairs", "list_twap_orders", "mint_tokens", "refresh_session", "register_deposit_address", "replace_order", "revoke_delegated_agent", "revoke_session", "set_trader_code", "simulate_fees", "simulate_order_risk", "simulate_parent_margin_order_risk", "simulate_risk_bucket_order_risk", "submit_whitelist", "transfer_collateral_from_margin_account", "transfer_collateral_from_parent_margin_account", "transfer_collateral_to_margin_account", "transfer_collateral_to_parent_margin_account", "transfer_collateral_to_risk_bucket", "transfer_rewards", "update_sub_account_limit", "upsert_delegated_agent", "verify_signature"];
14
14
  /** Union of every OpenAPI operationId in the spec. */
15
15
  export type OperationId = (typeof OPERATION_IDS)[number];
@@ -72,6 +72,7 @@ export const OPERATION_IDS = [
72
72
  "get_trades",
73
73
  "get_trading_pair_by_id",
74
74
  "get_twap_order",
75
+ "get_twap_order_tca",
75
76
  "get_user_balance_by_asset",
76
77
  "get_user_balances",
77
78
  "get_user_movements",
@@ -1505,8 +1505,16 @@ export interface paths {
1505
1505
  * Failure semantics: an item that fails validation, or whose original
1506
1506
  * cannot be found, leaves the original resting. An item whose replacement
1507
1507
  * fails to PLACE (for example insufficient balance or self-trade
1508
- * prevention) reports the error with its original already cancelled — the
1509
- * original is never silently left resting after a failed replacement.
1508
+ * prevention) reports the error with its original already cancelled.
1509
+ *
1510
+ * POST_ONLY_WOULD_CROSS is the exception: a post-only replacement that
1511
+ * would cross is refused BEFORE the cancel pass, against the book as it
1512
+ * will stand once the batch's own cancels are applied, so the item
1513
+ * normally reports the error with its original still resting. It is not a
1514
+ * guarantee — the pre-cancel check cannot see what pass 2 will insert or
1515
+ * consume, nor an original that fails to cancel, and the same original
1516
+ * listed twice is judged per occurrence — so confirm against open orders
1517
+ * rather than relying on the code alone.
1510
1518
  */
1511
1519
  post: operations["batch_replace_orders"];
1512
1520
  delete?: never;
@@ -1652,6 +1660,37 @@ export interface paths {
1652
1660
  patch?: never;
1653
1661
  trace?: never;
1654
1662
  };
1663
+ "/api/v1/orders/twap/{twapOrderId}/tca": {
1664
+ parameters: {
1665
+ query?: never;
1666
+ header?: never;
1667
+ path?: never;
1668
+ cookie?: never;
1669
+ };
1670
+ /**
1671
+ * Get TWAP order TCA report
1672
+ * @description Get TWAP execution quality (TCA) report.
1673
+ *
1674
+ * Post-execution transaction cost analysis for a TERMINAL (COMPLETED or
1675
+ * CANCELLED) TWAP parent: the realized average price measured against the
1676
+ * arrival price and the interval-TWAP benchmark over the parent's ACTIVE
1677
+ * window (start to terminal time). Benchmark inputs are computed once by
1678
+ * the persistor when the terminal row is projected, from per-minute mark
1679
+ * history where the pair is marked, falling back to 1m candle closes;
1680
+ * when neither series covers the window sufficiently the benchmark fields
1681
+ * are null rather than approximate. Sign convention: positive bps means
1682
+ * execution BEAT the reference. Users can only access their own TWAP
1683
+ * orders; a non-terminal parent is a 400.
1684
+ */
1685
+ get: operations["get_twap_order_tca"];
1686
+ put?: never;
1687
+ post?: never;
1688
+ delete?: never;
1689
+ options?: never;
1690
+ head?: never;
1691
+ patch?: never;
1692
+ trace?: never;
1693
+ };
1655
1694
  "/api/v1/orders/{orderId}": {
1656
1695
  parameters: {
1657
1696
  query?: never;
@@ -2759,7 +2798,7 @@ export interface components {
2759
2798
  */
2760
2799
  postOnly?: boolean | null;
2761
2800
  /**
2762
- * @description Client-assigned correlation handle, echoed back on order reads (detail and list) and on order WebSocket events. At most 64 characters from [A-Za-z0-9._:-]; surrounding whitespace is trimmed before storage, and a blank or whitespace-only value is treated as absent. Monaco stores and returns this value but does NOT de-duplicate on it and does not enforce uniqueness — it correlates a response to a request, it does not make a retry safe. A create whose response was lost must still be reconciled against ListOrders before resubmitting.
2801
+ * @description Client-assigned correlation handle, echoed back on order reads (detail and list) and on order WebSocket events. At most 64 characters from [A-Za-z0-9._:-]; surrounding whitespace is trimmed before storage, and a blank or whitespace-only value is treated as absent. Uniqueness is enforced only among your orders that are RESTING on the book: an item reusing a value another of your resting orders still holds fails that item alone — the batch itself returns 2xx / OK and the offending item's result slot carries error.code CLIENT_ORDER_ID_CONFLICT; no transport-level 409 / ALREADY_EXISTS is raised. An order that never rests — MARKET, IOC/FOK, or a LIMIT that fills completely on arrival — does not take the handle, and the value is released the moment any order reaches a terminal state. It is therefore NOT an idempotency key: resubmitting after a lost response can execute a second time. Reconcile a lost response against the order WebSocket stream, which carries this value on the submit acknowledgement and on fills, including for orders that never rest; order history is replica-backed and can omit an order that did land. The stream is not replayable and has no sequence number to backfill from, and its fan-out is non-persistent Core NATS: the service can re-establish an ended upstream subscription without closing your socket, so events can be missed while your connection still looks healthy. A matching event is therefore positive proof the order was accepted, but silence is NEVER evidence it was not, and no client-observable condition makes it so. Absent positive evidence the outcome is unresolved: hold and escalate rather than resubmitting. Rejections split in two: an item rejected before acceptance (malformed, post-only would cross, handle conflict, a plain MARKET submit with no liquidity, rejected during pricing) fails only that item — a per-item error in an otherwise-successful response — emits no event and is never written to order history (an item rejected AFTER acceptance is persisted with status REJECTED, so only the pre-acceptance class is absent); an accepted LIMIT with IOC or FOK that cannot fill, or an accepted MARKET order whose apparent liquidity is invalidated by maker-risk revalidation, reports matchResult.status REJECTED in its result slot and emits OrderPlaced then OrderRejected, both carrying this value. Neither the batch's 2xx nor a populated result slot therefore means the order executed. Where no surface is conclusive, do not resubmit.
2763
2802
  * @example quote-btc-1130-a
2764
2803
  */
2765
2804
  clientOrderId?: string | null;
@@ -2838,7 +2877,7 @@ export interface components {
2838
2877
  */
2839
2878
  postOnly?: boolean | null;
2840
2879
  /**
2841
- * @description Client-assigned correlation handle, echoed back on order reads (detail and list) and on order WebSocket events. At most 64 characters from [A-Za-z0-9._:-]; surrounding whitespace is trimmed before storage, and a blank or whitespace-only value is treated as absent. Monaco stores and returns this value but does NOT de-duplicate on it and does not enforce uniqueness — it correlates a response to a request, it does not make a retry safe. A create whose response was lost must still be reconciled against ListOrders before resubmitting. A replacement is a new order with a new id, so it carries its own value and inherits nothing from the original — restate it to keep the handle.
2880
+ * @description Client-assigned correlation handle, echoed back on order reads (detail and list) and on order WebSocket events. At most 64 characters from [A-Za-z0-9._:-]; surrounding whitespace is trimmed before storage, and a blank or whitespace-only value is treated as absent. Uniqueness is enforced only among your orders that are RESTING on the book: an item reusing a value another of your resting orders still holds fails that item alone — the batch itself returns 2xx / OK and the offending item's result slot carries error.code CLIENT_ORDER_ID_CONFLICT; no transport-level 409 / ALREADY_EXISTS is raised. An order that never rests — MARKET, IOC/FOK, or a LIMIT that fills completely on arrival — does not take the handle, and the value is released the moment any order reaches a terminal state. It is therefore NOT an idempotency key, though on this replace path a retry cannot double-execute: the request names the original order id, and the sequencer revalidates that original in memory and on the book at sequence time, so once the first replacement succeeds the original is gone and a retry is rejected with ORDER_NOT_FOUND rather than placing a second order. That code is itself ambiguous — the original may instead have filled or been cancelled — so the risk here is acting on a stale view, not duplicate execution. Reconcile a lost response against the order WebSocket stream, which carries this value on the submit acknowledgement and on fills, including for orders that never rest; order history is replica-backed and can omit an order that did land. The stream is not replayable and has no sequence number to backfill from, and its fan-out is non-persistent Core NATS: the service can re-establish an ended upstream subscription without closing your socket, so events can be missed while your connection still looks healthy. A matching event is therefore positive proof the order was accepted, but silence is NEVER evidence it was not, and no client-observable condition makes it so. Absent positive evidence the outcome is unresolved: hold and escalate rather than resubmitting. A replace item carries no order type or time-in-force and its original must still be RESTING, so the IOC/FOK and MARKET post-acceptance rejection shapes are unreachable here. Failures split by WHEN they occur, and the difference decides whether your original survives. Before sequencing: a malformed value is caught up front and rejects the whole batch with HTTP 400 / gRPC INVALID_ARGUMENT, and a per-item validation failure fails that item with no mutation and no event. A pre-cancel gate then drops two more classes before anything is cancelled — a handle conflict, and a post-only replacement that would cross the book as it will stand once this batch's originals are cancelled — so in the ordinary POST_ONLY_WOULD_CROSS case the item fails with its original still RESTING and untouched. After that the endpoint is CANCEL-FIRST: the original is cancelled in pass 1, so a residual placement failure then — a self-trade rejection, or a crossing that only became apparent in pass 2 — leaves the original CANCELLED and emits its cancellation events (terminal reason 'replaced') while the item reports the placement error and no replacement is placed. Never assume a failed item left your original resting; read results[] and the stream. Single ReplaceOrder differs: it is atomic, and a rejected replacement leaves the original untouched. Where no surface is conclusive, do not resubmit. A replacement is a new order with a new id, so it carries its own value and inherits nothing from the original — restate it to keep the handle.
2842
2881
  * @example quote-btc-1130-a
2843
2882
  */
2844
2883
  clientOrderId?: string | null;
@@ -3265,7 +3304,7 @@ export interface components {
3265
3304
  */
3266
3305
  postOnly?: boolean | null;
3267
3306
  /**
3268
- * @description Client-assigned correlation handle, echoed back on order reads (detail and list) and on order WebSocket events. At most 64 characters from [A-Za-z0-9._:-]; surrounding whitespace is trimmed before storage, and a blank or whitespace-only value is treated as absent. Monaco stores and returns this value but does NOT de-duplicate on it and does not enforce uniqueness — it correlates a response to a request, it does not make a retry safe. A create whose response was lost must still be reconciled against ListOrders before resubmitting.
3307
+ * @description Client-assigned correlation handle, echoed back on order reads (detail and list) and on order WebSocket events. At most 64 characters from [A-Za-z0-9._:-]; surrounding whitespace is trimmed before storage, and a blank or whitespace-only value is treated as absent. Uniqueness is enforced only among your orders that are RESTING on the book: a create or replace reusing a value another of your resting orders still holds is rejected with HTTP 409 / gRPC ALREADY_EXISTS and the code CLIENT_ORDER_ID_CONFLICT. An order that never rests — MARKET, IOC/FOK, or a LIMIT that fills completely on arrival — does not take the handle, and the value is released the moment any order reaches a terminal state. It is therefore NOT an idempotency key: resubmitting after a lost response can execute a second time. Reconcile a lost response against the order WebSocket stream, which carries this value on the submit acknowledgement and on fills, including for orders that never rest; order history is replica-backed and can omit an order that did land. The stream is not replayable and has no sequence number to backfill from, and its fan-out is non-persistent Core NATS: the service can re-establish an ended upstream subscription without closing your socket, so events can be missed while your connection still looks healthy. A matching event is therefore positive proof the order was accepted, but silence is NEVER evidence it was not, and no client-observable condition makes it so. Absent positive evidence the outcome is unresolved: hold and escalate rather than resubmitting. Rejections split in two: one rejected before acceptance (malformed, post-only would cross, handle conflict, a plain MARKET submit with no liquidity, rejected during pricing) fails the request, emits no event and is never written to order history (an order rejected AFTER acceptance is persisted with status REJECTED, so only the pre-acceptance class is absent); an accepted LIMIT with IOC or FOK that cannot fill, or an accepted MARKET order whose apparent liquidity is invalidated by maker-risk revalidation, returns 2xx with matchResult.status REJECTED and emits OrderPlaced then OrderRejected, both carrying this value. A 2xx therefore does not mean the order executed. Where no surface is conclusive, do not resubmit.
3269
3308
  * @example quote-btc-1130-a
3270
3309
  */
3271
3310
  clientOrderId?: string | null;
@@ -3413,6 +3452,28 @@ export interface components {
3413
3452
  * @example oms-3f6a1c
3414
3453
  */
3415
3454
  clientTwapId?: string | null;
3455
+ /**
3456
+ * @description Child execution style. TAKER (default): each slice executes immediately as a banded MARKET order. PASSIVE: each slice rests as a post-only limit at the same-side touch (maker where possible, repriced to follow the touch, never beyond the parent limit price or the protective band), and the leftover is swept as a banded MARKET order at the slice deadline. Passive parents use a slower default slice cadence (~60s).
3457
+ * @example PASSIVE
3458
+ * @enum {string|null}
3459
+ */
3460
+ executionStyle?: "TAKER" | "PASSIVE" | null;
3461
+ /**
3462
+ * @description Price-triggered activation: the parent waits (PENDING) until the reference price reaches or crosses this price in triggerDirection, then runs a floating window of durationSeconds from the trigger instant. Requires triggerDirection and durationSeconds; incompatible with startTime/endTime. Rejected if the condition is already met at creation. Margin markets arm against the oracle mark and the create is rejected while the market has no mark yet (retry once one is published); once the parent is waiting, activation evaluates the mark when it is available and the venue reference price (book-derived median, last-trade fallback) when it is not, so a mark outage cannot leave a live trigger unwatched. Spot markets use the venue reference price throughout.
3463
+ * @example 98000.00
3464
+ */
3465
+ triggerPrice?: string | null;
3466
+ /**
3467
+ * @description Which way the reference price must cross triggerPrice to activate: ABOVE (activate when price rises to or through it) or BELOW (falls to or through it). Required with triggerPrice; no exact print is required — a reference price observed at or beyond the level activates. The condition is evaluated on the engine's own polling cadence rather than on every market tick, so a price that crosses and reverts entirely between two observations is not seen; there is no high/low watermark.
3468
+ * @example BELOW
3469
+ * @enum {string|null}
3470
+ */
3471
+ triggerDirection?: "ABOVE" | "BELOW" | null;
3472
+ /**
3473
+ * @description Deadline for the trigger to fire (RFC 3339, UTC only — Z suffix; non-UTC offsets rejected). If the trigger has not fired by then the parent cancels with terminal reason 'trigger expired'. Only valid with triggerPrice; must be in the future and at most the maximum window (default 30 days) from creation, which is also the default when omitted.
3474
+ * @example 2026-09-01T00:00:00Z
3475
+ */
3476
+ conditionalExpiry?: string | null;
3416
3477
  };
3417
3478
  CreateTwapOrderResponse: {
3418
3479
  /**
@@ -3432,7 +3493,7 @@ export interface components {
3432
3493
  */
3433
3494
  message?: string | null;
3434
3495
  /**
3435
- * @description Initial parent state: always PENDING at create; the parent activates on its first attempted slice
3496
+ * @description Initial parent state: always PENDING at create. An unconditional parent activates on its first attempted slice; a conditional one (triggerPrice set) stays PENDING until its trigger fires.
3436
3497
  * @example PENDING
3437
3498
  */
3438
3499
  state?: string | null;
@@ -3453,12 +3514,12 @@ export interface components {
3453
3514
  */
3454
3515
  plannedSlices?: number | null;
3455
3516
  /**
3456
- * @description Planned time of the first slice (ISO 8601)
3517
+ * @description Planned time of the first slice (ISO 8601). PROVISIONAL for a conditional parent (triggerPrice set): its window floats from the trigger instant, which is unknown at create, so this is computed from the provisional [create, create + durationSeconds] window and is re-anchored when the trigger fires. Read the armed window back from the TWAP read model (startAt / endAt / triggeredAt) instead of scheduling on this value.
3457
3518
  * @example 2026-08-01T10:00:27Z
3458
3519
  */
3459
3520
  firstSliceAt?: string | null;
3460
3521
  /**
3461
- * @description Resolved window end (ISO 8601). Execution never continues past it.
3522
+ * @description Resolved window end (ISO 8601). Execution never continues past it. PROVISIONAL for a conditional parent (triggerPrice set) for the same reason as firstSliceAt: the real window is [trigger instant, trigger instant + durationSeconds] and is re-anchored on activation.
3462
3523
  * @example 2026-08-01T11:00:00Z
3463
3524
  */
3464
3525
  endTime?: string | null;
@@ -4368,7 +4429,7 @@ export interface components {
4368
4429
  */
4369
4430
  remainingQuantityRaw?: string | null;
4370
4431
  /**
4371
- * @description Client-assigned correlation handle as stored: the value supplied on create or replace, with surrounding whitespace trimmed. Absent when the order carried none, including every order placed before the field was accepted on write. Not unique: Monaco neither de-duplicates nor enforces uniqueness, so several orders may share one value and a lookup by it can be ambiguous.
4432
+ * @description Client-assigned correlation handle as stored: the value supplied on create or replace, with surrounding whitespace trimmed. Absent when the order carried none, including every order placed before the field was accepted on write. Uniqueness is enforced only among your orders that are RESTING on the book, so several of your orders may share one value over time and a lookup by it can be ambiguous: an order that never rests never takes the handle, and the value is released the moment any order reaches a terminal state. It is a correlation handle, not an idempotency key.
4372
4433
  * @example quote-btc-1130-a
4373
4434
  */
4374
4435
  clientOrderId?: string | null;
@@ -4984,6 +5045,83 @@ export interface components {
4984
5045
  GetTradingPairResponse: {
4985
5046
  tradingPair?: components["schemas"]["TradingPairData"];
4986
5047
  };
5048
+ GetTwapOrderTcaResponse: {
5049
+ /**
5050
+ * Format: uuid
5051
+ * @description TWAP parent order UUID
5052
+ */
5053
+ twapOrderId?: string | null;
5054
+ /**
5055
+ * @description Terminal state the report covers: COMPLETED or CANCELLED
5056
+ * @example COMPLETED
5057
+ */
5058
+ state?: string | null;
5059
+ /**
5060
+ * @description Parent side: BUY or SELL (fixes the sign convention)
5061
+ * @example BUY
5062
+ */
5063
+ side?: string | null;
5064
+ /**
5065
+ * @description Active-window start (UTC, RFC 3339) — the measurement window's left edge
5066
+ * @example 2026-08-25T10:00:00Z
5067
+ */
5068
+ windowStartAt?: string | null;
5069
+ /**
5070
+ * @description Terminal time (completion or cancellation; UTC) — the measurement window's right edge. Never the planned end: a cancelled parent stopped trading here.
5071
+ * @example 2026-08-25T11:00:00Z
5072
+ */
5073
+ windowTerminalAt?: string | null;
5074
+ /**
5075
+ * @description Quantity filled over the window, as decimal string
5076
+ * @example 118.4
5077
+ */
5078
+ executedQuantity?: string | null;
5079
+ /**
5080
+ * @description Unfilled remainder (totalQuantity - executedQuantity, floored at zero)
5081
+ * @example 1.6
5082
+ */
5083
+ shortfallQuantity?: string | null;
5084
+ /**
5085
+ * @description Volume-weighted realized price (executedNotional / executedQuantity), GROSS of fees — the price the fills traded at, not the net cost after taker fees or maker rebates. Compared against price benchmarks, so fees are deliberately excluded. Null when nothing executed.
5086
+ * @example 2001.35
5087
+ */
5088
+ realizedAvgPrice?: string | null;
5089
+ /**
5090
+ * @description Reference price at (or immediately before) the window start, from benchmarkSource. Null when price history could not cover the window.
5091
+ * @example 2000.00
5092
+ */
5093
+ arrivalPrice?: string | null;
5094
+ /**
5095
+ * @description Average reference price over the active window, from benchmarkSource. Null when price history could not cover the window.
5096
+ * @example 2002.10
5097
+ */
5098
+ benchmarkTwapPrice?: string | null;
5099
+ /**
5100
+ * @description Price series both benchmark values came from: risk_mark_1m (marked pairs) or ohlcv_1m_close (spot fallback). Null when no series qualified.
5101
+ * @example risk_mark_1m
5102
+ */
5103
+ benchmarkSource?: string | null;
5104
+ /**
5105
+ * @description Signed basis points vs the arrival price; POSITIVE means execution beat it (BUY: bought below arrival; SELL: sold above). Null when realized or arrival is null.
5106
+ * @example -6.75
5107
+ */
5108
+ slippageVsArrivalBps?: string | null;
5109
+ /**
5110
+ * @description Signed basis points vs the window-TWAP benchmark; POSITIVE means execution beat the benchmark. Null when realized or benchmark is null.
5111
+ * @example 3.75
5112
+ */
5113
+ performanceVsBenchmarkBps?: string | null;
5114
+ /**
5115
+ * Format: uuid
5116
+ * @description Trading pair the parent executed on. Present so a report is self-describing: prices and bps are meaningless without the instrument, and a dashboard should not need a second lookup to label them.
5117
+ */
5118
+ tradingPairId?: string | null;
5119
+ /**
5120
+ * @description Parent order quantity, as decimal string. With executedQuantity and shortfallQuantity this closes the fill-rate arithmetic without a second lookup.
5121
+ * @example 120
5122
+ */
5123
+ totalQuantity?: string | null;
5124
+ };
4987
5125
  GetUserTradesResponse: {
4988
5126
  trades?: components["schemas"]["UserTrade"][] | null;
4989
5127
  /**
@@ -6347,7 +6485,7 @@ export interface components {
6347
6485
  };
6348
6486
  ReplaceOrderRequest: {
6349
6487
  /**
6350
- * @description Client-assigned correlation handle, echoed back on order reads (detail and list) and on order WebSocket events. At most 64 characters from [A-Za-z0-9._:-]; surrounding whitespace is trimmed before storage, and a blank or whitespace-only value is treated as absent. Monaco stores and returns this value but does NOT de-duplicate on it and does not enforce uniqueness — it correlates a response to a request, it does not make a retry safe. A create whose response was lost must still be reconciled against ListOrders before resubmitting. A replacement is a new order with a new id, so it carries its own value and inherits nothing from the original — restate it to keep the handle.
6488
+ * @description Client-assigned correlation handle, echoed back on order reads (detail and list) and on order WebSocket events. At most 64 characters from [A-Za-z0-9._:-]; surrounding whitespace is trimmed before storage, and a blank or whitespace-only value is treated as absent. Uniqueness is enforced only among your orders that are RESTING on the book: a create or replace reusing a value another of your resting orders still holds is rejected with HTTP 409 / gRPC ALREADY_EXISTS and the code CLIENT_ORDER_ID_CONFLICT. An order that never rests — MARKET, IOC/FOK, or a LIMIT that fills completely on arrival — does not take the handle, and the value is released the moment any order reaches a terminal state. It is therefore NOT an idempotency key, though on this replace path a retry cannot double-execute: the request names the original order id, and the sequencer revalidates that original in memory and on the book at sequence time, so once the first replacement succeeds the original is gone and a retry is rejected with ORDER_NOT_FOUND rather than placing a second order. That code is itself ambiguous — the original may instead have filled or been cancelled — so the risk here is acting on a stale view, not duplicate execution. Reconcile a lost response against the order WebSocket stream, which carries this value on the submit acknowledgement and on fills, including for orders that never rest; order history is replica-backed and can omit an order that did land. The stream is not replayable and has no sequence number to backfill from, and its fan-out is non-persistent Core NATS: the service can re-establish an ended upstream subscription without closing your socket, so events can be missed while your connection still looks healthy. A matching event is therefore positive proof the order was accepted, but silence is NEVER evidence it was not, and no client-observable condition makes it so. Absent positive evidence the outcome is unresolved: hold and escalate rather than resubmitting. A replacement carries no order type or time-in-force and its original must still be RESTING, so the post-acceptance rejection shapes are unreachable here: the reachable failures are all pre-acceptance — malformed, post-only would cross, handle conflict, or the original no longer resting (ORDER_NOT_FOUND) — and each fails the request, emits no event and is never written to order history (an order rejected AFTER acceptance is persisted with status REJECTED, so only the pre-acceptance class is absent). Where no surface is conclusive, do not resubmit. A replacement is a new order with a new id, so it carries its own value and inherits nothing from the original — restate it to keep the handle.
6351
6489
  * @example quote-btc-1130-a
6352
6490
  */
6353
6491
  clientOrderId?: string | null;
@@ -7352,6 +7490,19 @@ export interface components {
7352
7490
  cancelledAt?: string | null;
7353
7491
  /** @description Completion timestamp (ISO 8601) */
7354
7492
  completedAt?: string | null;
7493
+ /**
7494
+ * @description Child execution style: TAKER (banded MARKET slices) or PASSIVE (post-only children at the touch with a taker sweep at each slice deadline)
7495
+ * @example TAKER
7496
+ */
7497
+ executionStyle?: string | null;
7498
+ /** @description Price-triggered activation level, when this parent is conditional */
7499
+ triggerPrice?: string | null;
7500
+ /** @description Trigger crossing direction: ABOVE or BELOW */
7501
+ triggerDirection?: string | null;
7502
+ /** @description Deadline for the trigger to fire (ISO 8601 UTC); reaching it cancels the parent with terminal reason 'trigger expired' */
7503
+ conditionalExpiry?: string | null;
7504
+ /** @description When the trigger fired and the floating window started (ISO 8601 UTC); unset while waiting */
7505
+ triggeredAt?: string | null;
7355
7506
  };
7356
7507
  UpdateLimitRequest: {
7357
7508
  /**
@@ -11030,6 +11181,56 @@ export interface operations {
11030
11181
  };
11031
11182
  };
11032
11183
  };
11184
+ get_twap_order_tca: {
11185
+ parameters: {
11186
+ query?: never;
11187
+ header?: never;
11188
+ path: {
11189
+ twapOrderId: string;
11190
+ };
11191
+ cookie?: never;
11192
+ };
11193
+ requestBody?: never;
11194
+ responses: {
11195
+ /** @description OK */
11196
+ 200: {
11197
+ headers: {
11198
+ [name: string]: unknown;
11199
+ };
11200
+ content: {
11201
+ "application/json": components["schemas"]["GetTwapOrderTcaResponse"];
11202
+ };
11203
+ };
11204
+ /** @description Invalid TWAP order ID format, or the order has not reached a terminal state yet */
11205
+ 400: {
11206
+ headers: {
11207
+ [name: string]: unknown;
11208
+ };
11209
+ content?: never;
11210
+ };
11211
+ /** @description Authentication required or delegated-agent session (rejected for TWAP) */
11212
+ 401: {
11213
+ headers: {
11214
+ [name: string]: unknown;
11215
+ };
11216
+ content?: never;
11217
+ };
11218
+ /** @description TWAP order not found */
11219
+ 404: {
11220
+ headers: {
11221
+ [name: string]: unknown;
11222
+ };
11223
+ content?: never;
11224
+ };
11225
+ /** @description Internal server error */
11226
+ 500: {
11227
+ headers: {
11228
+ [name: string]: unknown;
11229
+ };
11230
+ content?: never;
11231
+ };
11232
+ };
11233
+ };
11033
11234
  get_order_by_id: {
11034
11235
  parameters: {
11035
11236
  query?: never;
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@0xmonaco/types",
3
- "version": "1.0.45",
3
+ "version": "1.0.47",
4
4
  "type": "module",
5
5
  "homepage": "https://docs.0xmonaco.com/sdk/typescript",
6
6
  "main": "./dist/index.js",
@@ -16,7 +16,7 @@
16
16
  "lint": "biome lint ."
17
17
  },
18
18
  "dependencies": {
19
- "@0xmonaco/contracts": "1.0.45",
19
+ "@0xmonaco/contracts": "1.0.47",
20
20
  "zod": "^4.1.12"
21
21
  },
22
22
  "peerDependencies": {