@xapy/orderbook 0.1.1 → 0.1.3

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dist/index.cjs CHANGED
@@ -849,9 +849,131 @@ function parseObjectLevels(rows) {
849
849
  var WS_SPOT2 = "wss://api.gateio.ws/ws/v4/";
850
850
  var WS_FUTURES = "wss://fx-ws.gateio.ws/v4/ws/usdt";
851
851
  function streamGateOrderbook(opts) {
852
- const isFutures = opts.market === "perpetual";
852
+ return opts.market === "perpetual" ? streamGateFuturesObu(opts) : streamGateSpot(opts);
853
+ }
854
+ function streamGateFuturesObu(opts) {
855
+ const gateSymbol = toGateSymbol(opts.symbol);
856
+ const level = snapFuturesLevel(opts.depth ?? 50);
857
+ const streamName = `ob.${gateSymbol}.${level}`;
858
+ const merger = new BookMerger();
859
+ let lastU = -1;
860
+ let ws = null;
861
+ const stream = new OrderbookStream(() => ws?.close());
862
+ const wsMsg = (event) => JSON.stringify({
863
+ time: Math.floor(Date.now() / 1e3),
864
+ channel: "futures.obu",
865
+ event,
866
+ payload: [streamName]
867
+ });
868
+ const pingMsg = () => JSON.stringify({
869
+ time: Math.floor(Date.now() / 1e3),
870
+ channel: "futures.ping"
871
+ });
872
+ const emitBook = (bids, asks, sequence, timestamp) => {
873
+ const book = {
874
+ exchange: "gate",
875
+ symbol: fromGateSymbol(gateSymbol),
876
+ market: "perpetual",
877
+ bids,
878
+ asks,
879
+ timestamp,
880
+ sequence
881
+ };
882
+ stream.emit("update", book);
883
+ };
884
+ const resubscribe = () => {
885
+ if (!ws) return;
886
+ merger.reset();
887
+ lastU = -1;
888
+ try {
889
+ ws.send(wsMsg("unsubscribe"));
890
+ ws.send(wsMsg("subscribe"));
891
+ } catch (err) {
892
+ stream.emit("error", err);
893
+ }
894
+ };
895
+ const processFrame = (frame) => {
896
+ const bids = parseObuLevels(frame.b);
897
+ const asks = parseObuLevels(frame.a);
898
+ if (frame.full) {
899
+ const r2 = merger.apply({
900
+ kind: "snapshot",
901
+ bids,
902
+ asks,
903
+ sequence: frame.u,
904
+ timestamp: frame.t
905
+ });
906
+ lastU = frame.u;
907
+ if (r2.ok) emitBook(r2.bids, r2.asks, r2.sequence, r2.timestamp);
908
+ return;
909
+ }
910
+ if (!merger.isReady() || lastU < 0 || frame.U === void 0) return;
911
+ if (frame.u <= lastU) return;
912
+ if (frame.U !== lastU + 1) {
913
+ resubscribe();
914
+ return;
915
+ }
916
+ const r = merger.apply({
917
+ kind: "delta",
918
+ bids,
919
+ asks,
920
+ sequence: frame.u,
921
+ prevSequence: lastU,
922
+ timestamp: frame.t
923
+ });
924
+ if (r.ok) {
925
+ lastU = frame.u;
926
+ emitBook(r.bids, r.asks, r.sequence, r.timestamp);
927
+ } else if (r.reason === "gap") {
928
+ resubscribe();
929
+ }
930
+ };
931
+ const onMessage = (raw) => {
932
+ if (typeof raw !== "string") return;
933
+ let msg;
934
+ try {
935
+ msg = JSON.parse(raw);
936
+ } catch {
937
+ return;
938
+ }
939
+ if (msg.error) {
940
+ stream.emit("error", new Error(`gate: ${msg.error.message}`));
941
+ return;
942
+ }
943
+ if (msg.channel !== "futures.obu") return;
944
+ if (msg.event === "subscribe" || msg.event === "unsubscribe") return;
945
+ if (msg.event !== "update" || !msg.result) return;
946
+ processFrame(msg.result);
947
+ };
948
+ ws = new WSClient({
949
+ url: WS_FUTURES,
950
+ onOpen: (sock) => {
951
+ merger.reset();
952
+ lastU = -1;
953
+ sock.send(wsMsg("subscribe"));
954
+ },
955
+ onMessage,
956
+ pingIntervalMs: 15e3,
957
+ pingPayload: pingMsg
958
+ });
959
+ ws.on("open", () => stream.emit("connected"));
960
+ ws.on("close", (r) => stream.emit("disconnected", r));
961
+ ws.on("reconnecting", (a, w) => stream.emit("reconnecting", a, w));
962
+ ws.on("error", (e) => stream.emit("error", e));
963
+ ws.connect().catch((e) => stream.emit("error", e));
964
+ return stream;
965
+ }
966
+ function snapFuturesLevel(depth) {
967
+ return depth <= 50 ? 50 : 400;
968
+ }
969
+ function parseObuLevels(rows) {
970
+ if (!rows) return [];
971
+ const out = [];
972
+ for (const [p, s] of rows) out.push({ price: Number(p), size: Number(s) });
973
+ return out;
974
+ }
975
+ function streamGateSpot(opts) {
853
976
  const gateSymbol = toGateSymbol(opts.symbol);
854
- const channel = isFutures ? "futures.order_book_update" : "spot.order_book_update";
855
977
  const interval = `${opts.intervalMs ?? 100}ms`;
856
978
  const depth = String(opts.depth ?? 100);
857
979
  const merger = new BookMerger();
@@ -863,22 +985,21 @@ function streamGateOrderbook(opts) {
863
985
  let recovering = false;
864
986
  let ws = null;
865
987
  const stream = new OrderbookStream(() => ws?.close());
866
- const payload = isFutures ? [gateSymbol, interval, depth] : [gateSymbol, depth, interval];
867
988
  const subscribeMsg = () => JSON.stringify({
868
989
  time: Math.floor(Date.now() / 1e3),
869
- channel,
990
+ channel: "spot.order_book_update",
870
991
  event: "subscribe",
871
- payload
992
+ payload: [gateSymbol, depth, interval]
872
993
  });
873
994
  const pingMsg = () => JSON.stringify({
874
995
  time: Math.floor(Date.now() / 1e3),
875
- channel: isFutures ? "futures.ping" : "spot.ping"
996
+ channel: "spot.ping"
876
997
  });
877
998
  const emitBook = (bids, asks, sequence, timestamp) => {
878
999
  const book = {
879
1000
  exchange: "gate",
880
1001
  symbol: fromGateSymbol(gateSymbol),
881
- market: opts.market,
1002
+ market: "spot",
882
1003
  bids,
883
1004
  asks,
884
1005
  timestamp,
@@ -891,7 +1012,7 @@ function streamGateOrderbook(opts) {
891
1012
  snapshotInFlight = true;
892
1013
  fetchGateOrderbook({
893
1014
  symbol: opts.symbol,
894
- market: opts.market,
1015
+ market: "spot",
895
1016
  depth: opts.depth ?? 100,
896
1017
  timeoutMs: opts.timeoutMs,
897
1018
  transformUrl: opts.transformUrl
@@ -967,9 +1088,16 @@ function streamGateOrderbook(opts) {
967
1088
  stream.emit("error", new Error(`gate: ${msg.error.message}`));
968
1089
  return;
969
1090
  }
970
- if (msg.channel !== channel || !msg.result) return;
1091
+ if (msg.channel !== "spot.order_book_update" || !msg.result) return;
971
1092
  if (msg.event !== "update") return;
972
- const ev = normalize(msg.result, isFutures);
1093
+ const r = msg.result;
1094
+ const ev = {
1095
+ U: r.U,
1096
+ u: r.u,
1097
+ t: r.t,
1098
+ bids: r.b.map(([p, s]) => ({ price: Number(p), size: Number(s) })),
1099
+ asks: r.a.map(([p, s]) => ({ price: Number(p), size: Number(s) }))
1100
+ };
973
1101
  if (!snapshotDone) {
974
1102
  buffered.push(ev);
975
1103
  if (!snapshotInFlight) triggerSnapshot();
@@ -978,7 +1106,7 @@ function streamGateOrderbook(opts) {
978
1106
  processDelta(ev);
979
1107
  };
980
1108
  ws = new WSClient({
981
- url: isFutures ? WS_FUTURES : WS_SPOT2,
1109
+ url: WS_SPOT2,
982
1110
  onOpen: (sock) => {
983
1111
  merger.reset();
984
1112
  snapshotSequence = -1;
@@ -998,26 +1126,6 @@ function streamGateOrderbook(opts) {
998
1126
  ws.connect().catch((e) => stream.emit("error", e));
999
1127
  return stream;
1000
1128
  }
1001
- function normalize(ev, isFutures) {
1002
- if (isFutures) {
1003
- const f = ev;
1004
- return {
1005
- U: f.U,
1006
- u: f.u,
1007
- t: f.t,
1008
- bids: f.b.map((row) => ({ price: Number(row.p), size: Number(row.s) })),
1009
- asks: f.a.map((row) => ({ price: Number(row.p), size: Number(row.s) }))
1010
- };
1011
- }
1012
- const s = ev;
1013
- return {
1014
- U: s.U,
1015
- u: s.u,
1016
- t: s.t,
1017
- bids: s.b.map(([p, sz]) => ({ price: Number(p), size: Number(sz) })),
1018
- asks: s.a.map(([p, sz]) => ({ price: Number(p), size: Number(sz) }))
1019
- };
1020
- }
1021
1129
 
1022
1130
  // src/exchanges/gate/index.ts
1023
1131
  var GateClient = class {
@@ -1058,10 +1166,6 @@ function toBingxSymbol(symbol) {
1058
1166
  }
1059
1167
  return `${base}_${quote}`.toUpperCase();
1060
1168
  }
1061
- function toBingxTopic(symbol, market, depth = 0.1) {
1062
- const prefix = market === "spot" ? "market-depth" : "swap-depth";
1063
- return `${prefix}.${toBingxSymbol(symbol)}.${depth}`;
1064
- }
1065
1169
  function fromBingxSymbol(s) {
1066
1170
  return s.toUpperCase().replace("_", "/");
1067
1171
  }
@@ -1126,9 +1230,10 @@ var WS_SPOT3 = "wss://open-api-ws.bingx.com/market";
1126
1230
  var WS_SWAP = "wss://open-api-swap.bingx.com/swap-market";
1127
1231
  function streamBingxOrderbook(opts) {
1128
1232
  const isSpot = opts.market === "spot";
1129
- const bingxSymbol = toBingxSymbol(opts.symbol);
1130
- const depth = opts.depth ?? 0.1;
1131
- const dataType = toBingxTopic(opts.symbol, opts.market, depth);
1233
+ const wsSymbol = opts.symbol.toUpperCase().replace("/", "-");
1234
+ const level = opts.depth ?? 20;
1235
+ const interval = opts.interval ?? "500ms";
1236
+ const dataType = `${wsSymbol}@depth${level}@${interval}`;
1132
1237
  const merger = new BookMerger();
1133
1238
  let ws = null;
1134
1239
  const stream = new OrderbookStream(() => ws?.close());
@@ -1163,7 +1268,7 @@ function streamBingxOrderbook(opts) {
1163
1268
  if (!r.ok) return;
1164
1269
  const book = {
1165
1270
  exchange: "bingx",
1166
- symbol: fromBingxSymbol(bingxSymbol),
1271
+ symbol: opts.symbol,
1167
1272
  market: opts.market,
1168
1273
  bids: r.bids,
1169
1274
  asks: r.asks,