@xapy/orderbook 0.1.2 → 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 {