@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.
Files changed (52) hide show
  1. package/README.md +1 -1
  2. package/dist/exchanges/binance/index.d.cts +1 -1
  3. package/dist/exchanges/binance/index.d.ts +1 -1
  4. package/dist/exchanges/bingx/index.d.cts +1 -1
  5. package/dist/exchanges/bingx/index.d.ts +1 -1
  6. package/dist/exchanges/bitget/index.d.cts +1 -1
  7. package/dist/exchanges/bitget/index.d.ts +1 -1
  8. package/dist/exchanges/bybit/index.d.cts +1 -1
  9. package/dist/exchanges/bybit/index.d.ts +1 -1
  10. package/dist/exchanges/coinex/index.d.cts +1 -1
  11. package/dist/exchanges/coinex/index.d.ts +1 -1
  12. package/dist/exchanges/deribit/index.d.cts +1 -1
  13. package/dist/exchanges/deribit/index.d.ts +1 -1
  14. package/dist/exchanges/edgex/index.cjs +39 -23
  15. package/dist/exchanges/edgex/index.cjs.map +1 -1
  16. package/dist/exchanges/edgex/index.d.cts +37 -3
  17. package/dist/exchanges/edgex/index.d.ts +37 -3
  18. package/dist/exchanges/edgex/index.js +37 -24
  19. package/dist/exchanges/edgex/index.js.map +1 -1
  20. package/dist/exchanges/gate/index.d.cts +1 -1
  21. package/dist/exchanges/gate/index.d.ts +1 -1
  22. package/dist/exchanges/hyperliquid/index.d.cts +1 -1
  23. package/dist/exchanges/hyperliquid/index.d.ts +1 -1
  24. package/dist/exchanges/kucoin/index.d.cts +1 -1
  25. package/dist/exchanges/kucoin/index.d.ts +1 -1
  26. package/dist/exchanges/lighter/index.cjs +1 -0
  27. package/dist/exchanges/lighter/index.cjs.map +1 -1
  28. package/dist/exchanges/lighter/index.d.cts +3 -2
  29. package/dist/exchanges/lighter/index.d.ts +3 -2
  30. package/dist/exchanges/lighter/index.js +1 -1
  31. package/dist/exchanges/lighter/index.js.map +1 -1
  32. package/dist/exchanges/okx/index.d.cts +1 -1
  33. package/dist/exchanges/okx/index.d.ts +1 -1
  34. package/dist/exchanges/{grvt → whitebit}/index.cjs +98 -116
  35. package/dist/exchanges/whitebit/index.cjs.map +1 -0
  36. package/dist/exchanges/whitebit/index.d.cts +57 -0
  37. package/dist/exchanges/whitebit/index.d.ts +57 -0
  38. package/dist/exchanges/{grvt → whitebit}/index.js +93 -114
  39. package/dist/exchanges/whitebit/index.js.map +1 -0
  40. package/dist/index.cjs +248 -257
  41. package/dist/index.cjs.map +1 -1
  42. package/dist/index.d.cts +3 -3
  43. package/dist/index.d.ts +3 -3
  44. package/dist/index.js +248 -257
  45. package/dist/index.js.map +1 -1
  46. package/dist/{stream-YYPvjMV4.d.cts → stream-D8ErAue9.d.cts} +1 -1
  47. package/dist/{stream-YYPvjMV4.d.ts → stream-D8ErAue9.d.ts} +1 -1
  48. package/package.json +12 -12
  49. package/dist/exchanges/grvt/index.cjs.map +0 -1
  50. package/dist/exchanges/grvt/index.d.cts +0 -47
  51. package/dist/exchanges/grvt/index.d.ts +0 -47
  52. 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 toLevels2 = (rows) => (rows ?? []).map((l) => ({ price: Number(l.price), size: Number(l.size) }));
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 = toLevels2(ob.bids);
3108
- const asks = toLevels2(ob.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://edgex-prod-v2.edgex.exchange";
2979
+ var EDGEX_REST = "https://cors.xapy.io/edgex";
3206
2980
  var EDGEX_WS = "wss://edgex-quote-prod-v2.edgex.exchange";
3207
- var cache2 = null;
3208
- var inflight2 = null;
3209
- async function fetchContracts(opts) {
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: `${EDGEX_REST}/api/v2/public/meta/getMetaData`,
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
- inflight2 ??= fetchContracts(opts).then(
3228
- (m) => {
3229
- cache2 = m;
3230
- inflight2 = null;
3231
- return m;
3232
- },
3233
- (err) => {
3234
- inflight2 = null;
3235
- throw err;
3236
- }
3237
- );
3238
- return inflight2;
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 = cache2?.get(k);
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 toLevels3 = (rows) => (rows ?? []).map((l) => ({ price: Number(l.price), size: Number(l.size) }));
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: `${EDGEX_REST}/api/v2/public/quote/getDepth?contractId=${contract.contractId}&level=${level}`,
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: toLevels3(depth.bids),
3302
- asks: toLevels3(depth.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 = toLevels3(d.bids);
3349
- const asks = toLevels3(d.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