@xapy/orderbook 0.1.27 → 0.1.29
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 +2 -1
- package/dist/exchanges/aster/index.cjs +610 -0
- package/dist/exchanges/aster/index.cjs.map +1 -0
- package/dist/exchanges/aster/index.d.cts +65 -0
- package/dist/exchanges/aster/index.d.ts +65 -0
- package/dist/exchanges/aster/index.js +602 -0
- package/dist/exchanges/aster/index.js.map +1 -0
- 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.d.cts +1 -1
- package/dist/exchanges/edgex/index.d.ts +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.d.cts +1 -1
- package/dist/exchanges/lighter/index.d.ts +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 +433 -235
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +4 -3
- package/dist/index.d.ts +4 -3
- package/dist/index.js +432 -235
- package/dist/index.js.map +1 -1
- package/dist/{stream-YYPvjMV4.d.cts → stream-D5-FyNGW.d.cts} +1 -1
- package/dist/{stream-YYPvjMV4.d.ts → stream-D5-FyNGW.d.ts} +1 -1
- package/package.json +23 -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 } : {
|
|
@@ -3284,7 +3058,7 @@ var ALLOWED_LEVELS = [15, 200];
|
|
|
3284
3058
|
function snapLevel(depth) {
|
|
3285
3059
|
return ALLOWED_LEVELS.find((l) => l >= depth) ?? ALLOWED_LEVELS[ALLOWED_LEVELS.length - 1];
|
|
3286
3060
|
}
|
|
3287
|
-
var
|
|
3061
|
+
var toLevels2 = (rows) => (rows ?? []).map((l) => ({ price: Number(l.price), size: Number(l.size) }));
|
|
3288
3062
|
async function fetchEdgexOrderbook(opts) {
|
|
3289
3063
|
const contractName = toEdgexSymbol(opts.symbol, opts.market);
|
|
3290
3064
|
const contract = await resolveEdgexContract(contractName, opts);
|
|
@@ -3306,8 +3080,8 @@ async function fetchEdgexOrderbook(opts) {
|
|
|
3306
3080
|
exchange: "edgex",
|
|
3307
3081
|
symbol: fromEdgexSymbol(depth.contractName ?? contractName),
|
|
3308
3082
|
market: opts.market,
|
|
3309
|
-
bids:
|
|
3310
|
-
asks:
|
|
3083
|
+
bids: toLevels2(depth.bids),
|
|
3084
|
+
asks: toLevels2(depth.asks),
|
|
3311
3085
|
timestamp,
|
|
3312
3086
|
sequence: Number(depth.endVersion ?? 0)
|
|
3313
3087
|
};
|
|
@@ -3353,8 +3127,8 @@ function streamEdgexOrderbook(opts) {
|
|
|
3353
3127
|
send("subscribe");
|
|
3354
3128
|
};
|
|
3355
3129
|
const buildEvent2 = (d, snapshot) => {
|
|
3356
|
-
const bids =
|
|
3357
|
-
const asks =
|
|
3130
|
+
const bids = toLevels2(d.bids);
|
|
3131
|
+
const asks = toLevels2(d.asks);
|
|
3358
3132
|
const timestamp = Date.now();
|
|
3359
3133
|
const sequence = Number(d.endVersion ?? 0);
|
|
3360
3134
|
return snapshot ? { kind: "snapshot", bids, asks, sequence, timestamp } : {
|
|
@@ -3458,6 +3232,430 @@ var EdgexClient = class {
|
|
|
3458
3232
|
}
|
|
3459
3233
|
};
|
|
3460
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({
|
|
3431
|
+
symbol: pair,
|
|
3432
|
+
market,
|
|
3433
|
+
depth: opts.depth,
|
|
3434
|
+
transformUrl: this.transformUrl,
|
|
3435
|
+
timeoutMs: this.timeoutMs
|
|
3436
|
+
});
|
|
3437
|
+
}
|
|
3438
|
+
};
|
|
3439
|
+
|
|
3440
|
+
// src/exchanges/aster/symbols.ts
|
|
3441
|
+
var QUOTES5 = ["USDT", "USD1", "FORM", "USDC", "BTC", "ETH", "U"];
|
|
3442
|
+
function toAsterSymbol(symbol) {
|
|
3443
|
+
const [base, quote] = symbol.split("/");
|
|
3444
|
+
if (!base || !quote) {
|
|
3445
|
+
throw new Error(`invalid symbol "${symbol}" \u2014 expected "BASE/QUOTE"`);
|
|
3446
|
+
}
|
|
3447
|
+
return `${base}${quote}`.toUpperCase();
|
|
3448
|
+
}
|
|
3449
|
+
function fromAsterSymbol(s) {
|
|
3450
|
+
const upper = s.toUpperCase();
|
|
3451
|
+
for (const q of QUOTES5) {
|
|
3452
|
+
if (upper.endsWith(q) && upper.length > q.length) {
|
|
3453
|
+
return `${upper.slice(0, -q.length)}/${q}`;
|
|
3454
|
+
}
|
|
3455
|
+
}
|
|
3456
|
+
return upper;
|
|
3457
|
+
}
|
|
3458
|
+
|
|
3459
|
+
// src/exchanges/aster/rest.ts
|
|
3460
|
+
var ASTER_SPOT_REST = "https://sapi.asterdex.com";
|
|
3461
|
+
var ASTER_FUTURES_REST = "https://fapi.asterdex.com";
|
|
3462
|
+
async function fetchAsterOrderbook(opts) {
|
|
3463
|
+
const symbol = toAsterSymbol(opts.symbol);
|
|
3464
|
+
const limit = opts.depth ?? 1e3;
|
|
3465
|
+
const url = opts.market === "spot" ? `${ASTER_SPOT_REST}/api/v3/depth?symbol=${symbol}&limit=${limit}` : `${ASTER_FUTURES_REST}/fapi/v1/depth?symbol=${symbol}&limit=${limit}`;
|
|
3466
|
+
const data = await httpJson({
|
|
3467
|
+
url,
|
|
3468
|
+
timeoutMs: opts.timeoutMs,
|
|
3469
|
+
transformUrl: opts.transformUrl
|
|
3470
|
+
});
|
|
3471
|
+
return {
|
|
3472
|
+
exchange: "aster",
|
|
3473
|
+
symbol: fromAsterSymbol(data.symbol ?? symbol),
|
|
3474
|
+
market: opts.market,
|
|
3475
|
+
bids: data.bids.map(([p, s]) => ({ price: Number(p), size: Number(s) })),
|
|
3476
|
+
asks: data.asks.map(([p, s]) => ({ price: Number(p), size: Number(s) })),
|
|
3477
|
+
timestamp: data.E ?? data.T ?? Date.now(),
|
|
3478
|
+
sequence: data.lastUpdateId
|
|
3479
|
+
};
|
|
3480
|
+
}
|
|
3481
|
+
|
|
3482
|
+
// src/exchanges/aster/ws.ts
|
|
3483
|
+
var WS_SPOT6 = "wss://sstream.asterdex.com/ws";
|
|
3484
|
+
var WS_FUTURES4 = "wss://fstream.asterdex.com/ws";
|
|
3485
|
+
function streamAsterOrderbook(opts) {
|
|
3486
|
+
const symbol = toAsterSymbol(opts.symbol);
|
|
3487
|
+
const isSpot = opts.market === "spot";
|
|
3488
|
+
const wsBase = isSpot ? WS_SPOT6 : WS_FUTURES4;
|
|
3489
|
+
const wsUrl = `${wsBase}/${symbol.toLowerCase()}@depth@${opts.interval ?? "100ms"}`;
|
|
3490
|
+
const merger = new BookMerger();
|
|
3491
|
+
let snapshotId = -1;
|
|
3492
|
+
let lastU = -1;
|
|
3493
|
+
let snapshotInFlight = false;
|
|
3494
|
+
let snapshotDone = false;
|
|
3495
|
+
let buffered = [];
|
|
3496
|
+
let recovering = false;
|
|
3497
|
+
let ws = null;
|
|
3498
|
+
const stream = new OrderbookStream(() => ws?.close());
|
|
3499
|
+
const emit = (r) => {
|
|
3500
|
+
const book = {
|
|
3501
|
+
exchange: "aster",
|
|
3502
|
+
symbol: fromAsterSymbol(symbol),
|
|
3503
|
+
market: opts.market,
|
|
3504
|
+
bids: r.bids,
|
|
3505
|
+
asks: r.asks,
|
|
3506
|
+
timestamp: r.timestamp,
|
|
3507
|
+
sequence: r.sequence
|
|
3508
|
+
};
|
|
3509
|
+
stream.emit("update", book);
|
|
3510
|
+
};
|
|
3511
|
+
const triggerSnapshot = () => {
|
|
3512
|
+
if (snapshotInFlight) return;
|
|
3513
|
+
snapshotInFlight = true;
|
|
3514
|
+
fetchAsterOrderbook({
|
|
3515
|
+
symbol: opts.symbol,
|
|
3516
|
+
market: opts.market,
|
|
3517
|
+
depth: opts.depth ?? 1e3,
|
|
3518
|
+
timeoutMs: opts.timeoutMs,
|
|
3519
|
+
transformUrl: opts.transformUrl
|
|
3520
|
+
}).then((snap) => {
|
|
3521
|
+
snapshotId = snap.sequence;
|
|
3522
|
+
const r = merger.apply({
|
|
3523
|
+
kind: "snapshot",
|
|
3524
|
+
bids: snap.bids,
|
|
3525
|
+
asks: snap.asks,
|
|
3526
|
+
sequence: snap.sequence,
|
|
3527
|
+
timestamp: snap.timestamp
|
|
3528
|
+
});
|
|
3529
|
+
if (r.ok) emit(r);
|
|
3530
|
+
snapshotDone = true;
|
|
3531
|
+
lastU = -1;
|
|
3532
|
+
const pending2 = buffered;
|
|
3533
|
+
buffered = [];
|
|
3534
|
+
for (const ev of pending2) processDelta(ev);
|
|
3535
|
+
}).catch((err) => stream.emit("error", err)).finally(() => {
|
|
3536
|
+
snapshotInFlight = false;
|
|
3537
|
+
});
|
|
3538
|
+
};
|
|
3539
|
+
const resync = () => {
|
|
3540
|
+
if (recovering) return;
|
|
3541
|
+
recovering = true;
|
|
3542
|
+
merger.reset();
|
|
3543
|
+
snapshotId = -1;
|
|
3544
|
+
snapshotDone = false;
|
|
3545
|
+
lastU = -1;
|
|
3546
|
+
buffered = [];
|
|
3547
|
+
setTimeout(() => {
|
|
3548
|
+
recovering = false;
|
|
3549
|
+
triggerSnapshot();
|
|
3550
|
+
}, 100);
|
|
3551
|
+
};
|
|
3552
|
+
const processDelta = (ev) => {
|
|
3553
|
+
if (ev.u < snapshotId) return;
|
|
3554
|
+
const bids = ev.b.map(([p, s]) => ({
|
|
3555
|
+
price: Number(p),
|
|
3556
|
+
size: Number(s)
|
|
3557
|
+
}));
|
|
3558
|
+
const asks = ev.a.map(([p, s]) => ({
|
|
3559
|
+
price: Number(p),
|
|
3560
|
+
size: Number(s)
|
|
3561
|
+
}));
|
|
3562
|
+
let prevSequence;
|
|
3563
|
+
if (lastU < 0) {
|
|
3564
|
+
if (ev.U > snapshotId) {
|
|
3565
|
+
resync();
|
|
3566
|
+
return;
|
|
3567
|
+
}
|
|
3568
|
+
prevSequence = snapshotId;
|
|
3569
|
+
} else {
|
|
3570
|
+
if (ev.pu === void 0) {
|
|
3571
|
+
resync();
|
|
3572
|
+
return;
|
|
3573
|
+
}
|
|
3574
|
+
prevSequence = ev.pu;
|
|
3575
|
+
}
|
|
3576
|
+
const r = merger.apply({
|
|
3577
|
+
kind: "delta",
|
|
3578
|
+
bids,
|
|
3579
|
+
asks,
|
|
3580
|
+
sequence: ev.u,
|
|
3581
|
+
prevSequence,
|
|
3582
|
+
timestamp: ev.E
|
|
3583
|
+
});
|
|
3584
|
+
if (r.ok) {
|
|
3585
|
+
lastU = ev.u;
|
|
3586
|
+
emit(r);
|
|
3587
|
+
} else if (r.reason === "gap" || r.reason === "no-snapshot") {
|
|
3588
|
+
resync();
|
|
3589
|
+
}
|
|
3590
|
+
};
|
|
3591
|
+
const onMessage = (raw) => {
|
|
3592
|
+
if (typeof raw !== "string") return;
|
|
3593
|
+
let msg;
|
|
3594
|
+
try {
|
|
3595
|
+
msg = JSON.parse(raw);
|
|
3596
|
+
} catch {
|
|
3597
|
+
return;
|
|
3598
|
+
}
|
|
3599
|
+
if (msg.e !== "depthUpdate") return;
|
|
3600
|
+
if (!snapshotDone) {
|
|
3601
|
+
buffered.push(msg);
|
|
3602
|
+
if (!snapshotInFlight) triggerSnapshot();
|
|
3603
|
+
return;
|
|
3604
|
+
}
|
|
3605
|
+
processDelta(msg);
|
|
3606
|
+
};
|
|
3607
|
+
ws = new WSClient({
|
|
3608
|
+
url: wsUrl,
|
|
3609
|
+
onOpen: () => {
|
|
3610
|
+
merger.reset();
|
|
3611
|
+
snapshotId = -1;
|
|
3612
|
+
snapshotDone = false;
|
|
3613
|
+
lastU = -1;
|
|
3614
|
+
buffered = [];
|
|
3615
|
+
},
|
|
3616
|
+
onMessage
|
|
3617
|
+
// Server-initiated WS-level ping; auto-handled by the WS impl.
|
|
3618
|
+
});
|
|
3619
|
+
ws.on("open", () => stream.emit("connected"));
|
|
3620
|
+
ws.on("close", (r) => stream.emit("disconnected", r));
|
|
3621
|
+
ws.on("reconnecting", (a, w) => stream.emit("reconnecting", a, w));
|
|
3622
|
+
ws.on("error", (e) => stream.emit("error", e));
|
|
3623
|
+
ws.connect().catch((e) => stream.emit("error", e));
|
|
3624
|
+
return stream;
|
|
3625
|
+
}
|
|
3626
|
+
|
|
3627
|
+
// src/exchanges/aster/index.ts
|
|
3628
|
+
var AsterClient = class {
|
|
3629
|
+
exchange = "aster";
|
|
3630
|
+
transformUrl;
|
|
3631
|
+
timeoutMs;
|
|
3632
|
+
constructor(opts = {}) {
|
|
3633
|
+
this.transformUrl = opts.transformUrl;
|
|
3634
|
+
this.timeoutMs = opts.timeoutMs;
|
|
3635
|
+
}
|
|
3636
|
+
fetchOrderbook(symbol, opts = {}) {
|
|
3637
|
+
const { pair, market } = parseSymbol(symbol);
|
|
3638
|
+
return fetchAsterOrderbook({
|
|
3639
|
+
symbol: pair,
|
|
3640
|
+
market,
|
|
3641
|
+
depth: opts.depth,
|
|
3642
|
+
timeoutMs: this.timeoutMs,
|
|
3643
|
+
transformUrl: this.transformUrl
|
|
3644
|
+
});
|
|
3645
|
+
}
|
|
3646
|
+
streamOrderbook(symbol, opts = {}) {
|
|
3647
|
+
const { pair, market } = parseSymbol(symbol);
|
|
3648
|
+
return streamAsterOrderbook({
|
|
3649
|
+
symbol: pair,
|
|
3650
|
+
market,
|
|
3651
|
+
depth: opts.depth,
|
|
3652
|
+
transformUrl: this.transformUrl,
|
|
3653
|
+
timeoutMs: this.timeoutMs
|
|
3654
|
+
});
|
|
3655
|
+
}
|
|
3656
|
+
};
|
|
3657
|
+
|
|
3658
|
+
exports.AsterClient = AsterClient;
|
|
3461
3659
|
exports.Backoff = Backoff;
|
|
3462
3660
|
exports.BinanceClient = BinanceClient;
|
|
3463
3661
|
exports.BingxClient = BingxClient;
|
|
@@ -3469,7 +3667,6 @@ exports.DeribitClient = DeribitClient;
|
|
|
3469
3667
|
exports.EdgexClient = EdgexClient;
|
|
3470
3668
|
exports.ExchangeError = ExchangeError;
|
|
3471
3669
|
exports.GateClient = GateClient;
|
|
3472
|
-
exports.GrvtClient = GrvtClient;
|
|
3473
3670
|
exports.HyperliquidClient = HyperliquidClient;
|
|
3474
3671
|
exports.KucoinClient = KucoinClient;
|
|
3475
3672
|
exports.LighterClient = LighterClient;
|
|
@@ -3477,6 +3674,7 @@ exports.OkxClient = OkxClient;
|
|
|
3477
3674
|
exports.OrderbookStream = OrderbookStream;
|
|
3478
3675
|
exports.RateLimitError = RateLimitError;
|
|
3479
3676
|
exports.SequenceGapError = SequenceGapError;
|
|
3677
|
+
exports.WhitebitClient = WhitebitClient;
|
|
3480
3678
|
exports.parseSymbol = parseSymbol;
|
|
3481
3679
|
//# sourceMappingURL=index.cjs.map
|
|
3482
3680
|
//# sourceMappingURL=index.cjs.map
|