@xapy/orderbook 0.1.26 → 0.1.28
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/README.md +1 -1
- package/dist/exchanges/binance/index.d.cts +1 -1
- package/dist/exchanges/binance/index.d.ts +1 -1
- package/dist/exchanges/bingx/index.d.cts +1 -1
- package/dist/exchanges/bingx/index.d.ts +1 -1
- package/dist/exchanges/bitget/index.d.cts +1 -1
- package/dist/exchanges/bitget/index.d.ts +1 -1
- package/dist/exchanges/bybit/index.d.cts +1 -1
- package/dist/exchanges/bybit/index.d.ts +1 -1
- package/dist/exchanges/coinex/index.d.cts +1 -1
- package/dist/exchanges/coinex/index.d.ts +1 -1
- package/dist/exchanges/deribit/index.d.cts +1 -1
- package/dist/exchanges/deribit/index.d.ts +1 -1
- package/dist/exchanges/edgex/index.cjs +39 -23
- package/dist/exchanges/edgex/index.cjs.map +1 -1
- package/dist/exchanges/edgex/index.d.cts +37 -3
- package/dist/exchanges/edgex/index.d.ts +37 -3
- package/dist/exchanges/edgex/index.js +37 -24
- package/dist/exchanges/edgex/index.js.map +1 -1
- package/dist/exchanges/gate/index.d.cts +1 -1
- package/dist/exchanges/gate/index.d.ts +1 -1
- package/dist/exchanges/hyperliquid/index.d.cts +1 -1
- package/dist/exchanges/hyperliquid/index.d.ts +1 -1
- package/dist/exchanges/kucoin/index.d.cts +1 -1
- package/dist/exchanges/kucoin/index.d.ts +1 -1
- package/dist/exchanges/lighter/index.cjs +1 -0
- package/dist/exchanges/lighter/index.cjs.map +1 -1
- package/dist/exchanges/lighter/index.d.cts +3 -2
- package/dist/exchanges/lighter/index.d.ts +3 -2
- package/dist/exchanges/lighter/index.js +1 -1
- package/dist/exchanges/lighter/index.js.map +1 -1
- package/dist/exchanges/okx/index.d.cts +1 -1
- package/dist/exchanges/okx/index.d.ts +1 -1
- package/dist/exchanges/{grvt → whitebit}/index.cjs +98 -116
- package/dist/exchanges/whitebit/index.cjs.map +1 -0
- package/dist/exchanges/whitebit/index.d.cts +57 -0
- package/dist/exchanges/whitebit/index.d.ts +57 -0
- package/dist/exchanges/{grvt → whitebit}/index.js +93 -114
- package/dist/exchanges/whitebit/index.js.map +1 -0
- package/dist/index.cjs +248 -257
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +3 -3
- package/dist/index.d.ts +3 -3
- package/dist/index.js +248 -257
- package/dist/index.js.map +1 -1
- package/dist/{stream-YYPvjMV4.d.cts → stream-D8ErAue9.d.cts} +1 -1
- package/dist/{stream-YYPvjMV4.d.ts → stream-D8ErAue9.d.ts} +1 -1
- package/package.json +12 -12
- package/dist/exchanges/grvt/index.cjs.map +0 -1
- package/dist/exchanges/grvt/index.d.cts +0 -47
- package/dist/exchanges/grvt/index.d.ts +0 -47
- package/dist/exchanges/grvt/index.js.map +0 -1
package/dist/index.cjs
CHANGED
|
@@ -2714,232 +2714,6 @@ var DeribitClient = class {
|
|
|
2714
2714
|
}
|
|
2715
2715
|
};
|
|
2716
2716
|
|
|
2717
|
-
// src/exchanges/grvt/symbols.ts
|
|
2718
|
-
function toGrvtSymbol(symbol, market) {
|
|
2719
|
-
const [base, quote] = symbol.split("/");
|
|
2720
|
-
if (!base || !quote) {
|
|
2721
|
-
throw new Error(`invalid symbol "${symbol}" \u2014 expected "BASE/QUOTE"`);
|
|
2722
|
-
}
|
|
2723
|
-
if (market !== "perpetual") {
|
|
2724
|
-
throw new Error(
|
|
2725
|
-
`grvt only lists perpetuals; use "${base.toUpperCase()}/${quote.toUpperCase()}:${quote.toUpperCase()}"`
|
|
2726
|
-
);
|
|
2727
|
-
}
|
|
2728
|
-
return `${base.toUpperCase()}_${quote.toUpperCase()}_Perp`;
|
|
2729
|
-
}
|
|
2730
|
-
function fromGrvtSymbol(instrument) {
|
|
2731
|
-
const parts = instrument.split("_");
|
|
2732
|
-
const [base, quote] = parts;
|
|
2733
|
-
return base && quote ? `${base.toUpperCase()}/${quote.toUpperCase()}` : instrument;
|
|
2734
|
-
}
|
|
2735
|
-
|
|
2736
|
-
// src/exchanges/grvt/rest.ts
|
|
2737
|
-
var BASE9 = "https://market-data.grvt.io";
|
|
2738
|
-
var ALLOWED_DEPTHS2 = [10, 50, 100, 500];
|
|
2739
|
-
function snapDepth(depth) {
|
|
2740
|
-
return ALLOWED_DEPTHS2.find((d) => d >= depth) ?? ALLOWED_DEPTHS2[ALLOWED_DEPTHS2.length - 1];
|
|
2741
|
-
}
|
|
2742
|
-
function nsToMs(ns) {
|
|
2743
|
-
const n = Number(ns);
|
|
2744
|
-
return Number.isFinite(n) && n > 0 ? Math.floor(n / 1e6) : Date.now();
|
|
2745
|
-
}
|
|
2746
|
-
var toLevels = (rows) => (rows ?? []).map((l) => ({ price: Number(l.price), size: Number(l.size) }));
|
|
2747
|
-
async function fetchGrvtOrderbook(opts) {
|
|
2748
|
-
const instrument = toGrvtSymbol(opts.symbol, opts.market);
|
|
2749
|
-
const depth = snapDepth(opts.depth ?? 50);
|
|
2750
|
-
const res = await httpJson({
|
|
2751
|
-
url: `${BASE9}/full/v1/book`,
|
|
2752
|
-
method: "POST",
|
|
2753
|
-
headers: { "Content-Type": "application/json" },
|
|
2754
|
-
body: JSON.stringify({ instrument, depth }),
|
|
2755
|
-
timeoutMs: opts.timeoutMs,
|
|
2756
|
-
transformUrl: opts.transformUrl
|
|
2757
|
-
});
|
|
2758
|
-
if (!res.result) {
|
|
2759
|
-
throw new ExchangeError(
|
|
2760
|
-
"grvt",
|
|
2761
|
-
res.code ? `${res.code}: ${res.message}` : "empty orderbook response"
|
|
2762
|
-
);
|
|
2763
|
-
}
|
|
2764
|
-
const ts = nsToMs(res.result.event_time);
|
|
2765
|
-
return {
|
|
2766
|
-
exchange: "grvt",
|
|
2767
|
-
symbol: fromGrvtSymbol(res.result.instrument ?? instrument),
|
|
2768
|
-
market: opts.market,
|
|
2769
|
-
bids: toLevels(res.result.bids),
|
|
2770
|
-
asks: toLevels(res.result.asks),
|
|
2771
|
-
timestamp: ts,
|
|
2772
|
-
sequence: ts
|
|
2773
|
-
};
|
|
2774
|
-
}
|
|
2775
|
-
|
|
2776
|
-
// src/exchanges/grvt/ws.ts
|
|
2777
|
-
var WS_FULL = "wss://market-data.grvt.io/ws/full";
|
|
2778
|
-
var STREAM = "v1.book.d";
|
|
2779
|
-
function streamGrvtOrderbook(opts) {
|
|
2780
|
-
const instrument = toGrvtSymbol(opts.symbol, opts.market);
|
|
2781
|
-
const selector = `${instrument}@${opts.rate ?? 500}`;
|
|
2782
|
-
const merger = new BookMerger();
|
|
2783
|
-
let ws = null;
|
|
2784
|
-
let sock = null;
|
|
2785
|
-
let recovering = false;
|
|
2786
|
-
let awaitingFirstDelta = false;
|
|
2787
|
-
let snapshotSequence = 0;
|
|
2788
|
-
let ackSequence = null;
|
|
2789
|
-
const stream = new OrderbookStream(() => ws?.close());
|
|
2790
|
-
let nextId = 1;
|
|
2791
|
-
const subscribeIds = /* @__PURE__ */ new Set();
|
|
2792
|
-
const rpc = (method) => {
|
|
2793
|
-
const id = nextId++;
|
|
2794
|
-
sock?.send(
|
|
2795
|
-
JSON.stringify({
|
|
2796
|
-
jsonrpc: "2.0",
|
|
2797
|
-
method,
|
|
2798
|
-
params: { stream: STREAM, selectors: [selector] },
|
|
2799
|
-
id
|
|
2800
|
-
})
|
|
2801
|
-
);
|
|
2802
|
-
return id;
|
|
2803
|
-
};
|
|
2804
|
-
const subscribe = () => subscribeIds.add(rpc("subscribe"));
|
|
2805
|
-
const emit = (r) => {
|
|
2806
|
-
const book = {
|
|
2807
|
-
exchange: "grvt",
|
|
2808
|
-
symbol: fromGrvtSymbol(instrument),
|
|
2809
|
-
market: opts.market,
|
|
2810
|
-
bids: r.bids,
|
|
2811
|
-
asks: r.asks,
|
|
2812
|
-
timestamp: r.timestamp,
|
|
2813
|
-
sequence: r.sequence
|
|
2814
|
-
};
|
|
2815
|
-
stream.emit("update", book);
|
|
2816
|
-
};
|
|
2817
|
-
const resync = () => {
|
|
2818
|
-
if (recovering) return;
|
|
2819
|
-
recovering = true;
|
|
2820
|
-
merger.reset();
|
|
2821
|
-
rpc("unsubscribe");
|
|
2822
|
-
subscribe();
|
|
2823
|
-
};
|
|
2824
|
-
const buildEvent2 = (feed, seq) => {
|
|
2825
|
-
const bids = toLevels(feed.bids);
|
|
2826
|
-
const asks = toLevels(feed.asks);
|
|
2827
|
-
const timestamp = nsToMs(feed.event_time);
|
|
2828
|
-
if (seq === 0) {
|
|
2829
|
-
snapshotSequence = ackSequence ?? 0;
|
|
2830
|
-
awaitingFirstDelta = true;
|
|
2831
|
-
return {
|
|
2832
|
-
kind: "snapshot",
|
|
2833
|
-
bids,
|
|
2834
|
-
asks,
|
|
2835
|
-
sequence: snapshotSequence,
|
|
2836
|
-
timestamp
|
|
2837
|
-
};
|
|
2838
|
-
}
|
|
2839
|
-
const prevSequence = awaitingFirstDelta ? snapshotSequence : seq - 1;
|
|
2840
|
-
awaitingFirstDelta = false;
|
|
2841
|
-
return { kind: "delta", bids, asks, sequence: seq, prevSequence, timestamp };
|
|
2842
|
-
};
|
|
2843
|
-
const handleFeed = (msg) => {
|
|
2844
|
-
if (!msg.feed) return;
|
|
2845
|
-
const seq = Number(msg.sequence_number ?? 0);
|
|
2846
|
-
if (!Number.isFinite(seq)) return;
|
|
2847
|
-
if (seq === 0) recovering = false;
|
|
2848
|
-
else if (recovering) return;
|
|
2849
|
-
const r = merger.apply(buildEvent2(msg.feed, seq));
|
|
2850
|
-
if (r.ok) {
|
|
2851
|
-
emit(r);
|
|
2852
|
-
} else if (r.reason === "gap" || r.reason === "no-snapshot") {
|
|
2853
|
-
resync();
|
|
2854
|
-
}
|
|
2855
|
-
};
|
|
2856
|
-
const onMessage = (raw) => {
|
|
2857
|
-
if (typeof raw !== "string") return;
|
|
2858
|
-
let msg;
|
|
2859
|
-
try {
|
|
2860
|
-
msg = JSON.parse(raw);
|
|
2861
|
-
} catch {
|
|
2862
|
-
return;
|
|
2863
|
-
}
|
|
2864
|
-
if (msg.feed) {
|
|
2865
|
-
if (msg.selector && msg.selector !== selector) return;
|
|
2866
|
-
handleFeed(msg);
|
|
2867
|
-
return;
|
|
2868
|
-
}
|
|
2869
|
-
if (msg.id === void 0) return;
|
|
2870
|
-
if (msg.error) {
|
|
2871
|
-
stream.emit(
|
|
2872
|
-
"error",
|
|
2873
|
-
new ExchangeError("grvt", `${msg.error.code}: ${msg.error.message}`)
|
|
2874
|
-
);
|
|
2875
|
-
return;
|
|
2876
|
-
}
|
|
2877
|
-
if (subscribeIds.delete(msg.id)) {
|
|
2878
|
-
const subs = msg.result?.subs ?? [];
|
|
2879
|
-
const latest = Number(msg.result?.latest_sequence_number?.[0]);
|
|
2880
|
-
ackSequence = Number.isFinite(latest) ? latest : null;
|
|
2881
|
-
if (!subs.includes(selector)) {
|
|
2882
|
-
stream.emit(
|
|
2883
|
-
"error",
|
|
2884
|
-
new ExchangeError(
|
|
2885
|
-
"grvt",
|
|
2886
|
-
`subscribe rejected for "${STREAM}" selector "${selector}"`
|
|
2887
|
-
)
|
|
2888
|
-
);
|
|
2889
|
-
}
|
|
2890
|
-
}
|
|
2891
|
-
};
|
|
2892
|
-
ws = new WSClient({
|
|
2893
|
-
url: WS_FULL,
|
|
2894
|
-
onOpen: (handle) => {
|
|
2895
|
-
sock = handle;
|
|
2896
|
-
merger.reset();
|
|
2897
|
-
recovering = false;
|
|
2898
|
-
awaitingFirstDelta = false;
|
|
2899
|
-
subscribeIds.clear();
|
|
2900
|
-
subscribe();
|
|
2901
|
-
},
|
|
2902
|
-
onMessage
|
|
2903
|
-
});
|
|
2904
|
-
ws.on("open", () => stream.emit("connected"));
|
|
2905
|
-
ws.on("close", (r) => stream.emit("disconnected", r));
|
|
2906
|
-
ws.on("reconnecting", (a, w) => stream.emit("reconnecting", a, w));
|
|
2907
|
-
ws.on("error", (e) => stream.emit("error", e));
|
|
2908
|
-
ws.connect().catch((e) => stream.emit("error", e));
|
|
2909
|
-
return stream;
|
|
2910
|
-
}
|
|
2911
|
-
|
|
2912
|
-
// src/exchanges/grvt/index.ts
|
|
2913
|
-
var GrvtClient = class {
|
|
2914
|
-
exchange = "grvt";
|
|
2915
|
-
transformUrl;
|
|
2916
|
-
timeoutMs;
|
|
2917
|
-
constructor(opts = {}) {
|
|
2918
|
-
this.transformUrl = opts.transformUrl;
|
|
2919
|
-
this.timeoutMs = opts.timeoutMs;
|
|
2920
|
-
}
|
|
2921
|
-
fetchOrderbook(symbol, opts = {}) {
|
|
2922
|
-
const { pair, market } = parseSymbol(symbol);
|
|
2923
|
-
return fetchGrvtOrderbook({
|
|
2924
|
-
symbol: pair,
|
|
2925
|
-
market,
|
|
2926
|
-
depth: opts.depth,
|
|
2927
|
-
timeoutMs: this.timeoutMs,
|
|
2928
|
-
transformUrl: this.transformUrl
|
|
2929
|
-
});
|
|
2930
|
-
}
|
|
2931
|
-
streamOrderbook(symbol, opts = {}) {
|
|
2932
|
-
const { pair, market } = parseSymbol(symbol);
|
|
2933
|
-
return streamGrvtOrderbook({
|
|
2934
|
-
symbol: pair,
|
|
2935
|
-
market,
|
|
2936
|
-
depth: opts.depth,
|
|
2937
|
-
transformUrl: this.transformUrl,
|
|
2938
|
-
timeoutMs: this.timeoutMs
|
|
2939
|
-
});
|
|
2940
|
-
}
|
|
2941
|
-
};
|
|
2942
|
-
|
|
2943
2717
|
// src/exchanges/lighter/markets.ts
|
|
2944
2718
|
var LIGHTER_REST = "https://mainnet.zklighter.elliot.ai";
|
|
2945
2719
|
var cache = null;
|
|
@@ -3065,7 +2839,7 @@ async function fetchLighterOrderbook(opts) {
|
|
|
3065
2839
|
// src/exchanges/lighter/ws.ts
|
|
3066
2840
|
var WS_URL2 = "wss://mainnet.zklighter.elliot.ai/stream";
|
|
3067
2841
|
var PING_INTERVAL_MS = 6e4;
|
|
3068
|
-
var
|
|
2842
|
+
var toLevels = (rows) => (rows ?? []).map((l) => ({ price: Number(l.price), size: Number(l.size) }));
|
|
3069
2843
|
function streamLighterOrderbook(opts) {
|
|
3070
2844
|
const name = toLighterSymbol(opts.symbol, opts.market);
|
|
3071
2845
|
const symbol = fromLighterSymbol(name);
|
|
@@ -3104,8 +2878,8 @@ function streamLighterOrderbook(opts) {
|
|
|
3104
2878
|
};
|
|
3105
2879
|
const buildEvent2 = (msg, snapshot) => {
|
|
3106
2880
|
const ob = msg.order_book ?? {};
|
|
3107
|
-
const bids =
|
|
3108
|
-
const asks =
|
|
2881
|
+
const bids = toLevels(ob.bids);
|
|
2882
|
+
const asks = toLevels(ob.asks);
|
|
3109
2883
|
const timestamp = msg.timestamp ?? usToMs(ob.last_updated_at);
|
|
3110
2884
|
const sequence = Number(ob.nonce ?? 0);
|
|
3111
2885
|
return snapshot ? { kind: "snapshot", bids, asks, sequence, timestamp } : {
|
|
@@ -3202,13 +2976,16 @@ var LighterClient = class {
|
|
|
3202
2976
|
};
|
|
3203
2977
|
|
|
3204
2978
|
// src/exchanges/edgex/markets.ts
|
|
3205
|
-
var EDGEX_REST = "https://
|
|
2979
|
+
var EDGEX_REST = "https://cors.xapy.io/edgex";
|
|
3206
2980
|
var EDGEX_WS = "wss://edgex-quote-prod-v2.edgex.exchange";
|
|
3207
|
-
|
|
3208
|
-
|
|
3209
|
-
|
|
2981
|
+
function edgexRestBase(opts) {
|
|
2982
|
+
return (opts.restBase ?? EDGEX_REST).replace(/\/+$/, "");
|
|
2983
|
+
}
|
|
2984
|
+
var caches = /* @__PURE__ */ new Map();
|
|
2985
|
+
var inflights = /* @__PURE__ */ new Map();
|
|
2986
|
+
async function fetchContracts(opts, base) {
|
|
3210
2987
|
const res = await httpJson({
|
|
3211
|
-
url: `${
|
|
2988
|
+
url: `${base}/api/v2/public/meta/getMetaData`,
|
|
3212
2989
|
timeoutMs: opts.timeoutMs,
|
|
3213
2990
|
transformUrl: opts.transformUrl
|
|
3214
2991
|
});
|
|
@@ -3223,24 +3000,29 @@ async function fetchContracts(opts) {
|
|
|
3223
3000
|
for (const row of rows) map.set(row.contractName.toUpperCase(), row);
|
|
3224
3001
|
return map;
|
|
3225
3002
|
}
|
|
3226
|
-
function loadContracts(opts) {
|
|
3227
|
-
|
|
3228
|
-
|
|
3229
|
-
|
|
3230
|
-
|
|
3231
|
-
|
|
3232
|
-
|
|
3233
|
-
|
|
3234
|
-
|
|
3235
|
-
|
|
3236
|
-
|
|
3237
|
-
|
|
3238
|
-
|
|
3003
|
+
function loadContracts(opts, base) {
|
|
3004
|
+
let pending2 = inflights.get(base);
|
|
3005
|
+
if (!pending2) {
|
|
3006
|
+
pending2 = fetchContracts(opts, base).then(
|
|
3007
|
+
(m) => {
|
|
3008
|
+
caches.set(base, m);
|
|
3009
|
+
inflights.delete(base);
|
|
3010
|
+
return m;
|
|
3011
|
+
},
|
|
3012
|
+
(err) => {
|
|
3013
|
+
inflights.delete(base);
|
|
3014
|
+
throw err;
|
|
3015
|
+
}
|
|
3016
|
+
);
|
|
3017
|
+
inflights.set(base, pending2);
|
|
3018
|
+
}
|
|
3019
|
+
return pending2;
|
|
3239
3020
|
}
|
|
3240
3021
|
async function resolveEdgexContract(contractName, opts) {
|
|
3022
|
+
const base = edgexRestBase(opts);
|
|
3241
3023
|
const k = contractName.toUpperCase();
|
|
3242
|
-
const hit =
|
|
3243
|
-
const contract = hit ?? (await loadContracts(opts)).get(k);
|
|
3024
|
+
const hit = caches.get(base)?.get(k);
|
|
3025
|
+
const contract = hit ?? (await loadContracts(opts, base)).get(k);
|
|
3244
3026
|
if (!contract) {
|
|
3245
3027
|
throw new ExchangeError(
|
|
3246
3028
|
"edgex",
|
|
@@ -3276,13 +3058,13 @@ var ALLOWED_LEVELS = [15, 200];
|
|
|
3276
3058
|
function snapLevel(depth) {
|
|
3277
3059
|
return ALLOWED_LEVELS.find((l) => l >= depth) ?? ALLOWED_LEVELS[ALLOWED_LEVELS.length - 1];
|
|
3278
3060
|
}
|
|
3279
|
-
var
|
|
3061
|
+
var toLevels2 = (rows) => (rows ?? []).map((l) => ({ price: Number(l.price), size: Number(l.size) }));
|
|
3280
3062
|
async function fetchEdgexOrderbook(opts) {
|
|
3281
3063
|
const contractName = toEdgexSymbol(opts.symbol, opts.market);
|
|
3282
3064
|
const contract = await resolveEdgexContract(contractName, opts);
|
|
3283
3065
|
const level = snapLevel(opts.depth ?? 50);
|
|
3284
3066
|
const res = await httpJson({
|
|
3285
|
-
url: `${
|
|
3067
|
+
url: `${edgexRestBase(opts)}/api/v2/public/quote/getDepth?contractId=${contract.contractId}&level=${level}`,
|
|
3286
3068
|
timeoutMs: opts.timeoutMs,
|
|
3287
3069
|
transformUrl: opts.transformUrl
|
|
3288
3070
|
});
|
|
@@ -3298,8 +3080,8 @@ async function fetchEdgexOrderbook(opts) {
|
|
|
3298
3080
|
exchange: "edgex",
|
|
3299
3081
|
symbol: fromEdgexSymbol(depth.contractName ?? contractName),
|
|
3300
3082
|
market: opts.market,
|
|
3301
|
-
bids:
|
|
3302
|
-
asks:
|
|
3083
|
+
bids: toLevels2(depth.bids),
|
|
3084
|
+
asks: toLevels2(depth.asks),
|
|
3303
3085
|
timestamp,
|
|
3304
3086
|
sequence: Number(depth.endVersion ?? 0)
|
|
3305
3087
|
};
|
|
@@ -3345,8 +3127,8 @@ function streamEdgexOrderbook(opts) {
|
|
|
3345
3127
|
send("subscribe");
|
|
3346
3128
|
};
|
|
3347
3129
|
const buildEvent2 = (d, snapshot) => {
|
|
3348
|
-
const bids =
|
|
3349
|
-
const asks =
|
|
3130
|
+
const bids = toLevels2(d.bids);
|
|
3131
|
+
const asks = toLevels2(d.asks);
|
|
3350
3132
|
const timestamp = Date.now();
|
|
3351
3133
|
const sequence = Number(d.endVersion ?? 0);
|
|
3352
3134
|
return snapshot ? { kind: "snapshot", bids, asks, sequence, timestamp } : {
|
|
@@ -3420,9 +3202,11 @@ var EdgexClient = class {
|
|
|
3420
3202
|
exchange = "edgex";
|
|
3421
3203
|
transformUrl;
|
|
3422
3204
|
timeoutMs;
|
|
3205
|
+
restBase;
|
|
3423
3206
|
constructor(opts = {}) {
|
|
3424
3207
|
this.transformUrl = opts.transformUrl;
|
|
3425
3208
|
this.timeoutMs = opts.timeoutMs;
|
|
3209
|
+
this.restBase = opts.restBase;
|
|
3426
3210
|
}
|
|
3427
3211
|
fetchOrderbook(symbol, opts = {}) {
|
|
3428
3212
|
const { pair, market } = parseSymbol(symbol);
|
|
@@ -3431,12 +3215,219 @@ var EdgexClient = class {
|
|
|
3431
3215
|
market,
|
|
3432
3216
|
depth: opts.depth,
|
|
3433
3217
|
timeoutMs: this.timeoutMs,
|
|
3434
|
-
transformUrl: this.transformUrl
|
|
3218
|
+
transformUrl: this.transformUrl,
|
|
3219
|
+
restBase: this.restBase
|
|
3435
3220
|
});
|
|
3436
3221
|
}
|
|
3437
3222
|
streamOrderbook(symbol, opts = {}) {
|
|
3438
3223
|
const { pair, market } = parseSymbol(symbol);
|
|
3439
3224
|
return streamEdgexOrderbook({
|
|
3225
|
+
symbol: pair,
|
|
3226
|
+
market,
|
|
3227
|
+
depth: opts.depth,
|
|
3228
|
+
transformUrl: this.transformUrl,
|
|
3229
|
+
timeoutMs: this.timeoutMs,
|
|
3230
|
+
restBase: this.restBase
|
|
3231
|
+
});
|
|
3232
|
+
}
|
|
3233
|
+
};
|
|
3234
|
+
|
|
3235
|
+
// src/exchanges/whitebit/symbols.ts
|
|
3236
|
+
function toWhitebitSymbol(symbol, market) {
|
|
3237
|
+
const [base, quote] = symbol.split("/");
|
|
3238
|
+
if (!base || !quote) {
|
|
3239
|
+
throw new Error(`invalid symbol "${symbol}" \u2014 expected "BASE/QUOTE"`);
|
|
3240
|
+
}
|
|
3241
|
+
if (market === "perpetual") {
|
|
3242
|
+
if (quote.toUpperCase() !== "USDT") {
|
|
3243
|
+
throw new Error(
|
|
3244
|
+
`whitebit perpetuals are USDT-margined; got "${quote}"`
|
|
3245
|
+
);
|
|
3246
|
+
}
|
|
3247
|
+
return `${base.toUpperCase()}_PERP`;
|
|
3248
|
+
}
|
|
3249
|
+
return `${base.toUpperCase()}_${quote.toUpperCase()}`;
|
|
3250
|
+
}
|
|
3251
|
+
function fromWhitebitSymbol(market) {
|
|
3252
|
+
const name = market.toUpperCase();
|
|
3253
|
+
if (name.endsWith("_PERP")) return `${name.slice(0, -5)}/USDT`;
|
|
3254
|
+
const [base, quote] = name.split("_");
|
|
3255
|
+
return base && quote ? `${base}/${quote}` : name;
|
|
3256
|
+
}
|
|
3257
|
+
|
|
3258
|
+
// src/exchanges/whitebit/rest.ts
|
|
3259
|
+
var WHITEBIT_REST = "https://whitebit.com";
|
|
3260
|
+
var MAX_REST_LIMIT = 100;
|
|
3261
|
+
function secToMs(sec) {
|
|
3262
|
+
return Number.isFinite(sec) && sec > 0 ? Math.round(sec * 1e3) : Date.now();
|
|
3263
|
+
}
|
|
3264
|
+
var toLevels3 = (rows) => (rows ?? []).map(([price, size]) => ({
|
|
3265
|
+
price: Number(price),
|
|
3266
|
+
size: Number(size)
|
|
3267
|
+
}));
|
|
3268
|
+
async function fetchWhitebitOrderbook(opts) {
|
|
3269
|
+
const market = toWhitebitSymbol(opts.symbol, opts.market);
|
|
3270
|
+
const limit = Math.min(opts.depth ?? 50, MAX_REST_LIMIT);
|
|
3271
|
+
const res = await httpJson({
|
|
3272
|
+
url: `${WHITEBIT_REST}/api/v4/public/orderbook/${market}?limit=${limit}`,
|
|
3273
|
+
timeoutMs: opts.timeoutMs,
|
|
3274
|
+
transformUrl: opts.transformUrl
|
|
3275
|
+
});
|
|
3276
|
+
if (!res.asks && !res.bids) {
|
|
3277
|
+
const detail = res.message ?? (res.errors ? JSON.stringify(res.errors) : "empty orderbook response");
|
|
3278
|
+
throw new ExchangeError("whitebit", detail);
|
|
3279
|
+
}
|
|
3280
|
+
const timestamp = secToMs(res.timestamp);
|
|
3281
|
+
return {
|
|
3282
|
+
exchange: "whitebit",
|
|
3283
|
+
symbol: fromWhitebitSymbol(res.ticker_id ?? market),
|
|
3284
|
+
market: opts.market,
|
|
3285
|
+
bids: toLevels3(res.bids),
|
|
3286
|
+
asks: toLevels3(res.asks),
|
|
3287
|
+
timestamp,
|
|
3288
|
+
// REST publishes no `update_id`; the WS chain is where ordering is enforced.
|
|
3289
|
+
sequence: timestamp
|
|
3290
|
+
};
|
|
3291
|
+
}
|
|
3292
|
+
|
|
3293
|
+
// src/exchanges/whitebit/ws.ts
|
|
3294
|
+
var WS_URL4 = "wss://api.whitebit.com/ws";
|
|
3295
|
+
var PING_INTERVAL_MS2 = 5e4;
|
|
3296
|
+
var ALLOWED_LIMITS = [1, 5, 10, 20, 30, 50, 100];
|
|
3297
|
+
function snapLimit(depth) {
|
|
3298
|
+
return ALLOWED_LIMITS.find((l) => l >= depth) ?? ALLOWED_LIMITS[ALLOWED_LIMITS.length - 1];
|
|
3299
|
+
}
|
|
3300
|
+
function streamWhitebitOrderbook(opts) {
|
|
3301
|
+
const market = toWhitebitSymbol(opts.symbol, opts.market);
|
|
3302
|
+
const symbol = fromWhitebitSymbol(market);
|
|
3303
|
+
const limit = snapLimit(opts.depth ?? 50);
|
|
3304
|
+
const merger = new BookMerger();
|
|
3305
|
+
let ws = null;
|
|
3306
|
+
let sock = null;
|
|
3307
|
+
let recovering = false;
|
|
3308
|
+
const stream = new OrderbookStream(() => ws?.close());
|
|
3309
|
+
let nextId = 1;
|
|
3310
|
+
const subscribeIds = /* @__PURE__ */ new Set();
|
|
3311
|
+
const call = (method, params) => {
|
|
3312
|
+
const id = nextId++;
|
|
3313
|
+
sock?.send(JSON.stringify({ id, method, params }));
|
|
3314
|
+
return id;
|
|
3315
|
+
};
|
|
3316
|
+
const subscribe = () => {
|
|
3317
|
+
subscribeIds.add(call("depth_subscribe", [market, limit, "0", true]));
|
|
3318
|
+
};
|
|
3319
|
+
const emit = (r) => {
|
|
3320
|
+
const book = {
|
|
3321
|
+
exchange: "whitebit",
|
|
3322
|
+
symbol,
|
|
3323
|
+
market: opts.market,
|
|
3324
|
+
bids: r.bids,
|
|
3325
|
+
asks: r.asks,
|
|
3326
|
+
timestamp: r.timestamp,
|
|
3327
|
+
sequence: r.sequence
|
|
3328
|
+
};
|
|
3329
|
+
stream.emit("update", book);
|
|
3330
|
+
};
|
|
3331
|
+
const resync = () => {
|
|
3332
|
+
if (recovering) return;
|
|
3333
|
+
recovering = true;
|
|
3334
|
+
merger.reset();
|
|
3335
|
+
call("depth_unsubscribe", []);
|
|
3336
|
+
subscribe();
|
|
3337
|
+
};
|
|
3338
|
+
const buildEvent2 = (d, snapshot) => {
|
|
3339
|
+
const bids = toLevels3(d.bids);
|
|
3340
|
+
const asks = toLevels3(d.asks);
|
|
3341
|
+
const timestamp = secToMs(d.timestamp ?? d.event_time);
|
|
3342
|
+
const sequence = Number(d.update_id ?? 0);
|
|
3343
|
+
return snapshot ? { kind: "snapshot", bids, asks, sequence, timestamp } : {
|
|
3344
|
+
kind: "delta",
|
|
3345
|
+
bids,
|
|
3346
|
+
asks,
|
|
3347
|
+
sequence,
|
|
3348
|
+
prevSequence: Number(d.past_update_id ?? 0),
|
|
3349
|
+
timestamp
|
|
3350
|
+
};
|
|
3351
|
+
};
|
|
3352
|
+
const handleDepth = (d, snapshot) => {
|
|
3353
|
+
if (snapshot) recovering = false;
|
|
3354
|
+
else if (recovering) return;
|
|
3355
|
+
const r = merger.apply(buildEvent2(d, snapshot));
|
|
3356
|
+
if (r.ok) {
|
|
3357
|
+
emit(r);
|
|
3358
|
+
} else if (r.reason === "gap" || r.reason === "no-snapshot") {
|
|
3359
|
+
resync();
|
|
3360
|
+
}
|
|
3361
|
+
};
|
|
3362
|
+
const onMessage = (raw) => {
|
|
3363
|
+
if (typeof raw !== "string") return;
|
|
3364
|
+
let msg;
|
|
3365
|
+
try {
|
|
3366
|
+
msg = JSON.parse(raw);
|
|
3367
|
+
} catch {
|
|
3368
|
+
return;
|
|
3369
|
+
}
|
|
3370
|
+
if (msg.method === "depth_update") {
|
|
3371
|
+
const [snapshot, data, frameMarket] = msg.params ?? [];
|
|
3372
|
+
if (!data || frameMarket && frameMarket !== market) return;
|
|
3373
|
+
handleDepth(data, snapshot === true);
|
|
3374
|
+
return;
|
|
3375
|
+
}
|
|
3376
|
+
if (msg.error) {
|
|
3377
|
+
stream.emit(
|
|
3378
|
+
"error",
|
|
3379
|
+
new ExchangeError(
|
|
3380
|
+
"whitebit",
|
|
3381
|
+
`${msg.error.code}: ${msg.error.message}`
|
|
3382
|
+
)
|
|
3383
|
+
);
|
|
3384
|
+
return;
|
|
3385
|
+
}
|
|
3386
|
+
if (typeof msg.id === "number") subscribeIds.delete(msg.id);
|
|
3387
|
+
};
|
|
3388
|
+
ws = new WSClient({
|
|
3389
|
+
url: WS_URL4,
|
|
3390
|
+
onOpen: (handle) => {
|
|
3391
|
+
sock = handle;
|
|
3392
|
+
merger.reset();
|
|
3393
|
+
recovering = false;
|
|
3394
|
+
subscribeIds.clear();
|
|
3395
|
+
subscribe();
|
|
3396
|
+
},
|
|
3397
|
+
onMessage,
|
|
3398
|
+
pingIntervalMs: PING_INTERVAL_MS2,
|
|
3399
|
+
pingPayload: () => JSON.stringify({ id: 0, method: "ping", params: [] })
|
|
3400
|
+
});
|
|
3401
|
+
ws.on("open", () => stream.emit("connected"));
|
|
3402
|
+
ws.on("close", (r) => stream.emit("disconnected", r));
|
|
3403
|
+
ws.on("reconnecting", (a, w) => stream.emit("reconnecting", a, w));
|
|
3404
|
+
ws.on("error", (e) => stream.emit("error", e));
|
|
3405
|
+
ws.connect().catch((e) => stream.emit("error", e));
|
|
3406
|
+
return stream;
|
|
3407
|
+
}
|
|
3408
|
+
|
|
3409
|
+
// src/exchanges/whitebit/index.ts
|
|
3410
|
+
var WhitebitClient = class {
|
|
3411
|
+
exchange = "whitebit";
|
|
3412
|
+
transformUrl;
|
|
3413
|
+
timeoutMs;
|
|
3414
|
+
constructor(opts = {}) {
|
|
3415
|
+
this.transformUrl = opts.transformUrl;
|
|
3416
|
+
this.timeoutMs = opts.timeoutMs;
|
|
3417
|
+
}
|
|
3418
|
+
fetchOrderbook(symbol, opts = {}) {
|
|
3419
|
+
const { pair, market } = parseSymbol(symbol);
|
|
3420
|
+
return fetchWhitebitOrderbook({
|
|
3421
|
+
symbol: pair,
|
|
3422
|
+
market,
|
|
3423
|
+
depth: opts.depth,
|
|
3424
|
+
timeoutMs: this.timeoutMs,
|
|
3425
|
+
transformUrl: this.transformUrl
|
|
3426
|
+
});
|
|
3427
|
+
}
|
|
3428
|
+
streamOrderbook(symbol, opts = {}) {
|
|
3429
|
+
const { pair, market } = parseSymbol(symbol);
|
|
3430
|
+
return streamWhitebitOrderbook({
|
|
3440
3431
|
symbol: pair,
|
|
3441
3432
|
market,
|
|
3442
3433
|
depth: opts.depth,
|
|
@@ -3457,7 +3448,6 @@ exports.DeribitClient = DeribitClient;
|
|
|
3457
3448
|
exports.EdgexClient = EdgexClient;
|
|
3458
3449
|
exports.ExchangeError = ExchangeError;
|
|
3459
3450
|
exports.GateClient = GateClient;
|
|
3460
|
-
exports.GrvtClient = GrvtClient;
|
|
3461
3451
|
exports.HyperliquidClient = HyperliquidClient;
|
|
3462
3452
|
exports.KucoinClient = KucoinClient;
|
|
3463
3453
|
exports.LighterClient = LighterClient;
|
|
@@ -3465,6 +3455,7 @@ exports.OkxClient = OkxClient;
|
|
|
3465
3455
|
exports.OrderbookStream = OrderbookStream;
|
|
3466
3456
|
exports.RateLimitError = RateLimitError;
|
|
3467
3457
|
exports.SequenceGapError = SequenceGapError;
|
|
3458
|
+
exports.WhitebitClient = WhitebitClient;
|
|
3468
3459
|
exports.parseSymbol = parseSymbol;
|
|
3469
3460
|
//# sourceMappingURL=index.cjs.map
|
|
3470
3461
|
//# sourceMappingURL=index.cjs.map
|