@0xmonaco/core 1.0.44 → 1.0.46
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.
- package/dist/api/perp/routes.d.ts +1 -0
- package/dist/api/perp/routes.js +1 -0
- package/dist/api/trading/api.d.ts +23 -1
- package/dist/api/trading/api.js +27 -0
- package/dist/api/websocket/types.d.ts +3 -1
- package/dist/api/websocket/websocket.js +198 -2
- package/dist/coverage.d.ts +1 -0
- package/dist/coverage.js +1 -0
- package/package.json +3 -3
|
@@ -34,6 +34,7 @@ export declare const perpRoutes: {
|
|
|
34
34
|
pageSize?: number;
|
|
35
35
|
}) => string;
|
|
36
36
|
readonly getTwap: (twapOrderId: string) => string;
|
|
37
|
+
readonly getTwapTca: (twapOrderId: string) => string;
|
|
37
38
|
readonly cancelTwap: (twapOrderId: string) => string;
|
|
38
39
|
};
|
|
39
40
|
readonly delegatedAgents: {
|
package/dist/api/perp/routes.js
CHANGED
|
@@ -32,6 +32,7 @@ export const perpRoutes = {
|
|
|
32
32
|
createTwap: () => `${API_V1}/orders/twap`,
|
|
33
33
|
listTwap: (params) => withQuery(`${API_V1}/orders/twap`, params),
|
|
34
34
|
getTwap: (twapOrderId) => `${API_V1}/orders/twap/${encodeSegment(twapOrderId)}`,
|
|
35
|
+
getTwapTca: (twapOrderId) => `${API_V1}/orders/twap/${encodeSegment(twapOrderId)}/tca`,
|
|
35
36
|
cancelTwap: (twapOrderId) => `${API_V1}/orders/twap/${encodeSegment(twapOrderId)}/cancel`,
|
|
36
37
|
},
|
|
37
38
|
delegatedAgents: {
|
|
@@ -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, ListConditionalOrdersParams, ListConditionalOrdersResponse, ListTwapOrdersParams, ListTwapOrdersResponse, MarginMode, OrderSide, ParentTpSlLegParams, PositionSide, ReplaceOrderResponse, TimeInForce, TradingAPI, TradingMode, TwapOrder } from "@0xmonaco/types";
|
|
24
|
+
import type { BatchCancelOrdersResponse, BatchCreateOrderParams, BatchCreateOrdersResponse, BatchReplaceOrderParams, BatchReplaceOrdersResponse, CancelConditionalOrderResponse, CancelOrderResponse, CancelTwapOrderResponse, ConditionalOrder, CreateOrderResponse, CreateTwapOrderOptions, CreateTwapOrderResponse, GetOrderResponse, GetPaginatedOrdersParams, GetPaginatedOrdersResponse, GetTwapOrderTcaResponse, ListConditionalOrdersParams, ListConditionalOrdersResponse, ListTwapOrdersParams, ListTwapOrdersResponse, MarginMode, OrderSide, ParentTpSlLegParams, PositionSide, ReplaceOrderResponse, 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
|
/**
|
|
@@ -444,6 +444,28 @@ export declare class TradingAPIImpl extends BaseAPI implements TradingAPI {
|
|
|
444
444
|
* ```
|
|
445
445
|
*/
|
|
446
446
|
getTwapOrder(twapOrderId: string): Promise<TwapOrder>;
|
|
447
|
+
/**
|
|
448
|
+
* Gets the post-execution TCA (transaction cost analysis) report for a
|
|
449
|
+
* TERMINAL TWAP parent order.
|
|
450
|
+
*
|
|
451
|
+
* Available once the parent is COMPLETED or CANCELLED (a non-terminal
|
|
452
|
+
* parent returns 400). Reports realized average price against the arrival
|
|
453
|
+
* price and the window-TWAP benchmark over the parent's active window.
|
|
454
|
+
* Sign convention: positive bps = execution beat the reference. Benchmark
|
|
455
|
+
* fields are null when no price series covered the window sufficiently.
|
|
456
|
+
*
|
|
457
|
+
* @param twapOrderId - The TWAP parent order UUID
|
|
458
|
+
* @returns Promise resolving to the TCA report
|
|
459
|
+
* @throws {APIError} When API communication fails, the order is not found,
|
|
460
|
+
* or the order has not reached a terminal state
|
|
461
|
+
*
|
|
462
|
+
* @example
|
|
463
|
+
* ```typescript
|
|
464
|
+
* const tca = await tradingAPI.getTwapOrderTca(twapOrderId);
|
|
465
|
+
* console.log(`vs benchmark: ${tca.performanceVsBenchmarkBps} bps (${tca.benchmarkSource})`);
|
|
466
|
+
* ```
|
|
467
|
+
*/
|
|
468
|
+
getTwapOrderTca(twapOrderId: string): Promise<GetTwapOrderTcaResponse>;
|
|
447
469
|
/**
|
|
448
470
|
* Lists TWAP parent orders for the authenticated user.
|
|
449
471
|
*
|
package/dist/api/trading/api.js
CHANGED
|
@@ -720,6 +720,33 @@ export class TradingAPIImpl extends BaseAPI {
|
|
|
720
720
|
method: "GET",
|
|
721
721
|
});
|
|
722
722
|
}
|
|
723
|
+
/**
|
|
724
|
+
* Gets the post-execution TCA (transaction cost analysis) report for a
|
|
725
|
+
* TERMINAL TWAP parent order.
|
|
726
|
+
*
|
|
727
|
+
* Available once the parent is COMPLETED or CANCELLED (a non-terminal
|
|
728
|
+
* parent returns 400). Reports realized average price against the arrival
|
|
729
|
+
* price and the window-TWAP benchmark over the parent's active window.
|
|
730
|
+
* Sign convention: positive bps = execution beat the reference. Benchmark
|
|
731
|
+
* fields are null when no price series covered the window sufficiently.
|
|
732
|
+
*
|
|
733
|
+
* @param twapOrderId - The TWAP parent order UUID
|
|
734
|
+
* @returns Promise resolving to the TCA report
|
|
735
|
+
* @throws {APIError} When API communication fails, the order is not found,
|
|
736
|
+
* or the order has not reached a terminal state
|
|
737
|
+
*
|
|
738
|
+
* @example
|
|
739
|
+
* ```typescript
|
|
740
|
+
* const tca = await tradingAPI.getTwapOrderTca(twapOrderId);
|
|
741
|
+
* console.log(`vs benchmark: ${tca.performanceVsBenchmarkBps} bps (${tca.benchmarkSource})`);
|
|
742
|
+
* ```
|
|
743
|
+
*/
|
|
744
|
+
async getTwapOrderTca(twapOrderId) {
|
|
745
|
+
validate(CancelTwapOrderSchema, { twapOrderId });
|
|
746
|
+
return await this.makeAuthenticatedRequest(perpRoutes.orders.getTwapTca(twapOrderId), {
|
|
747
|
+
method: "GET",
|
|
748
|
+
});
|
|
749
|
+
}
|
|
723
750
|
/**
|
|
724
751
|
* Lists TWAP parent orders for the authenticated user.
|
|
725
752
|
*
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import type { AccountEvent, ConditionalOrderEvent, Interval, LiquidationEvent, MarketStatsAllEvent, MarketStatsEvent, MonacoWebSocketConfig, OHLCVEvent, OHLCVPriceType, OrderbookEvent, OrderbookQuotationMode, OrderEvent, PositionEvent, SessionCredentials, TradeEvent, TradingMode, TwapOrderEvent, UserBalanceEvent, UserMovementEvent, WebSocketStatus } from "@0xmonaco/types";
|
|
1
|
+
import type { AccountEvent, ConditionalOrderEvent, InstrumentEvent, Interval, LiquidationEvent, MarketStatsAllEvent, MarketStatsEvent, MonacoWebSocketConfig, OHLCVEvent, OHLCVPriceType, OrderbookEvent, OrderbookQuotationMode, OrderEvent, PositionEvent, SessionCredentials, TradeEvent, TradingMode, TwapOrderEvent, UserBalanceEvent, UserMovementEvent, WebSocketStatus } from "@0xmonaco/types";
|
|
2
2
|
export type { ResyncHandler, StatusHandler, WebSocketResyncInfo } from "@0xmonaco/types";
|
|
3
3
|
export type MessageHandler<T> = (data: T) => void;
|
|
4
4
|
/**
|
|
@@ -46,6 +46,8 @@ export interface MonacoWebSocket {
|
|
|
46
46
|
liquidations: (handler: MessageHandler<LiquidationEvent>, tradingPairId?: string) => () => void;
|
|
47
47
|
/** Subscribe to account-level margin health updates for the authenticated user (requires authentication) */
|
|
48
48
|
account: (handler: MessageHandler<AccountEvent>) => () => void;
|
|
49
|
+
/** Subscribe to market lifecycle + config changes (public); omit `tradingPairId` for all markets */
|
|
50
|
+
instruments: (handler: MessageHandler<InstrumentEvent>, tradingPairId?: string) => () => void;
|
|
49
51
|
/** Subscribe to per-market statistics for one trading pair (public) */
|
|
50
52
|
marketStats: (tradingPairId: string, handler: MessageHandler<MarketStatsEvent>) => () => void;
|
|
51
53
|
/** Subscribe to the all-markets statistics summary (public) */
|
|
@@ -13,6 +13,7 @@ const CONDITIONAL_ORDER_TRIGGER_SOURCES = ["MARK_PRICE"];
|
|
|
13
13
|
const CONDITIONAL_ORDER_STATES = ["PENDING_PARENT", "ACTIVE", "TRIGGERING", "TRIGGERED", "CANCELLED", "EXPIRED", "FAILED"];
|
|
14
14
|
const CONDITIONAL_ORDER_ASSOCIATION_TYPES = ["POSITION", "PARENT_ORDER"];
|
|
15
15
|
const TWAP_ORDER_STATES = ["PENDING", "ACTIVE", "COMPLETED", "CANCELLED"];
|
|
16
|
+
const INSTRUMENT_CHANGE_KINDS = ["listing", "delisting", "halt", "unhalt", "config_change"];
|
|
16
17
|
function isRecord(value) {
|
|
17
18
|
return typeof value === "object" && value !== null && !Array.isArray(value);
|
|
18
19
|
}
|
|
@@ -48,6 +49,13 @@ function readOptionalNumber(source, field) {
|
|
|
48
49
|
}
|
|
49
50
|
return value;
|
|
50
51
|
}
|
|
52
|
+
function readStringArray(source, field) {
|
|
53
|
+
const value = source[field];
|
|
54
|
+
if (!Array.isArray(value) || value.some((item) => typeof item !== "string")) {
|
|
55
|
+
throw new Error(`Invalid WebSocket event: ${field} must be an array of strings`);
|
|
56
|
+
}
|
|
57
|
+
return value;
|
|
58
|
+
}
|
|
51
59
|
function readEnum(source, field, allowed) {
|
|
52
60
|
const value = readString(source, field);
|
|
53
61
|
if (!allowed.includes(value)) {
|
|
@@ -269,6 +277,42 @@ function parseMarketStatsAllEvent(rawData) {
|
|
|
269
277
|
markets: rawData.markets.map(parseMarketStatsSummary),
|
|
270
278
|
};
|
|
271
279
|
}
|
|
280
|
+
function parseInstrumentEventData(raw) {
|
|
281
|
+
if (!isRecord(raw)) {
|
|
282
|
+
throw new Error("Invalid instrument event: data must be an object");
|
|
283
|
+
}
|
|
284
|
+
const data = keysToCamelCase(raw);
|
|
285
|
+
return {
|
|
286
|
+
change: readEnum(data, "change", INSTRUMENT_CHANGE_KINDS),
|
|
287
|
+
tradingPairId: readString(data, "tradingPairId"),
|
|
288
|
+
symbol: readString(data, "symbol"),
|
|
289
|
+
isActive: readBoolean(data, "isActive"),
|
|
290
|
+
tickSize: readString(data, "tickSize"),
|
|
291
|
+
quantityStepSize: readString(data, "quantityStepSize"),
|
|
292
|
+
minOrderSize: readString(data, "minOrderSize"),
|
|
293
|
+
maxOrderSize: readString(data, "maxOrderSize"),
|
|
294
|
+
marketType: readString(data, "marketType"),
|
|
295
|
+
category: readString(data, "category"),
|
|
296
|
+
maxLeverage: readOptionalString(data, "maxLeverage"),
|
|
297
|
+
initialMarginRate: readOptionalString(data, "initialMarginRate"),
|
|
298
|
+
maintenanceMarginRate: readOptionalString(data, "maintenanceMarginRate"),
|
|
299
|
+
// Values are snake_case wire field NAMES (e.g. "tick_size"); only object
|
|
300
|
+
// keys are camelCased, so they pass through untouched.
|
|
301
|
+
changedFields: readStringArray(data, "changedFields"),
|
|
302
|
+
updatedAt: readString(data, "updatedAt"),
|
|
303
|
+
};
|
|
304
|
+
}
|
|
305
|
+
function parseInstrumentEvent(rawData) {
|
|
306
|
+
if (!isRecord(rawData)) {
|
|
307
|
+
throw new Error("Invalid instrument event: payload must be an object");
|
|
308
|
+
}
|
|
309
|
+
// The frame's top-level `trading_pair_id` is not inside `data`, so it stays
|
|
310
|
+
// snake_case (only `data` is camelCased by parseInstrumentEventData).
|
|
311
|
+
return {
|
|
312
|
+
tradingPairId: readString(rawData, "trading_pair_id"),
|
|
313
|
+
data: parseInstrumentEventData(rawData.data),
|
|
314
|
+
};
|
|
315
|
+
}
|
|
272
316
|
/**
|
|
273
317
|
* Parse a `twap_orders` channel frame into a typed event. Only the envelope
|
|
274
318
|
* and the snapshot's identity/state fields are validated strictly — the rest
|
|
@@ -337,6 +381,23 @@ export function createMonacoWebSocket(baseUrl, options = {}) {
|
|
|
337
381
|
let pendingResyncCode = null;
|
|
338
382
|
// Handler storage: channel -> set of handlers (supports multiple subscribers per channel)
|
|
339
383
|
const handlers = new Map();
|
|
384
|
+
// Subscribe-time Snapshot frame handlers, keyed by the RAW subscribed
|
|
385
|
+
// channel: the server echoes that exact channel on the Snapshot frame
|
|
386
|
+
// (unlike live Event frames, which can arrive on a more specific channel and
|
|
387
|
+
// are prefix-matched). Only subscriptions that opt in receive them — a
|
|
388
|
+
// snapshot's data shape differs from the channel's Event shape, so blindly
|
|
389
|
+
// reusing the Event handlers would feed every other channel's parser a frame
|
|
390
|
+
// it must reject.
|
|
391
|
+
const snapshotHandlers = new Map();
|
|
392
|
+
// Latest known full-config frame per market for each subscribed instrument
|
|
393
|
+
// channel, seeded by the subscribe-time Snapshot and superseded by every
|
|
394
|
+
// live frame (instrument frames are terminal-state, never deltas). The
|
|
395
|
+
// server sends a snapshot only for a genuinely new wire subscription, so a
|
|
396
|
+
// late local subscriber on an already-held channel bootstraps from this
|
|
397
|
+
// cache instead of starting blind. Dropped when the channel's last local
|
|
398
|
+
// subscriber unsubscribes — the next first subscriber triggers a fresh wire
|
|
399
|
+
// Subscribe whose snapshot is authoritative, never the stale cache.
|
|
400
|
+
const instrumentBaseline = new Map();
|
|
340
401
|
const getStatus = () => {
|
|
341
402
|
if (!ws)
|
|
342
403
|
return reconnectTimer ? "reconnecting" : "disconnected";
|
|
@@ -431,6 +492,11 @@ export function createMonacoWebSocket(baseUrl, options = {}) {
|
|
|
431
492
|
}
|
|
432
493
|
};
|
|
433
494
|
const resubscribeAll = () => {
|
|
495
|
+
// Every re-sent Subscribe is genuinely new to the server (new
|
|
496
|
+
// connection), so fresh authoritative snapshots are inbound: the
|
|
497
|
+
// pre-outage instrument cache is stale — markets may have been delisted
|
|
498
|
+
// during the gap — and must not be replayed to late subscribers.
|
|
499
|
+
instrumentBaseline.clear();
|
|
434
500
|
const channels = Array.from(handlers.keys());
|
|
435
501
|
if (channels.length > 0) {
|
|
436
502
|
send({ type: "Subscribe", channels });
|
|
@@ -460,7 +526,17 @@ export function createMonacoWebSocket(baseUrl, options = {}) {
|
|
|
460
526
|
return frameChannel === subscriptionChannel || frameChannel.startsWith(`${subscriptionChannel}:`);
|
|
461
527
|
};
|
|
462
528
|
const routeMessage = (msg) => {
|
|
463
|
-
if (
|
|
529
|
+
if (!msg.channel)
|
|
530
|
+
return;
|
|
531
|
+
if (msg.type === "Snapshot") {
|
|
532
|
+
// Exact match only: the Snapshot frame's channel echoes the raw
|
|
533
|
+
// subscribed channel, never a more specific one.
|
|
534
|
+
for (const handler of snapshotHandlers.get(msg.channel) ?? []) {
|
|
535
|
+
handler(msg.data);
|
|
536
|
+
}
|
|
537
|
+
return;
|
|
538
|
+
}
|
|
539
|
+
if (msg.type !== "Event")
|
|
464
540
|
return;
|
|
465
541
|
// Find matching handlers and call all of them
|
|
466
542
|
for (const [subscriptionChannel, channelHandlers] of handlers) {
|
|
@@ -579,23 +655,38 @@ export function createMonacoWebSocket(baseUrl, options = {}) {
|
|
|
579
655
|
stopHeartbeat();
|
|
580
656
|
pendingResyncCode = null;
|
|
581
657
|
handlers.clear();
|
|
658
|
+
snapshotHandlers.clear();
|
|
659
|
+
instrumentBaseline.clear();
|
|
582
660
|
ws?.close(1000, "Client disconnect");
|
|
583
661
|
ws = null;
|
|
584
662
|
options.onStatusChange?.("disconnected");
|
|
585
663
|
};
|
|
586
|
-
const subscribe = (channel, handler) => {
|
|
664
|
+
const subscribe = (channel, handler, snapshotHandler) => {
|
|
587
665
|
const isFirstSubscriber = !handlers.has(channel);
|
|
588
666
|
// Add handler to the set for this channel
|
|
589
667
|
if (!handlers.has(channel)) {
|
|
590
668
|
handlers.set(channel, new Set());
|
|
591
669
|
}
|
|
592
670
|
handlers.get(channel)?.add(handler);
|
|
671
|
+
if (snapshotHandler) {
|
|
672
|
+
if (!snapshotHandlers.has(channel)) {
|
|
673
|
+
snapshotHandlers.set(channel, new Set());
|
|
674
|
+
}
|
|
675
|
+
snapshotHandlers.get(channel)?.add(snapshotHandler);
|
|
676
|
+
}
|
|
593
677
|
// Only send Subscribe message on first subscriber for this channel
|
|
594
678
|
if (isFirstSubscriber && ws?.readyState === WebSocket.OPEN) {
|
|
595
679
|
send({ type: "Subscribe", channels: [channel] });
|
|
596
680
|
}
|
|
597
681
|
// Return unsubscribe function
|
|
598
682
|
return () => {
|
|
683
|
+
if (snapshotHandler) {
|
|
684
|
+
const channelSnapshotHandlers = snapshotHandlers.get(channel);
|
|
685
|
+
channelSnapshotHandlers?.delete(snapshotHandler);
|
|
686
|
+
if (channelSnapshotHandlers?.size === 0) {
|
|
687
|
+
snapshotHandlers.delete(channel);
|
|
688
|
+
}
|
|
689
|
+
}
|
|
599
690
|
const channelHandlers = handlers.get(channel);
|
|
600
691
|
if (channelHandlers) {
|
|
601
692
|
channelHandlers.delete(handler);
|
|
@@ -952,6 +1043,110 @@ export function createMonacoWebSocket(baseUrl, options = {}) {
|
|
|
952
1043
|
}
|
|
953
1044
|
});
|
|
954
1045
|
};
|
|
1046
|
+
/**
|
|
1047
|
+
* Subscribe to market lifecycle + configuration changes (public): listings,
|
|
1048
|
+
* delistings, and edits to tick size, step size, order-size bounds, or (for
|
|
1049
|
+
* perp markets) leverage/margin-rate bounds. Omit `tradingPairId` for all
|
|
1050
|
+
* markets, or pass one to filter. Every frame carries the market's FULL
|
|
1051
|
+
* current config as a terminal-state snapshot (never an additive delta);
|
|
1052
|
+
* `changedFields` names what the mutation touched. A subscribe-time snapshot
|
|
1053
|
+
* of the current active-pair list arrives before the live diffs; a
|
|
1054
|
+
* subscriber joining a channel this client already holds is replayed the
|
|
1055
|
+
* current baseline from a local cache instead (the server snapshots only
|
|
1056
|
+
* genuinely new wire subscriptions). Lag policy is Conflate: a lagging
|
|
1057
|
+
* subscriber is fast-forwarded, no gap signal.
|
|
1058
|
+
*/
|
|
1059
|
+
const subscribeInstruments = (handler, tradingPairId) => {
|
|
1060
|
+
if (!handler) {
|
|
1061
|
+
throw new Error("instruments subscription requires a handler");
|
|
1062
|
+
}
|
|
1063
|
+
if (tradingPairId !== undefined && tradingPairId.length === 0) {
|
|
1064
|
+
throw new Error("instruments tradingPairId cannot be empty");
|
|
1065
|
+
}
|
|
1066
|
+
// Every live frame is published on instrument:<pairId>; a bare
|
|
1067
|
+
// `instrument` subscription receives all of them via routeMessage's prefix
|
|
1068
|
+
// match, so unlike market_stats there is no distinct all-markets frame
|
|
1069
|
+
// shape to guard against.
|
|
1070
|
+
const channel = tradingPairId ? `instrument:${tradingPairId}` : "instrument";
|
|
1071
|
+
const cacheEvent = (event) => {
|
|
1072
|
+
let byPair = instrumentBaseline.get(channel);
|
|
1073
|
+
if (!byPair) {
|
|
1074
|
+
byPair = new Map();
|
|
1075
|
+
instrumentBaseline.set(channel, byPair);
|
|
1076
|
+
}
|
|
1077
|
+
byPair.set(event.tradingPairId, event);
|
|
1078
|
+
};
|
|
1079
|
+
// A subscriber joining a channel this connection already holds gets no
|
|
1080
|
+
// fresh server snapshot (the wire Subscribe is suppressed, and the server
|
|
1081
|
+
// snapshots only genuinely new subscriptions), so replay the cached
|
|
1082
|
+
// baseline to it instead. Asynchronously, matching how a real snapshot
|
|
1083
|
+
// arrives — never during the subscribe call itself.
|
|
1084
|
+
const isLateSubscriber = handlers.has(channel);
|
|
1085
|
+
let unsubscribed = false;
|
|
1086
|
+
const unsubscribe = subscribe(channel, (rawData) => {
|
|
1087
|
+
try {
|
|
1088
|
+
const event = parseInstrumentEvent(rawData);
|
|
1089
|
+
cacheEvent(event);
|
|
1090
|
+
handler(event);
|
|
1091
|
+
}
|
|
1092
|
+
catch (err) {
|
|
1093
|
+
console.error("WebSocket: Error processing instrument event", err);
|
|
1094
|
+
}
|
|
1095
|
+
},
|
|
1096
|
+
// The subscribe-time baseline arrives as one Snapshot frame whose data is
|
|
1097
|
+
// a flat InstrumentEventData[] (a `listing` per active market, no
|
|
1098
|
+
// per-item envelope). Fan it out as one event per market so the handler
|
|
1099
|
+
// sees the same shape for baseline and live frames.
|
|
1100
|
+
(rawData) => {
|
|
1101
|
+
try {
|
|
1102
|
+
if (!Array.isArray(rawData)) {
|
|
1103
|
+
throw new Error("Invalid instrument snapshot: data must be an array");
|
|
1104
|
+
}
|
|
1105
|
+
const events = rawData.map(parseInstrumentEventData).map((item) => ({ tradingPairId: item.tradingPairId, data: item }));
|
|
1106
|
+
// A snapshot is the authoritative active-market set: replace the
|
|
1107
|
+
// channel's cache wholesale rather than upserting, so markets absent
|
|
1108
|
+
// from it (delisted while disconnected) don't linger.
|
|
1109
|
+
instrumentBaseline.set(channel, new Map(events.map((event) => [event.tradingPairId, event])));
|
|
1110
|
+
for (const event of events) {
|
|
1111
|
+
handler(event);
|
|
1112
|
+
}
|
|
1113
|
+
}
|
|
1114
|
+
catch (err) {
|
|
1115
|
+
console.error("WebSocket: Error processing instrument snapshot", err);
|
|
1116
|
+
}
|
|
1117
|
+
});
|
|
1118
|
+
// Replay only while the socket is open: during abnormal-close backoff the
|
|
1119
|
+
// handlers and cache survive but the state is pre-outage — the reconnect's
|
|
1120
|
+
// resubscribe clears it and a fresh authoritative snapshot follows, so a
|
|
1121
|
+
// backoff-time subscriber must wait for that instead.
|
|
1122
|
+
if (isLateSubscriber && ws?.readyState === WebSocket.OPEN) {
|
|
1123
|
+
const cached = instrumentBaseline.get(channel);
|
|
1124
|
+
if (cached && cached.size > 0) {
|
|
1125
|
+
const replay = [...cached.values()];
|
|
1126
|
+
queueMicrotask(() => {
|
|
1127
|
+
if (unsubscribed)
|
|
1128
|
+
return;
|
|
1129
|
+
for (const event of replay) {
|
|
1130
|
+
try {
|
|
1131
|
+
handler(event);
|
|
1132
|
+
}
|
|
1133
|
+
catch (err) {
|
|
1134
|
+
console.error("WebSocket: Error processing instrument baseline replay", err);
|
|
1135
|
+
}
|
|
1136
|
+
}
|
|
1137
|
+
});
|
|
1138
|
+
}
|
|
1139
|
+
}
|
|
1140
|
+
return () => {
|
|
1141
|
+
unsubscribed = true;
|
|
1142
|
+
unsubscribe();
|
|
1143
|
+
// Last local subscriber gone: the wire Unsubscribe was sent, so the
|
|
1144
|
+
// cache would only go stale from here — drop it.
|
|
1145
|
+
if (!handlers.has(channel)) {
|
|
1146
|
+
instrumentBaseline.delete(channel);
|
|
1147
|
+
}
|
|
1148
|
+
};
|
|
1149
|
+
};
|
|
955
1150
|
/**
|
|
956
1151
|
* Subscribe to per-market statistics for a single trading pair (public). Each
|
|
957
1152
|
* frame carries the market's full current stats as a terminal-state snapshot.
|
|
@@ -1029,6 +1224,7 @@ export function createMonacoWebSocket(baseUrl, options = {}) {
|
|
|
1029
1224
|
positions: subscribePositions,
|
|
1030
1225
|
liquidations: subscribeLiquidations,
|
|
1031
1226
|
account: subscribeAccount,
|
|
1227
|
+
instruments: subscribeInstruments,
|
|
1032
1228
|
marketStats: subscribeMarketStats,
|
|
1033
1229
|
marketStatsAll: subscribeMarketStatsAll,
|
|
1034
1230
|
};
|
package/dist/coverage.d.ts
CHANGED
package/dist/coverage.js
CHANGED
|
@@ -13,6 +13,7 @@ export const COVERED = {
|
|
|
13
13
|
cancel_twap_order: "cancelTwapOrder",
|
|
14
14
|
create_twap_order: "createTwapOrder",
|
|
15
15
|
get_twap_order: "getTwapOrder",
|
|
16
|
+
get_twap_order_tca: "getTwapOrderTca",
|
|
16
17
|
list_twap_orders: "listTwapOrders",
|
|
17
18
|
close_position: "closePosition",
|
|
18
19
|
create_challenge: "createChallenge",
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@0xmonaco/core",
|
|
3
|
-
"version": "1.0.
|
|
3
|
+
"version": "1.0.46",
|
|
4
4
|
"type": "module",
|
|
5
5
|
"homepage": "https://docs.0xmonaco.com/sdk/typescript",
|
|
6
6
|
"main": "./dist/index.js",
|
|
@@ -19,8 +19,8 @@
|
|
|
19
19
|
"viem": "^2.45.2"
|
|
20
20
|
},
|
|
21
21
|
"dependencies": {
|
|
22
|
-
"@0xmonaco/contracts": "1.0.
|
|
23
|
-
"@0xmonaco/types": "1.0.
|
|
22
|
+
"@0xmonaco/contracts": "1.0.46",
|
|
23
|
+
"@0xmonaco/types": "1.0.46",
|
|
24
24
|
"@noble/curves": "^1.9.1",
|
|
25
25
|
"@noble/hashes": "^1.8.0",
|
|
26
26
|
"http-status-codes": "^2.3.0"
|