@0xmonaco/types 1.0.69 → 1.0.70

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.
@@ -0,0 +1,113 @@
1
+ /**
2
+ * Copy trading validation schemas (0XM-2851).
3
+ *
4
+ * Client-side checks for the copy trading request params, run before a
5
+ * request is sent. The server stays the authority on every business rule
6
+ * (minimum allocation, the platform's profit-share range, leader
7
+ * eligibility); these catch malformed input early and mirror the request
8
+ * shape the REST gateway accepts, which rejects unknown fields.
9
+ */
10
+ import { z } from "zod";
11
+ /** A leader's custom TraderCode handle, as used in the leader paths. */
12
+ export declare const LeaderHandleSchema: z.ZodString;
13
+ /** Params for registering as a lead trader or editing your profile. */
14
+ export declare const UpsertLeadTraderSchema: z.ZodObject<{
15
+ shareBps: z.ZodOptional<z.ZodNumber>;
16
+ bio: z.ZodOptional<z.ZodString>;
17
+ status: z.ZodOptional<z.ZodEnum<{
18
+ ACTIVE: "ACTIVE";
19
+ PAUSED: "PAUSED";
20
+ }>>;
21
+ minAllocation: z.ZodOptional<z.ZodString>;
22
+ }, z.core.$strict>;
23
+ /**
24
+ * Params for following a lead trader (no `followId`) or editing one of your
25
+ * follows (`followId` set). An edit REPLACES the follow's settings: every
26
+ * optional setting left out reverts to its default, so an edit that omits
27
+ * `slPct` removes the stop-loss.
28
+ */
29
+ export declare const UpsertFollowSchema: z.ZodObject<{
30
+ followId: z.ZodOptional<z.ZodUUID>;
31
+ leader: z.ZodOptional<z.ZodString>;
32
+ marginModePolicy: z.ZodOptional<z.ZodEnum<{
33
+ ISOLATED: "ISOLATED";
34
+ CROSS: "CROSS";
35
+ FOLLOW: "FOLLOW";
36
+ }>>;
37
+ sizingMode: z.ZodOptional<z.ZodEnum<{
38
+ PROPORTIONAL: "PROPORTIONAL";
39
+ FIXED_MARGIN: "FIXED_MARGIN";
40
+ }>>;
41
+ sizingParam: z.ZodOptional<z.ZodString>;
42
+ leverageMode: z.ZodOptional<z.ZodEnum<{
43
+ FOLLOW: "FOLLOW";
44
+ FIXED: "FIXED";
45
+ }>>;
46
+ fixedLeverage: z.ZodOptional<z.ZodString>;
47
+ allocation: z.ZodString;
48
+ slPct: z.ZodOptional<z.ZodString>;
49
+ tradingPairIds: z.ZodOptional<z.ZodArray<z.ZodUUID>>;
50
+ copyExisting: z.ZodOptional<z.ZodBoolean>;
51
+ }, z.core.$strict>;
52
+ /** Params for previewing what copying a leader's open positions would place. */
53
+ export declare const PreviewFollowSchema: z.ZodObject<{
54
+ leader: z.ZodString;
55
+ marginModePolicy: z.ZodOptional<z.ZodEnum<{
56
+ ISOLATED: "ISOLATED";
57
+ CROSS: "CROSS";
58
+ FOLLOW: "FOLLOW";
59
+ }>>;
60
+ sizingMode: z.ZodOptional<z.ZodEnum<{
61
+ PROPORTIONAL: "PROPORTIONAL";
62
+ FIXED_MARGIN: "FIXED_MARGIN";
63
+ }>>;
64
+ sizingParam: z.ZodOptional<z.ZodString>;
65
+ leverageMode: z.ZodOptional<z.ZodEnum<{
66
+ FOLLOW: "FOLLOW";
67
+ FIXED: "FIXED";
68
+ }>>;
69
+ fixedLeverage: z.ZodOptional<z.ZodString>;
70
+ allocation: z.ZodString;
71
+ tradingPairIds: z.ZodOptional<z.ZodArray<z.ZodUUID>>;
72
+ }, z.core.$strict>;
73
+ /** Params for the public leaderboard. */
74
+ export declare const ListLeadTradersSchema: z.ZodObject<{
75
+ pageSize: z.ZodOptional<z.ZodNumber>;
76
+ pageToken: z.ZodOptional<z.ZodString>;
77
+ sort: z.ZodOptional<z.ZodEnum<{
78
+ roi: "roi";
79
+ pnl: "pnl";
80
+ followers: "followers";
81
+ allocated: "allocated";
82
+ aum: "aum";
83
+ win_rate: "win_rate";
84
+ }>>;
85
+ window: z.ZodOptional<z.ZodEnum<{
86
+ "7d": "7d";
87
+ "30d": "30d";
88
+ "90d": "90d";
89
+ all: "all";
90
+ }>>;
91
+ }, z.core.$strict>;
92
+ /** Params for a leader's closed trades. */
93
+ export declare const ListLeadTraderClosedTradesSchema: z.ZodObject<{
94
+ pageSize: z.ZodOptional<z.ZodNumber>;
95
+ pageToken: z.ZodOptional<z.ZodString>;
96
+ }, z.core.$strict>;
97
+ /** Params for your follows. */
98
+ export declare const ListMyFollowsSchema: z.ZodObject<{
99
+ pageSize: z.ZodOptional<z.ZodNumber>;
100
+ pageToken: z.ZodOptional<z.ZodString>;
101
+ }, z.core.$strict>;
102
+ /** Params for your followers, as a leader. */
103
+ export declare const ListMyFollowersSchema: z.ZodObject<{
104
+ pageSize: z.ZodOptional<z.ZodNumber>;
105
+ pageToken: z.ZodOptional<z.ZodString>;
106
+ }, z.core.$strict>;
107
+ export type UpsertLeadTraderParams = z.input<typeof UpsertLeadTraderSchema>;
108
+ export type UpsertFollowParams = z.input<typeof UpsertFollowSchema>;
109
+ export type PreviewFollowParams = z.input<typeof PreviewFollowSchema>;
110
+ export type ListLeadTradersParams = z.input<typeof ListLeadTradersSchema>;
111
+ export type ListLeadTraderClosedTradesParams = z.input<typeof ListLeadTraderClosedTradesSchema>;
112
+ export type ListMyFollowsParams = z.input<typeof ListMyFollowsSchema>;
113
+ export type ListMyFollowersParams = z.input<typeof ListMyFollowersSchema>;
@@ -0,0 +1,141 @@
1
+ /**
2
+ * Copy trading validation schemas (0XM-2851).
3
+ *
4
+ * Client-side checks for the copy trading request params, run before a
5
+ * request is sent. The server stays the authority on every business rule
6
+ * (minimum allocation, the platform's profit-share range, leader
7
+ * eligibility); these catch malformed input early and mirror the request
8
+ * shape the REST gateway accepts, which rejects unknown fields.
9
+ */
10
+ import { z } from "zod";
11
+ import { PositiveDecimalStringSchema, UUIDSchema } from "./trading.js";
12
+ /** A leader's custom TraderCode handle, as used in the leader paths. */
13
+ export const LeaderHandleSchema = z.string().trim().min(1, "leader handle is required");
14
+ /** A decimal fraction in (0, 1], e.g. `"0.25"` for a stop-loss at a quarter of the allocation. */
15
+ const StopLossFractionSchema = z
16
+ .string()
17
+ .trim()
18
+ .regex(/^\d+(\.\d+)?$/, "slPct must be a decimal fraction (e.g. '0.25')")
19
+ .refine((value) => {
20
+ const parsed = Number.parseFloat(value);
21
+ return parsed > 0 && parsed <= 1;
22
+ }, "slPct must be greater than 0 and at most 1");
23
+ /** Page size bounded to `max`, and an opaque cursor from a previous response. */
24
+ function cursorPage(max) {
25
+ return {
26
+ pageSize: z.number().int("Page size must be an integer").min(1, "Page size must be at least 1").max(max, `Page size cannot exceed ${max}`).optional(),
27
+ pageToken: z.string().optional(),
28
+ };
29
+ }
30
+ /** Params for registering as a lead trader or editing your profile. */
31
+ export const UpsertLeadTraderSchema = z
32
+ .object({
33
+ /** Profit share in basis points; omitted keeps the current share (the platform default on a registration). */
34
+ shareBps: z.number().int("shareBps must be an integer").min(0, "shareBps cannot be negative").max(10000, "shareBps cannot exceed 10000").optional(),
35
+ /** Free-text profile description, at most 2,000 bytes; omitted clears it. */
36
+ bio: z
37
+ .string()
38
+ .refine((value) => new TextEncoder().encode(value).length <= 2000, "bio cannot exceed 2,000 bytes")
39
+ .optional(),
40
+ /** `ACTIVE` (default) or `PAUSED`. */
41
+ status: z.enum(["ACTIVE", "PAUSED"]).optional(),
42
+ /** Minimum follow allocation in quote units; omitted clears it. */
43
+ minAllocation: PositiveDecimalStringSchema.optional(),
44
+ })
45
+ .strict();
46
+ /**
47
+ * Params for following a lead trader (no `followId`) or editing one of your
48
+ * follows (`followId` set). An edit REPLACES the follow's settings: every
49
+ * optional setting left out reverts to its default, so an edit that omits
50
+ * `slPct` removes the stop-loss.
51
+ */
52
+ export const UpsertFollowSchema = z
53
+ .object({
54
+ /** Omit to create a follow; set to edit one of your follows. */
55
+ followId: UUIDSchema.optional(),
56
+ /** The leader's custom TraderCode handle; required on a create, optional on an edit. */
57
+ leader: LeaderHandleSchema.optional(),
58
+ /** `FOLLOW` (default: the leader's mode per market), `CROSS` or `ISOLATED`. */
59
+ marginModePolicy: z.enum(["FOLLOW", "CROSS", "ISOLATED"]).optional(),
60
+ /** `PROPORTIONAL` (default) or `FIXED_MARGIN`. */
61
+ sizingMode: z.enum(["PROPORTIONAL", "FIXED_MARGIN"]).optional(),
62
+ /** Margin per leader order in quote units; required for `FIXED_MARGIN`, omitted otherwise. */
63
+ sizingParam: PositiveDecimalStringSchema.optional(),
64
+ /** `FOLLOW` (default: the leader's leverage) or `FIXED`. */
65
+ leverageMode: z.enum(["FOLLOW", "FIXED"]).optional(),
66
+ /** Leverage for every copy; required for `FIXED`, omitted otherwise. */
67
+ fixedLeverage: PositiveDecimalStringSchema.optional(),
68
+ /** Collateral for the copy account, in quote units, at or above the minimum. */
69
+ allocation: PositiveDecimalStringSchema,
70
+ /** Copy stop-loss as a fraction of the allocation in (0, 1]; omitted means none. */
71
+ slPct: StopLossFractionSchema.optional(),
72
+ /** Markets to copy; omit (or pass an empty list) to copy every market the leader trades. */
73
+ tradingPairIds: z.array(UUIDSchema).max(256, "at most 256 markets can be copied").optional(),
74
+ /** On a create, also copy the leader's currently open positions (see `previewFollow`). Refused on an edit. */
75
+ copyExisting: z.boolean().optional(),
76
+ })
77
+ .strict()
78
+ .superRefine((params, ctx) => {
79
+ if (params.followId === undefined && params.leader === undefined) {
80
+ ctx.addIssue({ code: "custom", message: "leader is required when creating a follow", path: ["leader"] });
81
+ }
82
+ if (params.followId !== undefined && params.copyExisting === true) {
83
+ ctx.addIssue({ code: "custom", message: "copyExisting applies only when creating a follow", path: ["copyExisting"] });
84
+ }
85
+ termRules(params, ctx);
86
+ });
87
+ /** The sizing and leverage parameters are present exactly when their mode needs them. */
88
+ function termRules(params, ctx) {
89
+ const fixedMargin = params.sizingMode === "FIXED_MARGIN";
90
+ if (fixedMargin && params.sizingParam === undefined) {
91
+ ctx.addIssue({ code: "custom", message: "sizingParam is required for FIXED_MARGIN sizing", path: ["sizingParam"] });
92
+ }
93
+ if (!fixedMargin && params.sizingParam !== undefined) {
94
+ ctx.addIssue({ code: "custom", message: "sizingParam is only accepted with FIXED_MARGIN sizing", path: ["sizingParam"] });
95
+ }
96
+ const fixedLeverage = params.leverageMode === "FIXED";
97
+ if (fixedLeverage && params.fixedLeverage === undefined) {
98
+ ctx.addIssue({ code: "custom", message: "fixedLeverage is required for FIXED leverage", path: ["fixedLeverage"] });
99
+ }
100
+ if (!fixedLeverage && params.fixedLeverage !== undefined) {
101
+ ctx.addIssue({ code: "custom", message: "fixedLeverage is only accepted with FIXED leverage", path: ["fixedLeverage"] });
102
+ }
103
+ }
104
+ /** Params for previewing what copying a leader's open positions would place. */
105
+ export const PreviewFollowSchema = z
106
+ .object({
107
+ /** The leader's custom TraderCode handle. */
108
+ leader: LeaderHandleSchema,
109
+ /** `FOLLOW` (default), `CROSS` or `ISOLATED`. */
110
+ marginModePolicy: z.enum(["FOLLOW", "CROSS", "ISOLATED"]).optional(),
111
+ /** `PROPORTIONAL` (default) or `FIXED_MARGIN`. */
112
+ sizingMode: z.enum(["PROPORTIONAL", "FIXED_MARGIN"]).optional(),
113
+ /** Margin per leader order in quote units; required for `FIXED_MARGIN`, omitted otherwise. */
114
+ sizingParam: PositiveDecimalStringSchema.optional(),
115
+ /** `FOLLOW` (default) or `FIXED`. */
116
+ leverageMode: z.enum(["FOLLOW", "FIXED"]).optional(),
117
+ /** Leverage for every copy; required for `FIXED`, omitted otherwise. */
118
+ fixedLeverage: PositiveDecimalStringSchema.optional(),
119
+ /** Collateral the copy account would start with, in quote units. */
120
+ allocation: PositiveDecimalStringSchema,
121
+ /** Markets to copy; omit to copy every market the leader trades. */
122
+ tradingPairIds: z.array(UUIDSchema).max(256, "at most 256 markets can be copied").optional(),
123
+ })
124
+ .strict()
125
+ .superRefine((params, ctx) => termRules(params, ctx));
126
+ /** Params for the public leaderboard. */
127
+ export const ListLeadTradersSchema = z
128
+ .object({
129
+ /** Metric to rank by, highest first: `roi` (default), `pnl`, `followers`, `allocated`, `aum` (marked copy account equity; leaders without it last) or `win_rate`. */
130
+ sort: z.enum(["roi", "pnl", "followers", "allocated", "aum", "win_rate"]).optional(),
131
+ /** Stats window: `7d`, `30d` (default), `90d` or `all`. */
132
+ window: z.enum(["7d", "30d", "90d", "all"]).optional(),
133
+ ...cursorPage(100),
134
+ })
135
+ .strict();
136
+ /** Params for a leader's closed trades. */
137
+ export const ListLeadTraderClosedTradesSchema = z.object(cursorPage(100)).strict();
138
+ /** Params for your follows. */
139
+ export const ListMyFollowsSchema = z.object(cursorPage(1000)).strict();
140
+ /** Params for your followers, as a leader. */
141
+ export const ListMyFollowersSchema = z.object(cursorPage(1000)).strict();
@@ -14,6 +14,7 @@
14
14
  * - Market schemas (trading pairs, candlesticks)
15
15
  */
16
16
  export * from "./common.js";
17
+ export * from "./copy-trading.js";
17
18
  export * from "./margin-accounts.js";
18
19
  export * from "./market.js";
19
20
  export * from "./positions.js";
@@ -15,6 +15,7 @@
15
15
  */
16
16
  // Export validation utilities
17
17
  export * from "./common.js";
18
+ export * from "./copy-trading.js";
18
19
  export * from "./margin-accounts.js";
19
20
  export * from "./market.js";
20
21
  export * from "./positions.js";
@@ -21,9 +21,9 @@ export declare const ClosePositionSchema: z.ZodObject<{
21
21
  positionId: z.ZodUUID;
22
22
  request: z.ZodObject<{
23
23
  closeType: z.ZodEnum<{
24
+ IOC: "IOC";
24
25
  LIMIT: "LIMIT";
25
26
  MARKET: "MARKET";
26
- IOC: "IOC";
27
27
  }>;
28
28
  limitPrice: z.ZodOptional<z.ZodString>;
29
29
  slippageToleranceBps: z.ZodOptional<z.ZodNumber>;
@@ -76,7 +76,7 @@ export declare const GetUserMovementsSchema: z.ZodObject<{
76
76
  * All fields are optional — an empty object or undefined is valid.
77
77
  */
78
78
  export declare const GetUserTradesSchema: z.ZodObject<{
79
- page: z.ZodOptional<z.ZodNumber>;
79
+ page: z.ZodOptional<z.ZodNever>;
80
80
  pageSize: z.ZodOptional<z.ZodNumber>;
81
81
  tradingPairId: z.ZodOptional<z.ZodUUID>;
82
82
  pageToken: z.ZodOptional<z.ZodString>;
@@ -45,25 +45,17 @@ export const GetUserMovementsSchema = z.object({
45
45
  * Validates parameters for fetching paginated user trades.
46
46
  * All fields are optional — an empty object or undefined is valid.
47
47
  */
48
- export const GetUserTradesSchema = z
49
- .object({
50
- page: z.number().int("Page must be an integer").min(1, "Page must be at least 1").optional(),
48
+ export const GetUserTradesSchema = z.object({
49
+ /**
50
+ * Rejected, not ignored. A non-strict object would let a JS or `any`
51
+ * caller keep passing `page`, and the SDK would then send `pageToken=""`
52
+ * and hand back the first cursor page on every call — the same silent
53
+ * duplicate-page failure the REST 400 guard exists to prevent.
54
+ */
55
+ page: z.never().optional(),
51
56
  pageSize: z.number().int("Page size must be an integer").min(1, "Page size must be at least 1").max(1000, "Page size cannot exceed 1000").optional(),
52
57
  tradingPairId: UUIDSchema.optional(),
53
58
  pageToken: z.string().optional(),
54
- })
55
- .superRefine((params, ctx) => {
56
- // The higher 1000 cap only applies to cursor (pageToken) requests; the
57
- // server keeps the 100 cap for page-number requests (offset scans).
58
- // Legacy page-number mode is selected only by passing `page` without
59
- // `pageToken` — omitting both starts a cursor walk.
60
- if (params.page !== undefined && params.pageToken === undefined && params.pageSize !== undefined && params.pageSize > 100) {
61
- ctx.addIssue({
62
- code: z.ZodIssueCode.custom,
63
- message: "Page size cannot exceed 100 in page-number mode (page without pageToken)",
64
- path: ["pageSize"],
65
- });
66
- }
67
59
  });
68
60
  export const ListFundingPaymentsSchema = z.object({
69
61
  page: z.number().int("Page must be an integer").min(1, "Page must be at least 1").optional(),
@@ -332,10 +332,10 @@ export declare const ReplaceOrderSchema: z.ZodObject<{
332
332
  * Matches the getPaginatedOrders method signature which accepts:
333
333
  * - status: OrderStatus, or a non-empty array of them (optional filter)
334
334
  * - tradingPairId: trading pair UUID (optional filter)
335
- * - page: number (default: 1)
336
- * - pageSize: number (default: 10, max: 100 in page-number mode, 1000 when pageToken is present)
335
+ * - pageSize: number (default: 10, max: 1000)
337
336
  */
338
337
  export declare const GetPaginatedOrdersSchema: z.ZodObject<{
338
+ page: z.ZodOptional<z.ZodNever>;
339
339
  status: z.ZodOptional<z.ZodUnion<readonly [z.ZodEnum<{
340
340
  SUBMITTED: "SUBMITTED";
341
341
  PARTIALLY_FILLED: "PARTIALLY_FILLED";
@@ -361,7 +361,6 @@ export declare const GetPaginatedOrdersSchema: z.ZodObject<{
361
361
  MARGIN: "MARGIN";
362
362
  }>>;
363
363
  marginAccountId: z.ZodOptional<z.ZodUUID>;
364
- page: z.ZodOptional<z.ZodNumber>;
365
364
  pageSize: z.ZodOptional<z.ZodNumber>;
366
365
  pageToken: z.ZodOptional<z.ZodString>;
367
366
  }, z.core.$strip>;
@@ -390,6 +389,7 @@ export declare const CancelConditionalOrderSchema: z.ZodObject<{
390
389
  conditionalOrderId: z.ZodUUID;
391
390
  }, z.core.$strip>;
392
391
  export declare const ListConditionalOrdersSchema: z.ZodObject<{
392
+ page: z.ZodOptional<z.ZodNever>;
393
393
  marginAccountId: z.ZodOptional<z.ZodUUID>;
394
394
  tradingPairId: z.ZodOptional<z.ZodUUID>;
395
395
  state: z.ZodOptional<z.ZodEnum<{
@@ -401,7 +401,6 @@ export declare const ListConditionalOrdersSchema: z.ZodObject<{
401
401
  TRIGGERING: "TRIGGERING";
402
402
  TRIGGERED: "TRIGGERED";
403
403
  }>>;
404
- page: z.ZodOptional<z.ZodNumber>;
405
404
  pageSize: z.ZodOptional<z.ZodNumber>;
406
405
  pageToken: z.ZodOptional<z.ZodString>;
407
406
  }, z.core.$strip>;
@@ -456,6 +455,7 @@ export declare const CancelTwapOrderSchema: z.ZodObject<{
456
455
  twapOrderId: z.ZodUUID;
457
456
  }, z.core.$strip>;
458
457
  export declare const ListTwapOrdersSchema: z.ZodObject<{
458
+ page: z.ZodOptional<z.ZodNever>;
459
459
  state: z.ZodOptional<z.ZodEnum<{
460
460
  CANCELLED: "CANCELLED";
461
461
  ACTIVE: "ACTIVE";
@@ -463,7 +463,6 @@ export declare const ListTwapOrdersSchema: z.ZodObject<{
463
463
  COMPLETED: "COMPLETED";
464
464
  }>>;
465
465
  tradingPairId: z.ZodOptional<z.ZodUUID>;
466
- page: z.ZodOptional<z.ZodNumber>;
467
466
  pageSize: z.ZodOptional<z.ZodNumber>;
468
467
  pageToken: z.ZodOptional<z.ZodString>;
469
468
  }, z.core.$strip>;
@@ -379,31 +379,22 @@ export const ReplaceOrderSchema = z.object({
379
379
  * Matches the getPaginatedOrders method signature which accepts:
380
380
  * - status: OrderStatus, or a non-empty array of them (optional filter)
381
381
  * - tradingPairId: trading pair UUID (optional filter)
382
- * - page: number (default: 1)
383
- * - pageSize: number (default: 10, max: 100 in page-number mode, 1000 when pageToken is present)
382
+ * - pageSize: number (default: 10, max: 1000)
384
383
  */
385
- export const GetPaginatedOrdersSchema = z
386
- .object({
384
+ export const GetPaginatedOrdersSchema = z.object({
385
+ /**
386
+ * Rejected, not ignored. A non-strict object would let a JS or `any`
387
+ * caller keep passing `page`, and the SDK would then send `pageToken=""`
388
+ * and hand back the first cursor page on every call — the same silent
389
+ * duplicate-page failure the REST 400 guard exists to prevent.
390
+ */
391
+ page: z.never().optional(),
387
392
  status: z.union([z.enum(ORDER_STATUS_VALUES), z.array(z.enum(ORDER_STATUS_VALUES)).min(1, "Status filter must list at least one status")]).optional(),
388
393
  tradingPairId: UUIDSchema.optional(),
389
394
  tradingMode: TradingModeSchema.optional(),
390
395
  marginAccountId: UUIDSchema.optional(),
391
- page: z.number().int().min(1, "Page must be at least 1").optional(),
392
396
  pageSize: z.number().int().min(1, "Page size must be at least 1").max(1000, "Page size cannot exceed 1000").optional(),
393
397
  pageToken: z.string().optional(),
394
- })
395
- .superRefine((params, ctx) => {
396
- // The higher 1000 cap only applies to cursor (pageToken) requests; the
397
- // server keeps the 100 cap for page-number requests (offset scans).
398
- // Legacy page-number mode is selected only by passing `page` without
399
- // `pageToken` — omitting both starts a cursor walk.
400
- if (params.page !== undefined && params.pageToken === undefined && params.pageSize !== undefined && params.pageSize > 100) {
401
- ctx.addIssue({
402
- code: z.ZodIssueCode.custom,
403
- message: "Page size cannot exceed 100 in page-number mode (page without pageToken)",
404
- path: ["pageSize"],
405
- });
406
- }
407
398
  });
408
399
  export const ConditionalOrderConditionTypeSchema = z.enum(["STOP_LOSS", "TAKE_PROFIT", "TRAILING_STOP"], {
409
400
  message: 'conditionType must be "STOP_LOSS", "TAKE_PROFIT", or "TRAILING_STOP"',
@@ -420,27 +411,19 @@ export const ClosePositionSideSchema = z.enum(["LONG", "SHORT"], {
420
411
  export const CancelConditionalOrderSchema = z.object({
421
412
  conditionalOrderId: UUIDSchema,
422
413
  });
423
- export const ListConditionalOrdersSchema = z
424
- .object({
414
+ export const ListConditionalOrdersSchema = z.object({
415
+ /**
416
+ * Rejected, not ignored. A non-strict object would let a JS or `any`
417
+ * caller keep passing `page`, and the SDK would then send `pageToken=""`
418
+ * and hand back the first cursor page on every call — the same silent
419
+ * duplicate-page failure the REST 400 guard exists to prevent.
420
+ */
421
+ page: z.never().optional(),
425
422
  marginAccountId: UUIDSchema.optional(),
426
423
  tradingPairId: UUIDSchema.optional(),
427
424
  state: ConditionalOrderStateSchema.optional(),
428
- page: z.number().int().min(1, "Page must be at least 1").optional(),
429
425
  pageSize: z.number().int().min(1, "Page size must be at least 1").max(1000, "Page size cannot exceed 1000").optional(),
430
426
  pageToken: z.string().optional(),
431
- })
432
- .superRefine((params, ctx) => {
433
- // The higher 1000 cap only applies to cursor (pageToken) requests; the
434
- // server keeps the 100 cap for page-number requests (offset scans).
435
- // Legacy page-number mode is selected only by passing `page` without
436
- // `pageToken` — omitting both starts a cursor walk.
437
- if (params.page !== undefined && params.pageToken === undefined && params.pageSize !== undefined && params.pageSize > 100) {
438
- ctx.addIssue({
439
- code: z.ZodIssueCode.custom,
440
- message: "Page size cannot exceed 100 in page-number mode (page without pageToken)",
441
- path: ["pageSize"],
442
- });
443
- }
444
427
  });
445
428
  export const TwapOrderStateSchema = z.enum(["PENDING", "ACTIVE", "COMPLETED", "CANCELLED"], {
446
429
  message: "Invalid TWAP order state",
@@ -564,26 +547,18 @@ export const CreateTwapOrderSchema = z
564
547
  export const CancelTwapOrderSchema = z.object({
565
548
  twapOrderId: UUIDSchema,
566
549
  });
567
- export const ListTwapOrdersSchema = z
568
- .object({
550
+ export const ListTwapOrdersSchema = z.object({
551
+ /**
552
+ * Rejected, not ignored. A non-strict object would let a JS or `any`
553
+ * caller keep passing `page`, and the SDK would then send `pageToken=""`
554
+ * and hand back the first cursor page on every call — the same silent
555
+ * duplicate-page failure the REST 400 guard exists to prevent.
556
+ */
557
+ page: z.never().optional(),
569
558
  state: TwapOrderStateSchema.optional(),
570
559
  tradingPairId: UUIDSchema.optional(),
571
- page: z.number().int().min(1, "Page must be at least 1").optional(),
572
560
  pageSize: z.number().int().min(1, "Page size must be at least 1").max(1000, "Page size cannot exceed 1000").optional(),
573
561
  pageToken: z.string().optional(),
574
- })
575
- .superRefine((params, ctx) => {
576
- // The higher 1000 cap only applies to cursor (pageToken) requests; the
577
- // server keeps the 100 cap for page-number requests (offset scans).
578
- // Legacy page-number mode is selected only by passing `page` without
579
- // `pageToken` — omitting both starts a cursor walk.
580
- if (params.page !== undefined && params.pageToken === undefined && params.pageSize !== undefined && params.pageSize > 100) {
581
- ctx.addIssue({
582
- code: z.ZodIssueCode.custom,
583
- message: "Page size cannot exceed 100 in page-number mode (page without pageToken)",
584
- path: ["pageSize"],
585
- });
586
- }
587
562
  });
588
563
  /**
589
564
  * Single batch create order item validation schema
@@ -52,7 +52,7 @@ import type { Interval } from "../market";
52
52
  import type { TradingMode } from "../trading";
53
53
  import type { WebSocketStatus } from "./base";
54
54
  import type { OrderbookQuotationMode } from "./clients/orderbook-client";
55
- import type { AccountEvent, AccountEventData, ConditionalOrderEvent, ConditionalOrderEventData, InstrumentEvent, InstrumentEventData, LiquidationEvent, LiquidationEventData, MarketStatsAllEvent, MarketStatsData, MarketStatsEvent, MarketStatsSummary, OHLCVEvent, OHLCVPriceType, OrderbookEvent, OrderEvent, PositionEvent, PositionEventData, TradeEvent, TwapOrderEvent, TwapOrderEventData, UserBalanceEvent, UserBalanceEventData, UserMovementEvent } from "./events";
55
+ import type { AccountEvent, AccountEventData, ConditionalOrderEvent, ConditionalOrderEventData, CopyEvent, CopyRelationshipEventData, InstrumentEvent, InstrumentEventData, LiquidationEvent, LiquidationEventData, MarketStatsAllEvent, MarketStatsData, MarketStatsEvent, MarketStatsSummary, OHLCVEvent, OHLCVPriceType, OrderbookEvent, OrderEvent, PositionEvent, PositionEventData, TradeEvent, TwapOrderEvent, TwapOrderEventData, UserBalanceEvent, UserBalanceEventData, UserMovementEvent } from "./events";
56
56
  import type { OrderSnapshotItem, SnapshotHandler } from "./snapshots";
57
57
  export type MessageHandler<T> = (data: T) => void;
58
58
  export interface MonacoWebSocket {
@@ -90,6 +90,14 @@ export interface MonacoWebSocket {
90
90
  liquidations: (handler: MessageHandler<LiquidationEvent>, tradingPairId?: string, onSnapshot?: SnapshotHandler<LiquidationEventData>) => () => void;
91
91
  /** Subscribe to account-level margin health updates for the authenticated user (requires authentication) */
92
92
  account: (handler: MessageHandler<AccountEvent>, onSnapshot?: SnapshotHandler<AccountEventData>) => () => void;
93
+ /**
94
+ * Subscribe to your copy trading relationships as a follower (requires
95
+ * authentication): a `copy_relationship_update` frame on every change to one
96
+ * of your follows, and a `copy_skip` frame for every leader fill a follow
97
+ * could not mirror. There is no per-pair variant. `onSnapshot` receives every
98
+ * live follow (status `ACTIVE` or `CLOSE_ONLY`) with `change: "snapshot"`.
99
+ */
100
+ copy: (handler: MessageHandler<CopyEvent>, onSnapshot?: SnapshotHandler<CopyRelationshipEventData>) => () => void;
93
101
  /**
94
102
  * Subscribe to market lifecycle + config changes (public); omit
95
103
  * `tradingPairId` for all markets. `onSnapshot` receives the full current
@@ -0,0 +1,133 @@
1
+ /**
2
+ * Copy trading WebSocket event types (owner-private `copy` channel, 0XM-2848).
3
+ *
4
+ * The channel streams to the FOLLOWER: every change to one of your copy
5
+ * relationships (follows), and every leader fill a relationship could not
6
+ * mirror. A leader receives nothing on this channel.
7
+ *
8
+ * Field names mirror the backend payloads (snake_case on the wire, camelCased
9
+ * by the client). The SDK parser normalizes explicit `null` values to absent,
10
+ * so nullable wire fields are optional here.
11
+ *
12
+ * **The value unions below are the values the server sends today, not a
13
+ * runtime guarantee**, which is why each value field is typed
14
+ * `Union | (string & {})`: `@0xmonaco/core` validates no value field, so an
15
+ * unrecognized status, policy or reason arrives as-is rather than dropping the
16
+ * update (0XM-2628). `switch` over one with a `default` branch.
17
+ */
18
+ /** Lifecycle status of a copy relationship (follow). */
19
+ export type CopyRelationshipStatus = "ACTIVE" | "CLOSE_ONLY" | "STOPPED";
20
+ /** How a follow picks the margin mode of each copied position. */
21
+ export type CopyMarginModePolicy = "FOLLOW" | "CROSS" | "ISOLATED";
22
+ /** How a follow sizes each copied order. */
23
+ export type CopySizingMode = "PROPORTIONAL" | "FIXED_MARGIN";
24
+ /** How a follow picks the leverage of each copied order. */
25
+ export type CopyLeverageMode = "FOLLOW" | "FIXED";
26
+ /** Why a follow stopped. */
27
+ export type CopyStopReason = "FOLLOWER" | "STOP_LOSS" | "SKIP_LIMIT" | "ADMIN" | "LEADER_SUSPENDED";
28
+ /**
29
+ * Why a leader fill was not mirrored. Only `INSUFFICIENT_MARGIN` advances the
30
+ * relationship's `consecutiveSkips` streak.
31
+ */
32
+ export type CopySkipReason = "INSUFFICIENT_MARGIN" | "BELOW_MIN" | "LEVERAGE_MISMATCH" | "MARKET_STATE" | "LEADER_EQUITY" | "NO_LIQUIDITY";
33
+ /**
34
+ * Why a relationship frame was sent. `snapshot` marks a subscribe-time
35
+ * baseline row; every other value names the change that produced a live
36
+ * frame.
37
+ */
38
+ export type CopyRelationshipChange = "created" | "updated" | "close_only" | "stopped" | "skip_recorded" | "reactivated" | "snapshot";
39
+ /**
40
+ * A copy relationship's full state after one sequencer step: the
41
+ * `copy_relationship_update` payload, and (with `change: "snapshot"`) each
42
+ * subscribe-time snapshot row. Every frame carries the whole row, so it
43
+ * supersedes the previous one for the same `relationshipId`; compare
44
+ * `version` to order frames for one relationship.
45
+ */
46
+ export interface CopyRelationshipEventData {
47
+ /** The follow's id — the same value as `Follow.followId` over REST */
48
+ relationshipId: string;
49
+ /** The leader's wallet address (lowercase hex) */
50
+ leaderWallet: string;
51
+ status: CopyRelationshipStatus | (string & {});
52
+ marginModePolicy: CopyMarginModePolicy | (string & {});
53
+ /** The copy account: the follow's cross risk bucket in your margin account */
54
+ copyRiskBucketId: string;
55
+ sizingMode: CopySizingMode | (string & {});
56
+ /** Margin per leader order in quote units, for `FIXED_MARGIN` */
57
+ sizingParam?: string;
58
+ leverageMode: CopyLeverageMode | (string & {});
59
+ /** Leverage for every copy, for `FIXED` */
60
+ fixedLeverage?: string;
61
+ /** Collateral allocated to the follow, in quote units */
62
+ allocation: string;
63
+ /** Copy stop-loss as a fraction of the allocation; absent when none */
64
+ slPct?: string;
65
+ /** Whether the follow copied the leader's open positions at creation */
66
+ copyExisting: boolean;
67
+ /** Markets mirrored; empty means every market the leader trades */
68
+ tradingPairIds: string[];
69
+ /**
70
+ * The relationship's realized PnL net of fees since `startedAt`, in quote
71
+ * units — the profit-share basis. This is NOT the copy risk bucket's
72
+ * balance: that bucket's `realizedPnl` is its cash balance, which also moves
73
+ * on transfers with no trade.
74
+ */
75
+ cumNetRealized: string;
76
+ /** High-water mark of `cumNetRealized` — the profit-share basis, not a balance */
77
+ hwm: string;
78
+ /** Consecutive mirrors skipped for insufficient margin */
79
+ consecutiveSkips: number;
80
+ /** When the follow started (RFC 3339) */
81
+ startedAt: string;
82
+ /** When the follow stopped (RFC 3339); absent while live */
83
+ stoppedAt?: string;
84
+ /** Why the follow stopped; absent while live */
85
+ stopReason?: CopyStopReason | (string & {});
86
+ /** Why this frame was sent */
87
+ change: CopyRelationshipChange | (string & {});
88
+ /**
89
+ * Durable-log sequence of the entry that produced this state; higher is
90
+ * newer for one relationship. Absent when unknown.
91
+ */
92
+ version?: number;
93
+ }
94
+ /**
95
+ * One leader fill a relationship did NOT mirror: the `copy_skip` payload.
96
+ * Events only; the subscribe-time snapshot carries no skips.
97
+ */
98
+ export interface CopySkipEventData {
99
+ /** The follow's id */
100
+ relationshipId: string;
101
+ /** The leader's wallet address (lowercase hex) */
102
+ leaderWallet: string;
103
+ /** The leader's trade that was not mirrored */
104
+ leaderTradeId: string;
105
+ /** The leader's order behind that trade */
106
+ leaderOrderId: string;
107
+ tradingPairId: string;
108
+ reason: CopySkipReason | (string & {});
109
+ /** The copy size the sizing step asked for before the skip, in base units */
110
+ requestedQuantity: string;
111
+ /** The relationship's streak after this skip */
112
+ consecutiveSkips: number;
113
+ /** Optional human-readable diagnostics */
114
+ detail?: string;
115
+ /** Durable-log sequence of the entry that recorded the skip, when known */
116
+ version?: number;
117
+ }
118
+ /** A `copy_relationship_update` frame. */
119
+ export interface CopyRelationshipEvent {
120
+ eventType: "copy_relationship_update";
121
+ /** The follower (you) */
122
+ userId: string;
123
+ data: CopyRelationshipEventData;
124
+ }
125
+ /** A `copy_skip` frame. */
126
+ export interface CopySkipEvent {
127
+ eventType: "copy_skip";
128
+ /** The follower (you) */
129
+ userId: string;
130
+ data: CopySkipEventData;
131
+ }
132
+ /** Any `copy` channel frame; narrow on `eventType`. */
133
+ export type CopyEvent = CopyRelationshipEvent | CopySkipEvent;