@0xmonaco/core 1.1.2-develop.3a25169 → 1.1.5-develop.129b2c6

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,69 @@
1
+ /**
2
+ * RFQ (block trading) API Implementation
3
+ *
4
+ * A taker requests quotes for a size; makers quote privately; the taker
5
+ * accepts one side of the best allocation, and each selected maker then has a
6
+ * short last look (reject or improve; silence executes). Available only to
7
+ * accounts RFQ is enabled for: other callers get `403` with code
8
+ * `RFQ_DISABLED`.
9
+ *
10
+ * Every call is signed. Every write carries an `idempotencyKey` (generated
11
+ * when omitted) and is sent as a keyed submission, so a write whose outcome
12
+ * is unknown is resent with the same key and body and the server replays the
13
+ * first result. The body is built key by key from validated params, because
14
+ * the gateway rejects unknown fields and stores a maker's request exactly as
15
+ * signed.
16
+ *
17
+ * @example
18
+ * ```typescript
19
+ * // Taker
20
+ * const rfq = await sdk.rfq.createRfq({ tradingPairId, product: "PERPETUAL", quantity: "25" });
21
+ * const status = await sdk.rfq.getRfq(rfq.terms!.rfqId);
22
+ * await sdk.rfq.acceptRfq(rfq.terms!.rfqId, { side: "BUY", viewVersion: status.view!.viewVersion });
23
+ *
24
+ * // Maker
25
+ * const quote = await sdk.rfq.upsertQuote(rfqId, { expectedVersion: 0, ask: { price: "65001", capacity: "10" } });
26
+ * ```
27
+ */
28
+ import type { AcceptRfqParams, CancelRfqParams, CreateRfqParams, GetRfqCapabilitiesResponse, RespondRfqLastLookParams, RespondRfqLastLookResponse, RfqAPI, RfqQuoteVersion, RfqStatus, UpsertRfqQuoteParams, WithdrawRfqQuoteParams, WithdrawRfqQuoteResponse } from "@0xmonaco/types";
29
+ import { BaseAPI } from "../base";
30
+ export declare class RfqAPIImpl extends BaseAPI implements RfqAPI {
31
+ /** Your RFQ limits and the markets RFQ is offered on. */
32
+ getCapabilities(): Promise<GetRfqCapabilitiesResponse>;
33
+ /**
34
+ * Request quotes for up to `quantity` (not a guaranteed fill). Omit `side`
35
+ * for a two-way RFQ, whose direction stays hidden from makers; set, makers
36
+ * see it and may quote only the side that fills it (`terms.quoteSide`). On a perpetual give `leverage`
37
+ * (and optionally `marginMode`) for the order you accept with, as on an
38
+ * order. Delegated agents cannot request quotes.
39
+ */
40
+ createRfq(params: CreateRfqParams): Promise<RfqStatus>;
41
+ /**
42
+ * An RFQ's authoritative status. As taker: the current `view` and, once
43
+ * executed, the `outcome`. As maker: your latest quote, `ownLatestVersion`
44
+ * and, once selected, `ownSelection` for last look.
45
+ */
46
+ getRfq(rfqId: string): Promise<RfqStatus>;
47
+ /**
48
+ * Accept one side of the view you last saw. Binding: the selected makers'
49
+ * last look follows, and silence executes. A newer view refuses an older
50
+ * `viewVersion` (409); read the RFQ again and accept the current one.
51
+ */
52
+ acceptRfq(rfqId: string, params: AcceptRfqParams): Promise<RfqStatus>;
53
+ /** Cancel an RFQ you have not accepted, before its deadline. */
54
+ cancelRfq(rfqId: string, params?: CancelRfqParams): Promise<RfqStatus>;
55
+ /**
56
+ * As maker: place or replace your quote, a bid, an ask or both. Name your
57
+ * current version (`ownLatestVersion`; 0 for your first quote); a stale
58
+ * version is refused (409).
59
+ */
60
+ upsertQuote(rfqId: string, params: UpsertRfqQuoteParams): Promise<RfqQuoteVersion>;
61
+ /** As maker: withdraw your current quote, naming your current version. */
62
+ withdrawQuote(rfqId: string, params: WithdrawRfqQuoteParams): Promise<WithdrawRfqQuoteResponse>;
63
+ /**
64
+ * As a selected maker: reject, or improve your frozen terms (a strictly
65
+ * better price or a higher capacity), before the last-look deadline. There
66
+ * is no accept: silence executes.
67
+ */
68
+ respondLastLook(rfqId: string, params: RespondRfqLastLookParams): Promise<RespondRfqLastLookResponse>;
69
+ }
@@ -0,0 +1,129 @@
1
+ /**
2
+ * RFQ (block trading) API Implementation
3
+ *
4
+ * A taker requests quotes for a size; makers quote privately; the taker
5
+ * accepts one side of the best allocation, and each selected maker then has a
6
+ * short last look (reject or improve; silence executes). Available only to
7
+ * accounts RFQ is enabled for: other callers get `403` with code
8
+ * `RFQ_DISABLED`.
9
+ *
10
+ * Every call is signed. Every write carries an `idempotencyKey` (generated
11
+ * when omitted) and is sent as a keyed submission, so a write whose outcome
12
+ * is unknown is resent with the same key and body and the server replays the
13
+ * first result. The body is built key by key from validated params, because
14
+ * the gateway rejects unknown fields and stores a maker's request exactly as
15
+ * signed.
16
+ *
17
+ * @example
18
+ * ```typescript
19
+ * // Taker
20
+ * const rfq = await sdk.rfq.createRfq({ tradingPairId, product: "PERPETUAL", quantity: "25" });
21
+ * const status = await sdk.rfq.getRfq(rfq.terms!.rfqId);
22
+ * await sdk.rfq.acceptRfq(rfq.terms!.rfqId, { side: "BUY", viewVersion: status.view!.viewVersion });
23
+ *
24
+ * // Maker
25
+ * const quote = await sdk.rfq.upsertQuote(rfqId, { expectedVersion: 0, ask: { price: "65001", capacity: "10" } });
26
+ * ```
27
+ */
28
+ import { AcceptRfqSchema, CancelRfqSchema, CreateRfqSchema, RespondRfqLastLookSchema, UpsertRfqQuoteSchema, UUIDSchema, validate, WithdrawRfqQuoteSchema, } from "@0xmonaco/types";
29
+ import { BaseAPI } from "../base";
30
+ const BASE = "/api/v1/rfqs";
31
+ /** The RFQ path for a validated RFQ id. */
32
+ function rfqPath(rfqId) {
33
+ return `${BASE}/${validate(UUIDSchema, rfqId)}`;
34
+ }
35
+ /** The caller's key, or a fresh one. */
36
+ function keyOf(idempotencyKey) {
37
+ return idempotencyKey ?? crypto.randomUUID();
38
+ }
39
+ export class RfqAPIImpl extends BaseAPI {
40
+ /** Your RFQ limits and the markets RFQ is offered on. */
41
+ async getCapabilities() {
42
+ return await this.makeAuthenticatedRequest("/api/v1/rfq/capabilities", { method: "GET" });
43
+ }
44
+ /**
45
+ * Request quotes for up to `quantity` (not a guaranteed fill). Omit `side`
46
+ * for a two-way RFQ, whose direction stays hidden from makers; set, makers
47
+ * see it and may quote only the side that fills it (`terms.quoteSide`). On a perpetual give `leverage`
48
+ * (and optionally `marginMode`) for the order you accept with, as on an
49
+ * order. Delegated agents cannot request quotes.
50
+ */
51
+ async createRfq(params) {
52
+ const { tradingPairId, product, quantity, side, leverage, marginMode, idempotencyKey } = validate(CreateRfqSchema, params);
53
+ const body = { idempotencyKey: keyOf(idempotencyKey), tradingPairId, product, quantity };
54
+ if (side !== undefined)
55
+ body.side = side;
56
+ if (leverage !== undefined)
57
+ body.leverage = leverage;
58
+ if (marginMode !== undefined)
59
+ body.marginMode = marginMode;
60
+ return await this.makeKeyedSubmission(BASE, { method: "POST", body: JSON.stringify(body) });
61
+ }
62
+ /**
63
+ * An RFQ's authoritative status. As taker: the current `view` and, once
64
+ * executed, the `outcome`. As maker: your latest quote, `ownLatestVersion`
65
+ * and, once selected, `ownSelection` for last look.
66
+ */
67
+ async getRfq(rfqId) {
68
+ return await this.makeAuthenticatedRequest(rfqPath(rfqId), { method: "GET" });
69
+ }
70
+ /**
71
+ * Accept one side of the view you last saw. Binding: the selected makers'
72
+ * last look follows, and silence executes. A newer view refuses an older
73
+ * `viewVersion` (409); read the RFQ again and accept the current one.
74
+ */
75
+ async acceptRfq(rfqId, params) {
76
+ const path = rfqPath(rfqId);
77
+ const { side, viewVersion, idempotencyKey } = validate(AcceptRfqSchema, params);
78
+ const body = { idempotencyKey: keyOf(idempotencyKey), side, viewVersion };
79
+ return await this.makeKeyedSubmission(`${path}/accept`, { method: "POST", body: JSON.stringify(body) });
80
+ }
81
+ /** Cancel an RFQ you have not accepted, before its deadline. */
82
+ async cancelRfq(rfqId, params = {}) {
83
+ const path = rfqPath(rfqId);
84
+ const { idempotencyKey } = validate(CancelRfqSchema, params);
85
+ const body = { idempotencyKey: keyOf(idempotencyKey) };
86
+ return await this.makeKeyedSubmission(`${path}/cancel`, { method: "POST", body: JSON.stringify(body) });
87
+ }
88
+ /**
89
+ * As maker: place or replace your quote, a bid, an ask or both. Name your
90
+ * current version (`ownLatestVersion`; 0 for your first quote); a stale
91
+ * version is refused (409).
92
+ */
93
+ async upsertQuote(rfqId, params) {
94
+ const path = rfqPath(rfqId);
95
+ const { expectedVersion, bid, ask, validityMs, leverage, marginMode, idempotencyKey } = validate(UpsertRfqQuoteSchema, params);
96
+ const body = { idempotencyKey: keyOf(idempotencyKey), expectedVersion };
97
+ if (bid !== undefined)
98
+ body.bid = { price: bid.price, capacity: bid.capacity };
99
+ if (ask !== undefined)
100
+ body.ask = { price: ask.price, capacity: ask.capacity };
101
+ if (validityMs !== undefined)
102
+ body.validityMs = validityMs;
103
+ if (leverage !== undefined)
104
+ body.leverage = leverage;
105
+ if (marginMode !== undefined)
106
+ body.marginMode = marginMode;
107
+ return await this.makeKeyedSubmission(`${path}/quote`, { method: "PUT", body: JSON.stringify(body) });
108
+ }
109
+ /** As maker: withdraw your current quote, naming your current version. */
110
+ async withdrawQuote(rfqId, params) {
111
+ const path = rfqPath(rfqId);
112
+ const { expectedVersion, idempotencyKey } = validate(WithdrawRfqQuoteSchema, params);
113
+ const body = { idempotencyKey: keyOf(idempotencyKey), expectedVersion };
114
+ return await this.makeKeyedSubmission(`${path}/quote`, { method: "DELETE", body: JSON.stringify(body) });
115
+ }
116
+ /**
117
+ * As a selected maker: reject, or improve your frozen terms (a strictly
118
+ * better price or a higher capacity), before the last-look deadline. There
119
+ * is no accept: silence executes.
120
+ */
121
+ async respondLastLook(rfqId, params) {
122
+ const path = rfqPath(rfqId);
123
+ const { acceptanceId, response, improvedTerms, idempotencyKey } = validate(RespondRfqLastLookSchema, params);
124
+ const body = { idempotencyKey: keyOf(idempotencyKey), acceptanceId, response };
125
+ if (improvedTerms !== undefined)
126
+ body.improvedTerms = { price: improvedTerms.price, capacity: improvedTerms.capacity };
127
+ return await this.makeKeyedSubmission(`${path}/last-look`, { method: "POST", body: JSON.stringify(body) });
128
+ }
129
+ }
@@ -0,0 +1,6 @@
1
+ /**
2
+ * RFQ API Module
3
+ *
4
+ * Exports the RFQ (block trading) API implementation.
5
+ */
6
+ export { RfqAPIImpl } from "./api";
@@ -0,0 +1,6 @@
1
+ /**
2
+ * RFQ API Module
3
+ *
4
+ * Exports the RFQ (block trading) API implementation.
5
+ */
6
+ export { RfqAPIImpl } from "./api";
@@ -16,6 +16,13 @@ interface RawTradeEvent {
16
16
  makerSide: string;
17
17
  /** Absent when the trade has no recorded execution time */
18
18
  executedAt?: string;
19
+ /** CLOB or RFQ */
20
+ executionSource?: string;
21
+ makerAddress?: string;
22
+ takerAddress?: string;
23
+ takerFeeRaw?: string;
24
+ quoteVolumeRaw?: string;
25
+ takerApplicationId?: string;
19
26
  };
20
27
  }
21
28
  /**
@@ -34,6 +41,13 @@ export interface GetTradesOptions {
34
41
  */
35
42
  pageToken?: string;
36
43
  }
44
+ /**
45
+ * Options for the every-market trade feed.
46
+ */
47
+ export interface ListTradesOptions extends GetTradesOptions {
48
+ /** Only the trades this application (frontend) took, as the taker's application. */
49
+ takerApplicationId?: string;
50
+ }
37
51
  /**
38
52
  * One page of a pair's trade feed.
39
53
  *
@@ -74,6 +88,16 @@ export declare class TradesAPIImpl extends BaseAPI {
74
88
  * ```
75
89
  */
76
90
  getTrades(tradingPairId: string, options?: GetTradesOptions): Promise<GetTradesPage>;
91
+ /**
92
+ * Get one page of the trade feed: every market, newest first, recent and
93
+ * archived trades alike, or only the trades one application took when
94
+ * `takerApplicationId` is set. Each trade carries both wallets, the raw taker
95
+ * fee and quote volume, and the taker's application.
96
+ *
97
+ * Public; each client IP has a read budget (HTTP 429 over it). Start with no
98
+ * `pageToken` and follow `nextPageToken` until it comes back empty.
99
+ */
100
+ listTrades(options?: ListTradesOptions): Promise<GetTradesPage>;
77
101
  /**
78
102
  * Get a single trade by its UUID.
79
103
  *
@@ -1,4 +1,4 @@
1
- import { GetTradesSchema, validate } from "@0xmonaco/types";
1
+ import { GetTradesSchema, ListTradesSchema, validate } from "@0xmonaco/types";
2
2
  import { BaseAPI } from "../base";
3
3
  import { perpRoutes } from "../perp";
4
4
  /**
@@ -15,6 +15,12 @@ export function parseRawTradeEvent(raw) {
15
15
  quantity: raw.data.quantity,
16
16
  makerSide: raw.data.makerSide.toUpperCase(),
17
17
  executedAt: raw.data.executedAt ?? "",
18
+ ...(raw.data.executionSource ? { executionSource: raw.data.executionSource } : {}),
19
+ ...(raw.data.makerAddress ? { makerAddress: raw.data.makerAddress } : {}),
20
+ ...(raw.data.takerAddress ? { takerAddress: raw.data.takerAddress } : {}),
21
+ ...(raw.data.takerFeeRaw ? { takerFeeRaw: raw.data.takerFeeRaw } : {}),
22
+ ...(raw.data.quoteVolumeRaw ? { quoteVolumeRaw: raw.data.quoteVolumeRaw } : {}),
23
+ ...(raw.data.takerApplicationId ? { takerApplicationId: raw.data.takerApplicationId } : {}),
18
24
  },
19
25
  };
20
26
  }
@@ -67,6 +73,27 @@ export class TradesAPIImpl extends BaseAPI {
67
73
  nextPageToken: response.nextPageToken ?? "",
68
74
  };
69
75
  }
76
+ /**
77
+ * Get one page of the trade feed: every market, newest first, recent and
78
+ * archived trades alike, or only the trades one application took when
79
+ * `takerApplicationId` is set. Each trade carries both wallets, the raw taker
80
+ * fee and quote volume, and the taker's application.
81
+ *
82
+ * Public; each client IP has a read budget (HTTP 429 over it). Start with no
83
+ * `pageToken` and follow `nextPageToken` until it comes back empty.
84
+ */
85
+ async listTrades(options = {}) {
86
+ validate(ListTradesSchema, options);
87
+ const response = await this.makePublicRequest(perpRoutes.trades.feed({
88
+ ...(options.takerApplicationId ? { takerApplicationId: options.takerApplicationId } : {}),
89
+ ...(options.pageSize != null ? { pageSize: options.pageSize } : {}),
90
+ ...(options.pageToken ? { pageToken: options.pageToken } : {}),
91
+ }));
92
+ return {
93
+ trades: (response.trades ?? []).map(parseRawTradeEvent),
94
+ nextPageToken: response.nextPageToken ?? "",
95
+ };
96
+ }
70
97
  /**
71
98
  * Get a single trade by its UUID.
72
99
  *
@@ -21,7 +21,7 @@
21
21
  * );
22
22
  * ```
23
23
  */
24
- import type { BatchCancelOrdersResponse, BatchCreateOrderParams, BatchCreateOrdersResponse, BatchReplaceOrderParams, BatchReplaceOrdersResponse, CancelConditionalOrderResponse, CancelOrderResponse, CancelTwapOrderResponse, ConditionalOrder, CreateOrderResponse, CreateTwapOrderOptions, CreateTwapOrderResponse, GetOrderResponse, GetPaginatedOrdersParams, GetPaginatedOrdersResponse, GetTwapOrderTcaResponse, ListConditionalOrdersParams, ListConditionalOrdersResponse, ListTwapOrderSlicesParams, ListTwapOrderSlicesResponse, ListTwapOrdersParams, ListTwapOrdersResponse, MarginMode, OrderSide, ParentTpSlLegParams, ParentTrailingStopLegParams, PositionSide, ReplaceOrderResponse, SelfTradePreventionMode, TimeInForce, TradingAPI, TradingMode, TwapOrder } from "@0xmonaco/types";
24
+ import type { BatchCancelOrdersResponse, BatchCreateOrderParams, BatchCreateOrdersResponse, BatchReplaceOrderParams, BatchReplaceOrdersResponse, CancelAllAfterOptions, CancelAllAfterResponse, CancelConditionalOrderResponse, CancelOrderResponse, CancelTwapOrderResponse, ConditionalOrder, CreateOrderResponse, CreateTwapOrderOptions, CreateTwapOrderResponse, GetOrderResponse, GetPaginatedOrdersParams, GetPaginatedOrdersResponse, GetTwapOrderTcaResponse, ListConditionalOrdersParams, ListConditionalOrdersResponse, ListTwapOrderSlicesParams, ListTwapOrderSlicesResponse, ListTwapOrdersParams, ListTwapOrdersResponse, MarginMode, OrderSide, ParentTpSlLegParams, ParentTrailingStopLegParams, PositionSide, ReplaceOrderResponse, SelfTradePreventionMode, TimeInForce, TradingAPI, TradingMode, TwapOrder } from "@0xmonaco/types";
25
25
  import { BaseAPI } from "../base";
26
26
  export declare class TradingAPIImpl extends BaseAPI implements TradingAPI {
27
27
  /**
@@ -41,7 +41,7 @@ export declare class TradingAPIImpl extends BaseAPI implements TradingAPI {
41
41
  * @param quantity - The order quantity as string
42
42
  * @param price - The limit price as string
43
43
  * @param options - Optional parameters for the limit order
44
- * @param options.tradingMode - Trading mode (e.g., "SPOT")
44
+ * @param options.tradingMode - Trading mode ("SPOT" or "MARGIN"). Omit it to use the trading pair market type.
45
45
  * @param options.useMasterBalance - Whether to use master balance for sub-accounts
46
46
  * @param options.expirationDate - Optional expiration date for GTC orders (ISO 8601 format)
47
47
  * @param options.timeInForce - Time in force ("GTC", "IOC", or "FOK")
@@ -115,7 +115,7 @@ export declare class TradingAPIImpl extends BaseAPI implements TradingAPI {
115
115
  * @param side - The order side ("BUY" or "SELL")
116
116
  * @param quantity - The order quantity as string
117
117
  * @param options - Optional parameters for the market order
118
- * @param options.tradingMode - Trading mode (e.g., "SPOT")
118
+ * @param options.tradingMode - Trading mode ("SPOT" or "MARGIN"). Omit it to use the trading pair market type.
119
119
  * @param options.slippageTolerance - Tightens the mandatory 1,000 bps price band, as a decimal (e.g., 0.01 for 1%; 0 for best price only). It can only tighten the band, never widen it; omitting it applies the full 1,000 bps band.
120
120
  * @returns Promise resolving to CreateOrderResponse with order details
121
121
  *
@@ -241,6 +241,38 @@ export declare class TradingAPIImpl extends BaseAPI implements TradingAPI {
241
241
  * ```
242
242
  */
243
243
  batchCancelAll(tradingPairId?: string): Promise<BatchCancelOrdersResponse>;
244
+ /**
245
+ * Arms, refreshes or disarms a dead man's switch.
246
+ *
247
+ * Unless you call again before `timeoutMs` runs out, the exchange cancels
248
+ * your resting orders with terminalReason `CANCEL_ALL_AFTER`. Call it on an
249
+ * interval shorter than the timeout to keep pushing the deadline out; send
250
+ * 0 to disarm. A timer that fires is spent, so arm it again afterwards.
251
+ *
252
+ * @param timeoutMs - 0 disarms; otherwise 5000 to 900000 (5 s to 15 min)
253
+ * @param options - Optional `tradingPairId` to scope the timer to one market
254
+ * @returns Promise resolving to the deadline that was set
255
+ * @throws {ValidationError} When the timeout or trading pair id is invalid
256
+ * @throws {APIError} When API communication fails
257
+ *
258
+ * @example
259
+ * ```typescript
260
+ * // Cancel everything if this process stops refreshing for 30 seconds.
261
+ * const { cancelAt } = await tradingAPI.cancelAllAfter(30_000);
262
+ * // One refresh at a time, each after the previous call settles.
263
+ * let pending: Promise<unknown> = Promise.resolve();
264
+ * const refresh = setInterval(() => {
265
+ * pending = pending.then(() => tradingAPI.cancelAllAfter(30_000)).catch(() => undefined);
266
+ * }, 10_000);
267
+ *
268
+ * // Clean shutdown: let the last refresh settle before disarming, or it
269
+ * // could land after the disarm and re-arm the timer.
270
+ * clearInterval(refresh);
271
+ * await pending;
272
+ * await tradingAPI.cancelAllAfter(0);
273
+ * ```
274
+ */
275
+ cancelAllAfter(timeoutMs: number, options?: CancelAllAfterOptions): Promise<CancelAllAfterResponse>;
244
276
  replaceOrder(orderId: string, newOrder: {
245
277
  price?: string;
246
278
  quantity?: string;
@@ -21,7 +21,7 @@
21
21
  * );
22
22
  * ```
23
23
  */
24
- import { BatchCreateOrdersSchema, BatchReplaceOrdersSchema, CancelConditionalOrderSchema, CancelOrderSchema, CancelTwapOrderSchema, CreateTwapOrderSchema, GetPaginatedOrdersSchema, ListConditionalOrdersSchema, ListTwapOrderSlicesSchema, ListTwapOrdersSchema, PlaceLimitOrderSchema, PlaceMarketOrderSchema, ReplaceOrderSchema, validate, } from "@0xmonaco/types";
24
+ import { BatchCreateOrdersSchema, BatchReplaceOrdersSchema, CancelAllAfterSchema, CancelConditionalOrderSchema, CancelOrderSchema, CancelTwapOrderSchema, CreateTwapOrderSchema, GetPaginatedOrdersSchema, ListConditionalOrdersSchema, ListTwapOrderSlicesSchema, ListTwapOrdersSchema, PlaceLimitOrderSchema, PlaceMarketOrderSchema, ReplaceOrderSchema, validate, } from "@0xmonaco/types";
25
25
  import { BaseAPI } from "../base";
26
26
  import { perpRoutes } from "../perp";
27
27
  function parentTpSlLegToRequest(leg) {
@@ -77,7 +77,7 @@ export class TradingAPIImpl extends BaseAPI {
77
77
  * @param quantity - The order quantity as string
78
78
  * @param price - The limit price as string
79
79
  * @param options - Optional parameters for the limit order
80
- * @param options.tradingMode - Trading mode (e.g., "SPOT")
80
+ * @param options.tradingMode - Trading mode ("SPOT" or "MARGIN"). Omit it to use the trading pair market type.
81
81
  * @param options.useMasterBalance - Whether to use master balance for sub-accounts
82
82
  * @param options.expirationDate - Optional expiration date for GTC orders (ISO 8601 format)
83
83
  * @param options.timeInForce - Time in force ("GTC", "IOC", or "FOK")
@@ -131,7 +131,7 @@ export class TradingAPIImpl extends BaseAPI {
131
131
  side,
132
132
  price,
133
133
  quantity,
134
- tradingMode: options?.tradingMode || "SPOT",
134
+ tradingMode: options?.tradingMode,
135
135
  useMasterBalance: options?.useMasterBalance,
136
136
  expirationDate: options?.expirationDate,
137
137
  timeInForce: options?.timeInForce,
@@ -165,7 +165,7 @@ export class TradingAPIImpl extends BaseAPI {
165
165
  * @param side - The order side ("BUY" or "SELL")
166
166
  * @param quantity - The order quantity as string
167
167
  * @param options - Optional parameters for the market order
168
- * @param options.tradingMode - Trading mode (e.g., "SPOT")
168
+ * @param options.tradingMode - Trading mode ("SPOT" or "MARGIN"). Omit it to use the trading pair market type.
169
169
  * @param options.slippageTolerance - Tightens the mandatory 1,000 bps price band, as a decimal (e.g., 0.01 for 1%; 0 for best price only). It can only tighten the band, never widen it; omitting it applies the full 1,000 bps band.
170
170
  * @returns Promise resolving to CreateOrderResponse with order details
171
171
  *
@@ -211,7 +211,7 @@ export class TradingAPIImpl extends BaseAPI {
211
211
  slippageToleranceBps: options?.slippageTolerance !== undefined ? Math.round(options.slippageTolerance * 10000) : undefined,
212
212
  price: null, // Market orders don't need price
213
213
  quantity,
214
- tradingMode: options?.tradingMode || "SPOT",
214
+ tradingMode: options?.tradingMode,
215
215
  marginAccountId: options?.marginAccountId,
216
216
  marginMode: options?.marginMode,
217
217
  riskBucketId: options?.riskBucketId,
@@ -353,6 +353,48 @@ export class TradingAPIImpl extends BaseAPI {
353
353
  method: "POST",
354
354
  });
355
355
  }
356
+ /**
357
+ * Arms, refreshes or disarms a dead man's switch.
358
+ *
359
+ * Unless you call again before `timeoutMs` runs out, the exchange cancels
360
+ * your resting orders with terminalReason `CANCEL_ALL_AFTER`. Call it on an
361
+ * interval shorter than the timeout to keep pushing the deadline out; send
362
+ * 0 to disarm. A timer that fires is spent, so arm it again afterwards.
363
+ *
364
+ * @param timeoutMs - 0 disarms; otherwise 5000 to 900000 (5 s to 15 min)
365
+ * @param options - Optional `tradingPairId` to scope the timer to one market
366
+ * @returns Promise resolving to the deadline that was set
367
+ * @throws {ValidationError} When the timeout or trading pair id is invalid
368
+ * @throws {APIError} When API communication fails
369
+ *
370
+ * @example
371
+ * ```typescript
372
+ * // Cancel everything if this process stops refreshing for 30 seconds.
373
+ * const { cancelAt } = await tradingAPI.cancelAllAfter(30_000);
374
+ * // One refresh at a time, each after the previous call settles.
375
+ * let pending: Promise<unknown> = Promise.resolve();
376
+ * const refresh = setInterval(() => {
377
+ * pending = pending.then(() => tradingAPI.cancelAllAfter(30_000)).catch(() => undefined);
378
+ * }, 10_000);
379
+ *
380
+ * // Clean shutdown: let the last refresh settle before disarming, or it
381
+ * // could land after the disarm and re-arm the timer.
382
+ * clearInterval(refresh);
383
+ * await pending;
384
+ * await tradingAPI.cancelAllAfter(0);
385
+ * ```
386
+ */
387
+ async cancelAllAfter(timeoutMs, options) {
388
+ validate(CancelAllAfterSchema, { timeoutMs, options });
389
+ const body = { timeoutMs };
390
+ if (options?.tradingPairId !== undefined) {
391
+ body.tradingPairId = options.tradingPairId;
392
+ }
393
+ return this.makeAuthenticatedRequest(perpRoutes.orders.cancelAllAfter(), {
394
+ method: "POST",
395
+ body: JSON.stringify(body),
396
+ });
397
+ }
356
398
  async replaceOrder(orderId, newOrder) {
357
399
  // Validate inputs before making API call
358
400
  validate(ReplaceOrderSchema, { orderId, newOrder });
@@ -418,7 +460,7 @@ export class TradingAPIImpl extends BaseAPI {
418
460
  side: order.side,
419
461
  price: order.price,
420
462
  quantity: order.quantity,
421
- tradingMode: order.tradingMode || "SPOT",
463
+ tradingMode: order.tradingMode,
422
464
  slippageToleranceBps: order.slippageTolerance !== undefined ? Math.round(order.slippageTolerance * 10000) : undefined,
423
465
  useMasterBalance: order.useMasterBalance,
424
466
  expirationDate: order.expirationDate,
@@ -719,7 +761,7 @@ export class TradingAPIImpl extends BaseAPI {
719
761
  slippageToleranceBps: options.slippageToleranceBps,
720
762
  randomize: options.randomize,
721
763
  reduceOnly: options.reduceOnly,
722
- tradingMode: options.tradingMode || "SPOT",
764
+ tradingMode: options.tradingMode,
723
765
  marginAccountId: options.marginAccountId,
724
766
  riskBucketId: options.riskBucketId,
725
767
  marginMode: options.marginMode,
@@ -0,0 +1,112 @@
1
+ /**
2
+ * Local depth book and BBO state for the market-data `depth` / `bbo`
3
+ * WebSocket channels (0XM-3551).
4
+ *
5
+ * Handoff rules (the server enforces them; the client checks defensively):
6
+ * - A Snapshot replaces the local state, including mid-stream.
7
+ * - A frame with `book_seq <=` the current sequence is stale or a duplicate
8
+ * and is dropped.
9
+ * - Every kept frame must have `prev_book_seq <=` the current sequence (the
10
+ * snapshot's for the first frame, else the last applied frame's): a delta
11
+ * restates every level changed since its link, so it may link below the
12
+ * previous frame. A frame that links above it means a lost frame: rebuild
13
+ * from a new snapshot.
14
+ * - A BBO frame carries the whole top of book, so any frame newer than the
15
+ * state applies without a link check.
16
+ * - Frames that arrive before the snapshot are buffered and replayed through
17
+ * the same rules once it lands.
18
+ * - On a side whose snapshot was not complete, levels beyond its deepest
19
+ * returned level are unknown, so the book keeps only levels at or better
20
+ * than that boundary.
21
+ *
22
+ * Sequences are u64 durable-log positions, held as decimal strings and
23
+ * compared as `BigInt`.
24
+ */
25
+ import type { Bbo, BboEvent, BboSnapshot, DepthBook, DepthDelta, DepthSnapshot } from "@0xmonaco/types";
26
+ /**
27
+ * Quote the market-data sequence numbers in a raw frame before `JSON.parse`,
28
+ * so a u64 above `Number.MAX_SAFE_INTEGER` survives exactly. Only the
29
+ * unescaped `"book_seq":` / `"prev_book_seq":` keys followed by an integer are
30
+ * touched; inside a JSON string those quotes would be escaped.
31
+ */
32
+ export declare function quoteSequenceNumbers(raw: string): string;
33
+ /**
34
+ * {@link readSequence} that leaves an absent or malformed value `undefined`
35
+ * instead of throwing. A JSON number counts only while it is still exact (a
36
+ * quoted integer always does), so a rounded u64 never passes for a precise one.
37
+ */
38
+ export declare function tryReadSequence(data: Record<string, unknown>, key: string): string | undefined;
39
+ export declare function parseDepthSnapshot(raw: unknown): DepthSnapshot;
40
+ export declare function parseDepthDelta(raw: unknown): DepthDelta;
41
+ export declare function parseBboSnapshot(raw: unknown): BboSnapshot;
42
+ export declare function parseBboEvent(raw: unknown): BboEvent;
43
+ /** Compare two non-negative decimal strings numerically. */
44
+ export declare function compareDecimal(a: string, b: string): number;
45
+ /** The result of offering a frame to the local state. */
46
+ export type FrameOutcome =
47
+ /** Applied; the state moved forward. */
48
+ "applied"
49
+ /** Held until the snapshot arrives. */
50
+ | "buffered"
51
+ /** Stale or a duplicate. */
52
+ | "dropped"
53
+ /** Did not link: a frame was lost. */
54
+ | "gap"
55
+ /** Applied, but a side whose snapshot was incomplete is now empty: its remainder is unknown. */
56
+ | "truncated";
57
+ /** The result of applying a snapshot (with any buffered frames replayed). */
58
+ export type SnapshotOutcome = "applied" | "gap" | "truncated";
59
+ /**
60
+ * The sequence guard shared by `depth` and `bbo`: snapshot replaces, stale
61
+ * frames drop, a frame linking above the current sequence is a gap, frames
62
+ * before the snapshot buffer.
63
+ */
64
+ declare abstract class SequencedState<Snapshot extends {
65
+ bookSeq: string;
66
+ }, Frame extends {
67
+ bookSeq: string;
68
+ prevBookSeq: string;
69
+ }> {
70
+ private current;
71
+ private buffered;
72
+ /**
73
+ * Whether a frame must link to the current state. Depth deltas carry only
74
+ * changed levels, so a frame past a lost one is a gap; a BBO frame carries
75
+ * the whole top of book, so any newer one is complete on its own.
76
+ */
77
+ protected readonly checksLinks: boolean;
78
+ /** Whether a snapshot has been applied since the last reset. */
79
+ get ready(): boolean;
80
+ protected abstract resetFrom(snapshot: Snapshot): void;
81
+ protected abstract applyFrame(frame: Frame): void;
82
+ /** Whether the state can no longer be trusted past its known levels (see `"truncated"`). */
83
+ protected truncated(): boolean;
84
+ /** Replace the state with `snapshot`, then replay buffered frames. Returns `"gap"` if a buffered frame does not link. */
85
+ applySnapshot(snapshot: Snapshot): SnapshotOutcome;
86
+ /** Offer a live frame. */
87
+ offer(frame: Frame): FrameOutcome;
88
+ /** Discard everything; the next snapshot starts over. */
89
+ reset(): void;
90
+ }
91
+ /** The local depth book for one `depth` channel. */
92
+ export declare class LocalDepthBook extends SequencedState<DepthSnapshot, DepthDelta> {
93
+ private readonly bids;
94
+ private readonly asks;
95
+ private header;
96
+ protected resetFrom(snapshot: DepthSnapshot): void;
97
+ protected applyFrame(frame: DepthDelta): void;
98
+ protected truncated(): boolean;
99
+ reset(): void;
100
+ /** The current book, or `null` before a snapshot. */
101
+ view(): DepthBook | null;
102
+ }
103
+ /** The local top of book for one `bbo` channel. */
104
+ export declare class LocalBbo extends SequencedState<BboSnapshot, BboEvent> {
105
+ protected readonly checksLinks = false;
106
+ private state;
107
+ protected resetFrom(snapshot: BboSnapshot): void;
108
+ protected applyFrame(frame: BboEvent): void;
109
+ reset(): void;
110
+ view(): Bbo | null;
111
+ }
112
+ export {};