@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.d.cts CHANGED
@@ -1,5 +1,5 @@
1
- import { B as BookEvent, O as OrderbookLevel, M as Market } from './stream-YYPvjMV4.cjs';
2
- export { C as ClientOptions, E as ExchangeName, a as Orderbook, b as OrderbookOptions, c as OrderbookStream, S as Side, d as StreamEvents } from './stream-YYPvjMV4.cjs';
1
+ import { B as BookEvent, O as OrderbookLevel, M as Market } from './stream-D8ErAue9.cjs';
2
+ export { C as ClientOptions, E as ExchangeName, a as Orderbook, b as OrderbookOptions, c as OrderbookStream, S as Side, d as StreamEvents } from './stream-D8ErAue9.cjs';
3
3
  export { BybitClient } from './exchanges/bybit/index.cjs';
4
4
  export { BitgetClient } from './exchanges/bitget/index.cjs';
5
5
  export { GateClient } from './exchanges/gate/index.cjs';
@@ -10,9 +10,9 @@ export { BinanceClient } from './exchanges/binance/index.cjs';
10
10
  export { HyperliquidClient } from './exchanges/hyperliquid/index.cjs';
11
11
  export { KucoinClient } from './exchanges/kucoin/index.cjs';
12
12
  export { DeribitClient } from './exchanges/deribit/index.cjs';
13
- export { GrvtClient } from './exchanges/grvt/index.cjs';
14
13
  export { LighterClient } from './exchanges/lighter/index.cjs';
15
14
  export { EdgexClient } from './exchanges/edgex/index.cjs';
15
+ export { WhitebitClient } from './exchanges/whitebit/index.cjs';
16
16
 
17
17
  declare class ExchangeError extends Error {
18
18
  readonly exchange: string;
package/dist/index.d.ts CHANGED
@@ -1,5 +1,5 @@
1
- import { B as BookEvent, O as OrderbookLevel, M as Market } from './stream-YYPvjMV4.js';
2
- export { C as ClientOptions, E as ExchangeName, a as Orderbook, b as OrderbookOptions, c as OrderbookStream, S as Side, d as StreamEvents } from './stream-YYPvjMV4.js';
1
+ import { B as BookEvent, O as OrderbookLevel, M as Market } from './stream-D8ErAue9.js';
2
+ export { C as ClientOptions, E as ExchangeName, a as Orderbook, b as OrderbookOptions, c as OrderbookStream, S as Side, d as StreamEvents } from './stream-D8ErAue9.js';
3
3
  export { BybitClient } from './exchanges/bybit/index.js';
4
4
  export { BitgetClient } from './exchanges/bitget/index.js';
5
5
  export { GateClient } from './exchanges/gate/index.js';
@@ -10,9 +10,9 @@ export { BinanceClient } from './exchanges/binance/index.js';
10
10
  export { HyperliquidClient } from './exchanges/hyperliquid/index.js';
11
11
  export { KucoinClient } from './exchanges/kucoin/index.js';
12
12
  export { DeribitClient } from './exchanges/deribit/index.js';
13
- export { GrvtClient } from './exchanges/grvt/index.js';
14
13
  export { LighterClient } from './exchanges/lighter/index.js';
15
14
  export { EdgexClient } from './exchanges/edgex/index.js';
15
+ export { WhitebitClient } from './exchanges/whitebit/index.js';
16
16
 
17
17
  declare class ExchangeError extends Error {
18
18
  readonly exchange: string;
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 toLevels2 = (rows) => (rows ?? []).map((l) => ({ price: Number(l.price), size: Number(l.size) }));
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 = toLevels2(ob.bids);
3106
- const asks = toLevels2(ob.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 } : {
@@ -3200,13 +2974,16 @@ var LighterClient = class {
3200
2974
  };
3201
2975
 
3202
2976
  // src/exchanges/edgex/markets.ts
3203
- var EDGEX_REST = "https://edgex-prod-v2.edgex.exchange";
2977
+ var EDGEX_REST = "https://cors.xapy.io/edgex";
3204
2978
  var EDGEX_WS = "wss://edgex-quote-prod-v2.edgex.exchange";
3205
- var cache2 = null;
3206
- var inflight2 = null;
3207
- async function fetchContracts(opts) {
2979
+ function edgexRestBase(opts) {
2980
+ return (opts.restBase ?? EDGEX_REST).replace(/\/+$/, "");
2981
+ }
2982
+ var caches = /* @__PURE__ */ new Map();
2983
+ var inflights = /* @__PURE__ */ new Map();
2984
+ async function fetchContracts(opts, base) {
3208
2985
  const res = await httpJson({
3209
- url: `${EDGEX_REST}/api/v2/public/meta/getMetaData`,
2986
+ url: `${base}/api/v2/public/meta/getMetaData`,
3210
2987
  timeoutMs: opts.timeoutMs,
3211
2988
  transformUrl: opts.transformUrl
3212
2989
  });
@@ -3221,24 +2998,29 @@ async function fetchContracts(opts) {
3221
2998
  for (const row of rows) map.set(row.contractName.toUpperCase(), row);
3222
2999
  return map;
3223
3000
  }
3224
- function loadContracts(opts) {
3225
- inflight2 ??= fetchContracts(opts).then(
3226
- (m) => {
3227
- cache2 = m;
3228
- inflight2 = null;
3229
- return m;
3230
- },
3231
- (err) => {
3232
- inflight2 = null;
3233
- throw err;
3234
- }
3235
- );
3236
- return inflight2;
3001
+ function loadContracts(opts, base) {
3002
+ let pending2 = inflights.get(base);
3003
+ if (!pending2) {
3004
+ pending2 = fetchContracts(opts, base).then(
3005
+ (m) => {
3006
+ caches.set(base, m);
3007
+ inflights.delete(base);
3008
+ return m;
3009
+ },
3010
+ (err) => {
3011
+ inflights.delete(base);
3012
+ throw err;
3013
+ }
3014
+ );
3015
+ inflights.set(base, pending2);
3016
+ }
3017
+ return pending2;
3237
3018
  }
3238
3019
  async function resolveEdgexContract(contractName, opts) {
3020
+ const base = edgexRestBase(opts);
3239
3021
  const k = contractName.toUpperCase();
3240
- const hit = cache2?.get(k);
3241
- const contract = hit ?? (await loadContracts(opts)).get(k);
3022
+ const hit = caches.get(base)?.get(k);
3023
+ const contract = hit ?? (await loadContracts(opts, base)).get(k);
3242
3024
  if (!contract) {
3243
3025
  throw new ExchangeError(
3244
3026
  "edgex",
@@ -3274,13 +3056,13 @@ var ALLOWED_LEVELS = [15, 200];
3274
3056
  function snapLevel(depth) {
3275
3057
  return ALLOWED_LEVELS.find((l) => l >= depth) ?? ALLOWED_LEVELS[ALLOWED_LEVELS.length - 1];
3276
3058
  }
3277
- var toLevels3 = (rows) => (rows ?? []).map((l) => ({ price: Number(l.price), size: Number(l.size) }));
3059
+ var toLevels2 = (rows) => (rows ?? []).map((l) => ({ price: Number(l.price), size: Number(l.size) }));
3278
3060
  async function fetchEdgexOrderbook(opts) {
3279
3061
  const contractName = toEdgexSymbol(opts.symbol, opts.market);
3280
3062
  const contract = await resolveEdgexContract(contractName, opts);
3281
3063
  const level = snapLevel(opts.depth ?? 50);
3282
3064
  const res = await httpJson({
3283
- url: `${EDGEX_REST}/api/v2/public/quote/getDepth?contractId=${contract.contractId}&level=${level}`,
3065
+ url: `${edgexRestBase(opts)}/api/v2/public/quote/getDepth?contractId=${contract.contractId}&level=${level}`,
3284
3066
  timeoutMs: opts.timeoutMs,
3285
3067
  transformUrl: opts.transformUrl
3286
3068
  });
@@ -3296,8 +3078,8 @@ async function fetchEdgexOrderbook(opts) {
3296
3078
  exchange: "edgex",
3297
3079
  symbol: fromEdgexSymbol(depth.contractName ?? contractName),
3298
3080
  market: opts.market,
3299
- bids: toLevels3(depth.bids),
3300
- asks: toLevels3(depth.asks),
3081
+ bids: toLevels2(depth.bids),
3082
+ asks: toLevels2(depth.asks),
3301
3083
  timestamp,
3302
3084
  sequence: Number(depth.endVersion ?? 0)
3303
3085
  };
@@ -3343,8 +3125,8 @@ function streamEdgexOrderbook(opts) {
3343
3125
  send("subscribe");
3344
3126
  };
3345
3127
  const buildEvent2 = (d, snapshot) => {
3346
- const bids = toLevels3(d.bids);
3347
- const asks = toLevels3(d.asks);
3128
+ const bids = toLevels2(d.bids);
3129
+ const asks = toLevels2(d.asks);
3348
3130
  const timestamp = Date.now();
3349
3131
  const sequence = Number(d.endVersion ?? 0);
3350
3132
  return snapshot ? { kind: "snapshot", bids, asks, sequence, timestamp } : {
@@ -3418,9 +3200,11 @@ var EdgexClient = class {
3418
3200
  exchange = "edgex";
3419
3201
  transformUrl;
3420
3202
  timeoutMs;
3203
+ restBase;
3421
3204
  constructor(opts = {}) {
3422
3205
  this.transformUrl = opts.transformUrl;
3423
3206
  this.timeoutMs = opts.timeoutMs;
3207
+ this.restBase = opts.restBase;
3424
3208
  }
3425
3209
  fetchOrderbook(symbol, opts = {}) {
3426
3210
  const { pair, market } = parseSymbol(symbol);
@@ -3429,12 +3213,219 @@ var EdgexClient = class {
3429
3213
  market,
3430
3214
  depth: opts.depth,
3431
3215
  timeoutMs: this.timeoutMs,
3432
- transformUrl: this.transformUrl
3216
+ transformUrl: this.transformUrl,
3217
+ restBase: this.restBase
3433
3218
  });
3434
3219
  }
3435
3220
  streamOrderbook(symbol, opts = {}) {
3436
3221
  const { pair, market } = parseSymbol(symbol);
3437
3222
  return streamEdgexOrderbook({
3223
+ symbol: pair,
3224
+ market,
3225
+ depth: opts.depth,
3226
+ transformUrl: this.transformUrl,
3227
+ timeoutMs: this.timeoutMs,
3228
+ restBase: this.restBase
3229
+ });
3230
+ }
3231
+ };
3232
+
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({
3438
3429
  symbol: pair,
3439
3430
  market,
3440
3431
  depth: opts.depth,
@@ -3444,6 +3435,6 @@ var EdgexClient = class {
3444
3435
  }
3445
3436
  };
3446
3437
 
3447
- export { Backoff, BinanceClient, BingxClient, BitgetClient, BookMerger, BybitClient, CoinexClient, DeribitClient, EdgexClient, ExchangeError, GateClient, GrvtClient, HyperliquidClient, KucoinClient, LighterClient, OkxClient, OrderbookStream, RateLimitError, SequenceGapError, parseSymbol };
3438
+ export { Backoff, BinanceClient, BingxClient, BitgetClient, BookMerger, BybitClient, CoinexClient, DeribitClient, EdgexClient, ExchangeError, GateClient, HyperliquidClient, KucoinClient, LighterClient, OkxClient, OrderbookStream, RateLimitError, SequenceGapError, WhitebitClient, parseSymbol };
3448
3439
  //# sourceMappingURL=index.js.map
3449
3440
  //# sourceMappingURL=index.js.map