@xapy/orderbook 0.1.21 → 0.1.22
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/exchanges/coinex/index.cjs +139 -57
- package/dist/exchanges/coinex/index.cjs.map +1 -1
- package/dist/exchanges/coinex/index.d.cts +10 -16
- package/dist/exchanges/coinex/index.d.ts +10 -16
- package/dist/exchanges/coinex/index.js +139 -57
- package/dist/exchanges/coinex/index.js.map +1 -1
- package/dist/index.cjs +121 -68
- package/dist/index.cjs.map +1 -1
- package/dist/index.js +121 -68
- package/dist/index.js.map +1 -1
- package/package.json +1 -1
package/dist/index.cjs
CHANGED
|
@@ -1618,61 +1618,93 @@ async function fetchCoinexOrderbook(opts) {
|
|
|
1618
1618
|
}
|
|
1619
1619
|
|
|
1620
1620
|
// src/exchanges/coinex/ws.ts
|
|
1621
|
-
var WS_SPOT5 = "wss://socket.coinex.com/";
|
|
1622
|
-
var WS_FUTURES2 = "wss://
|
|
1621
|
+
var WS_SPOT5 = "wss://socket.coinex.com/v2/spot";
|
|
1622
|
+
var WS_FUTURES2 = "wss://socket.coinex.com/v2/futures";
|
|
1623
1623
|
function streamCoinexOrderbook(opts) {
|
|
1624
1624
|
const market = toCoinexSymbol(opts.symbol);
|
|
1625
1625
|
const depth = opts.depth ?? 50;
|
|
1626
1626
|
const merger = new BookMerger();
|
|
1627
|
-
let seq = 0;
|
|
1628
1627
|
let ws = null;
|
|
1629
1628
|
const stream = new OrderbookStream(() => ws?.close());
|
|
1630
|
-
const
|
|
1629
|
+
const subscribeDepth = JSON.stringify({
|
|
1631
1630
|
method: "depth.subscribe",
|
|
1632
|
-
params:
|
|
1631
|
+
params: {
|
|
1632
|
+
// [market, limit, interval, if_full] — `true` = every push is a full
|
|
1633
|
+
// snapshot; the merger revalidates monotonic ordering by updated_at.
|
|
1634
|
+
market_list: [[market, depth, "0", true]]
|
|
1635
|
+
},
|
|
1633
1636
|
id: 1
|
|
1634
1637
|
});
|
|
1635
|
-
const
|
|
1636
|
-
|
|
1638
|
+
const subscribeBbo = JSON.stringify({
|
|
1639
|
+
method: "bbo.subscribe",
|
|
1640
|
+
params: { market_list: [market] },
|
|
1641
|
+
id: 2
|
|
1642
|
+
});
|
|
1643
|
+
let lastMerged = null;
|
|
1644
|
+
let lastTicker = null;
|
|
1645
|
+
const emitOverlayed = () => {
|
|
1646
|
+
if (!lastMerged) return;
|
|
1647
|
+
let bids = lastMerged.bids;
|
|
1648
|
+
let asks = lastMerged.asks;
|
|
1649
|
+
let timestamp = lastMerged.timestamp;
|
|
1650
|
+
let sequence = lastMerged.sequence;
|
|
1651
|
+
if (lastTicker) {
|
|
1652
|
+
bids = overlayBid2(bids, lastTicker.bid);
|
|
1653
|
+
asks = overlayAsk2(asks, lastTicker.ask);
|
|
1654
|
+
if (lastTicker.ts > timestamp) {
|
|
1655
|
+
timestamp = lastTicker.ts;
|
|
1656
|
+
sequence = Math.max(sequence, lastTicker.ts);
|
|
1657
|
+
}
|
|
1658
|
+
}
|
|
1637
1659
|
const book = {
|
|
1638
1660
|
exchange: "coinex",
|
|
1639
1661
|
symbol: fromCoinexSymbol(market),
|
|
1640
1662
|
market: opts.market,
|
|
1663
|
+
bids,
|
|
1664
|
+
asks,
|
|
1665
|
+
timestamp,
|
|
1666
|
+
sequence
|
|
1667
|
+
};
|
|
1668
|
+
stream.emit("update", book);
|
|
1669
|
+
};
|
|
1670
|
+
const handleDepth = (d) => {
|
|
1671
|
+
if (d.market !== market) return;
|
|
1672
|
+
const inner = d.depth;
|
|
1673
|
+
const r = merger.apply({
|
|
1674
|
+
kind: "snapshot",
|
|
1675
|
+
bids: inner.bids.map(([p, s]) => ({
|
|
1676
|
+
price: Number(p),
|
|
1677
|
+
size: Number(s)
|
|
1678
|
+
})),
|
|
1679
|
+
asks: inner.asks.map(([p, s]) => ({
|
|
1680
|
+
price: Number(p),
|
|
1681
|
+
size: Number(s)
|
|
1682
|
+
})),
|
|
1683
|
+
sequence: inner.updated_at,
|
|
1684
|
+
timestamp: inner.updated_at
|
|
1685
|
+
});
|
|
1686
|
+
if (!r.ok) return;
|
|
1687
|
+
lastMerged = {
|
|
1641
1688
|
bids: r.bids,
|
|
1642
1689
|
asks: r.asks,
|
|
1643
1690
|
timestamp: r.timestamp,
|
|
1644
1691
|
sequence: r.sequence
|
|
1645
1692
|
};
|
|
1646
|
-
|
|
1693
|
+
emitOverlayed();
|
|
1647
1694
|
};
|
|
1648
|
-
const
|
|
1649
|
-
|
|
1650
|
-
|
|
1651
|
-
const
|
|
1652
|
-
const
|
|
1653
|
-
const
|
|
1654
|
-
|
|
1655
|
-
|
|
1656
|
-
|
|
1657
|
-
|
|
1658
|
-
|
|
1659
|
-
} : {
|
|
1660
|
-
kind: "delta",
|
|
1661
|
-
bids,
|
|
1662
|
-
asks,
|
|
1663
|
-
sequence: ++seq,
|
|
1664
|
-
timestamp: payload.time
|
|
1695
|
+
const handleBbo = (b) => {
|
|
1696
|
+
if (b.market !== market) return;
|
|
1697
|
+
const bidPrice = Number(b.best_bid_price);
|
|
1698
|
+
const askPrice = Number(b.best_ask_price);
|
|
1699
|
+
const bidSize = Number(b.best_bid_size);
|
|
1700
|
+
const askSize = Number(b.best_ask_size);
|
|
1701
|
+
if (!Number.isFinite(bidPrice) || !Number.isFinite(askPrice)) return;
|
|
1702
|
+
lastTicker = {
|
|
1703
|
+
bid: { price: bidPrice, size: bidSize },
|
|
1704
|
+
ask: { price: askPrice, size: askSize },
|
|
1705
|
+
ts: b.updated_at
|
|
1665
1706
|
};
|
|
1666
|
-
|
|
1667
|
-
if (r.ok) {
|
|
1668
|
-
emit(r);
|
|
1669
|
-
return;
|
|
1670
|
-
}
|
|
1671
|
-
if (r.reason === "no-snapshot") return;
|
|
1672
|
-
if (r.reason === "gap") {
|
|
1673
|
-
merger.reset();
|
|
1674
|
-
seq = 0;
|
|
1675
|
-
}
|
|
1707
|
+
emitOverlayed();
|
|
1676
1708
|
};
|
|
1677
1709
|
const handlePayload = (text) => {
|
|
1678
1710
|
let msg;
|
|
@@ -1682,31 +1714,32 @@ function streamCoinexOrderbook(opts) {
|
|
|
1682
1714
|
return;
|
|
1683
1715
|
}
|
|
1684
1716
|
if (msg.id != null && !msg.method) return;
|
|
1685
|
-
if (msg.
|
|
1686
|
-
|
|
1687
|
-
|
|
1688
|
-
|
|
1689
|
-
);
|
|
1690
|
-
return;
|
|
1691
|
-
}
|
|
1692
|
-
if (msg.method === "depth.update" && Array.isArray(msg.params)) {
|
|
1693
|
-
handleDepthPush(msg.params);
|
|
1717
|
+
if (!msg.data || !msg.method) return;
|
|
1718
|
+
if (msg.method === "depth.update") {
|
|
1719
|
+
handleDepth(msg.data);
|
|
1720
|
+
} else if (msg.method === "bbo.update") {
|
|
1721
|
+
handleBbo(msg.data);
|
|
1694
1722
|
}
|
|
1695
1723
|
};
|
|
1696
|
-
const onMessage = (raw) => {
|
|
1697
|
-
if (typeof raw
|
|
1698
|
-
|
|
1724
|
+
const onMessage = async (raw) => {
|
|
1725
|
+
if (typeof raw === "string") {
|
|
1726
|
+
handlePayload(raw);
|
|
1727
|
+
return;
|
|
1728
|
+
}
|
|
1729
|
+
const inflate = await getGzipInflate();
|
|
1730
|
+
handlePayload(inflate(raw));
|
|
1699
1731
|
};
|
|
1700
1732
|
ws = new WSClient({
|
|
1701
1733
|
url: opts.market === "spot" ? WS_SPOT5 : WS_FUTURES2,
|
|
1702
1734
|
onOpen: (sock) => {
|
|
1703
1735
|
merger.reset();
|
|
1704
|
-
|
|
1705
|
-
|
|
1736
|
+
lastMerged = null;
|
|
1737
|
+
lastTicker = null;
|
|
1738
|
+
sock.send(subscribeDepth);
|
|
1739
|
+
sock.send(subscribeBbo);
|
|
1706
1740
|
},
|
|
1707
|
-
onMessage
|
|
1708
|
-
|
|
1709
|
-
pingPayload
|
|
1741
|
+
onMessage
|
|
1742
|
+
// Native WS ping/pong frames keep the connection alive — no app-level ping.
|
|
1710
1743
|
});
|
|
1711
1744
|
ws.on("open", () => stream.emit("connected"));
|
|
1712
1745
|
ws.on("close", (r) => stream.emit("disconnected", r));
|
|
@@ -1715,11 +1748,31 @@ function streamCoinexOrderbook(opts) {
|
|
|
1715
1748
|
ws.connect().catch((e) => stream.emit("error", e));
|
|
1716
1749
|
return stream;
|
|
1717
1750
|
}
|
|
1718
|
-
function
|
|
1719
|
-
if (!
|
|
1720
|
-
|
|
1721
|
-
for (
|
|
1722
|
-
|
|
1751
|
+
function overlayAsk2(asks, top) {
|
|
1752
|
+
if (!(top.size > 0)) return asks;
|
|
1753
|
+
let i = 0;
|
|
1754
|
+
for (; i < asks.length; i++) {
|
|
1755
|
+
const level = asks[i];
|
|
1756
|
+
if (!level || level.price >= top.price) break;
|
|
1757
|
+
}
|
|
1758
|
+
const at = asks[i];
|
|
1759
|
+
if (at && at.price === top.price) {
|
|
1760
|
+
return [{ price: top.price, size: top.size }, ...asks.slice(i + 1)];
|
|
1761
|
+
}
|
|
1762
|
+
return [{ price: top.price, size: top.size }, ...asks.slice(i)];
|
|
1763
|
+
}
|
|
1764
|
+
function overlayBid2(bids, top) {
|
|
1765
|
+
if (!(top.size > 0)) return bids;
|
|
1766
|
+
let i = 0;
|
|
1767
|
+
for (; i < bids.length; i++) {
|
|
1768
|
+
const level = bids[i];
|
|
1769
|
+
if (!level || level.price <= top.price) break;
|
|
1770
|
+
}
|
|
1771
|
+
const at = bids[i];
|
|
1772
|
+
if (at && at.price === top.price) {
|
|
1773
|
+
return [{ price: top.price, size: top.size }, ...bids.slice(i + 1)];
|
|
1774
|
+
}
|
|
1775
|
+
return [{ price: top.price, size: top.size }, ...bids.slice(i)];
|
|
1723
1776
|
}
|
|
1724
1777
|
|
|
1725
1778
|
// src/exchanges/coinex/index.ts
|
|
@@ -2417,8 +2470,8 @@ async function fetchKucoinOrderbook(opts) {
|
|
|
2417
2470
|
exchange: "kucoin",
|
|
2418
2471
|
symbol: fromKucoinSpotSymbol(symbol2),
|
|
2419
2472
|
market: "spot",
|
|
2420
|
-
bids:
|
|
2421
|
-
asks:
|
|
2473
|
+
bids: parseLevels(res2.data.bids),
|
|
2474
|
+
asks: parseLevels(res2.data.asks),
|
|
2422
2475
|
timestamp: res2.data.time,
|
|
2423
2476
|
sequence: Number(res2.data.sequence)
|
|
2424
2477
|
};
|
|
@@ -2437,13 +2490,13 @@ async function fetchKucoinOrderbook(opts) {
|
|
|
2437
2490
|
exchange: "kucoin",
|
|
2438
2491
|
symbol: fromKucoinFuturesSymbol(symbol),
|
|
2439
2492
|
market: "perpetual",
|
|
2440
|
-
bids:
|
|
2441
|
-
asks:
|
|
2493
|
+
bids: parseLevels(res.data.bids),
|
|
2494
|
+
asks: parseLevels(res.data.asks),
|
|
2442
2495
|
timestamp: res.data.ts,
|
|
2443
2496
|
sequence: res.data.sequence
|
|
2444
2497
|
};
|
|
2445
2498
|
}
|
|
2446
|
-
function
|
|
2499
|
+
function parseLevels(rows) {
|
|
2447
2500
|
return rows.map(([p, s]) => ({ price: Number(p), size: Number(s) }));
|
|
2448
2501
|
}
|
|
2449
2502
|
|
|
@@ -2611,13 +2664,13 @@ async function fetchBitmartOrderbook(opts) {
|
|
|
2611
2664
|
exchange: "bitmart",
|
|
2612
2665
|
symbol: fromBitmartSymbol(res.data.symbol),
|
|
2613
2666
|
market: "perpetual",
|
|
2614
|
-
bids:
|
|
2615
|
-
asks:
|
|
2667
|
+
bids: parseLevels2(res.data.bids),
|
|
2668
|
+
asks: parseLevels2(res.data.asks),
|
|
2616
2669
|
timestamp: res.data.timestamp,
|
|
2617
2670
|
sequence: res.data.timestamp
|
|
2618
2671
|
};
|
|
2619
2672
|
}
|
|
2620
|
-
function
|
|
2673
|
+
function parseLevels2(rows) {
|
|
2621
2674
|
const out = [];
|
|
2622
2675
|
for (const row of rows) {
|
|
2623
2676
|
out.push({ price: Number(row[0]), size: Number(row[1]) });
|
|
@@ -2650,8 +2703,8 @@ function streamBitmartOrderbook(opts) {
|
|
|
2650
2703
|
if (frame.symbol !== symbol) return;
|
|
2651
2704
|
const event = {
|
|
2652
2705
|
kind: "snapshot",
|
|
2653
|
-
bids:
|
|
2654
|
-
asks:
|
|
2706
|
+
bids: parseLevels3(frame.bids),
|
|
2707
|
+
asks: parseLevels3(frame.asks),
|
|
2655
2708
|
sequence: frame.ms_t,
|
|
2656
2709
|
timestamp: frame.ms_t
|
|
2657
2710
|
};
|
|
@@ -2716,7 +2769,7 @@ function snapLevel(depth) {
|
|
|
2716
2769
|
if (depth === 20) return 20;
|
|
2717
2770
|
return 50;
|
|
2718
2771
|
}
|
|
2719
|
-
function
|
|
2772
|
+
function parseLevels3(rows) {
|
|
2720
2773
|
if (!rows) return [];
|
|
2721
2774
|
const out = [];
|
|
2722
2775
|
for (const r of rows) out.push({ price: Number(r.price), size: Number(r.vol) });
|