@0xmonaco/types 1.1.2 → 1.1.5-prerelease.de852e2

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.
Files changed (54) hide show
  1. package/README.md +1 -1
  2. package/dist/analytics/builders.d.ts +120 -0
  3. package/dist/analytics/builders.js +11 -0
  4. package/dist/analytics/index.d.ts +131 -0
  5. package/dist/analytics/index.js +22 -0
  6. package/dist/analytics/wallets.d.ts +193 -0
  7. package/dist/analytics/wallets.js +16 -0
  8. package/dist/applications/requests.d.ts +5 -5
  9. package/dist/buildercodes/index.d.ts +13 -11
  10. package/dist/buildercodes/responses.d.ts +1 -0
  11. package/dist/buildercodes/responses.js +10 -0
  12. package/dist/fees/responses.js +2 -2
  13. package/dist/index.d.ts +2 -0
  14. package/dist/index.js +2 -0
  15. package/dist/margin-accounts/index.d.ts +7 -2
  16. package/dist/market/index.d.ts +51 -2
  17. package/dist/profile/index.d.ts +25 -2
  18. package/dist/rfq/index.d.ts +44 -0
  19. package/dist/rfq/index.js +10 -0
  20. package/dist/rfq/responses.d.ts +276 -0
  21. package/dist/rfq/responses.js +182 -0
  22. package/dist/sdk/index.d.ts +25 -2
  23. package/dist/trading/index.d.ts +19 -4
  24. package/dist/trading/orders.d.ts +12 -1
  25. package/dist/trading/responses.d.ts +27 -2
  26. package/dist/validation/index.d.ts +1 -0
  27. package/dist/validation/index.js +1 -0
  28. package/dist/validation/margin-accounts.d.ts +2 -2
  29. package/dist/validation/margin-accounts.js +5 -5
  30. package/dist/validation/market.d.ts +7 -0
  31. package/dist/validation/market.js +4 -0
  32. package/dist/validation/rfq.d.ts +109 -0
  33. package/dist/validation/rfq.js +120 -0
  34. package/dist/validation/trading.d.ts +24 -0
  35. package/dist/validation/trading.js +55 -42
  36. package/dist/websocket/base.d.ts +47 -11
  37. package/dist/websocket/client.d.ts +51 -4
  38. package/dist/websocket/depth.d.ts +142 -0
  39. package/dist/websocket/depth.js +13 -0
  40. package/dist/websocket/events/index.d.ts +2 -0
  41. package/dist/websocket/events/index.js +2 -0
  42. package/dist/websocket/events/orderbook-events.d.ts +41 -1
  43. package/dist/websocket/events/rfq-events.d.ts +116 -0
  44. package/dist/websocket/events/rfq-events.js +15 -0
  45. package/dist/websocket/events/rfq-trade-events.d.ts +28 -0
  46. package/dist/websocket/events/rfq-trade-events.js +6 -0
  47. package/dist/websocket/events/trade-events.d.ts +53 -0
  48. package/dist/websocket/index.d.ts +1 -0
  49. package/dist/websocket/index.js +1 -0
  50. package/dist/websocket/snapshots.d.ts +23 -1
  51. package/dist/wire/operations.d.ts +1 -1
  52. package/dist/wire/operations.js +28 -0
  53. package/dist/wire/schema.d.ts +5372 -2040
  54. package/package.json +2 -2
@@ -0,0 +1,120 @@
1
+ /**
2
+ * RFQ (block trading) validation schemas.
3
+ *
4
+ * Client-side checks for the RFQ request params, run before a request is
5
+ * sent. The server stays the authority on every business rule (market size
6
+ * rules, deadlines, versions, last-look improvement); these catch malformed
7
+ * input early and mirror the request shape the REST gateway accepts, which
8
+ * rejects unknown fields.
9
+ */
10
+ import { z } from "zod";
11
+ import { MarginModeSchema, PositiveDecimalStringSchema, UUIDSchema } from "./trading.js";
12
+ /** The taker's side, or a quote's side as the taker sees it. */
13
+ export const RfqSideSchema = z.enum(["BUY", "SELL"]);
14
+ /** The market product an RFQ trades. */
15
+ export const RfqProductSchema = z.enum(["SPOT", "PERPETUAL"]);
16
+ /**
17
+ * A client idempotency key: 1-64 letters, digits, or `. _ : -`. A retry with
18
+ * the same key and body returns the first result. Omitted, the SDK generates
19
+ * one per call.
20
+ */
21
+ export const RfqIdempotencyKeySchema = z
22
+ .string()
23
+ .regex(/^[A-Za-z0-9._:-]{1,64}$/, "idempotencyKey must be 1-64 letters, digits, or . _ : - without whitespace");
24
+ /** A version a request names (`expectedVersion`, `viewVersion`). */
25
+ const VersionSchema = z.number().int("version must be an integer").min(0, "version cannot be negative");
26
+ /** A quote's price (quote units per base unit) and capacity (base units). */
27
+ export const RfqQuoteTermsSchema = z
28
+ .object({
29
+ price: PositiveDecimalStringSchema,
30
+ capacity: PositiveDecimalStringSchema,
31
+ })
32
+ .strict();
33
+ /** Params for requesting quotes. */
34
+ export const CreateRfqSchema = z
35
+ .object({
36
+ tradingPairId: UUIDSchema,
37
+ product: RfqProductSchema,
38
+ /** Maximum base quantity; not a guaranteed fill. */
39
+ quantity: PositiveDecimalStringSchema,
40
+ /**
41
+ * Omit for a two-way RFQ, whose direction stays hidden from makers. Set,
42
+ * makers see it and may quote only the side that fills it (an ask when
43
+ * you buy, a bid when you sell).
44
+ */
45
+ side: RfqSideSchema.optional(),
46
+ /**
47
+ * Perpetuals: the leverage your order takes when you accept, as on an
48
+ * order (required there, refused on spot). For a two-way RFQ it applies
49
+ * to whichever side you accept.
50
+ */
51
+ leverage: PositiveDecimalStringSchema.optional(),
52
+ /** Perpetuals: ISOLATED (the default) or CROSS, as on an order. */
53
+ marginMode: MarginModeSchema.optional(),
54
+ idempotencyKey: RfqIdempotencyKeySchema.optional(),
55
+ })
56
+ .strict();
57
+ /** Params for accepting one side of the view the taker last saw. */
58
+ export const AcceptRfqSchema = z
59
+ .object({
60
+ side: RfqSideSchema,
61
+ /** The `view.viewVersion` being accepted; a newer view refuses it. */
62
+ viewVersion: VersionSchema,
63
+ idempotencyKey: RfqIdempotencyKeySchema.optional(),
64
+ })
65
+ .strict();
66
+ /** Params for cancelling an RFQ. */
67
+ export const CancelRfqSchema = z
68
+ .object({
69
+ idempotencyKey: RfqIdempotencyKeySchema.optional(),
70
+ })
71
+ .strict();
72
+ /** Params for placing or replacing a quote: a bid, an ask or both. */
73
+ export const UpsertRfqQuoteSchema = z
74
+ .object({
75
+ /** Your current version for this RFQ (`ownLatestVersion`); 0 for your first quote. */
76
+ expectedVersion: VersionSchema,
77
+ /** You buying. */
78
+ bid: RfqQuoteTermsSchema.optional(),
79
+ /** You selling. */
80
+ ask: RfqQuoteTermsSchema.optional(),
81
+ /** 15000-60000 ms; omitted means 60000. Capped at the request deadline. */
82
+ validityMs: z
83
+ .number()
84
+ .int("validityMs must be an integer")
85
+ .min(15000, "validityMs must be at least 15000")
86
+ .max(60000, "validityMs cannot exceed 60000")
87
+ .optional(),
88
+ /**
89
+ * Perpetuals: the leverage each side's order reserves with, as on an
90
+ * order (required there, refused on spot). A quote reserves funds like a
91
+ * resting limit order; leverage, margin mode and bucket are fixed while a
92
+ * side rests, so to change them withdraw and quote again.
93
+ */
94
+ leverage: PositiveDecimalStringSchema.optional(),
95
+ /** Perpetuals: ISOLATED (the default) or CROSS, as on an order. */
96
+ marginMode: MarginModeSchema.optional(),
97
+ idempotencyKey: RfqIdempotencyKeySchema.optional(),
98
+ })
99
+ .strict()
100
+ .refine((params) => params.bid !== undefined || params.ask !== undefined, "a quote needs a bid, an ask or both");
101
+ /** Params for withdrawing your current quote. */
102
+ export const WithdrawRfqQuoteSchema = z
103
+ .object({
104
+ /** Your current version for this RFQ. */
105
+ expectedVersion: VersionSchema,
106
+ idempotencyKey: RfqIdempotencyKeySchema.optional(),
107
+ })
108
+ .strict();
109
+ /** Params for answering last look. There is no accept: silence executes. */
110
+ export const RespondRfqLastLookSchema = z
111
+ .object({
112
+ /** `ownSelection.acceptanceId` from the RFQ's status. */
113
+ acceptanceId: UUIDSchema,
114
+ response: z.enum(["REJECT", "IMPROVE"]),
115
+ /** IMPROVE only: a strictly better price or a higher capacity than the frozen terms. */
116
+ improvedTerms: RfqQuoteTermsSchema.optional(),
117
+ idempotencyKey: RfqIdempotencyKeySchema.optional(),
118
+ })
119
+ .strict()
120
+ .refine((params) => (params.response === "IMPROVE") === (params.improvedTerms !== undefined), "improvedTerms is required for IMPROVE and not allowed for REJECT");
@@ -126,6 +126,21 @@ export declare const ParentTrailingStopLegSchema: z.ZodObject<{
126
126
  slippageToleranceBps: z.ZodOptional<z.ZodNumber>;
127
127
  expiresAt: z.ZodOptional<z.ZodISODateTime>;
128
128
  }, z.core.$strip>;
129
+ /**
130
+ * An order's `strategyKey` names one of the caller's margin accounts by its
131
+ * sub-account key (`default` is the default margin account), as
132
+ * `marginAccountId` names it by id; a copy trading account's `copy:` key names
133
+ * it too, for the reduce-only close a copy account admits. The server refuses
134
+ * a key outside the sub-account key alphabet and a key naming none of the
135
+ * caller's open sub-accounts.
136
+ */
137
+ export declare const OrderStrategyKeySchema: z.ZodString;
138
+ /**
139
+ * A risk-bucket transfer's or preview's `strategyKey`: the order rule, and a
140
+ * reserved `copy:` key is refused (a copy account's risk buckets are funded
141
+ * by its follow only).
142
+ */
143
+ export declare const StrategyKeySchema: z.ZodString;
129
144
  /**
130
145
  * Place Limit Order validation schema
131
146
  */
@@ -451,6 +466,15 @@ export declare const CreateTwapOrderSchema: z.ZodObject<{
451
466
  conditionalExpiry: z.ZodOptional<z.ZodISODateTime>;
452
467
  }, z.core.$strip>;
453
468
  }, z.core.$strip>;
469
+ /** Bounds of a cancel-all-after timeout; 0 disarms. */
470
+ export declare const CANCEL_ALL_AFTER_MIN_TIMEOUT_MS = 5000;
471
+ export declare const CANCEL_ALL_AFTER_MAX_TIMEOUT_MS = 900000;
472
+ export declare const CancelAllAfterSchema: z.ZodObject<{
473
+ timeoutMs: z.ZodNumber;
474
+ options: z.ZodOptional<z.ZodObject<{
475
+ tradingPairId: z.ZodOptional<z.ZodUUID>;
476
+ }, z.core.$strict>>;
477
+ }, z.core.$strict>;
454
478
  export declare const CancelTwapOrderSchema: z.ZodObject<{
455
479
  twapOrderId: z.ZodUUID;
456
480
  }, z.core.$strip>;
@@ -171,6 +171,26 @@ export const TrailingStopLegSchema = z
171
171
  export const ParentTrailingStopLegSchema = TrailingStopLegSchema;
172
172
  const hasParentTpSl = (options) => options?.takeProfit !== undefined || options?.stopLoss !== undefined || options?.trailingStop !== undefined;
173
173
  const legacyPositionSideMatchesOrderSide = (side, positionSide) => positionSide === undefined || (positionSide !== "NONE" && ((side === "BUY" && positionSide === "LONG") || (side === "SELL" && positionSide === "SHORT")));
174
+ /**
175
+ * An order's `strategyKey` names one of the caller's margin accounts by its
176
+ * sub-account key (`default` is the default margin account), as
177
+ * `marginAccountId` names it by id; a copy trading account's `copy:` key names
178
+ * it too, for the reduce-only close a copy account admits. The server refuses
179
+ * a key outside the sub-account key alphabet and a key naming none of the
180
+ * caller's open sub-accounts.
181
+ */
182
+ export const OrderStrategyKeySchema = z
183
+ .string()
184
+ .trim()
185
+ .min(1, "strategyKey cannot be empty")
186
+ .max(64, "strategyKey must be a sub-account key: letters, digits, _ - . :, up to 64 characters")
187
+ .regex(/^[A-Za-z0-9_.:-]+$/, "strategyKey must be a sub-account key: letters, digits, _ - . :, up to 64 characters");
188
+ /**
189
+ * A risk-bucket transfer's or preview's `strategyKey`: the order rule, and a
190
+ * reserved `copy:` key is refused (a copy account's risk buckets are funded
191
+ * by its follow only).
192
+ */
193
+ export const StrategyKeySchema = OrderStrategyKeySchema.refine((key) => !key.toLowerCase().startsWith("copy:"), "strategy keys starting with copy: are reserved");
174
194
  /**
175
195
  * Place Limit Order validation schema
176
196
  */
@@ -196,7 +216,7 @@ export const PlaceLimitOrderSchema = z
196
216
  marginMode: MarginModeSchema.optional(),
197
217
  riskBucketId: UUIDSchema.optional(),
198
218
  riskBucketCollateral: PositiveDecimalStringSchema.optional(),
199
- strategyKey: z.string().min(1).max(128).optional(),
219
+ strategyKey: OrderStrategyKeySchema.optional(),
200
220
  positionSide: PositionSideSchema.optional(),
201
221
  leverage: PositiveDecimalStringSchema.optional(),
202
222
  reduceOnly: z.boolean().optional(),
@@ -216,7 +236,7 @@ export const PlaceLimitOrderSchema = z
216
236
  message: "leverage is required for normal MARGIN orders",
217
237
  path: ["options", "leverage"],
218
238
  })
219
- .refine((data) => !hasParentTpSl(data.options) || data.options?.tradingMode === "MARGIN", {
239
+ .refine((data) => !hasParentTpSl(data.options) || data.options?.tradingMode !== "SPOT", {
220
240
  message: "takeProfit/stopLoss/trailingStop are only supported for MARGIN orders",
221
241
  path: ["options"],
222
242
  })
@@ -224,17 +244,13 @@ export const PlaceLimitOrderSchema = z
224
244
  message: "takeProfit/stopLoss/trailingStop cannot be attached to reduce-only orders",
225
245
  path: ["options"],
226
246
  })
227
- .refine((data) => data.options?.riskBucketCollateral === undefined || data.options?.tradingMode === "MARGIN", {
247
+ .refine((data) => data.options?.riskBucketCollateral === undefined || data.options?.tradingMode !== "SPOT", {
228
248
  message: "riskBucketCollateral is only supported for MARGIN orders",
229
249
  path: ["options", "tradingMode"],
230
250
  })
231
- .refine((data) => data.options?.marginMode === undefined || data.options?.tradingMode === "MARGIN", {
251
+ .refine((data) => data.options?.marginMode === undefined || data.options?.tradingMode !== "SPOT", {
232
252
  message: "marginMode is only supported for MARGIN orders",
233
253
  path: ["options", "tradingMode"],
234
- })
235
- .refine((data) => data.options?.marginMode !== "CROSS" || data.options?.marginAccountId !== undefined, {
236
- message: "marginAccountId is required for CROSS margin orders",
237
- path: ["options", "marginAccountId"],
238
254
  })
239
255
  .refine((data) => data.options?.marginMode !== "CROSS" || data.options?.riskBucketId === undefined, {
240
256
  message: "marginMode CROSS cannot be combined with riskBucketId",
@@ -243,10 +259,6 @@ export const PlaceLimitOrderSchema = z
243
259
  .refine((data) => data.options?.marginMode !== "CROSS" || data.options?.riskBucketCollateral === undefined, {
244
260
  message: "marginMode CROSS cannot be combined with riskBucketCollateral",
245
261
  path: ["options", "riskBucketCollateral"],
246
- })
247
- .refine((data) => data.options?.marginMode !== "CROSS" || data.options?.strategyKey === undefined, {
248
- message: "marginMode CROSS cannot be combined with strategyKey",
249
- path: ["options", "strategyKey"],
250
262
  })
251
263
  .refine((data) => data.options?.riskBucketCollateral === undefined || data.options?.riskBucketId === undefined, {
252
264
  message: "riskBucketCollateral creates a new risk bucket and cannot be combined with riskBucketId",
@@ -277,7 +289,7 @@ export const PlaceMarketOrderSchema = z
277
289
  marginMode: MarginModeSchema.optional(),
278
290
  riskBucketId: UUIDSchema.optional(),
279
291
  riskBucketCollateral: PositiveDecimalStringSchema.optional(),
280
- strategyKey: z.string().min(1).max(128).optional(),
292
+ strategyKey: OrderStrategyKeySchema.optional(),
281
293
  positionSide: PositionSideSchema.optional(),
282
294
  leverage: PositiveDecimalStringSchema.optional(),
283
295
  reduceOnly: z.boolean().optional(),
@@ -296,7 +308,7 @@ export const PlaceMarketOrderSchema = z
296
308
  message: "leverage is required for normal MARGIN orders",
297
309
  path: ["options", "leverage"],
298
310
  })
299
- .refine((data) => !hasParentTpSl(data.options) || data.options?.tradingMode === "MARGIN", {
311
+ .refine((data) => !hasParentTpSl(data.options) || data.options?.tradingMode !== "SPOT", {
300
312
  message: "takeProfit/stopLoss/trailingStop are only supported for MARGIN orders",
301
313
  path: ["options"],
302
314
  })
@@ -304,17 +316,13 @@ export const PlaceMarketOrderSchema = z
304
316
  message: "takeProfit/stopLoss/trailingStop cannot be attached to reduce-only orders",
305
317
  path: ["options"],
306
318
  })
307
- .refine((data) => data.options?.riskBucketCollateral === undefined || data.options?.tradingMode === "MARGIN", {
319
+ .refine((data) => data.options?.riskBucketCollateral === undefined || data.options?.tradingMode !== "SPOT", {
308
320
  message: "riskBucketCollateral is only supported for MARGIN orders",
309
321
  path: ["options", "tradingMode"],
310
322
  })
311
- .refine((data) => data.options?.marginMode === undefined || data.options?.tradingMode === "MARGIN", {
323
+ .refine((data) => data.options?.marginMode === undefined || data.options?.tradingMode !== "SPOT", {
312
324
  message: "marginMode is only supported for MARGIN orders",
313
325
  path: ["options", "tradingMode"],
314
- })
315
- .refine((data) => data.options?.marginMode !== "CROSS" || data.options?.marginAccountId !== undefined, {
316
- message: "marginAccountId is required for CROSS margin orders",
317
- path: ["options", "marginAccountId"],
318
326
  })
319
327
  .refine((data) => data.options?.marginMode !== "CROSS" || data.options?.riskBucketId === undefined, {
320
328
  message: "marginMode CROSS cannot be combined with riskBucketId",
@@ -323,10 +331,6 @@ export const PlaceMarketOrderSchema = z
323
331
  .refine((data) => data.options?.marginMode !== "CROSS" || data.options?.riskBucketCollateral === undefined, {
324
332
  message: "marginMode CROSS cannot be combined with riskBucketCollateral",
325
333
  path: ["options", "riskBucketCollateral"],
326
- })
327
- .refine((data) => data.options?.marginMode !== "CROSS" || data.options?.strategyKey === undefined, {
328
- message: "marginMode CROSS cannot be combined with strategyKey",
329
- path: ["options", "strategyKey"],
330
334
  })
331
335
  .refine((data) => data.options?.riskBucketCollateral === undefined || data.options?.riskBucketId === undefined, {
332
336
  message: "riskBucketCollateral creates a new risk bucket and cannot be combined with riskBucketId",
@@ -466,7 +470,7 @@ export const CreateTwapOrderSchema = z
466
470
  riskBucketId: UUIDSchema.optional(),
467
471
  marginMode: MarginModeSchema.optional(),
468
472
  leverage: PositiveDecimalStringSchema.optional(),
469
- strategyKey: z.string().min(1).max(128).optional(),
473
+ strategyKey: OrderStrategyKeySchema.optional(),
470
474
  useMasterBalance: z.boolean().optional(),
471
475
  clientTwapId: z.string().min(1).max(36, "clientTwapId cannot exceed 36 characters").optional(),
472
476
  triggerPrice: PositiveDecimalStringSchema.optional(),
@@ -482,21 +486,17 @@ export const CreateTwapOrderSchema = z
482
486
  message: "startTime is only valid together with endTime",
483
487
  path: ["options", "startTime"],
484
488
  })
485
- .refine((data) => data.options.marginMode === undefined || data.options.tradingMode === "MARGIN", {
489
+ .refine((data) => data.options.marginMode === undefined || data.options.tradingMode !== "SPOT", {
486
490
  message: "marginMode is only supported for MARGIN orders",
487
491
  path: ["options", "tradingMode"],
488
492
  })
489
- .refine((data) => data.options.riskBucketId === undefined || data.options.tradingMode === "MARGIN", {
493
+ .refine((data) => data.options.riskBucketId === undefined || data.options.tradingMode !== "SPOT", {
490
494
  message: "riskBucketId is only supported for MARGIN orders",
491
495
  path: ["options", "tradingMode"],
492
496
  })
493
497
  .refine((data) => data.options.marginMode !== "CROSS" || data.options.riskBucketId === undefined, {
494
498
  message: "marginMode CROSS cannot be combined with riskBucketId",
495
499
  path: ["options", "riskBucketId"],
496
- })
497
- .refine((data) => data.options.marginMode !== "CROSS" || data.options.strategyKey === undefined, {
498
- message: "marginMode CROSS cannot be combined with strategyKey",
499
- path: ["options", "strategyKey"],
500
500
  })
501
501
  .refine((data) => data.options.tradingMode !== "MARGIN" || data.options.reduceOnly === true || data.options.leverage !== undefined, {
502
502
  message: "leverage is required for normal MARGIN TWAP orders",
@@ -544,6 +544,27 @@ export const CreateTwapOrderSchema = z
544
544
  message: "conditionalExpiry cannot be more than 30 days after creation (the maximum window)",
545
545
  path: ["options", "conditionalExpiry"],
546
546
  });
547
+ /** Bounds of a cancel-all-after timeout; 0 disarms. */
548
+ export const CANCEL_ALL_AFTER_MIN_TIMEOUT_MS = 5_000;
549
+ export const CANCEL_ALL_AFTER_MAX_TIMEOUT_MS = 900_000;
550
+ export const CancelAllAfterSchema = z
551
+ .object({
552
+ timeoutMs: z
553
+ .number()
554
+ .int()
555
+ .refine((ms) => ms === 0 || (ms >= CANCEL_ALL_AFTER_MIN_TIMEOUT_MS && ms <= CANCEL_ALL_AFTER_MAX_TIMEOUT_MS), {
556
+ message: `timeoutMs must be 0 or between ${CANCEL_ALL_AFTER_MIN_TIMEOUT_MS} and ${CANCEL_ALL_AFTER_MAX_TIMEOUT_MS}`,
557
+ }),
558
+ // Strict: a misspelled scope key must fail rather than be dropped, or the
559
+ // call would quietly arm the account-wide timer.
560
+ options: z
561
+ .object({
562
+ tradingPairId: UUIDSchema.optional(),
563
+ })
564
+ .strict()
565
+ .optional(),
566
+ })
567
+ .strict();
547
568
  export const CancelTwapOrderSchema = z.object({
548
569
  twapOrderId: UUIDSchema,
549
570
  });
@@ -593,7 +614,7 @@ export const BatchCreateOrderItemSchema = z
593
614
  marginMode: MarginModeSchema.optional(),
594
615
  riskBucketId: UUIDSchema.optional(),
595
616
  riskBucketCollateral: PositiveDecimalStringSchema.optional(),
596
- strategyKey: z.string().min(1).max(128).optional(),
617
+ strategyKey: OrderStrategyKeySchema.optional(),
597
618
  positionSide: PositionSideSchema.optional(),
598
619
  leverage: PositiveDecimalStringSchema.optional(),
599
620
  reduceOnly: z.boolean().optional(),
@@ -620,17 +641,13 @@ export const BatchCreateOrderItemSchema = z
620
641
  message: "postOnly cannot be combined with IOC/FOK time in force",
621
642
  path: ["postOnly"],
622
643
  })
623
- .refine((data) => data.riskBucketCollateral === undefined || data.tradingMode === "MARGIN", {
644
+ .refine((data) => data.riskBucketCollateral === undefined || data.tradingMode !== "SPOT", {
624
645
  message: "riskBucketCollateral is only supported for MARGIN orders",
625
646
  path: ["tradingMode"],
626
647
  })
627
- .refine((data) => data.marginMode === undefined || data.tradingMode === "MARGIN", {
648
+ .refine((data) => data.marginMode === undefined || data.tradingMode !== "SPOT", {
628
649
  message: "marginMode is only supported for MARGIN orders",
629
650
  path: ["tradingMode"],
630
- })
631
- .refine((data) => data.marginMode !== "CROSS" || data.marginAccountId !== undefined, {
632
- message: "marginAccountId is required for CROSS margin orders",
633
- path: ["marginAccountId"],
634
651
  })
635
652
  .refine((data) => data.marginMode !== "CROSS" || data.riskBucketId === undefined, {
636
653
  message: "marginMode CROSS cannot be combined with riskBucketId",
@@ -639,10 +656,6 @@ export const BatchCreateOrderItemSchema = z
639
656
  .refine((data) => data.marginMode !== "CROSS" || data.riskBucketCollateral === undefined, {
640
657
  message: "marginMode CROSS cannot be combined with riskBucketCollateral",
641
658
  path: ["riskBucketCollateral"],
642
- })
643
- .refine((data) => data.marginMode !== "CROSS" || data.strategyKey === undefined, {
644
- message: "marginMode CROSS cannot be combined with strategyKey",
645
- path: ["strategyKey"],
646
659
  })
647
660
  .refine((data) => data.riskBucketCollateral === undefined || data.riskBucketId === undefined, {
648
661
  message: "riskBucketCollateral creates a new risk bucket and cannot be combined with riskBucketId",
@@ -71,23 +71,43 @@ export interface WebSocketStatusInfo {
71
71
  */
72
72
  export type StatusHandler = (status: WebSocketStatus, info: WebSocketStatusInfo) => void;
73
73
  /**
74
- * Why the connection was re-established. Delivered by
75
- * {@link MonacoWebSocketConfig.onResync} after the socket reopens and sends its
76
- * authentication/subscription requests. It does not wait for server acks or
77
- * snapshots. The application owns channel-specific recovery; the SDK does not
78
- * automatically refetch REST state.
74
+ * Why the application should refetch REST state, delivered by
75
+ * {@link MonacoWebSocketConfig.onResync}. Two cases:
76
+ *
77
+ * - A reconnect (`reason` absent): sent after the socket reopens and sends its
78
+ * authentication/subscription requests, without waiting for server acks or
79
+ * snapshots. Refetch every channel.
80
+ * - A scoped resync (`reason: "resynced"`, 0XM-3553): the connection stayed
81
+ * open, but the server reported that an upstream loss may have cost the
82
+ * listed `channels` frames, or re-sent a snapshot for a channel that has no
83
+ * snapshot handler. Delivered once per burst, no earlier than the server's
84
+ * requested delay, with `code: 1013` and `slowClient: true` so a handler
85
+ * written for reconnects still refetches (everything). Refetch only
86
+ * `channels` to recover precisely. The subscriptions stay active.
87
+ *
88
+ * The application owns channel-specific recovery; the SDK does not refetch
89
+ * REST state itself.
79
90
  */
80
91
  export interface WebSocketResyncInfo {
81
92
  /** WebSocket close code of the connection that was lost. */
82
93
  code: number;
83
94
  /**
84
- * True when the server closed with 1013 (try again later): the client fell
85
- * behind a sequential stream (orders, conditional orders, liquidations,
86
- * trades, movements) and the stream has a gap. There is no wire sequence
87
- * number — the 1013 close is the gap signal, and a snapshot refetch is the
88
- * only safe repair.
95
+ * True when the server closed with 1013 (try again later): the connection
96
+ * fell behind, or the server lost events upstream, and any of its streams
97
+ * may have a gap. Order, account and engine-sourced public streams carry no
98
+ * wire sequence number, so the 1013 close is their gap signal and a snapshot
99
+ * refetch is the only safe repair. `depth`, `bbo` and a single market's
100
+ * market-data-sourced `trades` rebuild from the snapshot the reconnect
101
+ * brings; `trades:*` has no snapshot, so backfill its history over REST.
89
102
  */
90
103
  slowClient: boolean;
104
+ /** `"resynced"` for a scoped resync; absent on a reconnect. */
105
+ reason?: "resynced";
106
+ /**
107
+ * The channels to refetch, exactly as subscribed, on a scoped resync;
108
+ * absent on a reconnect, which covers every channel.
109
+ */
110
+ channels?: string[];
91
111
  }
92
112
  /** Handler invoked after a reconnect resumes the live stream. */
93
113
  export type ResyncHandler = (info: WebSocketResyncInfo) => void;
@@ -135,7 +155,13 @@ export type WebSocketErrorCode =
135
155
  * and back off before reconnecting — a hot re-subscribe loop trips the cap
136
156
  * again.
137
157
  */
138
- | "MESSAGE_RATE_LIMIT";
158
+ | "MESSAGE_RATE_LIMIT"
159
+ /**
160
+ * The `rfq` subscription was refused: RFQ is switched off, or this account
161
+ * is not on its access list. The same `RFQ_DISABLED` the REST and gRPC RFQ
162
+ * calls return. The connection stays open.
163
+ */
164
+ | "RFQ_DISABLED";
139
165
  /**
140
166
  * An `Error` frame the server sent on this connection.
141
167
  *
@@ -194,6 +220,16 @@ export type ReauthenticationRequiredHandler = (info: WebSocketReauthenticationIn
194
220
  export interface MonacoWebSocketConfig {
195
221
  /** Enable auto-reconnect on disconnect (default: true) */
196
222
  autoReconnect?: boolean;
223
+ /**
224
+ * Offer the server the `resync` capability (default: true when
225
+ * {@link onResync} is set, 0XM-3553; never without it, since the channels
226
+ * would have nowhere to go): on an
227
+ * upstream loss it can attribute, the server keeps the connection open and
228
+ * names the channels to refetch (delivered through {@link onResync} with
229
+ * `reason: "resynced"`), or re-sends their snapshots, instead of closing it
230
+ * with 1013. `false` keeps the close-and-reconnect behaviour.
231
+ */
232
+ scopedResync?: boolean;
197
233
  /**
198
234
  * Maximum reconnection attempts per outage (default: unlimited). Retry
199
235
  * delay is full-jitter exponential backoff capped at 30s, so unlimited
@@ -59,8 +59,14 @@ import type { Interval } from "../market";
59
59
  import type { TradingMode } from "../trading";
60
60
  import type { SubscriptionChangeHandler, WebSocketStatus, WebSocketSubscriptionState } from "./base";
61
61
  import type { OrderbookQuotationMode } from "./clients/orderbook-client";
62
- import type { AccountEvent, AccountEventData, ConditionalOrderEvent, ConditionalOrderEventData, CopyEvent, CopyRelationshipEventData, InstrumentEvent, InstrumentEventData, LiquidationEvent, LiquidationEventData, MarketLiquidationEvent, MarketStatsAllEvent, MarketStatsData, MarketStatsEvent, MarketStatsSummary, OHLCVEvent, OHLCVPriceType, OrderbookEvent, OrderEvent, PositionEvent, PositionEventData, PositionValuationEvent, TradeEvent, TradeEventData, TwapOrderEvent, TwapOrderEventData, UserBalanceEvent, UserBalanceEventData, UserMovementEvent } from "./events";
62
+ import type { Bbo, DepthBook, LocalBookOptions } from "./depth";
63
+ import type { AccountEvent, AccountEventData, ConditionalOrderEvent, ConditionalOrderEventData, CopyEvent, CopyRelationshipEventData, InstrumentEvent, InstrumentEventData, LiquidationEvent, LiquidationEventData, MarketLiquidationEvent, MarketStatsAllEvent, MarketStatsData, MarketStatsEvent, MarketStatsSummary, OHLCVEvent, OHLCVPriceType, OrderbookEvent, OrderEvent, PositionEvent, PositionEventData, PositionValuationEvent, RfqEvent, RfqTradeEvent, TradeEvent, TradeEventData, TradeGap, TwapOrderEvent, TwapOrderEventData, UserBalanceEvent, UserBalanceEventData, UserMovementEvent } from "./events";
63
64
  import type { OHLCVSnapshotCandle, OrderSnapshotItem, SnapshotHandler } from "./snapshots";
65
+ /** Options for `ws.trades`. */
66
+ export interface TradesOptions {
67
+ /** Called when a sequenced trade does not link to the last trade delivered (it is still delivered). */
68
+ onGap?: (gap: TradeGap) => void;
69
+ }
64
70
  export type MessageHandler<T> = (data: T) => void;
65
71
  export interface MonacoWebSocket {
66
72
  /** Connect to the WebSocket server */
@@ -92,10 +98,26 @@ export interface MonacoWebSocket {
92
98
  /** Subscribe to order events (requires authentication); `onSnapshot` receives every resting order on the pair */
93
99
  orders: (tradingPairId: string, tradingMode: TradingMode, handler: MessageHandler<OrderEvent>, onSnapshot?: SnapshotHandler<OrderSnapshotItem>) => () => void;
94
100
  /**
95
- * Subscribe to orderbook events (public); `onSnapshot` receives one event, the current book read from the matching
96
- * engine, in the same shape as a live event. Its `sequence` is 0. A pair or mode with no book gets `[]`.
101
+ * Subscribe to orderbook events (public); `onSnapshot` receives one event, the current book, in the same shape as a
102
+ * live event. From the matching engine it is the top 20 levels with `sequence` 0; from the market-data service it is
103
+ * grouped over the whole book (at most 20 levels or buckets per side), carries `bookSeq`, and its `sequence` equals
104
+ * `bookSeq`. From the engine, a pair or mode with no book gets `[]`; from the market-data service, an unknown pair
105
+ * gets a `SNAPSHOT_UNAVAILABLE` error (through `onError`) instead, and no snapshot until the pair is listed.
97
106
  */
98
107
  orderbook: (tradingPairId: string, tradingMode: TradingMode, magnitude: number, quotationMode: OrderbookQuotationMode, handler: MessageHandler<OrderbookEvent>, onSnapshot?: SnapshotHandler<OrderbookEvent>) => () => void;
108
+ /**
109
+ * Keep a local depth book from the market-data `depth:<tradingPairId>:<SPOT|MARGIN>` channel (public): up to 1000
110
+ * levels per side. The subscribe-time snapshot is the base; live frames that do not link to it are dropped or
111
+ * trigger a rebuild from a fresh snapshot. `onBook` receives the whole book after the snapshot and after every
112
+ * applied frame. Subscribers on the same channel share one book. Returns a function that stops listening.
113
+ */
114
+ depth: (tradingPairId: string, tradingMode: TradingMode, onBook: MessageHandler<DepthBook>, options?: LocalBookOptions) => () => void;
115
+ /**
116
+ * Keep the top of book from the market-data `bbo:<tradingPairId>:<SPOT|MARGIN>` channel (public), with the same
117
+ * snapshot handoff and link checks as `depth`. `onBbo` receives the current best bid and ask (an empty side is
118
+ * `null`) after the snapshot and after every applied frame.
119
+ */
120
+ bbo: (tradingPairId: string, tradingMode: TradingMode, onBbo: MessageHandler<Bbo>, options?: LocalBookOptions) => () => void;
99
121
  /**
100
122
  * Subscribe to OHLCV candlestick events (public); `onSnapshot` receives the series' newest 350 candles, oldest first.
101
123
  * The newest snapshot candle can be partial or missing, so upsert live candles by `T`. A `*` price type gets no snapshot.
@@ -104,8 +126,14 @@ export interface MonacoWebSocket {
104
126
  /**
105
127
  * Subscribe to trade events (public); `onSnapshot` receives the pair's newest 50 trades, newest first.
106
128
  * The snapshot can overlap the first live trades or miss one executed just before subscribing, so dedupe on `tradeId`.
129
+ *
130
+ * When the server sources trades from market-data, each trade carries `seq` / `matchIndex` / `prevSeq` /
131
+ * `prevMatchIndex`, and the snapshot's `meta.cursor` names the trade it is current as of. The helper then drops
132
+ * trades at or below the cursor or the last delivered trade, and reports one that does not link to the last
133
+ * delivered trade to `options.onGap` (still delivering it: the server forwards only linking trades, so a mismatch
134
+ * means a frame was lost on this client). Without those fields every trade is delivered as before.
107
135
  */
108
- trades: (tradingPairId: string, handler: MessageHandler<TradeEvent>, onSnapshot?: SnapshotHandler<TradeEventData>) => () => void;
136
+ trades: (tradingPairId: string, handler: MessageHandler<TradeEvent>, onSnapshot?: SnapshotHandler<TradeEventData>, options?: TradesOptions) => () => void;
109
137
  /** Subscribe to user movement events (requires authentication) */
110
138
  movements: (handler: MessageHandler<UserMovementEvent>) => () => void;
111
139
  /** Subscribe to user order events for all trading pairs (requires authentication); `onSnapshot` receives every resting order */
@@ -158,4 +186,23 @@ export interface MonacoWebSocket {
158
186
  marketStats: (tradingPairId: string, handler: MessageHandler<MarketStatsEvent>, onSnapshot?: SnapshotHandler<MarketStatsData>) => () => void;
159
187
  /** Subscribe to the all-markets statistics summary (public) */
160
188
  marketStatsAll: (handler: MessageHandler<MarketStatsAllEvent>, onSnapshot?: SnapshotHandler<MarketStatsSummary>) => () => void;
189
+ /**
190
+ * Subscribe to RFQ pushes for the authenticated account (requires
191
+ * authentication): `RfqRequested` for every new RFQ you can quote as a maker
192
+ * (never your own wallet family's), and for the RFQs you are party to the
193
+ * taker's `RfqView`, a selected maker's `RfqSelected` (its last-look prompt)
194
+ * and `RfqOutcome`. Narrow on `eventType` (the wire's `type` tag). There is
195
+ * no per-pair variant and no
196
+ * snapshot: a push is a notification, so read a missed RFQ with `getRfq`.
197
+ * While RFQ is not enabled for the account the subscribe is refused with an
198
+ * `RFQ_DISABLED` error to `onError`.
199
+ */
200
+ rfq: (handler: MessageHandler<RfqEvent>) => () => void;
201
+ /**
202
+ * Subscribe to the public, anonymised RFQ block prints for one pair. RFQ
203
+ * fills never print on `trades`. There is no snapshot: deduplicate on
204
+ * `data.executionId`, and backfill from the REST trade reads, which include
205
+ * RFQ fills with `executionSource: "RFQ"`.
206
+ */
207
+ rfqTrades: (tradingPairId: string, handler: MessageHandler<RfqTradeEvent>) => () => void;
161
208
  }