@xapy/orderbook 0.1.6 → 0.1.8

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (39) hide show
  1. package/dist/exchanges/binance/index.d.cts +1 -1
  2. package/dist/exchanges/binance/index.d.ts +1 -1
  3. package/dist/exchanges/bingx/index.d.cts +1 -1
  4. package/dist/exchanges/bingx/index.d.ts +1 -1
  5. package/dist/exchanges/bitget/index.d.cts +1 -1
  6. package/dist/exchanges/bitget/index.d.ts +1 -1
  7. package/dist/exchanges/bitmart/index.cjs +594 -0
  8. package/dist/exchanges/bitmart/index.cjs.map +1 -0
  9. package/dist/exchanges/bitmart/index.d.cts +56 -0
  10. package/dist/exchanges/bitmart/index.d.ts +56 -0
  11. package/dist/exchanges/bitmart/index.js +590 -0
  12. package/dist/exchanges/bitmart/index.js.map +1 -0
  13. package/dist/exchanges/bybit/index.d.cts +1 -1
  14. package/dist/exchanges/bybit/index.d.ts +1 -1
  15. package/dist/exchanges/coinex/index.cjs +57 -140
  16. package/dist/exchanges/coinex/index.cjs.map +1 -1
  17. package/dist/exchanges/coinex/index.d.cts +17 -11
  18. package/dist/exchanges/coinex/index.d.ts +17 -11
  19. package/dist/exchanges/coinex/index.js +57 -140
  20. package/dist/exchanges/coinex/index.js.map +1 -1
  21. package/dist/exchanges/gate/index.d.cts +1 -1
  22. package/dist/exchanges/gate/index.d.ts +1 -1
  23. package/dist/exchanges/huobi/index.d.cts +1 -1
  24. package/dist/exchanges/huobi/index.d.ts +1 -1
  25. package/dist/exchanges/hyperliquid/index.d.cts +1 -1
  26. package/dist/exchanges/hyperliquid/index.d.ts +1 -1
  27. package/dist/exchanges/kucoin/index.d.cts +1 -1
  28. package/dist/exchanges/kucoin/index.d.ts +1 -1
  29. package/dist/exchanges/okx/index.d.cts +1 -1
  30. package/dist/exchanges/okx/index.d.ts +1 -1
  31. package/dist/index.cjs +242 -116
  32. package/dist/index.cjs.map +1 -1
  33. package/dist/index.d.cts +3 -2
  34. package/dist/index.d.ts +3 -2
  35. package/dist/index.js +242 -117
  36. package/dist/index.js.map +1 -1
  37. package/dist/{stream-C73HPYqT.d.cts → stream-L9MJjYbE.d.cts} +1 -1
  38. package/dist/{stream-C73HPYqT.d.ts → stream-L9MJjYbE.d.ts} +1 -1
  39. package/package.json +11 -1
package/dist/index.d.cts CHANGED
@@ -1,5 +1,5 @@
1
- import { B as BookEvent, O as OrderbookLevel, M as Market } from './stream-C73HPYqT.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-C73HPYqT.cjs';
1
+ import { B as BookEvent, O as OrderbookLevel, M as Market } from './stream-L9MJjYbE.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-L9MJjYbE.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';
@@ -10,6 +10,7 @@ export { OkxClient } from './exchanges/okx/index.cjs';
10
10
  export { BinanceClient } from './exchanges/binance/index.cjs';
11
11
  export { HyperliquidClient } from './exchanges/hyperliquid/index.cjs';
12
12
  export { KucoinClient } from './exchanges/kucoin/index.cjs';
13
+ export { BitmartClient } from './exchanges/bitmart/index.cjs';
13
14
 
14
15
  declare class ExchangeError extends Error {
15
16
  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-C73HPYqT.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-C73HPYqT.js';
1
+ import { B as BookEvent, O as OrderbookLevel, M as Market } from './stream-L9MJjYbE.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-L9MJjYbE.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';
@@ -10,6 +10,7 @@ export { OkxClient } from './exchanges/okx/index.js';
10
10
  export { BinanceClient } from './exchanges/binance/index.js';
11
11
  export { HyperliquidClient } from './exchanges/hyperliquid/index.js';
12
12
  export { KucoinClient } from './exchanges/kucoin/index.js';
13
+ export { BitmartClient } from './exchanges/bitmart/index.js';
13
14
 
14
15
  declare class ExchangeError extends Error {
15
16
  readonly exchange: string;
package/dist/index.js CHANGED
@@ -1616,94 +1616,61 @@ async function fetchCoinexOrderbook(opts) {
1616
1616
  }
1617
1617
 
1618
1618
  // src/exchanges/coinex/ws.ts
1619
- var WS_SPOT5 = "wss://socket.coinex.com/v2/spot";
1620
- var WS_FUTURES2 = "wss://socket.coinex.com/v2/futures";
1619
+ var WS_SPOT5 = "wss://socket.coinex.com/";
1620
+ var WS_FUTURES2 = "wss://perpetual.coinex.com/";
1621
1621
  function streamCoinexOrderbook(opts) {
1622
1622
  const market = toCoinexSymbol(opts.symbol);
1623
1623
  const depth = opts.depth ?? 50;
1624
1624
  const merger = new BookMerger();
1625
+ let seq = 0;
1625
1626
  let ws = null;
1626
1627
  const stream = new OrderbookStream(() => ws?.close());
1627
- const subscribeDepth = JSON.stringify({
1628
+ const subscribePayload = JSON.stringify({
1628
1629
  method: "depth.subscribe",
1629
- params: {
1630
- // [market, limit, interval, diff_flag] — diff_flag=true → snapshots only
1631
- // (CoinEx flips the sense of the flag in V2; our handler treats every
1632
- // frame as a snapshot to stay correct under either reading).
1633
- market_list: [[market, depth, "0", true]]
1634
- },
1630
+ params: [market, depth, "0", true],
1635
1631
  id: 1
1636
1632
  });
1637
- const subscribeBbo = JSON.stringify({
1638
- method: "bbo.subscribe",
1639
- params: { market_list: [market] },
1640
- id: 2
1641
- });
1642
- let lastMerged = null;
1643
- let lastTicker = null;
1644
- const emitOverlayed = () => {
1645
- if (!lastMerged) return;
1646
- let bids = lastMerged.bids;
1647
- let asks = lastMerged.asks;
1648
- let timestamp = lastMerged.timestamp;
1649
- let sequence = lastMerged.sequence;
1650
- if (lastTicker) {
1651
- bids = overlayBid2(bids, lastTicker.bid);
1652
- asks = overlayAsk2(asks, lastTicker.ask);
1653
- if (lastTicker.ts > timestamp) {
1654
- timestamp = lastTicker.ts;
1655
- sequence = Math.max(sequence, lastTicker.ts);
1656
- }
1657
- }
1633
+ const pingPayload = () => JSON.stringify({ method: "server.ping", params: [], id: 11 });
1634
+ const emit = (r) => {
1658
1635
  const book = {
1659
1636
  exchange: "coinex",
1660
1637
  symbol: fromCoinexSymbol(market),
1661
1638
  market: opts.market,
1662
- bids,
1663
- asks,
1664
- timestamp,
1665
- sequence
1666
- };
1667
- stream.emit("update", book);
1668
- };
1669
- const handleDepth = (d) => {
1670
- if (d.market !== market) return;
1671
- const inner = d.depth;
1672
- const r = merger.apply({
1673
- kind: "snapshot",
1674
- bids: inner.bids.map(([p, s]) => ({
1675
- price: Number(p),
1676
- size: Number(s)
1677
- })),
1678
- asks: inner.asks.map(([p, s]) => ({
1679
- price: Number(p),
1680
- size: Number(s)
1681
- })),
1682
- sequence: inner.updated_at,
1683
- timestamp: inner.updated_at
1684
- });
1685
- if (!r.ok) return;
1686
- lastMerged = {
1687
1639
  bids: r.bids,
1688
1640
  asks: r.asks,
1689
1641
  timestamp: r.timestamp,
1690
1642
  sequence: r.sequence
1691
1643
  };
1692
- emitOverlayed();
1644
+ stream.emit("update", book);
1693
1645
  };
1694
- const handleBbo = (b) => {
1695
- if (b.market !== market) return;
1696
- const bidPrice = Number(b.best_bid_price);
1697
- const askPrice = Number(b.best_ask_price);
1698
- const bidSize = Number(b.best_bid_size);
1699
- const askSize = Number(b.best_ask_size);
1700
- if (!Number.isFinite(bidPrice) || !Number.isFinite(askPrice)) return;
1701
- lastTicker = {
1702
- bid: { price: bidPrice, size: bidSize },
1703
- ask: { price: askPrice, size: askSize },
1704
- ts: b.updated_at
1646
+ const handleDepthPush = (push) => {
1647
+ const [clean, payload, mkt] = push;
1648
+ if (mkt !== market) return;
1649
+ const bids = parseLevels(payload.bids);
1650
+ const asks = parseLevels(payload.asks);
1651
+ const event = clean ? {
1652
+ kind: "snapshot",
1653
+ bids,
1654
+ asks,
1655
+ sequence: ++seq,
1656
+ timestamp: payload.time
1657
+ } : {
1658
+ kind: "delta",
1659
+ bids,
1660
+ asks,
1661
+ sequence: ++seq,
1662
+ timestamp: payload.time
1705
1663
  };
1706
- emitOverlayed();
1664
+ const r = merger.apply(event);
1665
+ if (r.ok) {
1666
+ emit(r);
1667
+ return;
1668
+ }
1669
+ if (r.reason === "no-snapshot") return;
1670
+ if (r.reason === "gap") {
1671
+ merger.reset();
1672
+ seq = 0;
1673
+ }
1707
1674
  };
1708
1675
  const handlePayload = (text) => {
1709
1676
  let msg;
@@ -1713,32 +1680,31 @@ function streamCoinexOrderbook(opts) {
1713
1680
  return;
1714
1681
  }
1715
1682
  if (msg.id != null && !msg.method) return;
1716
- if (!msg.data || !msg.method) return;
1717
- if (msg.method === "depth.update") {
1718
- handleDepth(msg.data);
1719
- } else if (msg.method === "bbo.update") {
1720
- handleBbo(msg.data);
1721
- }
1722
- };
1723
- const onMessage = async (raw) => {
1724
- if (typeof raw === "string") {
1725
- handlePayload(raw);
1683
+ if (msg.error) {
1684
+ stream.emit(
1685
+ "error",
1686
+ new Error(`coinex: ${msg.error.code} ${msg.error.message}`)
1687
+ );
1726
1688
  return;
1727
1689
  }
1728
- const inflate = await getGzipInflate();
1729
- handlePayload(inflate(raw));
1690
+ if (msg.method === "depth.update" && Array.isArray(msg.params)) {
1691
+ handleDepthPush(msg.params);
1692
+ }
1693
+ };
1694
+ const onMessage = (raw) => {
1695
+ if (typeof raw !== "string") return;
1696
+ handlePayload(raw);
1730
1697
  };
1731
1698
  ws = new WSClient({
1732
1699
  url: opts.market === "spot" ? WS_SPOT5 : WS_FUTURES2,
1733
1700
  onOpen: (sock) => {
1734
1701
  merger.reset();
1735
- lastMerged = null;
1736
- lastTicker = null;
1737
- sock.send(subscribeDepth);
1738
- sock.send(subscribeBbo);
1702
+ seq = 0;
1703
+ sock.send(subscribePayload);
1739
1704
  },
1740
- onMessage
1741
- // Native WS ping/pong frames keep the connection alive — no app-level ping.
1705
+ onMessage,
1706
+ pingIntervalMs: 25e3,
1707
+ pingPayload
1742
1708
  });
1743
1709
  ws.on("open", () => stream.emit("connected"));
1744
1710
  ws.on("close", (r) => stream.emit("disconnected", r));
@@ -1747,31 +1713,11 @@ function streamCoinexOrderbook(opts) {
1747
1713
  ws.connect().catch((e) => stream.emit("error", e));
1748
1714
  return stream;
1749
1715
  }
1750
- function overlayAsk2(asks, top) {
1751
- if (!(top.size > 0)) return asks;
1752
- let i = 0;
1753
- for (; i < asks.length; i++) {
1754
- const level = asks[i];
1755
- if (!level || level.price >= top.price) break;
1756
- }
1757
- const at = asks[i];
1758
- if (at && at.price === top.price) {
1759
- return [{ price: top.price, size: top.size }, ...asks.slice(i + 1)];
1760
- }
1761
- return [{ price: top.price, size: top.size }, ...asks.slice(i)];
1762
- }
1763
- function overlayBid2(bids, top) {
1764
- if (!(top.size > 0)) return bids;
1765
- let i = 0;
1766
- for (; i < bids.length; i++) {
1767
- const level = bids[i];
1768
- if (!level || level.price <= top.price) break;
1769
- }
1770
- const at = bids[i];
1771
- if (at && at.price === top.price) {
1772
- return [{ price: top.price, size: top.size }, ...bids.slice(i + 1)];
1773
- }
1774
- return [{ price: top.price, size: top.size }, ...bids.slice(i)];
1716
+ function parseLevels(rows) {
1717
+ if (!rows) return [];
1718
+ const out = [];
1719
+ for (const [p, s] of rows) out.push({ price: Number(p), size: Number(s) });
1720
+ return out;
1775
1721
  }
1776
1722
 
1777
1723
  // src/exchanges/coinex/index.ts
@@ -2469,8 +2415,8 @@ async function fetchKucoinOrderbook(opts) {
2469
2415
  exchange: "kucoin",
2470
2416
  symbol: fromKucoinSpotSymbol(symbol2),
2471
2417
  market: "spot",
2472
- bids: parseLevels(res2.data.bids),
2473
- asks: parseLevels(res2.data.asks),
2418
+ bids: parseLevels2(res2.data.bids),
2419
+ asks: parseLevels2(res2.data.asks),
2474
2420
  timestamp: res2.data.time,
2475
2421
  sequence: Number(res2.data.sequence)
2476
2422
  };
@@ -2489,13 +2435,13 @@ async function fetchKucoinOrderbook(opts) {
2489
2435
  exchange: "kucoin",
2490
2436
  symbol: fromKucoinFuturesSymbol(symbol),
2491
2437
  market: "perpetual",
2492
- bids: parseLevels(res.data.bids),
2493
- asks: parseLevels(res.data.asks),
2438
+ bids: parseLevels2(res.data.bids),
2439
+ asks: parseLevels2(res.data.asks),
2494
2440
  timestamp: res.data.ts,
2495
2441
  sequence: res.data.sequence
2496
2442
  };
2497
2443
  }
2498
- function parseLevels(rows) {
2444
+ function parseLevels2(rows) {
2499
2445
  return rows.map(([p, s]) => ({ price: Number(p), size: Number(s) }));
2500
2446
  }
2501
2447
 
@@ -2621,6 +2567,185 @@ var KucoinClient = class {
2621
2567
  }
2622
2568
  };
2623
2569
 
2624
- export { Backoff, BinanceClient, BingxClient, BitgetClient, BookMerger, BybitClient, CoinexClient, ExchangeError, GateClient, HuobiClient, HyperliquidClient, KucoinClient, OkxClient, OrderbookStream, RateLimitError, SequenceGapError, parseSymbol };
2570
+ // src/exchanges/bitmart/symbols.ts
2571
+ var QUOTES6 = ["USDT", "USDC", "USD"];
2572
+ function toBitmartSymbol(symbol) {
2573
+ const [base, quote] = symbol.split("/");
2574
+ if (!base || !quote) {
2575
+ throw new Error(`invalid symbol "${symbol}" \u2014 expected "BASE/QUOTE"`);
2576
+ }
2577
+ return `${base}${quote}`.toUpperCase();
2578
+ }
2579
+ function fromBitmartSymbol(s) {
2580
+ const upper = s.toUpperCase();
2581
+ for (const q of QUOTES6) {
2582
+ if (upper.endsWith(q) && upper.length > q.length) {
2583
+ return `${upper.slice(0, -q.length)}/${q}`;
2584
+ }
2585
+ }
2586
+ return upper;
2587
+ }
2588
+
2589
+ // src/exchanges/bitmart/rest.ts
2590
+ var BASE8 = "https://api-cloud-v2.bitmart.com";
2591
+ async function fetchBitmartOrderbook(opts) {
2592
+ if (opts.market !== "perpetual") {
2593
+ throw new ExchangeError(
2594
+ "bitmart",
2595
+ "spot is not supported \u2014 only USDT-M perpetual"
2596
+ );
2597
+ }
2598
+ const symbol = toBitmartSymbol(opts.symbol);
2599
+ const url = `${BASE8}/contract/public/depth?symbol=${symbol}`;
2600
+ const res = await httpJson({
2601
+ url,
2602
+ timeoutMs: opts.timeoutMs,
2603
+ transformUrl: opts.transformUrl
2604
+ });
2605
+ if (res.code !== 1e3) {
2606
+ throw new ExchangeError("bitmart", `${res.code}: ${res.message ?? ""}`);
2607
+ }
2608
+ return {
2609
+ exchange: "bitmart",
2610
+ symbol: fromBitmartSymbol(res.data.symbol),
2611
+ market: "perpetual",
2612
+ bids: parseLevels3(res.data.bids),
2613
+ asks: parseLevels3(res.data.asks),
2614
+ timestamp: res.data.timestamp,
2615
+ sequence: res.data.timestamp
2616
+ };
2617
+ }
2618
+ function parseLevels3(rows) {
2619
+ const out = [];
2620
+ for (const row of rows) {
2621
+ out.push({ price: Number(row[0]), size: Number(row[1]) });
2622
+ }
2623
+ return out;
2624
+ }
2625
+
2626
+ // src/exchanges/bitmart/ws.ts
2627
+ var WS_FUTURES4 = "wss://openapi-wsv2.bitmart.com/api?protocol=1.1";
2628
+ function streamBitmartOrderbook(opts) {
2629
+ if (opts.market !== "perpetual") {
2630
+ throw new ExchangeError(
2631
+ "bitmart",
2632
+ "spot is not supported \u2014 only USDT-M perpetual"
2633
+ );
2634
+ }
2635
+ const symbol = toBitmartSymbol(opts.symbol);
2636
+ const channelArg = opts.interval ? `futures/depth:${symbol}@${opts.interval}` : `futures/depth:${symbol}`;
2637
+ const merger = new BookMerger();
2638
+ let ws = null;
2639
+ const stream = new OrderbookStream(() => ws?.close());
2640
+ const subscribePayload = JSON.stringify({
2641
+ action: "subscribe",
2642
+ args: [channelArg]
2643
+ });
2644
+ const pingPayload = () => JSON.stringify({ action: "ping" });
2645
+ const handleFrame = (frame) => {
2646
+ if (frame.symbol !== symbol) return;
2647
+ const event = {
2648
+ kind: "snapshot",
2649
+ bids: parseLevels4(frame.bids),
2650
+ asks: parseLevels4(frame.asks),
2651
+ sequence: frame.timestamp,
2652
+ timestamp: frame.timestamp
2653
+ };
2654
+ const r = merger.apply(event);
2655
+ if (!r.ok) return;
2656
+ const book = {
2657
+ exchange: "bitmart",
2658
+ symbol: fromBitmartSymbol(symbol),
2659
+ market: "perpetual",
2660
+ bids: r.bids,
2661
+ asks: r.asks,
2662
+ timestamp: r.timestamp,
2663
+ sequence: r.sequence
2664
+ };
2665
+ stream.emit("update", book);
2666
+ };
2667
+ const handlePayload = (text) => {
2668
+ let msg;
2669
+ try {
2670
+ msg = JSON.parse(text);
2671
+ } catch {
2672
+ return;
2673
+ }
2674
+ if (msg.action === "subscribe" || msg.action === "pong") return;
2675
+ if (msg.errorCode) {
2676
+ stream.emit(
2677
+ "error",
2678
+ new Error(`bitmart: ${msg.errorCode} ${msg.errorMessage ?? ""}`)
2679
+ );
2680
+ return;
2681
+ }
2682
+ if (msg.arg !== channelArg || !msg.data) return;
2683
+ handleFrame(msg.data);
2684
+ };
2685
+ const onMessage = async (raw) => {
2686
+ if (typeof raw === "string") {
2687
+ handlePayload(raw);
2688
+ return;
2689
+ }
2690
+ const inflate = await getGzipInflate();
2691
+ handlePayload(inflate(raw));
2692
+ };
2693
+ ws = new WSClient({
2694
+ url: WS_FUTURES4,
2695
+ onOpen: (sock) => {
2696
+ merger.reset();
2697
+ sock.send(subscribePayload);
2698
+ },
2699
+ onMessage,
2700
+ pingIntervalMs: 15e3,
2701
+ pingPayload
2702
+ });
2703
+ ws.on("open", () => stream.emit("connected"));
2704
+ ws.on("close", (r) => stream.emit("disconnected", r));
2705
+ ws.on("reconnecting", (a, w) => stream.emit("reconnecting", a, w));
2706
+ ws.on("error", (e) => stream.emit("error", e));
2707
+ ws.connect().catch((e) => stream.emit("error", e));
2708
+ return stream;
2709
+ }
2710
+ function parseLevels4(rows) {
2711
+ if (!rows) return [];
2712
+ const out = [];
2713
+ for (const row of rows) {
2714
+ out.push({ price: Number(row[0]), size: Number(row[1]) });
2715
+ }
2716
+ return out;
2717
+ }
2718
+
2719
+ // src/exchanges/bitmart/index.ts
2720
+ var BitmartClient = class {
2721
+ exchange = "bitmart";
2722
+ transformUrl;
2723
+ timeoutMs;
2724
+ constructor(opts = {}) {
2725
+ this.transformUrl = opts.transformUrl;
2726
+ this.timeoutMs = opts.timeoutMs;
2727
+ }
2728
+ fetchOrderbook(symbol, opts = {}) {
2729
+ const { pair, market } = parseSymbol(symbol);
2730
+ return fetchBitmartOrderbook({
2731
+ symbol: pair,
2732
+ market,
2733
+ depth: opts.depth,
2734
+ timeoutMs: this.timeoutMs,
2735
+ transformUrl: this.transformUrl
2736
+ });
2737
+ }
2738
+ streamOrderbook(symbol, _opts = {}) {
2739
+ const { pair, market } = parseSymbol(symbol);
2740
+ return streamBitmartOrderbook({
2741
+ symbol: pair,
2742
+ market,
2743
+ transformUrl: this.transformUrl,
2744
+ timeoutMs: this.timeoutMs
2745
+ });
2746
+ }
2747
+ };
2748
+
2749
+ export { Backoff, BinanceClient, BingxClient, BitgetClient, BitmartClient, BookMerger, BybitClient, CoinexClient, ExchangeError, GateClient, HuobiClient, HyperliquidClient, KucoinClient, OkxClient, OrderbookStream, RateLimitError, SequenceGapError, parseSymbol };
2625
2750
  //# sourceMappingURL=index.js.map
2626
2751
  //# sourceMappingURL=index.js.map