@xapy/orderbook 0.1.5 → 0.1.7

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/index.js CHANGED
@@ -1616,42 +1616,22 @@ 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
1628
  const subscribePayload = JSON.stringify({
1628
1629
  method: "depth.subscribe",
1629
- params: {
1630
- // [market, limit, interval, diff_flag] — diff_flag=false → full snapshots
1631
- market_list: [[market, depth, "0", true]]
1632
- },
1630
+ params: [market, depth, "0", true],
1633
1631
  id: 1
1634
1632
  });
1635
- const handlePayload = (text) => {
1636
- let msg;
1637
- try {
1638
- msg = JSON.parse(text);
1639
- } catch {
1640
- return;
1641
- }
1642
- if (msg.id != null && !msg.method) return;
1643
- if (msg.method !== "depth.update" || !msg.data) return;
1644
- if (msg.data.market !== market) return;
1645
- const d = msg.data.depth;
1646
- const event = {
1647
- kind: "snapshot",
1648
- bids: d.bids.map(([p, s]) => ({ price: Number(p), size: Number(s) })),
1649
- asks: d.asks.map(([p, s]) => ({ price: Number(p), size: Number(s) })),
1650
- sequence: d.updated_at,
1651
- timestamp: d.updated_at
1652
- };
1653
- const r = merger.apply(event);
1654
- if (!r.ok) return;
1633
+ const pingPayload = () => JSON.stringify({ method: "server.ping", params: [], id: 11 });
1634
+ const emit = (r) => {
1655
1635
  const book = {
1656
1636
  exchange: "coinex",
1657
1637
  symbol: fromCoinexSymbol(market),
@@ -1663,22 +1643,68 @@ function streamCoinexOrderbook(opts) {
1663
1643
  };
1664
1644
  stream.emit("update", book);
1665
1645
  };
1666
- const onMessage = async (raw) => {
1667
- if (typeof raw === "string") {
1668
- handlePayload(raw);
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
1663
+ };
1664
+ const r = merger.apply(event);
1665
+ if (r.ok) {
1666
+ emit(r);
1669
1667
  return;
1670
1668
  }
1671
- const inflate = await getGzipInflate();
1672
- handlePayload(inflate(raw));
1669
+ if (r.reason === "no-snapshot") return;
1670
+ if (r.reason === "gap") {
1671
+ merger.reset();
1672
+ seq = 0;
1673
+ }
1674
+ };
1675
+ const handlePayload = (text) => {
1676
+ let msg;
1677
+ try {
1678
+ msg = JSON.parse(text);
1679
+ } catch {
1680
+ return;
1681
+ }
1682
+ if (msg.id != null && !msg.method) return;
1683
+ if (msg.error) {
1684
+ stream.emit(
1685
+ "error",
1686
+ new Error(`coinex: ${msg.error.code} ${msg.error.message}`)
1687
+ );
1688
+ return;
1689
+ }
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);
1673
1697
  };
1674
1698
  ws = new WSClient({
1675
1699
  url: opts.market === "spot" ? WS_SPOT5 : WS_FUTURES2,
1676
1700
  onOpen: (sock) => {
1677
1701
  merger.reset();
1702
+ seq = 0;
1678
1703
  sock.send(subscribePayload);
1679
1704
  },
1680
- onMessage
1681
- // Native WS ping/pong frames keep the connection alive — no app-level ping.
1705
+ onMessage,
1706
+ pingIntervalMs: 25e3,
1707
+ pingPayload
1682
1708
  });
1683
1709
  ws.on("open", () => stream.emit("connected"));
1684
1710
  ws.on("close", (r) => stream.emit("disconnected", r));
@@ -1687,6 +1713,12 @@ function streamCoinexOrderbook(opts) {
1687
1713
  ws.connect().catch((e) => stream.emit("error", e));
1688
1714
  return stream;
1689
1715
  }
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;
1721
+ }
1690
1722
 
1691
1723
  // src/exchanges/coinex/index.ts
1692
1724
  var CoinexClient = class {
@@ -2383,8 +2415,8 @@ async function fetchKucoinOrderbook(opts) {
2383
2415
  exchange: "kucoin",
2384
2416
  symbol: fromKucoinSpotSymbol(symbol2),
2385
2417
  market: "spot",
2386
- bids: parseLevels(res2.data.bids),
2387
- asks: parseLevels(res2.data.asks),
2418
+ bids: parseLevels2(res2.data.bids),
2419
+ asks: parseLevels2(res2.data.asks),
2388
2420
  timestamp: res2.data.time,
2389
2421
  sequence: Number(res2.data.sequence)
2390
2422
  };
@@ -2403,13 +2435,13 @@ async function fetchKucoinOrderbook(opts) {
2403
2435
  exchange: "kucoin",
2404
2436
  symbol: fromKucoinFuturesSymbol(symbol),
2405
2437
  market: "perpetual",
2406
- bids: parseLevels(res.data.bids),
2407
- asks: parseLevels(res.data.asks),
2438
+ bids: parseLevels2(res.data.bids),
2439
+ asks: parseLevels2(res.data.asks),
2408
2440
  timestamp: res.data.ts,
2409
2441
  sequence: res.data.sequence
2410
2442
  };
2411
2443
  }
2412
- function parseLevels(rows) {
2444
+ function parseLevels2(rows) {
2413
2445
  return rows.map(([p, s]) => ({ price: Number(p), size: Number(s) }));
2414
2446
  }
2415
2447