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