@xapy/orderbook 0.1.29 → 0.1.31
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/README.md +3 -0
- package/dist/exchanges/aster/index.d.cts +1 -1
- package/dist/exchanges/aster/index.d.ts +1 -1
- package/dist/exchanges/binance/index.d.cts +1 -1
- package/dist/exchanges/binance/index.d.ts +1 -1
- package/dist/exchanges/bingx/index.cjs +8 -85
- package/dist/exchanges/bingx/index.cjs.map +1 -1
- package/dist/exchanges/bingx/index.d.cts +12 -7
- package/dist/exchanges/bingx/index.d.ts +12 -7
- package/dist/exchanges/bingx/index.js +8 -85
- package/dist/exchanges/bingx/index.js.map +1 -1
- package/dist/exchanges/bitget/index.d.cts +1 -1
- package/dist/exchanges/bitget/index.d.ts +1 -1
- package/dist/exchanges/bybit/index.d.cts +1 -1
- package/dist/exchanges/bybit/index.d.ts +1 -1
- package/dist/exchanges/coinex/index.d.cts +1 -1
- package/dist/exchanges/coinex/index.d.ts +1 -1
- package/dist/exchanges/deribit/index.d.cts +1 -1
- package/dist/exchanges/deribit/index.d.ts +1 -1
- package/dist/exchanges/edgex/index.d.cts +1 -1
- package/dist/exchanges/edgex/index.d.ts +1 -1
- package/dist/exchanges/gate/index.d.cts +1 -1
- package/dist/exchanges/gate/index.d.ts +1 -1
- package/dist/exchanges/hyperliquid/index.d.cts +1 -1
- package/dist/exchanges/hyperliquid/index.d.ts +1 -1
- package/dist/exchanges/kucoin/index.d.cts +1 -1
- package/dist/exchanges/kucoin/index.d.ts +1 -1
- package/dist/exchanges/lighter/index.d.cts +1 -1
- package/dist/exchanges/lighter/index.d.ts +1 -1
- package/dist/exchanges/mexc/index.cjs +729 -0
- package/dist/exchanges/mexc/index.cjs.map +1 -0
- package/dist/exchanges/mexc/index.d.cts +57 -0
- package/dist/exchanges/mexc/index.d.ts +57 -0
- package/dist/exchanges/mexc/index.js +721 -0
- package/dist/exchanges/mexc/index.js.map +1 -0
- package/dist/exchanges/okx/index.d.cts +1 -1
- package/dist/exchanges/okx/index.d.ts +1 -1
- package/dist/exchanges/whitebit/index.d.cts +1 -1
- package/dist/exchanges/whitebit/index.d.ts +1 -1
- package/dist/index.cjs +337 -89
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +3 -2
- package/dist/index.d.ts +3 -2
- package/dist/index.js +337 -90
- package/dist/index.js.map +1 -1
- package/dist/{stream-D5-FyNGW.d.cts → stream-Dyq24thx.d.cts} +1 -1
- package/dist/{stream-D5-FyNGW.d.ts → stream-Dyq24thx.d.ts} +1 -1
- package/package.json +12 -1
package/dist/index.d.cts
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
|
-
import { B as BookEvent, O as OrderbookLevel, M as Market } from './stream-
|
|
2
|
-
export { C as ClientOptions, E as ExchangeName, a as Orderbook, b as OrderbookOptions, c as OrderbookStream, S as Side, d as StreamEvents } from './stream-
|
|
1
|
+
import { B as BookEvent, O as OrderbookLevel, M as Market } from './stream-Dyq24thx.cjs';
|
|
2
|
+
export { C as ClientOptions, E as ExchangeName, a as Orderbook, b as OrderbookOptions, c as OrderbookStream, S as Side, d as StreamEvents } from './stream-Dyq24thx.cjs';
|
|
3
3
|
export { BybitClient } from './exchanges/bybit/index.cjs';
|
|
4
4
|
export { BitgetClient } from './exchanges/bitget/index.cjs';
|
|
5
5
|
export { GateClient } from './exchanges/gate/index.cjs';
|
|
@@ -14,6 +14,7 @@ export { LighterClient } from './exchanges/lighter/index.cjs';
|
|
|
14
14
|
export { EdgexClient } from './exchanges/edgex/index.cjs';
|
|
15
15
|
export { WhitebitClient } from './exchanges/whitebit/index.cjs';
|
|
16
16
|
export { AsterClient } from './exchanges/aster/index.cjs';
|
|
17
|
+
export { MexcClient } from './exchanges/mexc/index.cjs';
|
|
17
18
|
|
|
18
19
|
declare class ExchangeError extends Error {
|
|
19
20
|
readonly exchange: string;
|
package/dist/index.d.ts
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
|
-
import { B as BookEvent, O as OrderbookLevel, M as Market } from './stream-
|
|
2
|
-
export { C as ClientOptions, E as ExchangeName, a as Orderbook, b as OrderbookOptions, c as OrderbookStream, S as Side, d as StreamEvents } from './stream-
|
|
1
|
+
import { B as BookEvent, O as OrderbookLevel, M as Market } from './stream-Dyq24thx.js';
|
|
2
|
+
export { C as ClientOptions, E as ExchangeName, a as Orderbook, b as OrderbookOptions, c as OrderbookStream, S as Side, d as StreamEvents } from './stream-Dyq24thx.js';
|
|
3
3
|
export { BybitClient } from './exchanges/bybit/index.js';
|
|
4
4
|
export { BitgetClient } from './exchanges/bitget/index.js';
|
|
5
5
|
export { GateClient } from './exchanges/gate/index.js';
|
|
@@ -14,6 +14,7 @@ export { LighterClient } from './exchanges/lighter/index.js';
|
|
|
14
14
|
export { EdgexClient } from './exchanges/edgex/index.js';
|
|
15
15
|
export { WhitebitClient } from './exchanges/whitebit/index.js';
|
|
16
16
|
export { AsterClient } from './exchanges/aster/index.js';
|
|
17
|
+
export { MexcClient } from './exchanges/mexc/index.js';
|
|
17
18
|
|
|
18
19
|
declare class ExchangeError extends Error {
|
|
19
20
|
readonly exchange: string;
|
package/dist/index.js
CHANGED
|
@@ -1248,48 +1248,14 @@ function streamBingxOrderbook(opts) {
|
|
|
1248
1248
|
const level = opts.depth ?? 20;
|
|
1249
1249
|
const interval = opts.interval ?? "500ms";
|
|
1250
1250
|
const depthDataType = isSpot ? `${wsSymbol}@depth${level}` : `${wsSymbol}@depth${level}@${interval}`;
|
|
1251
|
-
const tickerDataType = `${wsSymbol}@bookTicker`;
|
|
1252
1251
|
const merger = new BookMerger();
|
|
1253
1252
|
let ws = null;
|
|
1254
1253
|
const stream = new OrderbookStream(() => ws?.close());
|
|
1255
|
-
const subId = Date.now();
|
|
1256
1254
|
const subscribeDepth = JSON.stringify({
|
|
1257
|
-
id: `sub-depth-${
|
|
1255
|
+
id: `sub-depth-${Date.now()}`,
|
|
1258
1256
|
reqType: "sub",
|
|
1259
1257
|
dataType: depthDataType
|
|
1260
1258
|
});
|
|
1261
|
-
const subscribeTicker = JSON.stringify({
|
|
1262
|
-
id: `sub-ticker-${subId}`,
|
|
1263
|
-
reqType: "sub",
|
|
1264
|
-
dataType: tickerDataType
|
|
1265
|
-
});
|
|
1266
|
-
let lastMerged = null;
|
|
1267
|
-
let lastTicker = null;
|
|
1268
|
-
const emitOverlayed = () => {
|
|
1269
|
-
if (!lastMerged) return;
|
|
1270
|
-
let bids = lastMerged.bids;
|
|
1271
|
-
let asks = lastMerged.asks;
|
|
1272
|
-
let timestamp = lastMerged.timestamp;
|
|
1273
|
-
let sequence = lastMerged.sequence;
|
|
1274
|
-
if (lastTicker) {
|
|
1275
|
-
bids = overlayBid(bids, lastTicker.bid);
|
|
1276
|
-
asks = overlayAsk(asks, lastTicker.ask);
|
|
1277
|
-
if (lastTicker.ts > timestamp) {
|
|
1278
|
-
timestamp = lastTicker.ts;
|
|
1279
|
-
sequence = Math.max(sequence, lastTicker.ts);
|
|
1280
|
-
}
|
|
1281
|
-
}
|
|
1282
|
-
const book = {
|
|
1283
|
-
exchange: "bingx",
|
|
1284
|
-
symbol: opts.symbol,
|
|
1285
|
-
market: opts.market,
|
|
1286
|
-
bids,
|
|
1287
|
-
asks,
|
|
1288
|
-
timestamp,
|
|
1289
|
-
sequence
|
|
1290
|
-
};
|
|
1291
|
-
stream.emit("update", book);
|
|
1292
|
-
};
|
|
1293
1259
|
const handleDepth = (d, frameTs) => {
|
|
1294
1260
|
const ts = d.T ?? d.ts ?? frameTs ?? Date.now();
|
|
1295
1261
|
const event = {
|
|
@@ -1301,26 +1267,16 @@ function streamBingxOrderbook(opts) {
|
|
|
1301
1267
|
};
|
|
1302
1268
|
const r = merger.apply(event);
|
|
1303
1269
|
if (!r.ok) return;
|
|
1304
|
-
|
|
1270
|
+
const book = {
|
|
1271
|
+
exchange: "bingx",
|
|
1272
|
+
symbol: opts.symbol,
|
|
1273
|
+
market: opts.market,
|
|
1305
1274
|
bids: r.bids,
|
|
1306
1275
|
asks: r.asks,
|
|
1307
1276
|
timestamp: r.timestamp,
|
|
1308
1277
|
sequence: r.sequence
|
|
1309
1278
|
};
|
|
1310
|
-
|
|
1311
|
-
};
|
|
1312
|
-
const handleTicker = (t) => {
|
|
1313
|
-
const bidPrice = Number(t.b);
|
|
1314
|
-
const askPrice = Number(t.a);
|
|
1315
|
-
const bidSize = Number(t.B);
|
|
1316
|
-
const askSize = Number(t.A);
|
|
1317
|
-
if (!Number.isFinite(bidPrice) || !Number.isFinite(askPrice)) return;
|
|
1318
|
-
lastTicker = {
|
|
1319
|
-
bid: { price: bidPrice, size: bidSize },
|
|
1320
|
-
ask: { price: askPrice, size: askSize },
|
|
1321
|
-
ts: t.T ?? t.E ?? Date.now()
|
|
1322
|
-
};
|
|
1323
|
-
emitOverlayed();
|
|
1279
|
+
stream.emit("update", book);
|
|
1324
1280
|
};
|
|
1325
1281
|
const handlePayload = (text) => {
|
|
1326
1282
|
if (text === "Ping" || text === "ping") {
|
|
@@ -1346,12 +1302,8 @@ function streamBingxOrderbook(opts) {
|
|
|
1346
1302
|
return;
|
|
1347
1303
|
}
|
|
1348
1304
|
if (!msg.dataType || !msg.data) return;
|
|
1349
|
-
|
|
1350
|
-
|
|
1351
|
-
handleDepth(msg.data, frameTs);
|
|
1352
|
-
} else if (msg.dataType === tickerDataType) {
|
|
1353
|
-
handleTicker(msg.data);
|
|
1354
|
-
}
|
|
1305
|
+
if (msg.dataType !== depthDataType) return;
|
|
1306
|
+
handleDepth(msg.data, msg.ts ?? msg.timestamp);
|
|
1355
1307
|
};
|
|
1356
1308
|
const onMessage = async (raw) => {
|
|
1357
1309
|
if (typeof raw === "string") {
|
|
@@ -1365,10 +1317,7 @@ function streamBingxOrderbook(opts) {
|
|
|
1365
1317
|
url: isSpot ? WS_SPOT3 : WS_SWAP,
|
|
1366
1318
|
onOpen: (sock) => {
|
|
1367
1319
|
merger.reset();
|
|
1368
|
-
lastMerged = null;
|
|
1369
|
-
lastTicker = null;
|
|
1370
1320
|
sock.send(subscribeDepth);
|
|
1371
|
-
sock.send(subscribeTicker);
|
|
1372
1321
|
},
|
|
1373
1322
|
onMessage
|
|
1374
1323
|
// bingx server is the one that sends Ping frames; we don't need client pings.
|
|
@@ -1380,32 +1329,6 @@ function streamBingxOrderbook(opts) {
|
|
|
1380
1329
|
ws.connect().catch((e) => stream.emit("error", e));
|
|
1381
1330
|
return stream;
|
|
1382
1331
|
}
|
|
1383
|
-
function overlayAsk(asks, top) {
|
|
1384
|
-
if (!(top.size > 0)) return asks;
|
|
1385
|
-
let i = 0;
|
|
1386
|
-
for (; i < asks.length; i++) {
|
|
1387
|
-
const level = asks[i];
|
|
1388
|
-
if (!level || level.price >= top.price) break;
|
|
1389
|
-
}
|
|
1390
|
-
const at = asks[i];
|
|
1391
|
-
if (at && at.price === top.price) {
|
|
1392
|
-
return [{ price: top.price, size: top.size }, ...asks.slice(i + 1)];
|
|
1393
|
-
}
|
|
1394
|
-
return [{ price: top.price, size: top.size }, ...asks.slice(i)];
|
|
1395
|
-
}
|
|
1396
|
-
function overlayBid(bids, top) {
|
|
1397
|
-
if (!(top.size > 0)) return bids;
|
|
1398
|
-
let i = 0;
|
|
1399
|
-
for (; i < bids.length; i++) {
|
|
1400
|
-
const level = bids[i];
|
|
1401
|
-
if (!level || level.price <= top.price) break;
|
|
1402
|
-
}
|
|
1403
|
-
const at = bids[i];
|
|
1404
|
-
if (at && at.price === top.price) {
|
|
1405
|
-
return [{ price: top.price, size: top.size }, ...bids.slice(i + 1)];
|
|
1406
|
-
}
|
|
1407
|
-
return [{ price: top.price, size: top.size }, ...bids.slice(i)];
|
|
1408
|
-
}
|
|
1409
1332
|
|
|
1410
1333
|
// src/exchanges/bingx/index.ts
|
|
1411
1334
|
var BingxClient = class {
|
|
@@ -1515,8 +1438,8 @@ function streamCoinexOrderbook(opts) {
|
|
|
1515
1438
|
let timestamp = lastMerged.timestamp;
|
|
1516
1439
|
let sequence = lastMerged.sequence;
|
|
1517
1440
|
if (lastTicker) {
|
|
1518
|
-
bids =
|
|
1519
|
-
asks =
|
|
1441
|
+
bids = overlayBid(bids, lastTicker.bid);
|
|
1442
|
+
asks = overlayAsk(asks, lastTicker.ask);
|
|
1520
1443
|
if (lastTicker.ts > timestamp) {
|
|
1521
1444
|
timestamp = lastTicker.ts;
|
|
1522
1445
|
sequence = Math.max(sequence, lastTicker.ts);
|
|
@@ -1614,7 +1537,7 @@ function streamCoinexOrderbook(opts) {
|
|
|
1614
1537
|
ws.connect().catch((e) => stream.emit("error", e));
|
|
1615
1538
|
return stream;
|
|
1616
1539
|
}
|
|
1617
|
-
function
|
|
1540
|
+
function overlayAsk(asks, top) {
|
|
1618
1541
|
if (!(top.size > 0)) return asks;
|
|
1619
1542
|
let i = 0;
|
|
1620
1543
|
for (; i < asks.length; i++) {
|
|
@@ -1627,7 +1550,7 @@ function overlayAsk2(asks, top) {
|
|
|
1627
1550
|
}
|
|
1628
1551
|
return [{ price: top.price, size: top.size }, ...asks.slice(i)];
|
|
1629
1552
|
}
|
|
1630
|
-
function
|
|
1553
|
+
function overlayBid(bids, top) {
|
|
1631
1554
|
if (!(top.size > 0)) return bids;
|
|
1632
1555
|
let i = 0;
|
|
1633
1556
|
for (; i < bids.length; i++) {
|
|
@@ -3653,6 +3576,330 @@ var AsterClient = class {
|
|
|
3653
3576
|
}
|
|
3654
3577
|
};
|
|
3655
3578
|
|
|
3656
|
-
|
|
3579
|
+
// src/exchanges/mexc/contracts.ts
|
|
3580
|
+
var MEXC_SPOT_REST = "https://api.mexc.com";
|
|
3581
|
+
var MEXC_FUTURES_REST = "https://contract.mexc.com";
|
|
3582
|
+
var cache2 = null;
|
|
3583
|
+
var inflight2 = null;
|
|
3584
|
+
async function fetchContracts2(opts) {
|
|
3585
|
+
const res = await httpJson({
|
|
3586
|
+
url: `${MEXC_FUTURES_REST}/api/v1/contract/detail`,
|
|
3587
|
+
timeoutMs: opts.timeoutMs,
|
|
3588
|
+
transformUrl: opts.transformUrl
|
|
3589
|
+
});
|
|
3590
|
+
const rows = res.data ?? [];
|
|
3591
|
+
if (rows.length === 0) {
|
|
3592
|
+
throw new ExchangeError(
|
|
3593
|
+
"mexc",
|
|
3594
|
+
res.message ? `${res.code}: ${res.message}` : "empty contract/detail response"
|
|
3595
|
+
);
|
|
3596
|
+
}
|
|
3597
|
+
const map = /* @__PURE__ */ new Map();
|
|
3598
|
+
for (const row of rows) map.set(row.symbol.toUpperCase(), row);
|
|
3599
|
+
return map;
|
|
3600
|
+
}
|
|
3601
|
+
function loadContracts2(opts) {
|
|
3602
|
+
inflight2 ??= fetchContracts2(opts).then(
|
|
3603
|
+
(m) => {
|
|
3604
|
+
cache2 = m;
|
|
3605
|
+
inflight2 = null;
|
|
3606
|
+
return m;
|
|
3607
|
+
},
|
|
3608
|
+
(err) => {
|
|
3609
|
+
inflight2 = null;
|
|
3610
|
+
throw err;
|
|
3611
|
+
}
|
|
3612
|
+
);
|
|
3613
|
+
return inflight2;
|
|
3614
|
+
}
|
|
3615
|
+
async function resolveContractSize(symbol, opts) {
|
|
3616
|
+
const k = symbol.toUpperCase();
|
|
3617
|
+
const hit = cache2?.get(k);
|
|
3618
|
+
const contract = hit ?? (await loadContracts2(opts)).get(k);
|
|
3619
|
+
if (!contract) {
|
|
3620
|
+
throw new ExchangeError(
|
|
3621
|
+
"mexc",
|
|
3622
|
+
`unknown futures contract "${symbol}" \u2014 not listed on mexc`
|
|
3623
|
+
);
|
|
3624
|
+
}
|
|
3625
|
+
const size = Number(contract.contractSize);
|
|
3626
|
+
if (!Number.isFinite(size) || size <= 0) {
|
|
3627
|
+
throw new ExchangeError(
|
|
3628
|
+
"mexc",
|
|
3629
|
+
`contract "${symbol}" has no usable contractSize (${contract.contractSize})`
|
|
3630
|
+
);
|
|
3631
|
+
}
|
|
3632
|
+
return size;
|
|
3633
|
+
}
|
|
3634
|
+
|
|
3635
|
+
// src/exchanges/mexc/symbols.ts
|
|
3636
|
+
var QUOTES6 = ["USDT", "USDC", "TUSD", "USD", "BTC", "ETH"];
|
|
3637
|
+
function toMexcSymbol(symbol, market) {
|
|
3638
|
+
const [base, quote] = symbol.split("/");
|
|
3639
|
+
if (!base || !quote) {
|
|
3640
|
+
throw new Error(`invalid symbol "${symbol}" \u2014 expected "BASE/QUOTE"`);
|
|
3641
|
+
}
|
|
3642
|
+
return market === "perpetual" ? `${base.toUpperCase()}_${quote.toUpperCase()}` : `${base}${quote}`.toUpperCase();
|
|
3643
|
+
}
|
|
3644
|
+
function fromMexcSymbol(s, market) {
|
|
3645
|
+
const upper = s.toUpperCase();
|
|
3646
|
+
if (market === "perpetual") {
|
|
3647
|
+
const [base, quote] = upper.split("_");
|
|
3648
|
+
return base && quote ? `${base}/${quote}` : upper;
|
|
3649
|
+
}
|
|
3650
|
+
for (const q of QUOTES6) {
|
|
3651
|
+
if (upper.endsWith(q) && upper.length > q.length) {
|
|
3652
|
+
return `${upper.slice(0, -q.length)}/${q}`;
|
|
3653
|
+
}
|
|
3654
|
+
}
|
|
3655
|
+
return upper;
|
|
3656
|
+
}
|
|
3657
|
+
|
|
3658
|
+
// src/exchanges/mexc/rest.ts
|
|
3659
|
+
var toSpotLevels = (rows) => (rows ?? []).map(([p, s]) => ({ price: Number(p), size: Number(s) }));
|
|
3660
|
+
var toFuturesLevels = (rows, contractSize) => (rows ?? []).map(([price, vol]) => ({
|
|
3661
|
+
price: Number(price),
|
|
3662
|
+
size: Number(vol) * contractSize
|
|
3663
|
+
}));
|
|
3664
|
+
async function fetchMexcOrderbook(opts) {
|
|
3665
|
+
const symbol = toMexcSymbol(opts.symbol, opts.market);
|
|
3666
|
+
const depth = opts.depth ?? 50;
|
|
3667
|
+
if (opts.market === "spot") {
|
|
3668
|
+
const data = await httpJson({
|
|
3669
|
+
url: `${MEXC_SPOT_REST}/api/v3/depth?symbol=${symbol}&limit=${depth}`,
|
|
3670
|
+
timeoutMs: opts.timeoutMs,
|
|
3671
|
+
transformUrl: opts.transformUrl
|
|
3672
|
+
});
|
|
3673
|
+
return {
|
|
3674
|
+
exchange: "mexc",
|
|
3675
|
+
symbol: fromMexcSymbol(symbol, opts.market),
|
|
3676
|
+
market: opts.market,
|
|
3677
|
+
bids: toSpotLevels(data.bids),
|
|
3678
|
+
asks: toSpotLevels(data.asks),
|
|
3679
|
+
// The spot depth endpoint returns no timestamp of its own.
|
|
3680
|
+
timestamp: Date.now(),
|
|
3681
|
+
sequence: data.lastUpdateId
|
|
3682
|
+
};
|
|
3683
|
+
}
|
|
3684
|
+
const contractSize = await resolveContractSize(symbol, opts);
|
|
3685
|
+
const res = await httpJson({
|
|
3686
|
+
url: `${MEXC_FUTURES_REST}/api/v1/contract/depth/${symbol}?limit=${depth}`,
|
|
3687
|
+
timeoutMs: opts.timeoutMs,
|
|
3688
|
+
transformUrl: opts.transformUrl
|
|
3689
|
+
});
|
|
3690
|
+
if (!res.data) {
|
|
3691
|
+
throw new ExchangeError(
|
|
3692
|
+
"mexc",
|
|
3693
|
+
res.message ? `${res.code}: ${res.message}` : "empty orderbook response"
|
|
3694
|
+
);
|
|
3695
|
+
}
|
|
3696
|
+
return {
|
|
3697
|
+
exchange: "mexc",
|
|
3698
|
+
symbol: fromMexcSymbol(symbol, opts.market),
|
|
3699
|
+
market: opts.market,
|
|
3700
|
+
bids: toFuturesLevels(res.data.bids, contractSize),
|
|
3701
|
+
asks: toFuturesLevels(res.data.asks, contractSize),
|
|
3702
|
+
timestamp: res.data.timestamp ?? Date.now(),
|
|
3703
|
+
sequence: Number(res.data.version ?? 0)
|
|
3704
|
+
};
|
|
3705
|
+
}
|
|
3706
|
+
|
|
3707
|
+
// src/exchanges/mexc/ws.ts
|
|
3708
|
+
var WS_FUTURES5 = "wss://contract.mexc.com/edge";
|
|
3709
|
+
var PING_INTERVAL_MS3 = 2e4;
|
|
3710
|
+
function streamMexcOrderbook(opts) {
|
|
3711
|
+
if (opts.market !== "perpetual") {
|
|
3712
|
+
throw new Error(
|
|
3713
|
+
`mexc spot streaming requires the protobuf WS feed, which this adapter does not decode \u2014 use fetchOrderbook("${opts.symbol}") for a REST snapshot, or stream the perpetual ("${opts.symbol}:${opts.symbol.split("/")[1]}")`
|
|
3714
|
+
);
|
|
3715
|
+
}
|
|
3716
|
+
const symbol = toMexcSymbol(opts.symbol, opts.market);
|
|
3717
|
+
const unified = fromMexcSymbol(symbol, opts.market);
|
|
3718
|
+
const contractSize = resolveContractSize(symbol, opts);
|
|
3719
|
+
contractSize.catch(() => {
|
|
3720
|
+
});
|
|
3721
|
+
const merger = new BookMerger();
|
|
3722
|
+
let seedVersion = -1;
|
|
3723
|
+
let lastVersion = -1;
|
|
3724
|
+
let snapshotInFlight = false;
|
|
3725
|
+
let snapshotDone = false;
|
|
3726
|
+
let buffered = [];
|
|
3727
|
+
let recovering = false;
|
|
3728
|
+
let scale = 0;
|
|
3729
|
+
let ws = null;
|
|
3730
|
+
const stream = new OrderbookStream(() => ws?.close());
|
|
3731
|
+
const emit = (r) => {
|
|
3732
|
+
const book = {
|
|
3733
|
+
exchange: "mexc",
|
|
3734
|
+
symbol: unified,
|
|
3735
|
+
market: opts.market,
|
|
3736
|
+
bids: r.bids,
|
|
3737
|
+
asks: r.asks,
|
|
3738
|
+
timestamp: r.timestamp,
|
|
3739
|
+
sequence: r.sequence
|
|
3740
|
+
};
|
|
3741
|
+
stream.emit("update", book);
|
|
3742
|
+
};
|
|
3743
|
+
const triggerSnapshot = () => {
|
|
3744
|
+
if (snapshotInFlight) return;
|
|
3745
|
+
snapshotInFlight = true;
|
|
3746
|
+
contractSize.then(async (size) => {
|
|
3747
|
+
scale = size;
|
|
3748
|
+
return fetchMexcOrderbook({
|
|
3749
|
+
symbol: opts.symbol,
|
|
3750
|
+
market: opts.market,
|
|
3751
|
+
depth: opts.depth ?? 200,
|
|
3752
|
+
timeoutMs: opts.timeoutMs,
|
|
3753
|
+
transformUrl: opts.transformUrl
|
|
3754
|
+
});
|
|
3755
|
+
}).then((snap) => {
|
|
3756
|
+
seedVersion = snap.sequence;
|
|
3757
|
+
const r = merger.apply({
|
|
3758
|
+
kind: "snapshot",
|
|
3759
|
+
bids: snap.bids,
|
|
3760
|
+
asks: snap.asks,
|
|
3761
|
+
sequence: snap.sequence,
|
|
3762
|
+
timestamp: snap.timestamp
|
|
3763
|
+
});
|
|
3764
|
+
if (r.ok) emit(r);
|
|
3765
|
+
snapshotDone = true;
|
|
3766
|
+
lastVersion = -1;
|
|
3767
|
+
const pending2 = buffered;
|
|
3768
|
+
buffered = [];
|
|
3769
|
+
for (const d of pending2) processDelta(d);
|
|
3770
|
+
}).catch((err) => stream.emit("error", err)).finally(() => {
|
|
3771
|
+
snapshotInFlight = false;
|
|
3772
|
+
});
|
|
3773
|
+
};
|
|
3774
|
+
const resync = () => {
|
|
3775
|
+
if (recovering) return;
|
|
3776
|
+
recovering = true;
|
|
3777
|
+
merger.reset();
|
|
3778
|
+
seedVersion = -1;
|
|
3779
|
+
snapshotDone = false;
|
|
3780
|
+
lastVersion = -1;
|
|
3781
|
+
buffered = [];
|
|
3782
|
+
setTimeout(() => {
|
|
3783
|
+
recovering = false;
|
|
3784
|
+
triggerSnapshot();
|
|
3785
|
+
}, 100);
|
|
3786
|
+
};
|
|
3787
|
+
const processDelta = (d) => {
|
|
3788
|
+
const end = Number(d.end ?? d.version ?? 0);
|
|
3789
|
+
const begin = Number(d.begin ?? end);
|
|
3790
|
+
if (end <= seedVersion) return;
|
|
3791
|
+
let prevSequence;
|
|
3792
|
+
if (lastVersion < 0) {
|
|
3793
|
+
if (begin - 1 > seedVersion) {
|
|
3794
|
+
resync();
|
|
3795
|
+
return;
|
|
3796
|
+
}
|
|
3797
|
+
prevSequence = seedVersion;
|
|
3798
|
+
} else {
|
|
3799
|
+
prevSequence = begin - 1;
|
|
3800
|
+
}
|
|
3801
|
+
const r = merger.apply({
|
|
3802
|
+
kind: "delta",
|
|
3803
|
+
bids: toFuturesLevels(d.bids, scale),
|
|
3804
|
+
asks: toFuturesLevels(d.asks, scale),
|
|
3805
|
+
sequence: end,
|
|
3806
|
+
prevSequence,
|
|
3807
|
+
timestamp: d.timestamp ?? d.cts ?? Date.now()
|
|
3808
|
+
});
|
|
3809
|
+
if (r.ok) {
|
|
3810
|
+
lastVersion = end;
|
|
3811
|
+
emit(r);
|
|
3812
|
+
} else if (r.reason === "gap" || r.reason === "no-snapshot") {
|
|
3813
|
+
resync();
|
|
3814
|
+
}
|
|
3815
|
+
};
|
|
3816
|
+
const onMessage = (raw) => {
|
|
3817
|
+
if (typeof raw !== "string") return;
|
|
3818
|
+
let msg;
|
|
3819
|
+
try {
|
|
3820
|
+
msg = JSON.parse(raw);
|
|
3821
|
+
} catch {
|
|
3822
|
+
return;
|
|
3823
|
+
}
|
|
3824
|
+
if (msg.channel === "push.depth") {
|
|
3825
|
+
if (msg.symbol && msg.symbol !== symbol) return;
|
|
3826
|
+
const d = msg.data;
|
|
3827
|
+
if (!d) return;
|
|
3828
|
+
if (!snapshotDone) {
|
|
3829
|
+
buffered.push(d);
|
|
3830
|
+
if (!snapshotInFlight) triggerSnapshot();
|
|
3831
|
+
return;
|
|
3832
|
+
}
|
|
3833
|
+
processDelta(d);
|
|
3834
|
+
return;
|
|
3835
|
+
}
|
|
3836
|
+
if (msg.channel === "rs.error" || msg.channel?.startsWith("rs.error")) {
|
|
3837
|
+
stream.emit("error", new ExchangeError("mexc", String(msg.data)));
|
|
3838
|
+
return;
|
|
3839
|
+
}
|
|
3840
|
+
if (msg.channel === "rs.sub.depth" && msg.data !== "success") {
|
|
3841
|
+
stream.emit(
|
|
3842
|
+
"error",
|
|
3843
|
+
new ExchangeError("mexc", `subscribe rejected: ${String(msg.data)}`)
|
|
3844
|
+
);
|
|
3845
|
+
}
|
|
3846
|
+
};
|
|
3847
|
+
ws = new WSClient({
|
|
3848
|
+
url: WS_FUTURES5,
|
|
3849
|
+
onOpen: (handle) => {
|
|
3850
|
+
merger.reset();
|
|
3851
|
+
seedVersion = -1;
|
|
3852
|
+
snapshotDone = false;
|
|
3853
|
+
lastVersion = -1;
|
|
3854
|
+
buffered = [];
|
|
3855
|
+
handle.send(
|
|
3856
|
+
JSON.stringify({ method: "sub.depth", param: { symbol } })
|
|
3857
|
+
);
|
|
3858
|
+
},
|
|
3859
|
+
onMessage,
|
|
3860
|
+
pingIntervalMs: PING_INTERVAL_MS3,
|
|
3861
|
+
pingPayload: () => JSON.stringify({ method: "ping" })
|
|
3862
|
+
});
|
|
3863
|
+
ws.on("open", () => stream.emit("connected"));
|
|
3864
|
+
ws.on("close", (r) => stream.emit("disconnected", r));
|
|
3865
|
+
ws.on("reconnecting", (a, w) => stream.emit("reconnecting", a, w));
|
|
3866
|
+
ws.on("error", (e) => stream.emit("error", e));
|
|
3867
|
+
ws.connect().catch((e) => stream.emit("error", e));
|
|
3868
|
+
return stream;
|
|
3869
|
+
}
|
|
3870
|
+
|
|
3871
|
+
// src/exchanges/mexc/index.ts
|
|
3872
|
+
var MexcClient = class {
|
|
3873
|
+
exchange = "mexc";
|
|
3874
|
+
transformUrl;
|
|
3875
|
+
timeoutMs;
|
|
3876
|
+
constructor(opts = {}) {
|
|
3877
|
+
this.transformUrl = opts.transformUrl;
|
|
3878
|
+
this.timeoutMs = opts.timeoutMs;
|
|
3879
|
+
}
|
|
3880
|
+
fetchOrderbook(symbol, opts = {}) {
|
|
3881
|
+
const { pair, market } = parseSymbol(symbol);
|
|
3882
|
+
return fetchMexcOrderbook({
|
|
3883
|
+
symbol: pair,
|
|
3884
|
+
market,
|
|
3885
|
+
depth: opts.depth,
|
|
3886
|
+
timeoutMs: this.timeoutMs,
|
|
3887
|
+
transformUrl: this.transformUrl
|
|
3888
|
+
});
|
|
3889
|
+
}
|
|
3890
|
+
/** Perpetuals only — see the class note on spot. */
|
|
3891
|
+
streamOrderbook(symbol, opts = {}) {
|
|
3892
|
+
const { pair, market } = parseSymbol(symbol);
|
|
3893
|
+
return streamMexcOrderbook({
|
|
3894
|
+
symbol: pair,
|
|
3895
|
+
market,
|
|
3896
|
+
depth: opts.depth,
|
|
3897
|
+
transformUrl: this.transformUrl,
|
|
3898
|
+
timeoutMs: this.timeoutMs
|
|
3899
|
+
});
|
|
3900
|
+
}
|
|
3901
|
+
};
|
|
3902
|
+
|
|
3903
|
+
export { AsterClient, Backoff, BinanceClient, BingxClient, BitgetClient, BookMerger, BybitClient, CoinexClient, DeribitClient, EdgexClient, ExchangeError, GateClient, HyperliquidClient, KucoinClient, LighterClient, MexcClient, OkxClient, OrderbookStream, RateLimitError, SequenceGapError, WhitebitClient, parseSymbol };
|
|
3657
3904
|
//# sourceMappingURL=index.js.map
|
|
3658
3905
|
//# sourceMappingURL=index.js.map
|