@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.js
CHANGED
|
@@ -2712,232 +2712,6 @@ var DeribitClient = class {
|
|
|
2712
2712
|
}
|
|
2713
2713
|
};
|
|
2714
2714
|
|
|
2715
|
-
// src/exchanges/grvt/symbols.ts
|
|
2716
|
-
function toGrvtSymbol(symbol, market) {
|
|
2717
|
-
const [base, quote] = symbol.split("/");
|
|
2718
|
-
if (!base || !quote) {
|
|
2719
|
-
throw new Error(`invalid symbol "${symbol}" \u2014 expected "BASE/QUOTE"`);
|
|
2720
|
-
}
|
|
2721
|
-
if (market !== "perpetual") {
|
|
2722
|
-
throw new Error(
|
|
2723
|
-
`grvt only lists perpetuals; use "${base.toUpperCase()}/${quote.toUpperCase()}:${quote.toUpperCase()}"`
|
|
2724
|
-
);
|
|
2725
|
-
}
|
|
2726
|
-
return `${base.toUpperCase()}_${quote.toUpperCase()}_Perp`;
|
|
2727
|
-
}
|
|
2728
|
-
function fromGrvtSymbol(instrument) {
|
|
2729
|
-
const parts = instrument.split("_");
|
|
2730
|
-
const [base, quote] = parts;
|
|
2731
|
-
return base && quote ? `${base.toUpperCase()}/${quote.toUpperCase()}` : instrument;
|
|
2732
|
-
}
|
|
2733
|
-
|
|
2734
|
-
// src/exchanges/grvt/rest.ts
|
|
2735
|
-
var BASE9 = "https://market-data.grvt.io";
|
|
2736
|
-
var ALLOWED_DEPTHS2 = [10, 50, 100, 500];
|
|
2737
|
-
function snapDepth(depth) {
|
|
2738
|
-
return ALLOWED_DEPTHS2.find((d) => d >= depth) ?? ALLOWED_DEPTHS2[ALLOWED_DEPTHS2.length - 1];
|
|
2739
|
-
}
|
|
2740
|
-
function nsToMs(ns) {
|
|
2741
|
-
const n = Number(ns);
|
|
2742
|
-
return Number.isFinite(n) && n > 0 ? Math.floor(n / 1e6) : Date.now();
|
|
2743
|
-
}
|
|
2744
|
-
var toLevels = (rows) => (rows ?? []).map((l) => ({ price: Number(l.price), size: Number(l.size) }));
|
|
2745
|
-
async function fetchGrvtOrderbook(opts) {
|
|
2746
|
-
const instrument = toGrvtSymbol(opts.symbol, opts.market);
|
|
2747
|
-
const depth = snapDepth(opts.depth ?? 50);
|
|
2748
|
-
const res = await httpJson({
|
|
2749
|
-
url: `${BASE9}/full/v1/book`,
|
|
2750
|
-
method: "POST",
|
|
2751
|
-
headers: { "Content-Type": "application/json" },
|
|
2752
|
-
body: JSON.stringify({ instrument, depth }),
|
|
2753
|
-
timeoutMs: opts.timeoutMs,
|
|
2754
|
-
transformUrl: opts.transformUrl
|
|
2755
|
-
});
|
|
2756
|
-
if (!res.result) {
|
|
2757
|
-
throw new ExchangeError(
|
|
2758
|
-
"grvt",
|
|
2759
|
-
res.code ? `${res.code}: ${res.message}` : "empty orderbook response"
|
|
2760
|
-
);
|
|
2761
|
-
}
|
|
2762
|
-
const ts = nsToMs(res.result.event_time);
|
|
2763
|
-
return {
|
|
2764
|
-
exchange: "grvt",
|
|
2765
|
-
symbol: fromGrvtSymbol(res.result.instrument ?? instrument),
|
|
2766
|
-
market: opts.market,
|
|
2767
|
-
bids: toLevels(res.result.bids),
|
|
2768
|
-
asks: toLevels(res.result.asks),
|
|
2769
|
-
timestamp: ts,
|
|
2770
|
-
sequence: ts
|
|
2771
|
-
};
|
|
2772
|
-
}
|
|
2773
|
-
|
|
2774
|
-
// src/exchanges/grvt/ws.ts
|
|
2775
|
-
var WS_FULL = "wss://market-data.grvt.io/ws/full";
|
|
2776
|
-
var STREAM = "v1.book.d";
|
|
2777
|
-
function streamGrvtOrderbook(opts) {
|
|
2778
|
-
const instrument = toGrvtSymbol(opts.symbol, opts.market);
|
|
2779
|
-
const selector = `${instrument}@${opts.rate ?? 500}`;
|
|
2780
|
-
const merger = new BookMerger();
|
|
2781
|
-
let ws = null;
|
|
2782
|
-
let sock = null;
|
|
2783
|
-
let recovering = false;
|
|
2784
|
-
let awaitingFirstDelta = false;
|
|
2785
|
-
let snapshotSequence = 0;
|
|
2786
|
-
let ackSequence = null;
|
|
2787
|
-
const stream = new OrderbookStream(() => ws?.close());
|
|
2788
|
-
let nextId = 1;
|
|
2789
|
-
const subscribeIds = /* @__PURE__ */ new Set();
|
|
2790
|
-
const rpc = (method) => {
|
|
2791
|
-
const id = nextId++;
|
|
2792
|
-
sock?.send(
|
|
2793
|
-
JSON.stringify({
|
|
2794
|
-
jsonrpc: "2.0",
|
|
2795
|
-
method,
|
|
2796
|
-
params: { stream: STREAM, selectors: [selector] },
|
|
2797
|
-
id
|
|
2798
|
-
})
|
|
2799
|
-
);
|
|
2800
|
-
return id;
|
|
2801
|
-
};
|
|
2802
|
-
const subscribe = () => subscribeIds.add(rpc("subscribe"));
|
|
2803
|
-
const emit = (r) => {
|
|
2804
|
-
const book = {
|
|
2805
|
-
exchange: "grvt",
|
|
2806
|
-
symbol: fromGrvtSymbol(instrument),
|
|
2807
|
-
market: opts.market,
|
|
2808
|
-
bids: r.bids,
|
|
2809
|
-
asks: r.asks,
|
|
2810
|
-
timestamp: r.timestamp,
|
|
2811
|
-
sequence: r.sequence
|
|
2812
|
-
};
|
|
2813
|
-
stream.emit("update", book);
|
|
2814
|
-
};
|
|
2815
|
-
const resync = () => {
|
|
2816
|
-
if (recovering) return;
|
|
2817
|
-
recovering = true;
|
|
2818
|
-
merger.reset();
|
|
2819
|
-
rpc("unsubscribe");
|
|
2820
|
-
subscribe();
|
|
2821
|
-
};
|
|
2822
|
-
const buildEvent2 = (feed, seq) => {
|
|
2823
|
-
const bids = toLevels(feed.bids);
|
|
2824
|
-
const asks = toLevels(feed.asks);
|
|
2825
|
-
const timestamp = nsToMs(feed.event_time);
|
|
2826
|
-
if (seq === 0) {
|
|
2827
|
-
snapshotSequence = ackSequence ?? 0;
|
|
2828
|
-
awaitingFirstDelta = true;
|
|
2829
|
-
return {
|
|
2830
|
-
kind: "snapshot",
|
|
2831
|
-
bids,
|
|
2832
|
-
asks,
|
|
2833
|
-
sequence: snapshotSequence,
|
|
2834
|
-
timestamp
|
|
2835
|
-
};
|
|
2836
|
-
}
|
|
2837
|
-
const prevSequence = awaitingFirstDelta ? snapshotSequence : seq - 1;
|
|
2838
|
-
awaitingFirstDelta = false;
|
|
2839
|
-
return { kind: "delta", bids, asks, sequence: seq, prevSequence, timestamp };
|
|
2840
|
-
};
|
|
2841
|
-
const handleFeed = (msg) => {
|
|
2842
|
-
if (!msg.feed) return;
|
|
2843
|
-
const seq = Number(msg.sequence_number ?? 0);
|
|
2844
|
-
if (!Number.isFinite(seq)) return;
|
|
2845
|
-
if (seq === 0) recovering = false;
|
|
2846
|
-
else if (recovering) return;
|
|
2847
|
-
const r = merger.apply(buildEvent2(msg.feed, seq));
|
|
2848
|
-
if (r.ok) {
|
|
2849
|
-
emit(r);
|
|
2850
|
-
} else if (r.reason === "gap" || r.reason === "no-snapshot") {
|
|
2851
|
-
resync();
|
|
2852
|
-
}
|
|
2853
|
-
};
|
|
2854
|
-
const onMessage = (raw) => {
|
|
2855
|
-
if (typeof raw !== "string") return;
|
|
2856
|
-
let msg;
|
|
2857
|
-
try {
|
|
2858
|
-
msg = JSON.parse(raw);
|
|
2859
|
-
} catch {
|
|
2860
|
-
return;
|
|
2861
|
-
}
|
|
2862
|
-
if (msg.feed) {
|
|
2863
|
-
if (msg.selector && msg.selector !== selector) return;
|
|
2864
|
-
handleFeed(msg);
|
|
2865
|
-
return;
|
|
2866
|
-
}
|
|
2867
|
-
if (msg.id === void 0) return;
|
|
2868
|
-
if (msg.error) {
|
|
2869
|
-
stream.emit(
|
|
2870
|
-
"error",
|
|
2871
|
-
new ExchangeError("grvt", `${msg.error.code}: ${msg.error.message}`)
|
|
2872
|
-
);
|
|
2873
|
-
return;
|
|
2874
|
-
}
|
|
2875
|
-
if (subscribeIds.delete(msg.id)) {
|
|
2876
|
-
const subs = msg.result?.subs ?? [];
|
|
2877
|
-
const latest = Number(msg.result?.latest_sequence_number?.[0]);
|
|
2878
|
-
ackSequence = Number.isFinite(latest) ? latest : null;
|
|
2879
|
-
if (!subs.includes(selector)) {
|
|
2880
|
-
stream.emit(
|
|
2881
|
-
"error",
|
|
2882
|
-
new ExchangeError(
|
|
2883
|
-
"grvt",
|
|
2884
|
-
`subscribe rejected for "${STREAM}" selector "${selector}"`
|
|
2885
|
-
)
|
|
2886
|
-
);
|
|
2887
|
-
}
|
|
2888
|
-
}
|
|
2889
|
-
};
|
|
2890
|
-
ws = new WSClient({
|
|
2891
|
-
url: WS_FULL,
|
|
2892
|
-
onOpen: (handle) => {
|
|
2893
|
-
sock = handle;
|
|
2894
|
-
merger.reset();
|
|
2895
|
-
recovering = false;
|
|
2896
|
-
awaitingFirstDelta = false;
|
|
2897
|
-
subscribeIds.clear();
|
|
2898
|
-
subscribe();
|
|
2899
|
-
},
|
|
2900
|
-
onMessage
|
|
2901
|
-
});
|
|
2902
|
-
ws.on("open", () => stream.emit("connected"));
|
|
2903
|
-
ws.on("close", (r) => stream.emit("disconnected", r));
|
|
2904
|
-
ws.on("reconnecting", (a, w) => stream.emit("reconnecting", a, w));
|
|
2905
|
-
ws.on("error", (e) => stream.emit("error", e));
|
|
2906
|
-
ws.connect().catch((e) => stream.emit("error", e));
|
|
2907
|
-
return stream;
|
|
2908
|
-
}
|
|
2909
|
-
|
|
2910
|
-
// src/exchanges/grvt/index.ts
|
|
2911
|
-
var GrvtClient = class {
|
|
2912
|
-
exchange = "grvt";
|
|
2913
|
-
transformUrl;
|
|
2914
|
-
timeoutMs;
|
|
2915
|
-
constructor(opts = {}) {
|
|
2916
|
-
this.transformUrl = opts.transformUrl;
|
|
2917
|
-
this.timeoutMs = opts.timeoutMs;
|
|
2918
|
-
}
|
|
2919
|
-
fetchOrderbook(symbol, opts = {}) {
|
|
2920
|
-
const { pair, market } = parseSymbol(symbol);
|
|
2921
|
-
return fetchGrvtOrderbook({
|
|
2922
|
-
symbol: pair,
|
|
2923
|
-
market,
|
|
2924
|
-
depth: opts.depth,
|
|
2925
|
-
timeoutMs: this.timeoutMs,
|
|
2926
|
-
transformUrl: this.transformUrl
|
|
2927
|
-
});
|
|
2928
|
-
}
|
|
2929
|
-
streamOrderbook(symbol, opts = {}) {
|
|
2930
|
-
const { pair, market } = parseSymbol(symbol);
|
|
2931
|
-
return streamGrvtOrderbook({
|
|
2932
|
-
symbol: pair,
|
|
2933
|
-
market,
|
|
2934
|
-
depth: opts.depth,
|
|
2935
|
-
transformUrl: this.transformUrl,
|
|
2936
|
-
timeoutMs: this.timeoutMs
|
|
2937
|
-
});
|
|
2938
|
-
}
|
|
2939
|
-
};
|
|
2940
|
-
|
|
2941
2715
|
// src/exchanges/lighter/markets.ts
|
|
2942
2716
|
var LIGHTER_REST = "https://mainnet.zklighter.elliot.ai";
|
|
2943
2717
|
var cache = null;
|
|
@@ -3063,7 +2837,7 @@ async function fetchLighterOrderbook(opts) {
|
|
|
3063
2837
|
// src/exchanges/lighter/ws.ts
|
|
3064
2838
|
var WS_URL2 = "wss://mainnet.zklighter.elliot.ai/stream";
|
|
3065
2839
|
var PING_INTERVAL_MS = 6e4;
|
|
3066
|
-
var
|
|
2840
|
+
var toLevels = (rows) => (rows ?? []).map((l) => ({ price: Number(l.price), size: Number(l.size) }));
|
|
3067
2841
|
function streamLighterOrderbook(opts) {
|
|
3068
2842
|
const name = toLighterSymbol(opts.symbol, opts.market);
|
|
3069
2843
|
const symbol = fromLighterSymbol(name);
|
|
@@ -3102,8 +2876,8 @@ function streamLighterOrderbook(opts) {
|
|
|
3102
2876
|
};
|
|
3103
2877
|
const buildEvent2 = (msg, snapshot) => {
|
|
3104
2878
|
const ob = msg.order_book ?? {};
|
|
3105
|
-
const bids =
|
|
3106
|
-
const asks =
|
|
2879
|
+
const bids = toLevels(ob.bids);
|
|
2880
|
+
const asks = toLevels(ob.asks);
|
|
3107
2881
|
const timestamp = msg.timestamp ?? usToMs(ob.last_updated_at);
|
|
3108
2882
|
const sequence = Number(ob.nonce ?? 0);
|
|
3109
2883
|
return snapshot ? { kind: "snapshot", bids, asks, sequence, timestamp } : {
|
|
@@ -3282,7 +3056,7 @@ var ALLOWED_LEVELS = [15, 200];
|
|
|
3282
3056
|
function snapLevel(depth) {
|
|
3283
3057
|
return ALLOWED_LEVELS.find((l) => l >= depth) ?? ALLOWED_LEVELS[ALLOWED_LEVELS.length - 1];
|
|
3284
3058
|
}
|
|
3285
|
-
var
|
|
3059
|
+
var toLevels2 = (rows) => (rows ?? []).map((l) => ({ price: Number(l.price), size: Number(l.size) }));
|
|
3286
3060
|
async function fetchEdgexOrderbook(opts) {
|
|
3287
3061
|
const contractName = toEdgexSymbol(opts.symbol, opts.market);
|
|
3288
3062
|
const contract = await resolveEdgexContract(contractName, opts);
|
|
@@ -3304,8 +3078,8 @@ async function fetchEdgexOrderbook(opts) {
|
|
|
3304
3078
|
exchange: "edgex",
|
|
3305
3079
|
symbol: fromEdgexSymbol(depth.contractName ?? contractName),
|
|
3306
3080
|
market: opts.market,
|
|
3307
|
-
bids:
|
|
3308
|
-
asks:
|
|
3081
|
+
bids: toLevels2(depth.bids),
|
|
3082
|
+
asks: toLevels2(depth.asks),
|
|
3309
3083
|
timestamp,
|
|
3310
3084
|
sequence: Number(depth.endVersion ?? 0)
|
|
3311
3085
|
};
|
|
@@ -3351,8 +3125,8 @@ function streamEdgexOrderbook(opts) {
|
|
|
3351
3125
|
send("subscribe");
|
|
3352
3126
|
};
|
|
3353
3127
|
const buildEvent2 = (d, snapshot) => {
|
|
3354
|
-
const bids =
|
|
3355
|
-
const asks =
|
|
3128
|
+
const bids = toLevels2(d.bids);
|
|
3129
|
+
const asks = toLevels2(d.asks);
|
|
3356
3130
|
const timestamp = Date.now();
|
|
3357
3131
|
const sequence = Number(d.endVersion ?? 0);
|
|
3358
3132
|
return snapshot ? { kind: "snapshot", bids, asks, sequence, timestamp } : {
|
|
@@ -3456,6 +3230,429 @@ var EdgexClient = class {
|
|
|
3456
3230
|
}
|
|
3457
3231
|
};
|
|
3458
3232
|
|
|
3459
|
-
|
|
3233
|
+
// src/exchanges/whitebit/symbols.ts
|
|
3234
|
+
function toWhitebitSymbol(symbol, market) {
|
|
3235
|
+
const [base, quote] = symbol.split("/");
|
|
3236
|
+
if (!base || !quote) {
|
|
3237
|
+
throw new Error(`invalid symbol "${symbol}" \u2014 expected "BASE/QUOTE"`);
|
|
3238
|
+
}
|
|
3239
|
+
if (market === "perpetual") {
|
|
3240
|
+
if (quote.toUpperCase() !== "USDT") {
|
|
3241
|
+
throw new Error(
|
|
3242
|
+
`whitebit perpetuals are USDT-margined; got "${quote}"`
|
|
3243
|
+
);
|
|
3244
|
+
}
|
|
3245
|
+
return `${base.toUpperCase()}_PERP`;
|
|
3246
|
+
}
|
|
3247
|
+
return `${base.toUpperCase()}_${quote.toUpperCase()}`;
|
|
3248
|
+
}
|
|
3249
|
+
function fromWhitebitSymbol(market) {
|
|
3250
|
+
const name = market.toUpperCase();
|
|
3251
|
+
if (name.endsWith("_PERP")) return `${name.slice(0, -5)}/USDT`;
|
|
3252
|
+
const [base, quote] = name.split("_");
|
|
3253
|
+
return base && quote ? `${base}/${quote}` : name;
|
|
3254
|
+
}
|
|
3255
|
+
|
|
3256
|
+
// src/exchanges/whitebit/rest.ts
|
|
3257
|
+
var WHITEBIT_REST = "https://whitebit.com";
|
|
3258
|
+
var MAX_REST_LIMIT = 100;
|
|
3259
|
+
function secToMs(sec) {
|
|
3260
|
+
return Number.isFinite(sec) && sec > 0 ? Math.round(sec * 1e3) : Date.now();
|
|
3261
|
+
}
|
|
3262
|
+
var toLevels3 = (rows) => (rows ?? []).map(([price, size]) => ({
|
|
3263
|
+
price: Number(price),
|
|
3264
|
+
size: Number(size)
|
|
3265
|
+
}));
|
|
3266
|
+
async function fetchWhitebitOrderbook(opts) {
|
|
3267
|
+
const market = toWhitebitSymbol(opts.symbol, opts.market);
|
|
3268
|
+
const limit = Math.min(opts.depth ?? 50, MAX_REST_LIMIT);
|
|
3269
|
+
const res = await httpJson({
|
|
3270
|
+
url: `${WHITEBIT_REST}/api/v4/public/orderbook/${market}?limit=${limit}`,
|
|
3271
|
+
timeoutMs: opts.timeoutMs,
|
|
3272
|
+
transformUrl: opts.transformUrl
|
|
3273
|
+
});
|
|
3274
|
+
if (!res.asks && !res.bids) {
|
|
3275
|
+
const detail = res.message ?? (res.errors ? JSON.stringify(res.errors) : "empty orderbook response");
|
|
3276
|
+
throw new ExchangeError("whitebit", detail);
|
|
3277
|
+
}
|
|
3278
|
+
const timestamp = secToMs(res.timestamp);
|
|
3279
|
+
return {
|
|
3280
|
+
exchange: "whitebit",
|
|
3281
|
+
symbol: fromWhitebitSymbol(res.ticker_id ?? market),
|
|
3282
|
+
market: opts.market,
|
|
3283
|
+
bids: toLevels3(res.bids),
|
|
3284
|
+
asks: toLevels3(res.asks),
|
|
3285
|
+
timestamp,
|
|
3286
|
+
// REST publishes no `update_id`; the WS chain is where ordering is enforced.
|
|
3287
|
+
sequence: timestamp
|
|
3288
|
+
};
|
|
3289
|
+
}
|
|
3290
|
+
|
|
3291
|
+
// src/exchanges/whitebit/ws.ts
|
|
3292
|
+
var WS_URL4 = "wss://api.whitebit.com/ws";
|
|
3293
|
+
var PING_INTERVAL_MS2 = 5e4;
|
|
3294
|
+
var ALLOWED_LIMITS = [1, 5, 10, 20, 30, 50, 100];
|
|
3295
|
+
function snapLimit(depth) {
|
|
3296
|
+
return ALLOWED_LIMITS.find((l) => l >= depth) ?? ALLOWED_LIMITS[ALLOWED_LIMITS.length - 1];
|
|
3297
|
+
}
|
|
3298
|
+
function streamWhitebitOrderbook(opts) {
|
|
3299
|
+
const market = toWhitebitSymbol(opts.symbol, opts.market);
|
|
3300
|
+
const symbol = fromWhitebitSymbol(market);
|
|
3301
|
+
const limit = snapLimit(opts.depth ?? 50);
|
|
3302
|
+
const merger = new BookMerger();
|
|
3303
|
+
let ws = null;
|
|
3304
|
+
let sock = null;
|
|
3305
|
+
let recovering = false;
|
|
3306
|
+
const stream = new OrderbookStream(() => ws?.close());
|
|
3307
|
+
let nextId = 1;
|
|
3308
|
+
const subscribeIds = /* @__PURE__ */ new Set();
|
|
3309
|
+
const call = (method, params) => {
|
|
3310
|
+
const id = nextId++;
|
|
3311
|
+
sock?.send(JSON.stringify({ id, method, params }));
|
|
3312
|
+
return id;
|
|
3313
|
+
};
|
|
3314
|
+
const subscribe = () => {
|
|
3315
|
+
subscribeIds.add(call("depth_subscribe", [market, limit, "0", true]));
|
|
3316
|
+
};
|
|
3317
|
+
const emit = (r) => {
|
|
3318
|
+
const book = {
|
|
3319
|
+
exchange: "whitebit",
|
|
3320
|
+
symbol,
|
|
3321
|
+
market: opts.market,
|
|
3322
|
+
bids: r.bids,
|
|
3323
|
+
asks: r.asks,
|
|
3324
|
+
timestamp: r.timestamp,
|
|
3325
|
+
sequence: r.sequence
|
|
3326
|
+
};
|
|
3327
|
+
stream.emit("update", book);
|
|
3328
|
+
};
|
|
3329
|
+
const resync = () => {
|
|
3330
|
+
if (recovering) return;
|
|
3331
|
+
recovering = true;
|
|
3332
|
+
merger.reset();
|
|
3333
|
+
call("depth_unsubscribe", []);
|
|
3334
|
+
subscribe();
|
|
3335
|
+
};
|
|
3336
|
+
const buildEvent2 = (d, snapshot) => {
|
|
3337
|
+
const bids = toLevels3(d.bids);
|
|
3338
|
+
const asks = toLevels3(d.asks);
|
|
3339
|
+
const timestamp = secToMs(d.timestamp ?? d.event_time);
|
|
3340
|
+
const sequence = Number(d.update_id ?? 0);
|
|
3341
|
+
return snapshot ? { kind: "snapshot", bids, asks, sequence, timestamp } : {
|
|
3342
|
+
kind: "delta",
|
|
3343
|
+
bids,
|
|
3344
|
+
asks,
|
|
3345
|
+
sequence,
|
|
3346
|
+
prevSequence: Number(d.past_update_id ?? 0),
|
|
3347
|
+
timestamp
|
|
3348
|
+
};
|
|
3349
|
+
};
|
|
3350
|
+
const handleDepth = (d, snapshot) => {
|
|
3351
|
+
if (snapshot) recovering = false;
|
|
3352
|
+
else if (recovering) return;
|
|
3353
|
+
const r = merger.apply(buildEvent2(d, snapshot));
|
|
3354
|
+
if (r.ok) {
|
|
3355
|
+
emit(r);
|
|
3356
|
+
} else if (r.reason === "gap" || r.reason === "no-snapshot") {
|
|
3357
|
+
resync();
|
|
3358
|
+
}
|
|
3359
|
+
};
|
|
3360
|
+
const onMessage = (raw) => {
|
|
3361
|
+
if (typeof raw !== "string") return;
|
|
3362
|
+
let msg;
|
|
3363
|
+
try {
|
|
3364
|
+
msg = JSON.parse(raw);
|
|
3365
|
+
} catch {
|
|
3366
|
+
return;
|
|
3367
|
+
}
|
|
3368
|
+
if (msg.method === "depth_update") {
|
|
3369
|
+
const [snapshot, data, frameMarket] = msg.params ?? [];
|
|
3370
|
+
if (!data || frameMarket && frameMarket !== market) return;
|
|
3371
|
+
handleDepth(data, snapshot === true);
|
|
3372
|
+
return;
|
|
3373
|
+
}
|
|
3374
|
+
if (msg.error) {
|
|
3375
|
+
stream.emit(
|
|
3376
|
+
"error",
|
|
3377
|
+
new ExchangeError(
|
|
3378
|
+
"whitebit",
|
|
3379
|
+
`${msg.error.code}: ${msg.error.message}`
|
|
3380
|
+
)
|
|
3381
|
+
);
|
|
3382
|
+
return;
|
|
3383
|
+
}
|
|
3384
|
+
if (typeof msg.id === "number") subscribeIds.delete(msg.id);
|
|
3385
|
+
};
|
|
3386
|
+
ws = new WSClient({
|
|
3387
|
+
url: WS_URL4,
|
|
3388
|
+
onOpen: (handle) => {
|
|
3389
|
+
sock = handle;
|
|
3390
|
+
merger.reset();
|
|
3391
|
+
recovering = false;
|
|
3392
|
+
subscribeIds.clear();
|
|
3393
|
+
subscribe();
|
|
3394
|
+
},
|
|
3395
|
+
onMessage,
|
|
3396
|
+
pingIntervalMs: PING_INTERVAL_MS2,
|
|
3397
|
+
pingPayload: () => JSON.stringify({ id: 0, method: "ping", params: [] })
|
|
3398
|
+
});
|
|
3399
|
+
ws.on("open", () => stream.emit("connected"));
|
|
3400
|
+
ws.on("close", (r) => stream.emit("disconnected", r));
|
|
3401
|
+
ws.on("reconnecting", (a, w) => stream.emit("reconnecting", a, w));
|
|
3402
|
+
ws.on("error", (e) => stream.emit("error", e));
|
|
3403
|
+
ws.connect().catch((e) => stream.emit("error", e));
|
|
3404
|
+
return stream;
|
|
3405
|
+
}
|
|
3406
|
+
|
|
3407
|
+
// src/exchanges/whitebit/index.ts
|
|
3408
|
+
var WhitebitClient = class {
|
|
3409
|
+
exchange = "whitebit";
|
|
3410
|
+
transformUrl;
|
|
3411
|
+
timeoutMs;
|
|
3412
|
+
constructor(opts = {}) {
|
|
3413
|
+
this.transformUrl = opts.transformUrl;
|
|
3414
|
+
this.timeoutMs = opts.timeoutMs;
|
|
3415
|
+
}
|
|
3416
|
+
fetchOrderbook(symbol, opts = {}) {
|
|
3417
|
+
const { pair, market } = parseSymbol(symbol);
|
|
3418
|
+
return fetchWhitebitOrderbook({
|
|
3419
|
+
symbol: pair,
|
|
3420
|
+
market,
|
|
3421
|
+
depth: opts.depth,
|
|
3422
|
+
timeoutMs: this.timeoutMs,
|
|
3423
|
+
transformUrl: this.transformUrl
|
|
3424
|
+
});
|
|
3425
|
+
}
|
|
3426
|
+
streamOrderbook(symbol, opts = {}) {
|
|
3427
|
+
const { pair, market } = parseSymbol(symbol);
|
|
3428
|
+
return streamWhitebitOrderbook({
|
|
3429
|
+
symbol: pair,
|
|
3430
|
+
market,
|
|
3431
|
+
depth: opts.depth,
|
|
3432
|
+
transformUrl: this.transformUrl,
|
|
3433
|
+
timeoutMs: this.timeoutMs
|
|
3434
|
+
});
|
|
3435
|
+
}
|
|
3436
|
+
};
|
|
3437
|
+
|
|
3438
|
+
// src/exchanges/aster/symbols.ts
|
|
3439
|
+
var QUOTES5 = ["USDT", "USD1", "FORM", "USDC", "BTC", "ETH", "U"];
|
|
3440
|
+
function toAsterSymbol(symbol) {
|
|
3441
|
+
const [base, quote] = symbol.split("/");
|
|
3442
|
+
if (!base || !quote) {
|
|
3443
|
+
throw new Error(`invalid symbol "${symbol}" \u2014 expected "BASE/QUOTE"`);
|
|
3444
|
+
}
|
|
3445
|
+
return `${base}${quote}`.toUpperCase();
|
|
3446
|
+
}
|
|
3447
|
+
function fromAsterSymbol(s) {
|
|
3448
|
+
const upper = s.toUpperCase();
|
|
3449
|
+
for (const q of QUOTES5) {
|
|
3450
|
+
if (upper.endsWith(q) && upper.length > q.length) {
|
|
3451
|
+
return `${upper.slice(0, -q.length)}/${q}`;
|
|
3452
|
+
}
|
|
3453
|
+
}
|
|
3454
|
+
return upper;
|
|
3455
|
+
}
|
|
3456
|
+
|
|
3457
|
+
// src/exchanges/aster/rest.ts
|
|
3458
|
+
var ASTER_SPOT_REST = "https://sapi.asterdex.com";
|
|
3459
|
+
var ASTER_FUTURES_REST = "https://fapi.asterdex.com";
|
|
3460
|
+
async function fetchAsterOrderbook(opts) {
|
|
3461
|
+
const symbol = toAsterSymbol(opts.symbol);
|
|
3462
|
+
const limit = opts.depth ?? 1e3;
|
|
3463
|
+
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}`;
|
|
3464
|
+
const data = await httpJson({
|
|
3465
|
+
url,
|
|
3466
|
+
timeoutMs: opts.timeoutMs,
|
|
3467
|
+
transformUrl: opts.transformUrl
|
|
3468
|
+
});
|
|
3469
|
+
return {
|
|
3470
|
+
exchange: "aster",
|
|
3471
|
+
symbol: fromAsterSymbol(data.symbol ?? symbol),
|
|
3472
|
+
market: opts.market,
|
|
3473
|
+
bids: data.bids.map(([p, s]) => ({ price: Number(p), size: Number(s) })),
|
|
3474
|
+
asks: data.asks.map(([p, s]) => ({ price: Number(p), size: Number(s) })),
|
|
3475
|
+
timestamp: data.E ?? data.T ?? Date.now(),
|
|
3476
|
+
sequence: data.lastUpdateId
|
|
3477
|
+
};
|
|
3478
|
+
}
|
|
3479
|
+
|
|
3480
|
+
// src/exchanges/aster/ws.ts
|
|
3481
|
+
var WS_SPOT6 = "wss://sstream.asterdex.com/ws";
|
|
3482
|
+
var WS_FUTURES4 = "wss://fstream.asterdex.com/ws";
|
|
3483
|
+
function streamAsterOrderbook(opts) {
|
|
3484
|
+
const symbol = toAsterSymbol(opts.symbol);
|
|
3485
|
+
const isSpot = opts.market === "spot";
|
|
3486
|
+
const wsBase = isSpot ? WS_SPOT6 : WS_FUTURES4;
|
|
3487
|
+
const wsUrl = `${wsBase}/${symbol.toLowerCase()}@depth@${opts.interval ?? "100ms"}`;
|
|
3488
|
+
const merger = new BookMerger();
|
|
3489
|
+
let snapshotId = -1;
|
|
3490
|
+
let lastU = -1;
|
|
3491
|
+
let snapshotInFlight = false;
|
|
3492
|
+
let snapshotDone = false;
|
|
3493
|
+
let buffered = [];
|
|
3494
|
+
let recovering = false;
|
|
3495
|
+
let ws = null;
|
|
3496
|
+
const stream = new OrderbookStream(() => ws?.close());
|
|
3497
|
+
const emit = (r) => {
|
|
3498
|
+
const book = {
|
|
3499
|
+
exchange: "aster",
|
|
3500
|
+
symbol: fromAsterSymbol(symbol),
|
|
3501
|
+
market: opts.market,
|
|
3502
|
+
bids: r.bids,
|
|
3503
|
+
asks: r.asks,
|
|
3504
|
+
timestamp: r.timestamp,
|
|
3505
|
+
sequence: r.sequence
|
|
3506
|
+
};
|
|
3507
|
+
stream.emit("update", book);
|
|
3508
|
+
};
|
|
3509
|
+
const triggerSnapshot = () => {
|
|
3510
|
+
if (snapshotInFlight) return;
|
|
3511
|
+
snapshotInFlight = true;
|
|
3512
|
+
fetchAsterOrderbook({
|
|
3513
|
+
symbol: opts.symbol,
|
|
3514
|
+
market: opts.market,
|
|
3515
|
+
depth: opts.depth ?? 1e3,
|
|
3516
|
+
timeoutMs: opts.timeoutMs,
|
|
3517
|
+
transformUrl: opts.transformUrl
|
|
3518
|
+
}).then((snap) => {
|
|
3519
|
+
snapshotId = snap.sequence;
|
|
3520
|
+
const r = merger.apply({
|
|
3521
|
+
kind: "snapshot",
|
|
3522
|
+
bids: snap.bids,
|
|
3523
|
+
asks: snap.asks,
|
|
3524
|
+
sequence: snap.sequence,
|
|
3525
|
+
timestamp: snap.timestamp
|
|
3526
|
+
});
|
|
3527
|
+
if (r.ok) emit(r);
|
|
3528
|
+
snapshotDone = true;
|
|
3529
|
+
lastU = -1;
|
|
3530
|
+
const pending2 = buffered;
|
|
3531
|
+
buffered = [];
|
|
3532
|
+
for (const ev of pending2) processDelta(ev);
|
|
3533
|
+
}).catch((err) => stream.emit("error", err)).finally(() => {
|
|
3534
|
+
snapshotInFlight = false;
|
|
3535
|
+
});
|
|
3536
|
+
};
|
|
3537
|
+
const resync = () => {
|
|
3538
|
+
if (recovering) return;
|
|
3539
|
+
recovering = true;
|
|
3540
|
+
merger.reset();
|
|
3541
|
+
snapshotId = -1;
|
|
3542
|
+
snapshotDone = false;
|
|
3543
|
+
lastU = -1;
|
|
3544
|
+
buffered = [];
|
|
3545
|
+
setTimeout(() => {
|
|
3546
|
+
recovering = false;
|
|
3547
|
+
triggerSnapshot();
|
|
3548
|
+
}, 100);
|
|
3549
|
+
};
|
|
3550
|
+
const processDelta = (ev) => {
|
|
3551
|
+
if (ev.u < snapshotId) return;
|
|
3552
|
+
const bids = ev.b.map(([p, s]) => ({
|
|
3553
|
+
price: Number(p),
|
|
3554
|
+
size: Number(s)
|
|
3555
|
+
}));
|
|
3556
|
+
const asks = ev.a.map(([p, s]) => ({
|
|
3557
|
+
price: Number(p),
|
|
3558
|
+
size: Number(s)
|
|
3559
|
+
}));
|
|
3560
|
+
let prevSequence;
|
|
3561
|
+
if (lastU < 0) {
|
|
3562
|
+
if (ev.U > snapshotId) {
|
|
3563
|
+
resync();
|
|
3564
|
+
return;
|
|
3565
|
+
}
|
|
3566
|
+
prevSequence = snapshotId;
|
|
3567
|
+
} else {
|
|
3568
|
+
if (ev.pu === void 0) {
|
|
3569
|
+
resync();
|
|
3570
|
+
return;
|
|
3571
|
+
}
|
|
3572
|
+
prevSequence = ev.pu;
|
|
3573
|
+
}
|
|
3574
|
+
const r = merger.apply({
|
|
3575
|
+
kind: "delta",
|
|
3576
|
+
bids,
|
|
3577
|
+
asks,
|
|
3578
|
+
sequence: ev.u,
|
|
3579
|
+
prevSequence,
|
|
3580
|
+
timestamp: ev.E
|
|
3581
|
+
});
|
|
3582
|
+
if (r.ok) {
|
|
3583
|
+
lastU = ev.u;
|
|
3584
|
+
emit(r);
|
|
3585
|
+
} else if (r.reason === "gap" || r.reason === "no-snapshot") {
|
|
3586
|
+
resync();
|
|
3587
|
+
}
|
|
3588
|
+
};
|
|
3589
|
+
const onMessage = (raw) => {
|
|
3590
|
+
if (typeof raw !== "string") return;
|
|
3591
|
+
let msg;
|
|
3592
|
+
try {
|
|
3593
|
+
msg = JSON.parse(raw);
|
|
3594
|
+
} catch {
|
|
3595
|
+
return;
|
|
3596
|
+
}
|
|
3597
|
+
if (msg.e !== "depthUpdate") return;
|
|
3598
|
+
if (!snapshotDone) {
|
|
3599
|
+
buffered.push(msg);
|
|
3600
|
+
if (!snapshotInFlight) triggerSnapshot();
|
|
3601
|
+
return;
|
|
3602
|
+
}
|
|
3603
|
+
processDelta(msg);
|
|
3604
|
+
};
|
|
3605
|
+
ws = new WSClient({
|
|
3606
|
+
url: wsUrl,
|
|
3607
|
+
onOpen: () => {
|
|
3608
|
+
merger.reset();
|
|
3609
|
+
snapshotId = -1;
|
|
3610
|
+
snapshotDone = false;
|
|
3611
|
+
lastU = -1;
|
|
3612
|
+
buffered = [];
|
|
3613
|
+
},
|
|
3614
|
+
onMessage
|
|
3615
|
+
// Server-initiated WS-level ping; auto-handled by the WS impl.
|
|
3616
|
+
});
|
|
3617
|
+
ws.on("open", () => stream.emit("connected"));
|
|
3618
|
+
ws.on("close", (r) => stream.emit("disconnected", r));
|
|
3619
|
+
ws.on("reconnecting", (a, w) => stream.emit("reconnecting", a, w));
|
|
3620
|
+
ws.on("error", (e) => stream.emit("error", e));
|
|
3621
|
+
ws.connect().catch((e) => stream.emit("error", e));
|
|
3622
|
+
return stream;
|
|
3623
|
+
}
|
|
3624
|
+
|
|
3625
|
+
// src/exchanges/aster/index.ts
|
|
3626
|
+
var AsterClient = class {
|
|
3627
|
+
exchange = "aster";
|
|
3628
|
+
transformUrl;
|
|
3629
|
+
timeoutMs;
|
|
3630
|
+
constructor(opts = {}) {
|
|
3631
|
+
this.transformUrl = opts.transformUrl;
|
|
3632
|
+
this.timeoutMs = opts.timeoutMs;
|
|
3633
|
+
}
|
|
3634
|
+
fetchOrderbook(symbol, opts = {}) {
|
|
3635
|
+
const { pair, market } = parseSymbol(symbol);
|
|
3636
|
+
return fetchAsterOrderbook({
|
|
3637
|
+
symbol: pair,
|
|
3638
|
+
market,
|
|
3639
|
+
depth: opts.depth,
|
|
3640
|
+
timeoutMs: this.timeoutMs,
|
|
3641
|
+
transformUrl: this.transformUrl
|
|
3642
|
+
});
|
|
3643
|
+
}
|
|
3644
|
+
streamOrderbook(symbol, opts = {}) {
|
|
3645
|
+
const { pair, market } = parseSymbol(symbol);
|
|
3646
|
+
return streamAsterOrderbook({
|
|
3647
|
+
symbol: pair,
|
|
3648
|
+
market,
|
|
3649
|
+
depth: opts.depth,
|
|
3650
|
+
transformUrl: this.transformUrl,
|
|
3651
|
+
timeoutMs: this.timeoutMs
|
|
3652
|
+
});
|
|
3653
|
+
}
|
|
3654
|
+
};
|
|
3655
|
+
|
|
3656
|
+
export { AsterClient, Backoff, BinanceClient, BingxClient, BitgetClient, BookMerger, BybitClient, CoinexClient, DeribitClient, EdgexClient, ExchangeError, GateClient, HyperliquidClient, KucoinClient, LighterClient, OkxClient, OrderbookStream, RateLimitError, SequenceGapError, WhitebitClient, parseSymbol };
|
|
3460
3657
|
//# sourceMappingURL=index.js.map
|
|
3461
3658
|
//# sourceMappingURL=index.js.map
|