@xapy/orderbook 0.1.3 → 0.1.4

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.js CHANGED
@@ -1231,29 +1231,50 @@ function streamBingxOrderbook(opts) {
1231
1231
  const wsSymbol = opts.symbol.toUpperCase().replace("/", "-");
1232
1232
  const level = opts.depth ?? 20;
1233
1233
  const interval = opts.interval ?? "500ms";
1234
- const dataType = `${wsSymbol}@depth${level}@${interval}`;
1234
+ const depthDataType = `${wsSymbol}@depth${level}@${interval}`;
1235
+ const tickerDataType = `${wsSymbol}@bookTicker`;
1235
1236
  const merger = new BookMerger();
1236
1237
  let ws = null;
1237
1238
  const stream = new OrderbookStream(() => ws?.close());
1238
- const subscribePayload = JSON.stringify({
1239
- id: `sub-${Date.now()}`,
1239
+ const subId = Date.now();
1240
+ const subscribeDepth = JSON.stringify({
1241
+ id: `sub-depth-${subId}`,
1240
1242
  reqType: "sub",
1241
- dataType
1243
+ dataType: depthDataType
1242
1244
  });
1243
- const handlePayload = (text) => {
1244
- if (text === "Ping" || text === "ping") {
1245
- ws?.send(text === "Ping" ? "Pong" : "pong");
1246
- return;
1247
- }
1248
- let msg;
1249
- try {
1250
- msg = JSON.parse(text);
1251
- } catch {
1252
- return;
1245
+ const subscribeTicker = JSON.stringify({
1246
+ id: `sub-ticker-${subId}`,
1247
+ reqType: "sub",
1248
+ dataType: tickerDataType
1249
+ });
1250
+ let lastMerged = null;
1251
+ let lastTicker = null;
1252
+ const emitOverlayed = () => {
1253
+ if (!lastMerged) return;
1254
+ let bids = lastMerged.bids;
1255
+ let asks = lastMerged.asks;
1256
+ let timestamp = lastMerged.timestamp;
1257
+ let sequence = lastMerged.sequence;
1258
+ if (lastTicker) {
1259
+ bids = overlayBid(bids, lastTicker.bid);
1260
+ asks = overlayAsk(asks, lastTicker.ask);
1261
+ if (lastTicker.ts > timestamp) {
1262
+ timestamp = lastTicker.ts;
1263
+ sequence = Math.max(sequence, lastTicker.ts);
1264
+ }
1253
1265
  }
1254
- if (msg.id && !msg.data) return;
1255
- if (msg.dataType !== dataType || !msg.data) return;
1256
- const d = msg.data;
1266
+ const book = {
1267
+ exchange: "bingx",
1268
+ symbol: opts.symbol,
1269
+ market: opts.market,
1270
+ bids,
1271
+ asks,
1272
+ timestamp,
1273
+ sequence
1274
+ };
1275
+ stream.emit("update", book);
1276
+ };
1277
+ const handleDepth = (d) => {
1257
1278
  const ts = d.T ?? d.ts ?? Date.now();
1258
1279
  const event = {
1259
1280
  kind: "snapshot",
@@ -1264,16 +1285,45 @@ function streamBingxOrderbook(opts) {
1264
1285
  };
1265
1286
  const r = merger.apply(event);
1266
1287
  if (!r.ok) return;
1267
- const book = {
1268
- exchange: "bingx",
1269
- symbol: opts.symbol,
1270
- market: opts.market,
1288
+ lastMerged = {
1271
1289
  bids: r.bids,
1272
1290
  asks: r.asks,
1273
1291
  timestamp: r.timestamp,
1274
1292
  sequence: r.sequence
1275
1293
  };
1276
- stream.emit("update", book);
1294
+ emitOverlayed();
1295
+ };
1296
+ const handleTicker = (t) => {
1297
+ const bidPrice = Number(t.b);
1298
+ const askPrice = Number(t.a);
1299
+ const bidSize = Number(t.B);
1300
+ const askSize = Number(t.A);
1301
+ if (!Number.isFinite(bidPrice) || !Number.isFinite(askPrice)) return;
1302
+ lastTicker = {
1303
+ bid: { price: bidPrice, size: bidSize },
1304
+ ask: { price: askPrice, size: askSize },
1305
+ ts: t.T ?? t.E ?? Date.now()
1306
+ };
1307
+ emitOverlayed();
1308
+ };
1309
+ const handlePayload = (text) => {
1310
+ if (text === "Ping" || text === "ping") {
1311
+ ws?.send(text === "Ping" ? "Pong" : "pong");
1312
+ return;
1313
+ }
1314
+ let msg;
1315
+ try {
1316
+ msg = JSON.parse(text);
1317
+ } catch {
1318
+ return;
1319
+ }
1320
+ if (msg.id && !msg.data) return;
1321
+ if (!msg.dataType || !msg.data) return;
1322
+ if (msg.dataType === depthDataType) {
1323
+ handleDepth(msg.data);
1324
+ } else if (msg.dataType === tickerDataType) {
1325
+ handleTicker(msg.data);
1326
+ }
1277
1327
  };
1278
1328
  const onMessage = async (raw) => {
1279
1329
  if (typeof raw === "string") {
@@ -1287,7 +1337,10 @@ function streamBingxOrderbook(opts) {
1287
1337
  url: isSpot ? WS_SPOT3 : WS_SWAP,
1288
1338
  onOpen: (sock) => {
1289
1339
  merger.reset();
1290
- sock.send(subscribePayload);
1340
+ lastMerged = null;
1341
+ lastTicker = null;
1342
+ sock.send(subscribeDepth);
1343
+ sock.send(subscribeTicker);
1291
1344
  },
1292
1345
  onMessage
1293
1346
  // bingx server is the one that sends Ping frames; we don't need client pings.
@@ -1299,6 +1352,32 @@ function streamBingxOrderbook(opts) {
1299
1352
  ws.connect().catch((e) => stream.emit("error", e));
1300
1353
  return stream;
1301
1354
  }
1355
+ function overlayAsk(asks, top) {
1356
+ if (!(top.size > 0)) return asks;
1357
+ let i = 0;
1358
+ for (; i < asks.length; i++) {
1359
+ const level = asks[i];
1360
+ if (!level || level.price >= top.price) break;
1361
+ }
1362
+ const at = asks[i];
1363
+ if (at && at.price === top.price) {
1364
+ return [{ price: top.price, size: top.size }, ...asks.slice(i + 1)];
1365
+ }
1366
+ return [{ price: top.price, size: top.size }, ...asks.slice(i)];
1367
+ }
1368
+ function overlayBid(bids, top) {
1369
+ if (!(top.size > 0)) return bids;
1370
+ let i = 0;
1371
+ for (; i < bids.length; i++) {
1372
+ const level = bids[i];
1373
+ if (!level || level.price <= top.price) break;
1374
+ }
1375
+ const at = bids[i];
1376
+ if (at && at.price === top.price) {
1377
+ return [{ price: top.price, size: top.size }, ...bids.slice(i + 1)];
1378
+ }
1379
+ return [{ price: top.price, size: top.size }, ...bids.slice(i)];
1380
+ }
1302
1381
 
1303
1382
  // src/exchanges/bingx/index.ts
1304
1383
  var BingxClient = class {