@xapy/orderbook 0.1.4 → 0.1.6
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/binance/index.cjs +605 -0
- package/dist/exchanges/binance/index.cjs.map +1 -0
- package/dist/exchanges/binance/index.d.cts +60 -0
- package/dist/exchanges/binance/index.d.ts +60 -0
- package/dist/exchanges/binance/index.js +601 -0
- package/dist/exchanges/binance/index.js.map +1 -0
- package/dist/exchanges/bingx/index.cjs +2 -1
- package/dist/exchanges/bingx/index.cjs.map +1 -1
- package/dist/exchanges/bingx/index.d.cts +1 -1
- package/dist/exchanges/bingx/index.d.ts +1 -1
- package/dist/exchanges/bingx/index.js +2 -1
- package/dist/exchanges/bingx/index.js.map +1 -1
- package/dist/exchanges/bitget/index.cjs +2 -1
- package/dist/exchanges/bitget/index.cjs.map +1 -1
- package/dist/exchanges/bitget/index.d.cts +1 -1
- package/dist/exchanges/bitget/index.d.ts +1 -1
- package/dist/exchanges/bitget/index.js +2 -1
- package/dist/exchanges/bitget/index.js.map +1 -1
- package/dist/exchanges/bybit/index.cjs +2 -1
- package/dist/exchanges/bybit/index.cjs.map +1 -1
- package/dist/exchanges/bybit/index.d.cts +1 -1
- package/dist/exchanges/bybit/index.d.ts +1 -1
- package/dist/exchanges/bybit/index.js +2 -1
- package/dist/exchanges/bybit/index.js.map +1 -1
- package/dist/exchanges/coinex/index.cjs +111 -24
- package/dist/exchanges/coinex/index.cjs.map +1 -1
- package/dist/exchanges/coinex/index.d.cts +13 -5
- package/dist/exchanges/coinex/index.d.ts +13 -5
- package/dist/exchanges/coinex/index.js +111 -24
- package/dist/exchanges/coinex/index.js.map +1 -1
- package/dist/exchanges/gate/index.cjs +2 -1
- package/dist/exchanges/gate/index.cjs.map +1 -1
- package/dist/exchanges/gate/index.d.cts +1 -1
- package/dist/exchanges/gate/index.d.ts +1 -1
- package/dist/exchanges/gate/index.js +2 -1
- package/dist/exchanges/gate/index.js.map +1 -1
- package/dist/exchanges/huobi/index.cjs +2 -1
- package/dist/exchanges/huobi/index.cjs.map +1 -1
- package/dist/exchanges/huobi/index.d.cts +1 -1
- package/dist/exchanges/huobi/index.d.ts +1 -1
- package/dist/exchanges/huobi/index.js +2 -1
- package/dist/exchanges/huobi/index.js.map +1 -1
- package/dist/exchanges/hyperliquid/index.cjs +536 -0
- package/dist/exchanges/hyperliquid/index.cjs.map +1 -0
- package/dist/exchanges/hyperliquid/index.d.cts +45 -0
- package/dist/exchanges/hyperliquid/index.d.ts +45 -0
- package/dist/exchanges/hyperliquid/index.js +532 -0
- package/dist/exchanges/hyperliquid/index.js.map +1 -0
- package/dist/exchanges/kucoin/index.cjs +614 -0
- package/dist/exchanges/kucoin/index.cjs.map +1 -0
- package/dist/exchanges/kucoin/index.d.cts +60 -0
- package/dist/exchanges/kucoin/index.d.ts +60 -0
- package/dist/exchanges/kucoin/index.js +609 -0
- package/dist/exchanges/kucoin/index.js.map +1 -0
- package/dist/exchanges/okx/index.cjs +2 -1
- package/dist/exchanges/okx/index.cjs.map +1 -1
- package/dist/exchanges/okx/index.d.cts +1 -1
- package/dist/exchanges/okx/index.d.ts +1 -1
- package/dist/exchanges/okx/index.js +2 -1
- package/dist/exchanges/okx/index.js.map +1 -1
- package/dist/index.cjs +736 -24
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +5 -2
- package/dist/index.d.ts +5 -2
- package/dist/index.js +734 -25
- package/dist/index.js.map +1 -1
- package/dist/{stream-BKpWmRYN.d.cts → stream-C73HPYqT.d.cts} +1 -1
- package/dist/{stream-BKpWmRYN.d.ts → stream-C73HPYqT.d.ts} +1 -1
- package/package.json +34 -1
package/dist/index.js
CHANGED
|
@@ -356,7 +356,8 @@ var WSClient = class extends TypedEmitter {
|
|
|
356
356
|
async connect() {
|
|
357
357
|
if (this.closed) return;
|
|
358
358
|
const WS = await getWebSocketCtor();
|
|
359
|
-
const
|
|
359
|
+
const url = typeof this.cfg.url === "function" ? await this.cfg.url() : this.cfg.url;
|
|
360
|
+
const ws = new WS(url);
|
|
360
361
|
ws.binaryType = "arraybuffer";
|
|
361
362
|
this.ws = ws;
|
|
362
363
|
ws.onopen = async () => {
|
|
@@ -1623,44 +1624,101 @@ function streamCoinexOrderbook(opts) {
|
|
|
1623
1624
|
const merger = new BookMerger();
|
|
1624
1625
|
let ws = null;
|
|
1625
1626
|
const stream = new OrderbookStream(() => ws?.close());
|
|
1626
|
-
const
|
|
1627
|
+
const subscribeDepth = JSON.stringify({
|
|
1627
1628
|
method: "depth.subscribe",
|
|
1628
1629
|
params: {
|
|
1629
|
-
// [market, limit, interval, diff_flag] — diff_flag=
|
|
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).
|
|
1630
1633
|
market_list: [[market, depth, "0", true]]
|
|
1631
1634
|
},
|
|
1632
1635
|
id: 1
|
|
1633
1636
|
});
|
|
1634
|
-
const
|
|
1635
|
-
|
|
1636
|
-
|
|
1637
|
-
|
|
1638
|
-
|
|
1639
|
-
|
|
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
|
+
}
|
|
1640
1657
|
}
|
|
1641
|
-
if (msg.id != null && !msg.method) return;
|
|
1642
|
-
if (msg.method !== "depth.update" || !msg.data) return;
|
|
1643
|
-
if (msg.data.market !== market) return;
|
|
1644
|
-
const d = msg.data.depth;
|
|
1645
|
-
const event = {
|
|
1646
|
-
kind: "snapshot",
|
|
1647
|
-
bids: d.bids.map(([p, s]) => ({ price: Number(p), size: Number(s) })),
|
|
1648
|
-
asks: d.asks.map(([p, s]) => ({ price: Number(p), size: Number(s) })),
|
|
1649
|
-
sequence: d.updated_at,
|
|
1650
|
-
timestamp: d.updated_at
|
|
1651
|
-
};
|
|
1652
|
-
const r = merger.apply(event);
|
|
1653
|
-
if (!r.ok) return;
|
|
1654
1658
|
const book = {
|
|
1655
1659
|
exchange: "coinex",
|
|
1656
1660
|
symbol: fromCoinexSymbol(market),
|
|
1657
1661
|
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 = {
|
|
1658
1687
|
bids: r.bids,
|
|
1659
1688
|
asks: r.asks,
|
|
1660
1689
|
timestamp: r.timestamp,
|
|
1661
1690
|
sequence: r.sequence
|
|
1662
1691
|
};
|
|
1663
|
-
|
|
1692
|
+
emitOverlayed();
|
|
1693
|
+
};
|
|
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
|
|
1705
|
+
};
|
|
1706
|
+
emitOverlayed();
|
|
1707
|
+
};
|
|
1708
|
+
const handlePayload = (text) => {
|
|
1709
|
+
let msg;
|
|
1710
|
+
try {
|
|
1711
|
+
msg = JSON.parse(text);
|
|
1712
|
+
} catch {
|
|
1713
|
+
return;
|
|
1714
|
+
}
|
|
1715
|
+
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
|
+
}
|
|
1664
1722
|
};
|
|
1665
1723
|
const onMessage = async (raw) => {
|
|
1666
1724
|
if (typeof raw === "string") {
|
|
@@ -1674,7 +1732,10 @@ function streamCoinexOrderbook(opts) {
|
|
|
1674
1732
|
url: opts.market === "spot" ? WS_SPOT5 : WS_FUTURES2,
|
|
1675
1733
|
onOpen: (sock) => {
|
|
1676
1734
|
merger.reset();
|
|
1677
|
-
|
|
1735
|
+
lastMerged = null;
|
|
1736
|
+
lastTicker = null;
|
|
1737
|
+
sock.send(subscribeDepth);
|
|
1738
|
+
sock.send(subscribeBbo);
|
|
1678
1739
|
},
|
|
1679
1740
|
onMessage
|
|
1680
1741
|
// Native WS ping/pong frames keep the connection alive — no app-level ping.
|
|
@@ -1686,6 +1747,32 @@ function streamCoinexOrderbook(opts) {
|
|
|
1686
1747
|
ws.connect().catch((e) => stream.emit("error", e));
|
|
1687
1748
|
return stream;
|
|
1688
1749
|
}
|
|
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)];
|
|
1775
|
+
}
|
|
1689
1776
|
|
|
1690
1777
|
// src/exchanges/coinex/index.ts
|
|
1691
1778
|
var CoinexClient = class {
|
|
@@ -1912,6 +1999,628 @@ var OkxClient = class {
|
|
|
1912
1999
|
}
|
|
1913
2000
|
};
|
|
1914
2001
|
|
|
1915
|
-
|
|
2002
|
+
// src/exchanges/binance/symbols.ts
|
|
2003
|
+
var QUOTES5 = [
|
|
2004
|
+
"USDT",
|
|
2005
|
+
"USDC",
|
|
2006
|
+
"FDUSD",
|
|
2007
|
+
"BUSD",
|
|
2008
|
+
"TUSD",
|
|
2009
|
+
"USD",
|
|
2010
|
+
"BTC",
|
|
2011
|
+
"ETH",
|
|
2012
|
+
"BNB"
|
|
2013
|
+
];
|
|
2014
|
+
function toBinanceSymbol(symbol) {
|
|
2015
|
+
const [base, quote] = symbol.split("/");
|
|
2016
|
+
if (!base || !quote) {
|
|
2017
|
+
throw new Error(`invalid symbol "${symbol}" \u2014 expected "BASE/QUOTE"`);
|
|
2018
|
+
}
|
|
2019
|
+
return `${base}${quote}`.toUpperCase();
|
|
2020
|
+
}
|
|
2021
|
+
function fromBinanceSymbol(s) {
|
|
2022
|
+
const upper = s.toUpperCase();
|
|
2023
|
+
for (const q of QUOTES5) {
|
|
2024
|
+
if (upper.endsWith(q) && upper.length > q.length) {
|
|
2025
|
+
return `${upper.slice(0, -q.length)}/${q}`;
|
|
2026
|
+
}
|
|
2027
|
+
}
|
|
2028
|
+
return upper;
|
|
2029
|
+
}
|
|
2030
|
+
|
|
2031
|
+
// src/exchanges/binance/rest.ts
|
|
2032
|
+
var BASE_SPOT = "https://api.binance.com";
|
|
2033
|
+
var BASE_FAPI = "https://fapi.binance.com";
|
|
2034
|
+
async function fetchBinanceOrderbook(opts) {
|
|
2035
|
+
const symbol = toBinanceSymbol(opts.symbol);
|
|
2036
|
+
const limit = opts.depth ?? 1e3;
|
|
2037
|
+
const url = opts.market === "spot" ? `${BASE_SPOT}/api/v3/depth?symbol=${symbol}&limit=${limit}` : `${BASE_FAPI}/fapi/v1/depth?symbol=${symbol}&limit=${limit}`;
|
|
2038
|
+
const data = await httpJson({
|
|
2039
|
+
url,
|
|
2040
|
+
timeoutMs: opts.timeoutMs,
|
|
2041
|
+
transformUrl: opts.transformUrl
|
|
2042
|
+
});
|
|
2043
|
+
return {
|
|
2044
|
+
exchange: "binance",
|
|
2045
|
+
symbol: fromBinanceSymbol(symbol),
|
|
2046
|
+
market: opts.market,
|
|
2047
|
+
bids: data.bids.map(([p, s]) => ({ price: Number(p), size: Number(s) })),
|
|
2048
|
+
asks: data.asks.map(([p, s]) => ({ price: Number(p), size: Number(s) })),
|
|
2049
|
+
timestamp: data.E ?? data.T ?? Date.now(),
|
|
2050
|
+
sequence: data.lastUpdateId
|
|
2051
|
+
};
|
|
2052
|
+
}
|
|
2053
|
+
|
|
2054
|
+
// src/exchanges/binance/ws.ts
|
|
2055
|
+
var WS_SPOT6 = "wss://stream.binance.com:9443/ws";
|
|
2056
|
+
var WS_FUTURES3 = "wss://fstream.binance.com/ws";
|
|
2057
|
+
function streamBinanceOrderbook(opts) {
|
|
2058
|
+
const symbol = toBinanceSymbol(opts.symbol);
|
|
2059
|
+
const isSpot = opts.market === "spot";
|
|
2060
|
+
const interval = opts.interval ?? "100ms";
|
|
2061
|
+
const wsBase = isSpot ? WS_SPOT6 : WS_FUTURES3;
|
|
2062
|
+
const wsUrl = `${wsBase}/${symbol.toLowerCase()}@depth@${interval}`;
|
|
2063
|
+
const merger = new BookMerger();
|
|
2064
|
+
let snapshotId = -1;
|
|
2065
|
+
let lastU = -1;
|
|
2066
|
+
let snapshotInFlight = false;
|
|
2067
|
+
let snapshotDone = false;
|
|
2068
|
+
let buffered = [];
|
|
2069
|
+
let recovering = false;
|
|
2070
|
+
let ws = null;
|
|
2071
|
+
const stream = new OrderbookStream(() => ws?.close());
|
|
2072
|
+
const emit = (r) => {
|
|
2073
|
+
const book = {
|
|
2074
|
+
exchange: "binance",
|
|
2075
|
+
symbol: fromBinanceSymbol(symbol),
|
|
2076
|
+
market: opts.market,
|
|
2077
|
+
bids: r.bids,
|
|
2078
|
+
asks: r.asks,
|
|
2079
|
+
timestamp: r.timestamp,
|
|
2080
|
+
sequence: r.sequence
|
|
2081
|
+
};
|
|
2082
|
+
stream.emit("update", book);
|
|
2083
|
+
};
|
|
2084
|
+
const triggerSnapshot = () => {
|
|
2085
|
+
if (snapshotInFlight) return;
|
|
2086
|
+
snapshotInFlight = true;
|
|
2087
|
+
fetchBinanceOrderbook({
|
|
2088
|
+
symbol: opts.symbol,
|
|
2089
|
+
market: opts.market,
|
|
2090
|
+
depth: opts.depth ?? 1e3,
|
|
2091
|
+
timeoutMs: opts.timeoutMs,
|
|
2092
|
+
transformUrl: opts.transformUrl
|
|
2093
|
+
}).then((snap) => {
|
|
2094
|
+
snapshotId = snap.sequence;
|
|
2095
|
+
const r = merger.apply({
|
|
2096
|
+
kind: "snapshot",
|
|
2097
|
+
bids: snap.bids,
|
|
2098
|
+
asks: snap.asks,
|
|
2099
|
+
sequence: snap.sequence,
|
|
2100
|
+
timestamp: snap.timestamp
|
|
2101
|
+
});
|
|
2102
|
+
if (r.ok) emit(r);
|
|
2103
|
+
snapshotDone = true;
|
|
2104
|
+
lastU = -1;
|
|
2105
|
+
const pending2 = buffered;
|
|
2106
|
+
buffered = [];
|
|
2107
|
+
for (const ev of pending2) processDelta(ev);
|
|
2108
|
+
}).catch((err) => stream.emit("error", err)).finally(() => {
|
|
2109
|
+
snapshotInFlight = false;
|
|
2110
|
+
});
|
|
2111
|
+
};
|
|
2112
|
+
const resync = () => {
|
|
2113
|
+
if (recovering) return;
|
|
2114
|
+
recovering = true;
|
|
2115
|
+
merger.reset();
|
|
2116
|
+
snapshotId = -1;
|
|
2117
|
+
snapshotDone = false;
|
|
2118
|
+
lastU = -1;
|
|
2119
|
+
buffered = [];
|
|
2120
|
+
setTimeout(() => {
|
|
2121
|
+
recovering = false;
|
|
2122
|
+
triggerSnapshot();
|
|
2123
|
+
}, 100);
|
|
2124
|
+
};
|
|
2125
|
+
const processDelta = (ev) => {
|
|
2126
|
+
if (ev.u < snapshotId + 1) return;
|
|
2127
|
+
const bids = ev.b.map(([p, s]) => ({
|
|
2128
|
+
price: Number(p),
|
|
2129
|
+
size: Number(s)
|
|
2130
|
+
}));
|
|
2131
|
+
const asks = ev.a.map(([p, s]) => ({
|
|
2132
|
+
price: Number(p),
|
|
2133
|
+
size: Number(s)
|
|
2134
|
+
}));
|
|
2135
|
+
let prevSequence;
|
|
2136
|
+
if (lastU < 0) {
|
|
2137
|
+
const overlap = isSpot ? ev.U <= snapshotId + 1 : ev.U <= snapshotId;
|
|
2138
|
+
if (!overlap) {
|
|
2139
|
+
resync();
|
|
2140
|
+
return;
|
|
2141
|
+
}
|
|
2142
|
+
prevSequence = snapshotId;
|
|
2143
|
+
} else if (isSpot) {
|
|
2144
|
+
prevSequence = ev.U - 1;
|
|
2145
|
+
} else {
|
|
2146
|
+
if (ev.pu === void 0) {
|
|
2147
|
+
resync();
|
|
2148
|
+
return;
|
|
2149
|
+
}
|
|
2150
|
+
prevSequence = ev.pu;
|
|
2151
|
+
}
|
|
2152
|
+
const r = merger.apply({
|
|
2153
|
+
kind: "delta",
|
|
2154
|
+
bids,
|
|
2155
|
+
asks,
|
|
2156
|
+
sequence: ev.u,
|
|
2157
|
+
prevSequence,
|
|
2158
|
+
timestamp: ev.E
|
|
2159
|
+
});
|
|
2160
|
+
if (r.ok) {
|
|
2161
|
+
lastU = ev.u;
|
|
2162
|
+
emit(r);
|
|
2163
|
+
} else if (r.reason === "gap" || r.reason === "no-snapshot") {
|
|
2164
|
+
resync();
|
|
2165
|
+
}
|
|
2166
|
+
};
|
|
2167
|
+
const onMessage = (raw) => {
|
|
2168
|
+
if (typeof raw !== "string") return;
|
|
2169
|
+
let msg;
|
|
2170
|
+
try {
|
|
2171
|
+
msg = JSON.parse(raw);
|
|
2172
|
+
} catch {
|
|
2173
|
+
return;
|
|
2174
|
+
}
|
|
2175
|
+
if (msg.e !== "depthUpdate") return;
|
|
2176
|
+
if (!snapshotDone) {
|
|
2177
|
+
buffered.push(msg);
|
|
2178
|
+
if (!snapshotInFlight) triggerSnapshot();
|
|
2179
|
+
return;
|
|
2180
|
+
}
|
|
2181
|
+
processDelta(msg);
|
|
2182
|
+
};
|
|
2183
|
+
ws = new WSClient({
|
|
2184
|
+
url: wsUrl,
|
|
2185
|
+
onOpen: () => {
|
|
2186
|
+
merger.reset();
|
|
2187
|
+
snapshotId = -1;
|
|
2188
|
+
snapshotDone = false;
|
|
2189
|
+
lastU = -1;
|
|
2190
|
+
buffered = [];
|
|
2191
|
+
},
|
|
2192
|
+
onMessage
|
|
2193
|
+
// Binance server-initiated WS-level ping; auto-handled by the WS impl.
|
|
2194
|
+
});
|
|
2195
|
+
ws.on("open", () => stream.emit("connected"));
|
|
2196
|
+
ws.on("close", (r) => stream.emit("disconnected", r));
|
|
2197
|
+
ws.on("reconnecting", (a, w) => stream.emit("reconnecting", a, w));
|
|
2198
|
+
ws.on("error", (e) => stream.emit("error", e));
|
|
2199
|
+
ws.connect().catch((e) => stream.emit("error", e));
|
|
2200
|
+
return stream;
|
|
2201
|
+
}
|
|
2202
|
+
|
|
2203
|
+
// src/exchanges/binance/index.ts
|
|
2204
|
+
var BinanceClient = class {
|
|
2205
|
+
exchange = "binance";
|
|
2206
|
+
transformUrl;
|
|
2207
|
+
timeoutMs;
|
|
2208
|
+
constructor(opts = {}) {
|
|
2209
|
+
this.transformUrl = opts.transformUrl;
|
|
2210
|
+
this.timeoutMs = opts.timeoutMs;
|
|
2211
|
+
}
|
|
2212
|
+
fetchOrderbook(symbol, opts = {}) {
|
|
2213
|
+
const { pair, market } = parseSymbol(symbol);
|
|
2214
|
+
return fetchBinanceOrderbook({
|
|
2215
|
+
symbol: pair,
|
|
2216
|
+
market,
|
|
2217
|
+
depth: opts.depth,
|
|
2218
|
+
timeoutMs: this.timeoutMs,
|
|
2219
|
+
transformUrl: this.transformUrl
|
|
2220
|
+
});
|
|
2221
|
+
}
|
|
2222
|
+
streamOrderbook(symbol, opts = {}) {
|
|
2223
|
+
const { pair, market } = parseSymbol(symbol);
|
|
2224
|
+
return streamBinanceOrderbook({
|
|
2225
|
+
symbol: pair,
|
|
2226
|
+
market,
|
|
2227
|
+
depth: opts.depth,
|
|
2228
|
+
transformUrl: this.transformUrl,
|
|
2229
|
+
timeoutMs: this.timeoutMs
|
|
2230
|
+
});
|
|
2231
|
+
}
|
|
2232
|
+
};
|
|
2233
|
+
|
|
2234
|
+
// src/exchanges/hyperliquid/symbols.ts
|
|
2235
|
+
function toHyperliquidSymbol(symbol) {
|
|
2236
|
+
const [base, quote] = symbol.split("/");
|
|
2237
|
+
if (!base || !quote) {
|
|
2238
|
+
throw new Error(`invalid symbol "${symbol}" \u2014 expected "BASE/QUOTE"`);
|
|
2239
|
+
}
|
|
2240
|
+
if (quote.toUpperCase() !== "USDC") {
|
|
2241
|
+
throw new Error(
|
|
2242
|
+
`hyperliquid only supports USDC-quoted pairs; got "${quote}"`
|
|
2243
|
+
);
|
|
2244
|
+
}
|
|
2245
|
+
return base.toUpperCase();
|
|
2246
|
+
}
|
|
2247
|
+
function fromHyperliquidSymbol(coin) {
|
|
2248
|
+
return `${coin.toUpperCase()}/USDC`;
|
|
2249
|
+
}
|
|
2250
|
+
|
|
2251
|
+
// src/exchanges/hyperliquid/rest.ts
|
|
2252
|
+
var BASE7 = "https://api.hyperliquid.xyz";
|
|
2253
|
+
async function fetchHyperliquidOrderbook(opts) {
|
|
2254
|
+
if (opts.market !== "perpetual") {
|
|
2255
|
+
throw new Error(
|
|
2256
|
+
"hyperliquid: only perpetual markets are supported (spot uses @N-indexed coins)"
|
|
2257
|
+
);
|
|
2258
|
+
}
|
|
2259
|
+
const coin = toHyperliquidSymbol(opts.symbol);
|
|
2260
|
+
const data = await httpJson({
|
|
2261
|
+
url: `${BASE7}/info`,
|
|
2262
|
+
method: "POST",
|
|
2263
|
+
headers: { "Content-Type": "application/json" },
|
|
2264
|
+
body: JSON.stringify({ type: "l2Book", coin }),
|
|
2265
|
+
timeoutMs: opts.timeoutMs,
|
|
2266
|
+
transformUrl: opts.transformUrl
|
|
2267
|
+
});
|
|
2268
|
+
return {
|
|
2269
|
+
exchange: "hyperliquid",
|
|
2270
|
+
symbol: fromHyperliquidSymbol(data.coin),
|
|
2271
|
+
market: "perpetual",
|
|
2272
|
+
bids: data.levels[0].map((l) => ({
|
|
2273
|
+
price: Number(l.px),
|
|
2274
|
+
size: Number(l.sz)
|
|
2275
|
+
})),
|
|
2276
|
+
asks: data.levels[1].map((l) => ({
|
|
2277
|
+
price: Number(l.px),
|
|
2278
|
+
size: Number(l.sz)
|
|
2279
|
+
})),
|
|
2280
|
+
timestamp: data.time,
|
|
2281
|
+
sequence: data.time
|
|
2282
|
+
};
|
|
2283
|
+
}
|
|
2284
|
+
|
|
2285
|
+
// src/exchanges/hyperliquid/ws.ts
|
|
2286
|
+
var WS_URL = "wss://api.hyperliquid.xyz/ws";
|
|
2287
|
+
function streamHyperliquidOrderbook(opts) {
|
|
2288
|
+
if (opts.market !== "perpetual") {
|
|
2289
|
+
const s = new OrderbookStream(() => void 0);
|
|
2290
|
+
queueMicrotask(
|
|
2291
|
+
() => s.emit(
|
|
2292
|
+
"error",
|
|
2293
|
+
new Error(
|
|
2294
|
+
"hyperliquid: only perpetual markets are supported (spot uses @N-indexed coins)"
|
|
2295
|
+
)
|
|
2296
|
+
)
|
|
2297
|
+
);
|
|
2298
|
+
return s;
|
|
2299
|
+
}
|
|
2300
|
+
const coin = toHyperliquidSymbol(opts.symbol);
|
|
2301
|
+
const merger = new BookMerger();
|
|
2302
|
+
let ws = null;
|
|
2303
|
+
const stream = new OrderbookStream(() => ws?.close());
|
|
2304
|
+
const subscribePayload = JSON.stringify({
|
|
2305
|
+
method: "subscribe",
|
|
2306
|
+
subscription: { type: "l2Book", coin }
|
|
2307
|
+
});
|
|
2308
|
+
const onMessage = (raw) => {
|
|
2309
|
+
if (typeof raw !== "string") return;
|
|
2310
|
+
let msg;
|
|
2311
|
+
try {
|
|
2312
|
+
msg = JSON.parse(raw);
|
|
2313
|
+
} catch {
|
|
2314
|
+
return;
|
|
2315
|
+
}
|
|
2316
|
+
if (msg.channel === "pong" || msg.channel === "subscriptionResponse") {
|
|
2317
|
+
return;
|
|
2318
|
+
}
|
|
2319
|
+
if (msg.channel !== "l2Book" || !msg.data) return;
|
|
2320
|
+
const book = msg.data;
|
|
2321
|
+
if (book.coin !== coin) return;
|
|
2322
|
+
const event = {
|
|
2323
|
+
kind: "snapshot",
|
|
2324
|
+
bids: book.levels[0].map((l) => ({
|
|
2325
|
+
price: Number(l.px),
|
|
2326
|
+
size: Number(l.sz)
|
|
2327
|
+
})),
|
|
2328
|
+
asks: book.levels[1].map((l) => ({
|
|
2329
|
+
price: Number(l.px),
|
|
2330
|
+
size: Number(l.sz)
|
|
2331
|
+
})),
|
|
2332
|
+
sequence: book.time,
|
|
2333
|
+
timestamp: book.time
|
|
2334
|
+
};
|
|
2335
|
+
const r = merger.apply(event);
|
|
2336
|
+
if (!r.ok) return;
|
|
2337
|
+
const out = {
|
|
2338
|
+
exchange: "hyperliquid",
|
|
2339
|
+
symbol: fromHyperliquidSymbol(coin),
|
|
2340
|
+
market: "perpetual",
|
|
2341
|
+
bids: r.bids,
|
|
2342
|
+
asks: r.asks,
|
|
2343
|
+
timestamp: r.timestamp,
|
|
2344
|
+
sequence: r.sequence
|
|
2345
|
+
};
|
|
2346
|
+
stream.emit("update", out);
|
|
2347
|
+
};
|
|
2348
|
+
ws = new WSClient({
|
|
2349
|
+
url: WS_URL,
|
|
2350
|
+
onOpen: (sock) => {
|
|
2351
|
+
merger.reset();
|
|
2352
|
+
sock.send(subscribePayload);
|
|
2353
|
+
},
|
|
2354
|
+
onMessage,
|
|
2355
|
+
pingIntervalMs: 5e4,
|
|
2356
|
+
pingPayload: () => JSON.stringify({ method: "ping" })
|
|
2357
|
+
});
|
|
2358
|
+
ws.on("open", () => stream.emit("connected"));
|
|
2359
|
+
ws.on("close", (r) => stream.emit("disconnected", r));
|
|
2360
|
+
ws.on("reconnecting", (a, w) => stream.emit("reconnecting", a, w));
|
|
2361
|
+
ws.on("error", (e) => stream.emit("error", e));
|
|
2362
|
+
ws.connect().catch((e) => stream.emit("error", e));
|
|
2363
|
+
return stream;
|
|
2364
|
+
}
|
|
2365
|
+
|
|
2366
|
+
// src/exchanges/hyperliquid/index.ts
|
|
2367
|
+
var HyperliquidClient = class {
|
|
2368
|
+
exchange = "hyperliquid";
|
|
2369
|
+
transformUrl;
|
|
2370
|
+
timeoutMs;
|
|
2371
|
+
constructor(opts = {}) {
|
|
2372
|
+
this.transformUrl = opts.transformUrl;
|
|
2373
|
+
this.timeoutMs = opts.timeoutMs;
|
|
2374
|
+
}
|
|
2375
|
+
fetchOrderbook(symbol, opts = {}) {
|
|
2376
|
+
const { pair, market } = parseSymbol(symbol);
|
|
2377
|
+
return fetchHyperliquidOrderbook({
|
|
2378
|
+
symbol: pair,
|
|
2379
|
+
market,
|
|
2380
|
+
depth: opts.depth,
|
|
2381
|
+
timeoutMs: this.timeoutMs,
|
|
2382
|
+
transformUrl: this.transformUrl
|
|
2383
|
+
});
|
|
2384
|
+
}
|
|
2385
|
+
streamOrderbook(symbol, opts = {}) {
|
|
2386
|
+
const { pair, market } = parseSymbol(symbol);
|
|
2387
|
+
return streamHyperliquidOrderbook({
|
|
2388
|
+
symbol: pair,
|
|
2389
|
+
market,
|
|
2390
|
+
depth: opts.depth,
|
|
2391
|
+
transformUrl: this.transformUrl,
|
|
2392
|
+
timeoutMs: this.timeoutMs
|
|
2393
|
+
});
|
|
2394
|
+
}
|
|
2395
|
+
};
|
|
2396
|
+
|
|
2397
|
+
// src/exchanges/kucoin/symbols.ts
|
|
2398
|
+
var FUTURES_QUOTES = ["USDT", "USDC", "USD"];
|
|
2399
|
+
function toKucoinSpotSymbol(symbol) {
|
|
2400
|
+
const [base, quote] = symbol.split("/");
|
|
2401
|
+
if (!base || !quote) {
|
|
2402
|
+
throw new Error(`invalid symbol "${symbol}" \u2014 expected "BASE/QUOTE"`);
|
|
2403
|
+
}
|
|
2404
|
+
return `${base.toUpperCase()}-${quote.toUpperCase()}`;
|
|
2405
|
+
}
|
|
2406
|
+
function fromKucoinSpotSymbol(s) {
|
|
2407
|
+
return s.toUpperCase().replace("-", "/");
|
|
2408
|
+
}
|
|
2409
|
+
function toKucoinFuturesSymbol(symbol) {
|
|
2410
|
+
const [base, quote] = symbol.split("/");
|
|
2411
|
+
if (!base || !quote) {
|
|
2412
|
+
throw new Error(`invalid symbol "${symbol}" \u2014 expected "BASE/QUOTE"`);
|
|
2413
|
+
}
|
|
2414
|
+
const b = base.toUpperCase() === "BTC" ? "XBT" : base.toUpperCase();
|
|
2415
|
+
return `${b}${quote.toUpperCase()}M`;
|
|
2416
|
+
}
|
|
2417
|
+
function fromKucoinFuturesSymbol(s) {
|
|
2418
|
+
const upper = s.toUpperCase();
|
|
2419
|
+
const stripped = upper.endsWith("M") ? upper.slice(0, -1) : upper;
|
|
2420
|
+
for (const q of FUTURES_QUOTES) {
|
|
2421
|
+
if (stripped.endsWith(q) && stripped.length > q.length) {
|
|
2422
|
+
let base = stripped.slice(0, -q.length);
|
|
2423
|
+
if (base === "XBT") base = "BTC";
|
|
2424
|
+
return `${base}/${q}`;
|
|
2425
|
+
}
|
|
2426
|
+
}
|
|
2427
|
+
return upper;
|
|
2428
|
+
}
|
|
2429
|
+
|
|
2430
|
+
// src/exchanges/kucoin/rest.ts
|
|
2431
|
+
var BASE_SPOT2 = "https://api.kucoin.com";
|
|
2432
|
+
var BASE_FUTURES = "https://api-futures.kucoin.com";
|
|
2433
|
+
async function fetchKucoinBullet(opts) {
|
|
2434
|
+
const base = opts.market === "spot" ? BASE_SPOT2 : BASE_FUTURES;
|
|
2435
|
+
const res = await httpJson({
|
|
2436
|
+
url: `${base}/api/v1/bullet-public`,
|
|
2437
|
+
method: "POST",
|
|
2438
|
+
timeoutMs: opts.timeoutMs,
|
|
2439
|
+
transformUrl: opts.transformUrl
|
|
2440
|
+
});
|
|
2441
|
+
if (res.code !== "200000") {
|
|
2442
|
+
throw new ExchangeError("kucoin", `bullet failed: ${res.code}`);
|
|
2443
|
+
}
|
|
2444
|
+
const server = res.data.instanceServers[0];
|
|
2445
|
+
if (!server) {
|
|
2446
|
+
throw new ExchangeError("kucoin", "bullet returned no instance servers");
|
|
2447
|
+
}
|
|
2448
|
+
return {
|
|
2449
|
+
endpoint: server.endpoint,
|
|
2450
|
+
token: res.data.token,
|
|
2451
|
+
pingInterval: server.pingInterval,
|
|
2452
|
+
pingTimeout: server.pingTimeout
|
|
2453
|
+
};
|
|
2454
|
+
}
|
|
2455
|
+
async function fetchKucoinOrderbook(opts) {
|
|
2456
|
+
const tier = (opts.depth ?? 100) <= 20 ? 20 : 100;
|
|
2457
|
+
if (opts.market === "spot") {
|
|
2458
|
+
const symbol2 = toKucoinSpotSymbol(opts.symbol);
|
|
2459
|
+
const url2 = `${BASE_SPOT2}/api/v1/market/orderbook/level2_${tier}?symbol=${symbol2}`;
|
|
2460
|
+
const res2 = await httpJson({
|
|
2461
|
+
url: url2,
|
|
2462
|
+
timeoutMs: opts.timeoutMs,
|
|
2463
|
+
transformUrl: opts.transformUrl
|
|
2464
|
+
});
|
|
2465
|
+
if (res2.code !== "200000") {
|
|
2466
|
+
throw new ExchangeError("kucoin", `orderbook failed: ${res2.code}`);
|
|
2467
|
+
}
|
|
2468
|
+
return {
|
|
2469
|
+
exchange: "kucoin",
|
|
2470
|
+
symbol: fromKucoinSpotSymbol(symbol2),
|
|
2471
|
+
market: "spot",
|
|
2472
|
+
bids: parseLevels(res2.data.bids),
|
|
2473
|
+
asks: parseLevels(res2.data.asks),
|
|
2474
|
+
timestamp: res2.data.time,
|
|
2475
|
+
sequence: Number(res2.data.sequence)
|
|
2476
|
+
};
|
|
2477
|
+
}
|
|
2478
|
+
const symbol = toKucoinFuturesSymbol(opts.symbol);
|
|
2479
|
+
const url = `${BASE_FUTURES}/api/v1/level2/depth${tier}?symbol=${symbol}`;
|
|
2480
|
+
const res = await httpJson({
|
|
2481
|
+
url,
|
|
2482
|
+
timeoutMs: opts.timeoutMs,
|
|
2483
|
+
transformUrl: opts.transformUrl
|
|
2484
|
+
});
|
|
2485
|
+
if (res.code !== "200000") {
|
|
2486
|
+
throw new ExchangeError("kucoin", `orderbook failed: ${res.code}`);
|
|
2487
|
+
}
|
|
2488
|
+
return {
|
|
2489
|
+
exchange: "kucoin",
|
|
2490
|
+
symbol: fromKucoinFuturesSymbol(symbol),
|
|
2491
|
+
market: "perpetual",
|
|
2492
|
+
bids: parseLevels(res.data.bids),
|
|
2493
|
+
asks: parseLevels(res.data.asks),
|
|
2494
|
+
timestamp: res.data.ts,
|
|
2495
|
+
sequence: res.data.sequence
|
|
2496
|
+
};
|
|
2497
|
+
}
|
|
2498
|
+
function parseLevels(rows) {
|
|
2499
|
+
return rows.map(([p, s]) => ({ price: Number(p), size: Number(s) }));
|
|
2500
|
+
}
|
|
2501
|
+
|
|
2502
|
+
// src/exchanges/kucoin/ws.ts
|
|
2503
|
+
function streamKucoinOrderbook(opts) {
|
|
2504
|
+
const isSpot = opts.market === "spot";
|
|
2505
|
+
const symbol = isSpot ? toKucoinSpotSymbol(opts.symbol) : toKucoinFuturesSymbol(opts.symbol);
|
|
2506
|
+
const level = (opts.depth ?? 50) <= 5 ? 5 : 50;
|
|
2507
|
+
const topic = isSpot ? `/spotMarket/level2Depth${level}:${symbol}` : `/contractMarket/level2Depth${level}:${symbol}`;
|
|
2508
|
+
const merger = new BookMerger();
|
|
2509
|
+
let ws = null;
|
|
2510
|
+
const stream = new OrderbookStream(() => ws?.close());
|
|
2511
|
+
const urlProvider = async () => {
|
|
2512
|
+
const bullet = await fetchKucoinBullet({
|
|
2513
|
+
market: opts.market,
|
|
2514
|
+
timeoutMs: opts.timeoutMs,
|
|
2515
|
+
transformUrl: opts.transformUrl
|
|
2516
|
+
});
|
|
2517
|
+
const connectId = `xapy-${Date.now()}-${Math.floor(Math.random() * 1e6)}`;
|
|
2518
|
+
return `${bullet.endpoint}?token=${encodeURIComponent(bullet.token)}&connectId=${connectId}`;
|
|
2519
|
+
};
|
|
2520
|
+
const subscribePayload = () => JSON.stringify({
|
|
2521
|
+
id: `sub-${Date.now()}`,
|
|
2522
|
+
type: "subscribe",
|
|
2523
|
+
topic,
|
|
2524
|
+
privateChannel: false,
|
|
2525
|
+
response: true
|
|
2526
|
+
});
|
|
2527
|
+
const pingPayload = () => JSON.stringify({ id: `${Date.now()}`, type: "ping" });
|
|
2528
|
+
const handleDepth = (d) => {
|
|
2529
|
+
const ts = d.timestamp ?? d.ts ?? Date.now();
|
|
2530
|
+
const event = {
|
|
2531
|
+
kind: "snapshot",
|
|
2532
|
+
bids: (d.bids ?? []).map(([p, s]) => ({
|
|
2533
|
+
price: Number(p),
|
|
2534
|
+
size: Number(s)
|
|
2535
|
+
})),
|
|
2536
|
+
asks: (d.asks ?? []).map(([p, s]) => ({
|
|
2537
|
+
price: Number(p),
|
|
2538
|
+
size: Number(s)
|
|
2539
|
+
})),
|
|
2540
|
+
sequence: ts,
|
|
2541
|
+
timestamp: ts
|
|
2542
|
+
};
|
|
2543
|
+
const r = merger.apply(event);
|
|
2544
|
+
if (!r.ok) return;
|
|
2545
|
+
const book = {
|
|
2546
|
+
exchange: "kucoin",
|
|
2547
|
+
symbol: isSpot ? fromKucoinSpotSymbol(symbol) : fromKucoinFuturesSymbol(symbol),
|
|
2548
|
+
market: opts.market,
|
|
2549
|
+
bids: r.bids,
|
|
2550
|
+
asks: r.asks,
|
|
2551
|
+
timestamp: r.timestamp,
|
|
2552
|
+
sequence: r.sequence
|
|
2553
|
+
};
|
|
2554
|
+
stream.emit("update", book);
|
|
2555
|
+
};
|
|
2556
|
+
const onMessage = (raw) => {
|
|
2557
|
+
if (typeof raw !== "string") return;
|
|
2558
|
+
let msg;
|
|
2559
|
+
try {
|
|
2560
|
+
msg = JSON.parse(raw);
|
|
2561
|
+
} catch {
|
|
2562
|
+
return;
|
|
2563
|
+
}
|
|
2564
|
+
if (msg.type === "welcome" || msg.type === "ack" || msg.type === "pong") {
|
|
2565
|
+
return;
|
|
2566
|
+
}
|
|
2567
|
+
if (msg.type === "error") {
|
|
2568
|
+
stream.emit("error", new Error(`kucoin: ${JSON.stringify(msg)}`));
|
|
2569
|
+
return;
|
|
2570
|
+
}
|
|
2571
|
+
if (msg.type !== "message" || msg.topic !== topic || !msg.data) return;
|
|
2572
|
+
handleDepth(msg.data);
|
|
2573
|
+
};
|
|
2574
|
+
ws = new WSClient({
|
|
2575
|
+
url: urlProvider,
|
|
2576
|
+
onOpen: (sock) => {
|
|
2577
|
+
merger.reset();
|
|
2578
|
+
sock.send(subscribePayload());
|
|
2579
|
+
},
|
|
2580
|
+
onMessage,
|
|
2581
|
+
// KuCoin's documented bullet `pingInterval` is 18000ms — stay under it.
|
|
2582
|
+
pingIntervalMs: 15e3,
|
|
2583
|
+
pingPayload
|
|
2584
|
+
});
|
|
2585
|
+
ws.on("open", () => stream.emit("connected"));
|
|
2586
|
+
ws.on("close", (r) => stream.emit("disconnected", r));
|
|
2587
|
+
ws.on("reconnecting", (a, w) => stream.emit("reconnecting", a, w));
|
|
2588
|
+
ws.on("error", (e) => stream.emit("error", e));
|
|
2589
|
+
ws.connect().catch((e) => stream.emit("error", e));
|
|
2590
|
+
return stream;
|
|
2591
|
+
}
|
|
2592
|
+
|
|
2593
|
+
// src/exchanges/kucoin/index.ts
|
|
2594
|
+
var KucoinClient = class {
|
|
2595
|
+
exchange = "kucoin";
|
|
2596
|
+
transformUrl;
|
|
2597
|
+
timeoutMs;
|
|
2598
|
+
constructor(opts = {}) {
|
|
2599
|
+
this.transformUrl = opts.transformUrl;
|
|
2600
|
+
this.timeoutMs = opts.timeoutMs;
|
|
2601
|
+
}
|
|
2602
|
+
fetchOrderbook(symbol, opts = {}) {
|
|
2603
|
+
const { pair, market } = parseSymbol(symbol);
|
|
2604
|
+
return fetchKucoinOrderbook({
|
|
2605
|
+
symbol: pair,
|
|
2606
|
+
market,
|
|
2607
|
+
depth: opts.depth,
|
|
2608
|
+
timeoutMs: this.timeoutMs,
|
|
2609
|
+
transformUrl: this.transformUrl
|
|
2610
|
+
});
|
|
2611
|
+
}
|
|
2612
|
+
streamOrderbook(symbol, opts = {}) {
|
|
2613
|
+
const { pair, market } = parseSymbol(symbol);
|
|
2614
|
+
return streamKucoinOrderbook({
|
|
2615
|
+
symbol: pair,
|
|
2616
|
+
market,
|
|
2617
|
+
depth: opts.depth,
|
|
2618
|
+
transformUrl: this.transformUrl,
|
|
2619
|
+
timeoutMs: this.timeoutMs
|
|
2620
|
+
});
|
|
2621
|
+
}
|
|
2622
|
+
};
|
|
2623
|
+
|
|
2624
|
+
export { Backoff, BinanceClient, BingxClient, BitgetClient, BookMerger, BybitClient, CoinexClient, ExchangeError, GateClient, HuobiClient, HyperliquidClient, KucoinClient, OkxClient, OrderbookStream, RateLimitError, SequenceGapError, parseSymbol };
|
|
1916
2625
|
//# sourceMappingURL=index.js.map
|
|
1917
2626
|
//# sourceMappingURL=index.js.map
|