@xapy/orderbook 0.1.22 → 0.1.23

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 (80) hide show
  1. package/README.md +14 -9
  2. package/dist/exchanges/binance/index.cjs +21 -6
  3. package/dist/exchanges/binance/index.cjs.map +1 -1
  4. package/dist/exchanges/binance/index.d.cts +1 -1
  5. package/dist/exchanges/binance/index.d.ts +1 -1
  6. package/dist/exchanges/binance/index.js +21 -6
  7. package/dist/exchanges/binance/index.js.map +1 -1
  8. package/dist/exchanges/bingx/index.cjs +38 -11
  9. package/dist/exchanges/bingx/index.cjs.map +1 -1
  10. package/dist/exchanges/bingx/index.d.cts +1 -1
  11. package/dist/exchanges/bingx/index.d.ts +1 -1
  12. package/dist/exchanges/bingx/index.js +38 -11
  13. package/dist/exchanges/bingx/index.js.map +1 -1
  14. package/dist/exchanges/bitget/index.cjs +21 -6
  15. package/dist/exchanges/bitget/index.cjs.map +1 -1
  16. package/dist/exchanges/bitget/index.d.cts +1 -1
  17. package/dist/exchanges/bitget/index.d.ts +1 -1
  18. package/dist/exchanges/bitget/index.js +21 -6
  19. package/dist/exchanges/bitget/index.js.map +1 -1
  20. package/dist/exchanges/bybit/index.cjs +21 -6
  21. package/dist/exchanges/bybit/index.cjs.map +1 -1
  22. package/dist/exchanges/bybit/index.d.cts +1 -1
  23. package/dist/exchanges/bybit/index.d.ts +1 -1
  24. package/dist/exchanges/bybit/index.js +21 -6
  25. package/dist/exchanges/bybit/index.js.map +1 -1
  26. package/dist/exchanges/coinex/index.cjs +21 -6
  27. package/dist/exchanges/coinex/index.cjs.map +1 -1
  28. package/dist/exchanges/coinex/index.d.cts +1 -1
  29. package/dist/exchanges/coinex/index.d.ts +1 -1
  30. package/dist/exchanges/coinex/index.js +21 -6
  31. package/dist/exchanges/coinex/index.js.map +1 -1
  32. package/dist/exchanges/{bitmart → deribit}/index.cjs +174 -148
  33. package/dist/exchanges/deribit/index.cjs.map +1 -0
  34. package/dist/exchanges/deribit/index.d.cts +48 -0
  35. package/dist/exchanges/deribit/index.d.ts +48 -0
  36. package/dist/exchanges/{bitmart → deribit}/index.js +172 -146
  37. package/dist/exchanges/deribit/index.js.map +1 -0
  38. package/dist/exchanges/gate/index.cjs +21 -6
  39. package/dist/exchanges/gate/index.cjs.map +1 -1
  40. package/dist/exchanges/gate/index.d.cts +1 -1
  41. package/dist/exchanges/gate/index.d.ts +1 -1
  42. package/dist/exchanges/gate/index.js +21 -6
  43. package/dist/exchanges/gate/index.js.map +1 -1
  44. package/dist/exchanges/huobi/index.cjs +21 -6
  45. package/dist/exchanges/huobi/index.cjs.map +1 -1
  46. package/dist/exchanges/huobi/index.d.cts +1 -1
  47. package/dist/exchanges/huobi/index.d.ts +1 -1
  48. package/dist/exchanges/huobi/index.js +21 -6
  49. package/dist/exchanges/huobi/index.js.map +1 -1
  50. package/dist/exchanges/hyperliquid/index.cjs +21 -6
  51. package/dist/exchanges/hyperliquid/index.cjs.map +1 -1
  52. package/dist/exchanges/hyperliquid/index.d.cts +1 -1
  53. package/dist/exchanges/hyperliquid/index.d.ts +1 -1
  54. package/dist/exchanges/hyperliquid/index.js +21 -6
  55. package/dist/exchanges/hyperliquid/index.js.map +1 -1
  56. package/dist/exchanges/kucoin/index.cjs +21 -6
  57. package/dist/exchanges/kucoin/index.cjs.map +1 -1
  58. package/dist/exchanges/kucoin/index.d.cts +1 -1
  59. package/dist/exchanges/kucoin/index.d.ts +1 -1
  60. package/dist/exchanges/kucoin/index.js +21 -6
  61. package/dist/exchanges/kucoin/index.js.map +1 -1
  62. package/dist/exchanges/okx/index.cjs +21 -6
  63. package/dist/exchanges/okx/index.cjs.map +1 -1
  64. package/dist/exchanges/okx/index.d.cts +1 -1
  65. package/dist/exchanges/okx/index.d.ts +1 -1
  66. package/dist/exchanges/okx/index.js +21 -6
  67. package/dist/exchanges/okx/index.js.map +1 -1
  68. package/dist/index.cjs +189 -122
  69. package/dist/index.cjs.map +1 -1
  70. package/dist/index.d.cts +3 -3
  71. package/dist/index.d.ts +3 -3
  72. package/dist/index.js +189 -122
  73. package/dist/index.js.map +1 -1
  74. package/dist/{stream-L9MJjYbE.d.cts → stream-CO3LD9jf.d.cts} +1 -1
  75. package/dist/{stream-L9MJjYbE.d.ts → stream-CO3LD9jf.d.ts} +1 -1
  76. package/package.json +7 -6
  77. package/dist/exchanges/bitmart/index.cjs.map +0 -1
  78. package/dist/exchanges/bitmart/index.d.cts +0 -68
  79. package/dist/exchanges/bitmart/index.d.ts +0 -68
  80. package/dist/exchanges/bitmart/index.js.map +0 -1
@@ -59,61 +59,61 @@ var ExchangeError = class extends Error {
59
59
  exchange;
60
60
  };
61
61
 
62
- // src/exchanges/bitmart/symbols.ts
63
- var QUOTES = ["USDT", "USDC", "USD"];
64
- function toBitmartSymbol(symbol) {
62
+ // src/exchanges/deribit/symbols.ts
63
+ function toDeribitSymbol(symbol, market) {
65
64
  const [base, quote] = symbol.split("/");
66
65
  if (!base || !quote) {
67
66
  throw new Error(`invalid symbol "${symbol}" \u2014 expected "BASE/QUOTE"`);
68
67
  }
69
- return `${base}${quote}`.toUpperCase();
70
- }
71
- function fromBitmartSymbol(s) {
72
- const upper = s.toUpperCase();
73
- for (const q of QUOTES) {
74
- if (upper.endsWith(q) && upper.length > q.length) {
75
- return `${upper.slice(0, -q.length)}/${q}`;
76
- }
68
+ const pair = `${base}_${quote}`.toUpperCase();
69
+ if (market !== "perpetual") return pair;
70
+ if (quote.toUpperCase() !== "USDC") {
71
+ throw new Error(
72
+ `deribit linear perpetuals are USDC-margined; got "${quote}" (inverse contracts like BTC-PERPETUAL are unsupported)`
73
+ );
77
74
  }
78
- return upper;
75
+ return `${pair}-PERPETUAL`;
76
+ }
77
+ function fromDeribitSymbol(instrument) {
78
+ let s = instrument.toUpperCase();
79
+ if (s.endsWith("-PERPETUAL")) s = s.slice(0, -"-PERPETUAL".length);
80
+ const [base, quote] = s.split("_");
81
+ return base && quote ? `${base}/${quote}` : s;
79
82
  }
80
83
 
81
- // src/exchanges/bitmart/rest.ts
82
- var BASE = "https://api-cloud-v2.bitmart.com";
83
- async function fetchBitmartOrderbook(opts) {
84
- if (opts.market !== "perpetual") {
85
- throw new ExchangeError(
86
- "bitmart",
87
- "spot is not supported \u2014 only USDT-M perpetual"
88
- );
89
- }
90
- const symbol = toBitmartSymbol(opts.symbol);
91
- const url = `${BASE}/contract/public/depth?symbol=${symbol}`;
84
+ // src/exchanges/deribit/rest.ts
85
+ var BASE = "https://www.deribit.com";
86
+ var ALLOWED_DEPTHS = [1, 5, 10, 20, 50, 100, 1e3, 1e4];
87
+ function clampDepth(depth) {
88
+ return ALLOWED_DEPTHS.find((d) => d >= depth) ?? ALLOWED_DEPTHS[ALLOWED_DEPTHS.length - 1];
89
+ }
90
+ async function fetchDeribitOrderbook(opts) {
91
+ const instrument = toDeribitSymbol(opts.symbol, opts.market);
92
+ const depth = clampDepth(opts.depth ?? 50);
93
+ const url = `${BASE}/api/v2/public/get_order_book?instrument_name=${instrument}&depth=${depth}`;
92
94
  const res = await httpJson({
93
95
  url,
94
96
  timeoutMs: opts.timeoutMs,
95
97
  transformUrl: opts.transformUrl
96
98
  });
97
- if (res.code !== 1e3) {
98
- throw new ExchangeError("bitmart", `${res.code}: ${res.message ?? ""}`);
99
+ if (res.error) {
100
+ throw new ExchangeError(
101
+ "deribit",
102
+ `${res.error.code}: ${res.error.message}`
103
+ );
99
104
  }
105
+ const r = res.result;
106
+ if (!r) throw new ExchangeError("deribit", "empty orderbook response");
100
107
  return {
101
- exchange: "bitmart",
102
- symbol: fromBitmartSymbol(res.data.symbol),
103
- market: "perpetual",
104
- bids: parseLevels(res.data.bids),
105
- asks: parseLevels(res.data.asks),
106
- timestamp: res.data.timestamp,
107
- sequence: res.data.timestamp
108
+ exchange: "deribit",
109
+ symbol: fromDeribitSymbol(r.instrument_name ?? instrument),
110
+ market: opts.market,
111
+ bids: r.bids.map(([price, size]) => ({ price, size })),
112
+ asks: r.asks.map(([price, size]) => ({ price, size })),
113
+ timestamp: r.timestamp,
114
+ sequence: r.change_id
108
115
  };
109
116
  }
110
- function parseLevels(rows) {
111
- const out = [];
112
- for (const row of rows) {
113
- out.push({ price: Number(row[0]), size: Number(row[1]) });
114
- }
115
- return out;
116
- }
117
117
 
118
118
  // src/core/book-merger.ts
119
119
  var BookMerger = class {
@@ -321,6 +321,21 @@ var Backoff = class _Backoff {
321
321
  var hasNativeWebSocket = typeof globalThis.WebSocket !== "undefined";
322
322
 
323
323
  // src/transport/ws.ts
324
+ function toError(value, fallback = "unknown error") {
325
+ if (value instanceof Error) {
326
+ return value.message ? value : new Error(`${value.name}: ${fallback}`);
327
+ }
328
+ if (typeof value === "string" && value) return new Error(value);
329
+ return new Error(fallback);
330
+ }
331
+ function normalizeWsError(ev) {
332
+ const detail = ev?.message || (ev?.error instanceof Error ? ev.error.message : void 0) || (typeof ev?.error === "string" ? ev.error : void 0);
333
+ return new Error(detail ? `WebSocket error: ${detail}` : "WebSocket error");
334
+ }
335
+ function describeClose(ev) {
336
+ if (ev?.reason) return ev.reason;
337
+ return ev?.code === void 0 ? "closed" : `closed (code ${ev.code})`;
338
+ }
324
339
  var cachedCtor = null;
325
340
  async function getWebSocketCtor() {
326
341
  if (cachedCtor) return cachedCtor;
@@ -368,7 +383,7 @@ var WSClient = class extends TypedEmitter {
368
383
  close: () => this.close()
369
384
  });
370
385
  } catch (err) {
371
- this.emit("error", err);
386
+ this.emit("error", toError(err, "onOpen handler failed"));
372
387
  }
373
388
  };
374
389
  ws.onmessage = (e) => {
@@ -376,21 +391,21 @@ var WSClient = class extends TypedEmitter {
376
391
  try {
377
392
  result = this.cfg.onMessage(e.data);
378
393
  } catch (err) {
379
- this.emit("error", err);
394
+ this.emit("error", toError(err, "onMessage handler failed"));
380
395
  return;
381
396
  }
382
397
  if (result && typeof result.catch === "function") {
383
398
  result.catch(
384
- (err) => this.emit("error", err)
399
+ (err) => this.emit("error", toError(err, "onMessage handler failed"))
385
400
  );
386
401
  }
387
402
  };
388
403
  ws.onerror = (e) => {
389
- this.emit("error", new Error(e?.message ?? "WebSocket error"));
404
+ this.emit("error", normalizeWsError(e));
390
405
  };
391
406
  ws.onclose = (e) => {
392
407
  this.stopPing();
393
- const reason = e?.reason ?? "closed";
408
+ const reason = describeClose(e);
394
409
  this.emit("close", reason);
395
410
  if (!this.closed) this.scheduleReconnect();
396
411
  };
@@ -430,76 +445,37 @@ var WSClient = class extends TypedEmitter {
430
445
  }
431
446
  this.emit("reconnecting", next.attempt, next.wait);
432
447
  setTimeout(() => {
433
- this.connect().catch((e) => this.emit("error", e));
448
+ this.connect().catch((e) => this.emit("error", toError(e, "reconnect failed")));
434
449
  }, next.wait);
435
450
  }
436
451
  };
437
452
 
438
- // src/utils/gzip.ts
439
- var cached = null;
440
- var pending = null;
441
- function getGzipInflate() {
442
- if (cached) return Promise.resolve(cached);
443
- if (pending) return pending;
444
- pending = (async () => {
445
- let pako;
446
- try {
447
- pako = await import('pako');
448
- } catch (err) {
449
- throw new Error(
450
- "gzip decompression requires the `pako` peer dependency. " + (err instanceof Error ? err.message : String(err))
451
- );
452
- }
453
- const inflate = (data) => {
454
- const buf = data instanceof ArrayBuffer ? new Uint8Array(data) : data;
455
- try {
456
- return pako.ungzip(buf, { to: "string" });
457
- } catch {
458
- return pako.inflate(buf, { to: "string" });
459
- }
460
- };
461
- cached = inflate;
462
- return inflate;
463
- })();
464
- return pending;
465
- }
466
-
467
- // src/exchanges/bitmart/ws.ts
468
- var WS_FUTURES = "wss://openapi-ws-v2.bitmart.com/api?protocol=1.1";
469
- function streamBitmartOrderbook(opts) {
470
- if (opts.market !== "perpetual") {
471
- throw new ExchangeError(
472
- "bitmart",
473
- "spot is not supported \u2014 only USDT-M perpetual"
474
- );
475
- }
476
- const symbol = toBitmartSymbol(opts.symbol);
477
- const level = snapLevel(opts.depth);
478
- const interval = opts.interval ?? "200ms";
479
- const channelArg = `futures/depthAll${level}:${symbol}@${interval}`;
453
+ // src/exchanges/deribit/ws.ts
454
+ var WS_PUBLIC = "wss://www.deribit.com/ws/api/v2";
455
+ var HEARTBEAT_SECONDS = 30;
456
+ function streamDeribitOrderbook(opts) {
457
+ const instrument = toDeribitSymbol(opts.symbol, opts.market);
458
+ const channel = `book.${instrument}.${opts.interval ?? "100ms"}`;
480
459
  const merger = new BookMerger();
481
460
  let ws = null;
461
+ let sock = null;
462
+ let recovering = false;
482
463
  const stream = new OrderbookStream(() => ws?.close());
483
- const subscribePayload = JSON.stringify({
484
- action: "subscribe",
485
- args: [channelArg]
486
- });
487
- const pingPayload = () => JSON.stringify({ action: "ping" });
488
- const handleFrame = (frame) => {
489
- if (frame.symbol !== symbol) return;
490
- const event = {
491
- kind: "snapshot",
492
- bids: parseLevels2(frame.bids),
493
- asks: parseLevels2(frame.asks),
494
- sequence: frame.ms_t,
495
- timestamp: frame.ms_t
496
- };
497
- const r = merger.apply(event);
498
- if (!r.ok) return;
464
+ let nextId = 1;
465
+ const subscribeIds = /* @__PURE__ */ new Set();
466
+ const rpc = (method, params) => {
467
+ const id = nextId++;
468
+ sock?.send(JSON.stringify({ jsonrpc: "2.0", id, method, params }));
469
+ return id;
470
+ };
471
+ const subscribe = () => {
472
+ subscribeIds.add(rpc("public/subscribe", { channels: [channel] }));
473
+ };
474
+ const emit = (r) => {
499
475
  const book = {
500
- exchange: "bitmart",
501
- symbol: fromBitmartSymbol(symbol),
502
- market: "perpetual",
476
+ exchange: "deribit",
477
+ symbol: fromDeribitSymbol(instrument),
478
+ market: opts.market,
503
479
  bids: r.bids,
504
480
  asks: r.asks,
505
481
  timestamp: r.timestamp,
@@ -507,41 +483,101 @@ function streamBitmartOrderbook(opts) {
507
483
  };
508
484
  stream.emit("update", book);
509
485
  };
510
- const handlePayload = (text) => {
486
+ const resync = () => {
487
+ if (recovering) return;
488
+ recovering = true;
489
+ merger.reset();
490
+ rpc("public/unsubscribe", { channels: [channel] });
491
+ subscribe();
492
+ };
493
+ const toLevels = (entries) => entries.map(([action, price, amount]) => ({
494
+ price,
495
+ size: action === "delete" ? 0 : amount
496
+ }));
497
+ const buildEvent = (d) => {
498
+ const bids = toLevels(d.bids);
499
+ const asks = toLevels(d.asks);
500
+ if (d.type === "snapshot") {
501
+ return {
502
+ kind: "snapshot",
503
+ bids,
504
+ asks,
505
+ sequence: d.change_id,
506
+ timestamp: d.timestamp
507
+ };
508
+ }
509
+ return {
510
+ kind: "delta",
511
+ bids,
512
+ asks,
513
+ sequence: d.change_id,
514
+ prevSequence: d.prev_change_id,
515
+ timestamp: d.timestamp
516
+ };
517
+ };
518
+ const handleData = (d) => {
519
+ if (d.type === "snapshot") recovering = false;
520
+ else if (recovering) return;
521
+ const r = merger.apply(buildEvent(d));
522
+ if (r.ok) {
523
+ emit(r);
524
+ } else if (r.reason === "gap" || r.reason === "no-snapshot") {
525
+ resync();
526
+ }
527
+ };
528
+ const onMessage = (raw) => {
529
+ if (typeof raw !== "string") return;
511
530
  let msg;
512
531
  try {
513
- msg = JSON.parse(text);
532
+ msg = JSON.parse(raw);
514
533
  } catch {
515
534
  return;
516
535
  }
517
- if (msg.action === "subscribe" || msg.action === "pong") return;
518
- if (msg.errorCode) {
519
- stream.emit(
520
- "error",
521
- new Error(`bitmart: ${msg.errorCode} ${msg.errorMessage ?? ""}`)
522
- );
536
+ if (msg.method === "heartbeat") {
537
+ if (msg.params?.type === "test_request") rpc("public/test", {});
523
538
  return;
524
539
  }
525
- if (msg.group !== channelArg || !msg.data) return;
526
- handleFrame(msg.data);
527
- };
528
- const onMessage = async (raw) => {
529
- if (typeof raw === "string") {
530
- handlePayload(raw);
540
+ if (msg.method === "subscription") {
541
+ if (msg.params?.channel !== channel || !msg.params.data) return;
542
+ handleData(msg.params.data);
531
543
  return;
532
544
  }
533
- const inflate = await getGzipInflate();
534
- handlePayload(inflate(raw));
545
+ if (msg.id !== void 0) {
546
+ if (msg.error) {
547
+ stream.emit(
548
+ "error",
549
+ new ExchangeError(
550
+ "deribit",
551
+ `${msg.error.code}: ${msg.error.message}`
552
+ )
553
+ );
554
+ return;
555
+ }
556
+ if (subscribeIds.delete(msg.id)) {
557
+ const channels = Array.isArray(msg.result) ? msg.result : [];
558
+ if (!channels.includes(channel)) {
559
+ stream.emit(
560
+ "error",
561
+ new ExchangeError(
562
+ "deribit",
563
+ `subscribe rejected for "${channel}" \u2014 unknown instrument or interval`
564
+ )
565
+ );
566
+ }
567
+ }
568
+ }
535
569
  };
536
570
  ws = new WSClient({
537
- url: WS_FUTURES,
538
- onOpen: (sock) => {
571
+ url: WS_PUBLIC,
572
+ onOpen: (handle) => {
573
+ sock = handle;
539
574
  merger.reset();
540
- sock.send(subscribePayload);
575
+ recovering = false;
576
+ subscribeIds.clear();
577
+ rpc("public/set_heartbeat", { interval: HEARTBEAT_SECONDS });
578
+ subscribe();
541
579
  },
542
- onMessage,
543
- pingIntervalMs: 15e3,
544
- pingPayload
580
+ onMessage
545
581
  });
546
582
  ws.on("open", () => stream.emit("connected"));
547
583
  ws.on("close", (r) => stream.emit("disconnected", r));
@@ -550,21 +586,10 @@ function streamBitmartOrderbook(opts) {
550
586
  ws.connect().catch((e) => stream.emit("error", e));
551
587
  return stream;
552
588
  }
553
- function snapLevel(depth) {
554
- if (depth === 5) return 5;
555
- if (depth === 20) return 20;
556
- return 50;
557
- }
558
- function parseLevels2(rows) {
559
- if (!rows) return [];
560
- const out = [];
561
- for (const r of rows) out.push({ price: Number(r.price), size: Number(r.vol) });
562
- return out;
563
- }
564
589
 
565
- // src/exchanges/bitmart/index.ts
566
- var BitmartClient = class {
567
- exchange = "bitmart";
590
+ // src/exchanges/deribit/index.ts
591
+ var DeribitClient = class {
592
+ exchange = "deribit";
568
593
  transformUrl;
569
594
  timeoutMs;
570
595
  constructor(opts = {}) {
@@ -573,7 +598,7 @@ var BitmartClient = class {
573
598
  }
574
599
  fetchOrderbook(symbol, opts = {}) {
575
600
  const { pair, market } = parseSymbol(symbol);
576
- return fetchBitmartOrderbook({
601
+ return fetchDeribitOrderbook({
577
602
  symbol: pair,
578
603
  market,
579
604
  depth: opts.depth,
@@ -581,19 +606,20 @@ var BitmartClient = class {
581
606
  transformUrl: this.transformUrl
582
607
  });
583
608
  }
584
- streamOrderbook(symbol, _opts = {}) {
609
+ streamOrderbook(symbol, opts = {}) {
585
610
  const { pair, market } = parseSymbol(symbol);
586
- return streamBitmartOrderbook({
611
+ return streamDeribitOrderbook({
587
612
  symbol: pair,
588
613
  market,
614
+ depth: opts.depth,
589
615
  transformUrl: this.transformUrl,
590
616
  timeoutMs: this.timeoutMs
591
617
  });
592
618
  }
593
619
  };
594
620
 
595
- exports.BitmartClient = BitmartClient;
596
- exports.fetchBitmartOrderbook = fetchBitmartOrderbook;
597
- exports.streamBitmartOrderbook = streamBitmartOrderbook;
621
+ exports.DeribitClient = DeribitClient;
622
+ exports.fetchDeribitOrderbook = fetchDeribitOrderbook;
623
+ exports.streamDeribitOrderbook = streamDeribitOrderbook;
598
624
  //# sourceMappingURL=index.cjs.map
599
625
  //# sourceMappingURL=index.cjs.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../../../src/core/symbol.ts","../../../src/transport/http.ts","../../../src/core/errors.ts","../../../src/exchanges/deribit/symbols.ts","../../../src/exchanges/deribit/rest.ts","../../../src/core/book-merger.ts","../../../src/core/event-emitter.ts","../../../src/core/stream.ts","../../../src/core/reconnect.ts","../../../src/utils/runtime.ts","../../../src/transport/ws.ts","../../../src/exchanges/deribit/ws.ts","../../../src/exchanges/deribit/index.ts"],"names":[],"mappings":"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","file":"index.cjs","sourcesContent":["import type { Market } from \"./types.js\";\n\nexport interface ParsedSymbol {\n base: string;\n quote: string;\n market: Market;\n /** \"BASE/QUOTE\" — settlement suffix stripped. Pass this to per-exchange formatters. */\n pair: string;\n}\n\n/**\n * CCXT-style unified symbol:\n * \"BTC/USDT\" → spot\n * \"BTC/USDT:USDT\" → linear perpetual (USDT-margined)\n *\n * Inverse/coin-margined contracts (e.g. \"BTC/USD:BTC\") are intentionally\n * unsupported by this lib.\n */\nexport function parseSymbol(symbol: string): ParsedSymbol {\n const [pair, settle] = symbol.split(\":\");\n const parts = (pair ?? \"\").split(\"/\");\n const base = parts[0]?.toUpperCase();\n const quote = parts[1]?.toUpperCase();\n if (!base || !quote) {\n throw new Error(\n `invalid symbol \"${symbol}\" — expected \"BASE/QUOTE\" or \"BASE/QUOTE:SETTLE\"`\n );\n }\n if (settle && settle.toUpperCase() !== quote) {\n throw new Error(\n `unsupported settlement currency in \"${symbol}\" — only linear (settle === quote) is supported`\n );\n }\n return {\n base,\n quote,\n market: settle ? \"perpetual\" : \"spot\",\n pair: `${base}/${quote}`,\n };\n}\n","export interface HttpRequest {\n url: string;\n method?: \"GET\" | \"POST\";\n headers?: Record<string, string>;\n body?: string;\n timeoutMs?: number;\n /** Applied to `url` before the request fires. Use for CORS proxying. */\n transformUrl?: (url: string) => string;\n}\n\nexport async function httpJson<T>(req: HttpRequest): Promise<T> {\n const url = req.transformUrl ? req.transformUrl(req.url) : req.url;\n const controller = new AbortController();\n const timer = setTimeout(() => controller.abort(), req.timeoutMs ?? 10_000);\n try {\n const res = await fetch(url, {\n method: req.method ?? \"GET\",\n headers: req.headers,\n body: req.body,\n signal: controller.signal,\n });\n if (!res.ok) {\n const text = await res.text().catch(() => \"\");\n throw new Error(`HTTP ${res.status} ${res.statusText}: ${text.slice(0, 200)}`);\n }\n return (await res.json()) as T;\n } finally {\n clearTimeout(timer);\n }\n}\n","export class ExchangeError extends Error {\n constructor(\n public readonly exchange: string,\n message: string,\n cause?: unknown\n ) {\n super(\n `[${exchange}] ${message}`,\n cause === undefined ? undefined : { cause }\n );\n this.name = \"ExchangeError\";\n }\n}\n\nexport class RateLimitError extends ExchangeError {\n constructor(exchange: string, message = \"rate limited\") {\n super(exchange, message);\n this.name = \"RateLimitError\";\n }\n}\n\nexport class SequenceGapError extends Error {\n constructor(\n public readonly expected: number,\n public readonly received: number\n ) {\n super(`sequence gap: expected ${expected}, received ${received}`);\n this.name = \"SequenceGapError\";\n }\n}\n","import type { Market } from \"../../core/types.js\";\n\n/**\n * Deribit instrument names:\n * Spot: BTC_USDC, XRP_USDC, USDC_USDT\n * Linear perpetual: BTC_USDC-PERPETUAL, SOL_USDC-PERPETUAL\n *\n * Every linear perpetual on Deribit is USDC-margined — the only non-USDC\n * perps are `BTC-PERPETUAL` / `ETH-PERPETUAL`, which are inverse (amounts\n * quoted in USD) and therefore out of scope for this lib, same as the other\n * coin-margined contracts. Non-USDC perp quotes are rejected here rather than\n * failing later as a silent empty subscription.\n */\nexport function toDeribitSymbol(symbol: string, market: Market): string {\n const [base, quote] = symbol.split(\"/\");\n if (!base || !quote) {\n throw new Error(`invalid symbol \"${symbol}\" — expected \"BASE/QUOTE\"`);\n }\n const pair = `${base}_${quote}`.toUpperCase();\n if (market !== \"perpetual\") return pair;\n if (quote.toUpperCase() !== \"USDC\") {\n throw new Error(\n `deribit linear perpetuals are USDC-margined; got \"${quote}\" ` +\n `(inverse contracts like BTC-PERPETUAL are unsupported)`\n );\n }\n return `${pair}-PERPETUAL`;\n}\n\nexport function fromDeribitSymbol(instrument: string): string {\n let s = instrument.toUpperCase();\n if (s.endsWith(\"-PERPETUAL\")) s = s.slice(0, -\"-PERPETUAL\".length);\n const [base, quote] = s.split(\"_\");\n return base && quote ? `${base}/${quote}` : s;\n}\n","import { httpJson } from \"../../transport/http.js\";\nimport { ExchangeError } from \"../../core/errors.js\";\nimport type { Market, Orderbook } from \"../../core/types.js\";\nimport { fromDeribitSymbol, toDeribitSymbol } from \"./symbols.js\";\nimport type { DeribitOrderbookResult, DeribitRpcResponse } from \"./types.js\";\n\nexport interface FetchDeribitOrderbookOptions {\n symbol: string;\n market: Market;\n depth?: number;\n timeoutMs?: number;\n transformUrl?: (url: string) => string;\n}\n\nconst BASE = \"https://www.deribit.com\";\n\n/** Deribit only accepts these depths; anything else is a 400. */\nconst ALLOWED_DEPTHS = [1, 5, 10, 20, 50, 100, 1000, 10000];\n\nfunction clampDepth(depth: number): number {\n return (\n ALLOWED_DEPTHS.find((d) => d >= depth) ??\n ALLOWED_DEPTHS[ALLOWED_DEPTHS.length - 1]!\n );\n}\n\nexport async function fetchDeribitOrderbook(\n opts: FetchDeribitOrderbookOptions\n): Promise<Orderbook> {\n const instrument = toDeribitSymbol(opts.symbol, opts.market);\n const depth = clampDepth(opts.depth ?? 50);\n const url =\n `${BASE}/api/v2/public/get_order_book` +\n `?instrument_name=${instrument}&depth=${depth}`;\n\n const res = await httpJson<DeribitRpcResponse<DeribitOrderbookResult>>({\n url,\n timeoutMs: opts.timeoutMs,\n transformUrl: opts.transformUrl,\n });\n\n if (res.error) {\n throw new ExchangeError(\n \"deribit\",\n `${res.error.code}: ${res.error.message}`\n );\n }\n const r = res.result;\n if (!r) throw new ExchangeError(\"deribit\", \"empty orderbook response\");\n\n return {\n exchange: \"deribit\",\n symbol: fromDeribitSymbol(r.instrument_name ?? instrument),\n market: opts.market,\n bids: r.bids.map(([price, size]) => ({ price, size })),\n asks: r.asks.map(([price, size]) => ({ price, size })),\n timestamp: r.timestamp,\n sequence: r.change_id,\n };\n}\n","import type { BookEvent, OrderbookLevel } from \"./types.js\";\n\n/**\n * BookMerger maintains a sorted orderbook from a stream of snapshot and delta\n * events. It is exchange-agnostic — adapters translate raw WS frames into the\n * normalized BookEvent shape and feed them here.\n *\n * Gap detection:\n * - If the delta carries a `prevSequence`, we check it equals our current sequence.\n * - Otherwise we expect strictly consecutive sequence numbers.\n * - On gap, the caller is expected to re-fetch a REST snapshot, call reset(),\n * then apply the snapshot.\n */\n\nexport type MergeResult =\n | {\n ok: true;\n bids: OrderbookLevel[]; // descending\n asks: OrderbookLevel[]; // ascending\n sequence: number;\n timestamp: number;\n }\n | { ok: false; reason: \"gap\"; expected: number; received: number }\n | { ok: false; reason: \"no-snapshot\" };\n\nexport class BookMerger {\n private readonly bids = new Map<number, number>(); // price -> size\n private readonly asks = new Map<number, number>();\n private sequence = -1;\n private timestamp = 0;\n private hasSnapshot = false;\n\n apply(event: BookEvent): MergeResult {\n if (event.kind === \"snapshot\") {\n this.bids.clear();\n this.asks.clear();\n for (const { price, size } of event.bids) {\n if (size > 0) this.bids.set(price, size);\n }\n for (const { price, size } of event.asks) {\n if (size > 0) this.asks.set(price, size);\n }\n this.sequence = event.sequence;\n this.timestamp = event.timestamp;\n this.hasSnapshot = true;\n return this.emit();\n }\n\n if (!this.hasSnapshot) {\n return { ok: false, reason: \"no-snapshot\" };\n }\n\n // Stale or duplicate delta: silently ignore but emit current state.\n if (event.sequence <= this.sequence) {\n return this.emit();\n }\n\n // Gap detection\n if (event.prevSequence !== undefined) {\n if (event.prevSequence !== this.sequence) {\n return {\n ok: false,\n reason: \"gap\",\n expected: this.sequence,\n received: event.prevSequence,\n };\n }\n } else if (event.sequence !== this.sequence + 1) {\n return {\n ok: false,\n reason: \"gap\",\n expected: this.sequence + 1,\n received: event.sequence,\n };\n }\n\n for (const { price, size } of event.bids) {\n if (size === 0) this.bids.delete(price);\n else this.bids.set(price, size);\n }\n for (const { price, size } of event.asks) {\n if (size === 0) this.asks.delete(price);\n else this.asks.set(price, size);\n }\n this.sequence = event.sequence;\n this.timestamp = event.timestamp;\n return this.emit();\n }\n\n reset(): void {\n this.bids.clear();\n this.asks.clear();\n this.sequence = -1;\n this.timestamp = 0;\n this.hasSnapshot = false;\n }\n\n /** Returns true once the merger has a usable book. */\n isReady(): boolean {\n return this.hasSnapshot;\n }\n\n private emit(): Extract<MergeResult, { ok: true }> {\n const bids: OrderbookLevel[] = [];\n for (const [price, size] of this.bids) bids.push({ price, size });\n bids.sort((a, b) => b.price - a.price);\n\n const asks: OrderbookLevel[] = [];\n for (const [price, size] of this.asks) asks.push({ price, size });\n asks.sort((a, b) => a.price - b.price);\n\n return {\n ok: true,\n bids,\n asks,\n sequence: this.sequence,\n timestamp: this.timestamp,\n };\n }\n}\n","/**\n * Tiny typed event emitter — keeps the package dependency-free across runtimes\n * (Node's `events` doesn't work in browser bundles without polyfills).\n */\n// eslint-disable-next-line @typescript-eslint/no-explicit-any\ntype EventMap = Record<string, (...args: any[]) => void>;\n\nexport class TypedEmitter<E extends EventMap> {\n private readonly listeners = new Map<keyof E, Set<E[keyof E]>>();\n\n on<K extends keyof E>(event: K, fn: E[K]): this {\n let set = this.listeners.get(event);\n if (!set) {\n set = new Set();\n this.listeners.set(event, set as Set<E[keyof E]>);\n }\n (set as Set<E[K]>).add(fn);\n return this;\n }\n\n off<K extends keyof E>(event: K, fn: E[K]): this {\n (this.listeners.get(event) as Set<E[K]> | undefined)?.delete(fn);\n return this;\n }\n\n emit<K extends keyof E>(event: K, ...args: Parameters<E[K]>): boolean {\n const set = this.listeners.get(event) as Set<E[K]> | undefined;\n if (!set || set.size === 0) return false;\n for (const fn of set) fn(...args);\n return true;\n }\n\n removeAllListeners(): void {\n this.listeners.clear();\n }\n}\n","import { TypedEmitter } from \"./event-emitter.js\";\nimport type { Orderbook } from \"./types.js\";\n\nexport type StreamEvents = {\n update: (book: Orderbook) => void;\n error: (err: Error) => void;\n connected: () => void;\n disconnected: (reason?: string) => void;\n reconnecting: (attempt: number, waitMs: number) => void;\n};\n\n/**\n * OrderbookStream is the consumer-facing handle for a maintained orderbook.\n * It exposes both an EventEmitter API and an async-iterator (`for await`).\n */\nexport class OrderbookStream extends TypedEmitter<StreamEvents> {\n private closed = false;\n\n constructor(private readonly onClose: () => void) {\n super();\n }\n\n close(): void {\n if (this.closed) return;\n this.closed = true;\n this.onClose();\n this.removeAllListeners();\n }\n\n /** Yields each maintained book as it becomes available. */\n async *iter(): AsyncGenerator<Orderbook, void, undefined> {\n const queue: Orderbook[] = [];\n let resolveNext: (() => void) | null = null;\n let pendingError: Error | null = null;\n let ended = false;\n\n const onUpdate = (b: Orderbook) => {\n queue.push(b);\n resolveNext?.();\n };\n const onError = (e: Error) => {\n pendingError = e;\n resolveNext?.();\n };\n const onClose = () => {\n ended = true;\n resolveNext?.();\n };\n\n this.on(\"update\", onUpdate);\n this.on(\"error\", onError);\n this.on(\"disconnected\", onClose);\n\n try {\n while (true) {\n if (pendingError) throw pendingError;\n const next = queue.shift();\n if (next) {\n yield next;\n continue;\n }\n if (ended || this.closed) return;\n await new Promise<void>((r) => {\n resolveNext = r;\n });\n resolveNext = null;\n }\n } finally {\n this.off(\"update\", onUpdate);\n this.off(\"error\", onError);\n this.off(\"disconnected\", onClose);\n }\n }\n}\n","export interface BackoffOptions {\n initialMs?: number;\n maxMs?: number;\n factor?: number;\n /** 0..1 — random jitter as a fraction of the delay. */\n jitter?: number;\n /** 0 = infinite. */\n maxAttempts?: number;\n}\n\ninterface ResolvedBackoff {\n initialMs: number;\n maxMs: number;\n factor: number;\n jitter: number;\n maxAttempts: number;\n}\n\nexport class Backoff {\n private attempt = 0;\n private readonly opts: ResolvedBackoff;\n\n private constructor(opts: ResolvedBackoff) {\n this.opts = opts;\n }\n\n static withDefaults(opts: BackoffOptions = {}): Backoff {\n return new Backoff({\n initialMs: opts.initialMs ?? 500,\n maxMs: opts.maxMs ?? 30_000,\n factor: opts.factor ?? 2,\n jitter: opts.jitter ?? 0.3,\n maxAttempts: opts.maxAttempts ?? 0,\n });\n }\n\n next(): { wait: number; attempt: number } | null {\n if (this.opts.maxAttempts > 0 && this.attempt >= this.opts.maxAttempts) {\n return null;\n }\n this.attempt += 1;\n const base = Math.min(\n this.opts.initialMs * Math.pow(this.opts.factor, this.attempt - 1),\n this.opts.maxMs\n );\n const jitterRange = base * this.opts.jitter;\n const wait = Math.max(0, base + (Math.random() * 2 - 1) * jitterRange);\n return { wait, attempt: this.attempt };\n }\n\n reset(): void {\n this.attempt = 0;\n }\n}\n","declare const globalThis: typeof global & {\n window?: unknown;\n document?: unknown;\n WebSocket?: unknown;\n};\n\nexport const isBrowser =\n typeof globalThis.window !== \"undefined\" &&\n typeof globalThis.document !== \"undefined\";\n\nexport const hasNativeWebSocket = typeof globalThis.WebSocket !== \"undefined\";\n","import { TypedEmitter } from \"../core/event-emitter.js\";\nimport { Backoff, type BackoffOptions } from \"../core/reconnect.js\";\nimport { hasNativeWebSocket } from \"../utils/runtime.js\";\n\nexport interface WSConfig {\n /**\n * WebSocket URL, or a function resolved on every (re)connect. The function\n * form lets adapters that need a fresh handshake token (e.g. KuCoin's bullet\n * endpoint) hand out a new URL each time without ditching WSClient.\n */\n url: string | (() => string | Promise<string>);\n /** Called every time the socket opens (initial + after each reconnect). */\n onOpen?: (sock: SocketHandle) => void | Promise<void>;\n /** Called for every inbound frame. Adapter is responsible for JSON-parsing. */\n onMessage: (data: string | ArrayBuffer) => void | Promise<void>;\n /** Periodic ping; omit to disable. */\n pingIntervalMs?: number;\n pingPayload?: () => string;\n reconnect?: BackoffOptions;\n}\n\nexport interface SocketHandle {\n send(data: string): void;\n close(): void;\n}\n\ntype WSEvents = {\n open: () => void;\n close: (reason: string) => void;\n reconnecting: (attempt: number, waitMs: number) => void;\n error: (err: Error) => void;\n};\n\ninterface WebSocketLike {\n binaryType: string;\n send(data: string): void;\n close(): void;\n onopen: ((ev?: unknown) => void) | null;\n onclose: ((ev?: { reason?: string; code?: number }) => void) | null;\n onerror: ((ev?: WSErrorEventLike) => void) | null;\n onmessage: ((ev: { data: string | ArrayBuffer }) => void) | null;\n}\n\n/**\n * Shape of what lands in `onerror` across runtimes:\n * - browsers: a bare `Event` (no `message`), or an `ErrorEvent` whose\n * `message` defaults to the **empty string** — which is why the fallbacks\n * below test truthiness, not nullishness. `?? \"...\"` lets `\"\"` through and\n * the consumer ends up rendering a blank error.\n * - node (native/undici) + `ws`: `ErrorEvent` with `message` + `error`.\n */\ninterface WSErrorEventLike {\n message?: string;\n type?: string;\n error?: unknown;\n}\n\n/** Never returns an Error with an empty message. */\nexport function toError(value: unknown, fallback = \"unknown error\"): Error {\n if (value instanceof Error) {\n return value.message ? value : new Error(`${value.name}: ${fallback}`);\n }\n if (typeof value === \"string\" && value) return new Error(value);\n return new Error(fallback);\n}\n\n/** Turns a WebSocket `error` event into an Error that always says something. */\nexport function normalizeWsError(ev: WSErrorEventLike | undefined): Error {\n const detail =\n ev?.message ||\n (ev?.error instanceof Error ? ev.error.message : undefined) ||\n (typeof ev?.error === \"string\" ? ev.error : undefined);\n return new Error(detail ? `WebSocket error: ${detail}` : \"WebSocket error\");\n}\n\n/** Close events routinely carry `reason: \"\"` — surface the code instead. */\nexport function describeClose(ev: { reason?: string; code?: number } | undefined): string {\n if (ev?.reason) return ev.reason;\n return ev?.code === undefined ? \"closed\" : `closed (code ${ev.code})`;\n}\n\ntype WebSocketCtor = new (url: string) => WebSocketLike;\n\nlet cachedCtor: WebSocketCtor | null = null;\n\nasync function getWebSocketCtor(): Promise<WebSocketCtor> {\n if (cachedCtor) return cachedCtor;\n if (hasNativeWebSocket) {\n cachedCtor = (globalThis as unknown as { WebSocket: WebSocketCtor })\n .WebSocket;\n return cachedCtor;\n }\n try {\n const mod = (await import(\"ws\")) as unknown as {\n default?: WebSocketCtor;\n WebSocket?: WebSocketCtor;\n };\n const ctor = mod.default ?? mod.WebSocket;\n if (!ctor) throw new Error(\"missing default export\");\n cachedCtor = ctor;\n return cachedCtor;\n } catch (err) {\n throw new Error(\n \"WebSocket unavailable; install peer dep `ws` for Node <22. \" +\n (err instanceof Error ? err.message : String(err))\n );\n }\n}\n\nexport class WSClient extends TypedEmitter<WSEvents> {\n private ws: WebSocketLike | null = null;\n private readonly backoff: Backoff;\n private closed = false;\n private pingTimer: ReturnType<typeof setInterval> | null = null;\n\n constructor(private readonly cfg: WSConfig) {\n super();\n this.backoff = Backoff.withDefaults(cfg.reconnect);\n }\n\n async connect(): Promise<void> {\n if (this.closed) return;\n const WS = await getWebSocketCtor();\n const url =\n typeof this.cfg.url === \"function\" ? await this.cfg.url() : this.cfg.url;\n const ws = new WS(url);\n ws.binaryType = \"arraybuffer\";\n this.ws = ws;\n\n ws.onopen = async () => {\n this.backoff.reset();\n this.startPing();\n this.emit(\"open\");\n try {\n await this.cfg.onOpen?.({\n send: (d) => ws.send(d),\n close: () => this.close(),\n });\n } catch (err) {\n this.emit(\"error\", toError(err, \"onOpen handler failed\"));\n }\n };\n\n ws.onmessage = (e) => {\n let result: void | Promise<void>;\n try {\n result = this.cfg.onMessage(e.data);\n } catch (err) {\n this.emit(\"error\", toError(err, \"onMessage handler failed\"));\n return;\n }\n if (result && typeof (result as Promise<void>).catch === \"function\") {\n (result as Promise<void>).catch((err) =>\n this.emit(\"error\", toError(err, \"onMessage handler failed\"))\n );\n }\n };\n\n ws.onerror = (e) => {\n this.emit(\"error\", normalizeWsError(e));\n };\n\n ws.onclose = (e) => {\n this.stopPing();\n const reason = describeClose(e);\n this.emit(\"close\", reason);\n if (!this.closed) this.scheduleReconnect();\n };\n }\n\n send(data: string): void {\n this.ws?.send(data);\n }\n\n close(): void {\n this.closed = true;\n this.stopPing();\n try {\n this.ws?.close();\n } catch {\n /* ignore */\n }\n }\n\n private startPing(): void {\n if (!this.cfg.pingIntervalMs || !this.cfg.pingPayload) return;\n this.stopPing();\n this.pingTimer = setInterval(() => {\n try {\n this.ws?.send(this.cfg.pingPayload!());\n } catch {\n /* ignore — close handler will reconnect */\n }\n }, this.cfg.pingIntervalMs);\n }\n\n private stopPing(): void {\n if (this.pingTimer) {\n clearInterval(this.pingTimer);\n this.pingTimer = null;\n }\n }\n\n private scheduleReconnect(): void {\n const next = this.backoff.next();\n if (!next) {\n this.emit(\"error\", new Error(\"max reconnect attempts exceeded\"));\n return;\n }\n this.emit(\"reconnecting\", next.attempt, next.wait);\n setTimeout(() => {\n this.connect().catch((e) => this.emit(\"error\", toError(e, \"reconnect failed\")));\n }, next.wait);\n }\n}\n","import { BookMerger } from \"../../core/book-merger.js\";\nimport { ExchangeError } from \"../../core/errors.js\";\nimport { OrderbookStream } from \"../../core/stream.js\";\nimport type { BookEvent, Market, Orderbook } from \"../../core/types.js\";\nimport { WSClient, type SocketHandle } from \"../../transport/ws.js\";\nimport { fromDeribitSymbol, toDeribitSymbol } from \"./symbols.js\";\nimport type {\n DeribitBookData,\n DeribitBookEntry,\n DeribitWSMessage,\n} from \"./types.js\";\n\nexport interface StreamDeribitOrderbookOptions {\n symbol: string;\n market: Market;\n depth?: number;\n /**\n * Book channel interval. `\"raw\"` is per-change and only available to\n * authenticated clients, so the public default is `\"100ms\"`; `\"agg2\"` is\n * the 2-second aggregate.\n */\n interval?: \"100ms\" | \"agg2\" | \"raw\";\n transformUrl?: (url: string) => string;\n timeoutMs?: number;\n}\n\nconst WS_PUBLIC = \"wss://www.deribit.com/ws/api/v2\";\n/** Server pushes `test_request` on this cadence; 10s is Deribit's minimum. */\nconst HEARTBEAT_SECONDS = 30;\n\n/**\n * Deribit `book.{instrument}.{interval}` channel — JSON-RPC 2.0 over WS.\n *\n * The first frame is `type: \"snapshot\"`, every later frame is\n * `type: \"change\"` carrying `prev_change_id`, which chains exactly to the\n * previous frame's `change_id`. That maps straight onto the merger's\n * `prevSequence === current` check, so no REST seed is needed — on a gap we\n * unsubscribe + resubscribe and the server replays a fresh snapshot.\n *\n * Levels arrive as `[action, price, amount]`; `delete` means remove the level,\n * which we hand to the merger as size 0.\n */\nexport function streamDeribitOrderbook(\n opts: StreamDeribitOrderbookOptions\n): OrderbookStream {\n const instrument = toDeribitSymbol(opts.symbol, opts.market);\n const channel = `book.${instrument}.${opts.interval ?? \"100ms\"}`;\n\n const merger = new BookMerger();\n let ws: WSClient | null = null;\n let sock: SocketHandle | null = null;\n let recovering = false;\n const stream = new OrderbookStream(() => ws?.close());\n\n let nextId = 1;\n /** Request ids we expect a subscribe result for. */\n const subscribeIds = new Set<number>();\n\n const rpc = (method: string, params: Record<string, unknown>): number => {\n const id = nextId++;\n sock?.send(JSON.stringify({ jsonrpc: \"2.0\", id, method, params }));\n return id;\n };\n\n const subscribe = () => {\n subscribeIds.add(rpc(\"public/subscribe\", { channels: [channel] }));\n };\n\n const emit = (\n r: Extract<ReturnType<BookMerger[\"apply\"]>, { ok: true }>\n ): void => {\n const book: Orderbook = {\n exchange: \"deribit\",\n symbol: fromDeribitSymbol(instrument),\n market: opts.market,\n bids: r.bids,\n asks: r.asks,\n timestamp: r.timestamp,\n sequence: r.sequence,\n };\n stream.emit(\"update\", book);\n };\n\n /** The channel only replays a snapshot on (re)subscribe — so do that. */\n const resync = () => {\n if (recovering) return;\n recovering = true;\n merger.reset();\n rpc(\"public/unsubscribe\", { channels: [channel] });\n subscribe();\n };\n\n const toLevels = (entries: DeribitBookEntry[]) =>\n entries.map(([action, price, amount]) => ({\n price,\n size: action === \"delete\" ? 0 : amount,\n }));\n\n const buildEvent = (d: DeribitBookData): BookEvent => {\n const bids = toLevels(d.bids);\n const asks = toLevels(d.asks);\n if (d.type === \"snapshot\") {\n return {\n kind: \"snapshot\",\n bids,\n asks,\n sequence: d.change_id,\n timestamp: d.timestamp,\n };\n }\n return {\n kind: \"delta\",\n bids,\n asks,\n sequence: d.change_id,\n prevSequence: d.prev_change_id,\n timestamp: d.timestamp,\n };\n };\n\n const handleData = (d: DeribitBookData) => {\n if (d.type === \"snapshot\") recovering = false;\n else if (recovering) return; // waiting on the replayed snapshot\n\n const r = merger.apply(buildEvent(d));\n if (r.ok) {\n emit(r);\n } else if (r.reason === \"gap\" || r.reason === \"no-snapshot\") {\n resync();\n }\n };\n\n const onMessage = (raw: string | ArrayBuffer) => {\n if (typeof raw !== \"string\") return;\n let msg: DeribitWSMessage;\n try {\n msg = JSON.parse(raw) as DeribitWSMessage;\n } catch {\n return;\n }\n\n // Server keepalive: `test_request` must be answered or Deribit hangs up.\n if (msg.method === \"heartbeat\") {\n if (msg.params?.type === \"test_request\") rpc(\"public/test\", {});\n return;\n }\n\n if (msg.method === \"subscription\") {\n if (msg.params?.channel !== channel || !msg.params.data) return;\n handleData(msg.params.data);\n return;\n }\n\n // JSON-RPC reply. A rejected subscribe does NOT come back as an error —\n // an unknown instrument simply yields `result: []`, which would otherwise\n // look like a healthy stream that never ticks.\n if (msg.id !== undefined) {\n if (msg.error) {\n stream.emit(\n \"error\",\n new ExchangeError(\n \"deribit\",\n `${msg.error.code}: ${msg.error.message}`\n )\n );\n return;\n }\n if (subscribeIds.delete(msg.id)) {\n const channels = Array.isArray(msg.result) ? (msg.result as string[]) : [];\n if (!channels.includes(channel)) {\n stream.emit(\n \"error\",\n new ExchangeError(\n \"deribit\",\n `subscribe rejected for \"${channel}\" — unknown instrument or interval`\n )\n );\n }\n }\n }\n };\n\n ws = new WSClient({\n url: WS_PUBLIC,\n onOpen: (handle) => {\n sock = handle;\n merger.reset();\n recovering = false;\n subscribeIds.clear();\n rpc(\"public/set_heartbeat\", { interval: HEARTBEAT_SECONDS });\n subscribe();\n },\n onMessage,\n });\n\n ws.on(\"open\", () => stream.emit(\"connected\"));\n ws.on(\"close\", (r) => stream.emit(\"disconnected\", r));\n ws.on(\"reconnecting\", (a, w) => stream.emit(\"reconnecting\", a, w));\n ws.on(\"error\", (e) => stream.emit(\"error\", e));\n ws.connect().catch((e) => stream.emit(\"error\", e as Error));\n\n return stream;\n}\n","import type {\n ClientOptions,\n Orderbook,\n OrderbookOptions,\n} from \"../../core/types.js\";\nimport { OrderbookStream } from \"../../core/stream.js\";\nimport { parseSymbol } from \"../../core/symbol.js\";\nimport { fetchDeribitOrderbook } from \"./rest.js\";\nimport { streamDeribitOrderbook } from \"./ws.js\";\n\nexport class DeribitClient {\n readonly exchange = \"deribit\" as const;\n private readonly transformUrl?: (url: string) => string;\n private readonly timeoutMs?: number;\n\n constructor(opts: ClientOptions = {}) {\n this.transformUrl = opts.transformUrl;\n this.timeoutMs = opts.timeoutMs;\n }\n\n fetchOrderbook(\n symbol: string,\n opts: OrderbookOptions = {}\n ): Promise<Orderbook> {\n const { pair, market } = parseSymbol(symbol);\n return fetchDeribitOrderbook({\n symbol: pair,\n market,\n depth: opts.depth,\n timeoutMs: this.timeoutMs,\n transformUrl: this.transformUrl,\n });\n }\n\n streamOrderbook(\n symbol: string,\n opts: OrderbookOptions = {}\n ): OrderbookStream {\n const { pair, market } = parseSymbol(symbol);\n return streamDeribitOrderbook({\n symbol: pair,\n market,\n depth: opts.depth,\n transformUrl: this.transformUrl,\n timeoutMs: this.timeoutMs,\n });\n }\n}\n\nexport { fetchDeribitOrderbook } from \"./rest.js\";\nexport { streamDeribitOrderbook } from \"./ws.js\";\n"]}
@@ -0,0 +1,48 @@
1
+ import { M as Market, a as Orderbook, c as OrderbookStream, C as ClientOptions, b as OrderbookOptions } from '../../stream-CO3LD9jf.cjs';
2
+
3
+ interface FetchDeribitOrderbookOptions {
4
+ symbol: string;
5
+ market: Market;
6
+ depth?: number;
7
+ timeoutMs?: number;
8
+ transformUrl?: (url: string) => string;
9
+ }
10
+ declare function fetchDeribitOrderbook(opts: FetchDeribitOrderbookOptions): Promise<Orderbook>;
11
+
12
+ interface StreamDeribitOrderbookOptions {
13
+ symbol: string;
14
+ market: Market;
15
+ depth?: number;
16
+ /**
17
+ * Book channel interval. `"raw"` is per-change and only available to
18
+ * authenticated clients, so the public default is `"100ms"`; `"agg2"` is
19
+ * the 2-second aggregate.
20
+ */
21
+ interval?: "100ms" | "agg2" | "raw";
22
+ transformUrl?: (url: string) => string;
23
+ timeoutMs?: number;
24
+ }
25
+ /**
26
+ * Deribit `book.{instrument}.{interval}` channel — JSON-RPC 2.0 over WS.
27
+ *
28
+ * The first frame is `type: "snapshot"`, every later frame is
29
+ * `type: "change"` carrying `prev_change_id`, which chains exactly to the
30
+ * previous frame's `change_id`. That maps straight onto the merger's
31
+ * `prevSequence === current` check, so no REST seed is needed — on a gap we
32
+ * unsubscribe + resubscribe and the server replays a fresh snapshot.
33
+ *
34
+ * Levels arrive as `[action, price, amount]`; `delete` means remove the level,
35
+ * which we hand to the merger as size 0.
36
+ */
37
+ declare function streamDeribitOrderbook(opts: StreamDeribitOrderbookOptions): OrderbookStream;
38
+
39
+ declare class DeribitClient {
40
+ readonly exchange: "deribit";
41
+ private readonly transformUrl?;
42
+ private readonly timeoutMs?;
43
+ constructor(opts?: ClientOptions);
44
+ fetchOrderbook(symbol: string, opts?: OrderbookOptions): Promise<Orderbook>;
45
+ streamOrderbook(symbol: string, opts?: OrderbookOptions): OrderbookStream;
46
+ }
47
+
48
+ export { DeribitClient, fetchDeribitOrderbook, streamDeribitOrderbook };
@@ -0,0 +1,48 @@
1
+ import { M as Market, a as Orderbook, c as OrderbookStream, C as ClientOptions, b as OrderbookOptions } from '../../stream-CO3LD9jf.js';
2
+
3
+ interface FetchDeribitOrderbookOptions {
4
+ symbol: string;
5
+ market: Market;
6
+ depth?: number;
7
+ timeoutMs?: number;
8
+ transformUrl?: (url: string) => string;
9
+ }
10
+ declare function fetchDeribitOrderbook(opts: FetchDeribitOrderbookOptions): Promise<Orderbook>;
11
+
12
+ interface StreamDeribitOrderbookOptions {
13
+ symbol: string;
14
+ market: Market;
15
+ depth?: number;
16
+ /**
17
+ * Book channel interval. `"raw"` is per-change and only available to
18
+ * authenticated clients, so the public default is `"100ms"`; `"agg2"` is
19
+ * the 2-second aggregate.
20
+ */
21
+ interval?: "100ms" | "agg2" | "raw";
22
+ transformUrl?: (url: string) => string;
23
+ timeoutMs?: number;
24
+ }
25
+ /**
26
+ * Deribit `book.{instrument}.{interval}` channel — JSON-RPC 2.0 over WS.
27
+ *
28
+ * The first frame is `type: "snapshot"`, every later frame is
29
+ * `type: "change"` carrying `prev_change_id`, which chains exactly to the
30
+ * previous frame's `change_id`. That maps straight onto the merger's
31
+ * `prevSequence === current` check, so no REST seed is needed — on a gap we
32
+ * unsubscribe + resubscribe and the server replays a fresh snapshot.
33
+ *
34
+ * Levels arrive as `[action, price, amount]`; `delete` means remove the level,
35
+ * which we hand to the merger as size 0.
36
+ */
37
+ declare function streamDeribitOrderbook(opts: StreamDeribitOrderbookOptions): OrderbookStream;
38
+
39
+ declare class DeribitClient {
40
+ readonly exchange: "deribit";
41
+ private readonly transformUrl?;
42
+ private readonly timeoutMs?;
43
+ constructor(opts?: ClientOptions);
44
+ fetchOrderbook(symbol: string, opts?: OrderbookOptions): Promise<Orderbook>;
45
+ streamOrderbook(symbol: string, opts?: OrderbookOptions): OrderbookStream;
46
+ }
47
+
48
+ export { DeribitClient, fetchDeribitOrderbook, streamDeribitOrderbook };