ccxt 4.4.1 → 4.4.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.
Files changed (67) hide show
  1. package/README.md +4 -4
  2. package/dist/ccxt.browser.min.js +3 -3
  3. package/dist/cjs/ccxt.js +1 -1
  4. package/dist/cjs/src/base/Exchange.js +69 -0
  5. package/dist/cjs/src/binance.js +88 -18
  6. package/dist/cjs/src/bitmart.js +3 -1
  7. package/dist/cjs/src/bitstamp.js +24 -36
  8. package/dist/cjs/src/bybit.js +1 -0
  9. package/dist/cjs/src/cryptocom.js +3 -2
  10. package/dist/cjs/src/currencycom.js +1 -2
  11. package/dist/cjs/src/htx.js +1 -1
  12. package/dist/cjs/src/mexc.js +88 -1
  13. package/dist/cjs/src/pro/binance.js +3 -63
  14. package/dist/cjs/src/pro/bitget.js +1 -9
  15. package/dist/cjs/src/pro/bitmex.js +11 -1
  16. package/dist/cjs/src/pro/bybit.js +2 -54
  17. package/dist/cjs/src/pro/cryptocom.js +193 -61
  18. package/dist/cjs/src/pro/gate.js +1 -9
  19. package/dist/cjs/src/pro/hyperliquid.js +4 -36
  20. package/dist/cjs/src/pro/kucoin.js +2 -59
  21. package/dist/cjs/src/pro/kucoinfutures.js +139 -1
  22. package/dist/cjs/src/pro/mexc.js +82 -3
  23. package/dist/cjs/src/pro/okx.js +14 -39
  24. package/dist/cjs/src/pro/oxfun.js +75 -0
  25. package/dist/cjs/src/pro/phemex.js +45 -1
  26. package/dist/cjs/src/pro/woofipro.js +67 -0
  27. package/dist/cjs/src/xt.js +7 -2
  28. package/examples/js/cli.js +8 -4
  29. package/js/ccxt.d.ts +3 -3
  30. package/js/ccxt.js +1 -1
  31. package/js/src/abstract/bitmart.d.ts +1 -0
  32. package/js/src/base/Exchange.d.ts +2 -0
  33. package/js/src/base/Exchange.js +70 -1
  34. package/js/src/base/types.d.ts +5 -4
  35. package/js/src/binance.js +88 -18
  36. package/js/src/bitmart.js +3 -1
  37. package/js/src/bitstamp.js +24 -36
  38. package/js/src/bybit.js +2 -1
  39. package/js/src/cryptocom.js +3 -2
  40. package/js/src/currencycom.js +1 -2
  41. package/js/src/htx.js +1 -1
  42. package/js/src/mexc.js +88 -1
  43. package/js/src/pro/binance.d.ts +0 -1
  44. package/js/src/pro/binance.js +4 -64
  45. package/js/src/pro/bitget.js +1 -9
  46. package/js/src/pro/bitmex.js +11 -1
  47. package/js/src/pro/bybit.d.ts +1 -2
  48. package/js/src/pro/bybit.js +3 -55
  49. package/js/src/pro/cryptocom.d.ts +7 -2
  50. package/js/src/pro/cryptocom.js +194 -62
  51. package/js/src/pro/gate.js +2 -10
  52. package/js/src/pro/hyperliquid.js +5 -37
  53. package/js/src/pro/kucoin.d.ts +0 -1
  54. package/js/src/pro/kucoin.js +3 -60
  55. package/js/src/pro/kucoinfutures.d.ts +7 -1
  56. package/js/src/pro/kucoinfutures.js +139 -1
  57. package/js/src/pro/mexc.d.ts +3 -1
  58. package/js/src/pro/mexc.js +82 -3
  59. package/js/src/pro/okx.js +15 -40
  60. package/js/src/pro/oxfun.d.ts +3 -0
  61. package/js/src/pro/oxfun.js +75 -0
  62. package/js/src/pro/phemex.d.ts +2 -1
  63. package/js/src/pro/phemex.js +45 -1
  64. package/js/src/pro/woofipro.d.ts +3 -0
  65. package/js/src/pro/woofipro.js +67 -0
  66. package/js/src/xt.js +7 -2
  67. package/package.json +1 -1
@@ -178,6 +178,27 @@ export default class kucoinfutures extends kucoinfuturesRest {
178
178
  };
179
179
  return await this.watchMultiple(url, messageHashes, this.extend(request, params), subscriptionHashes, subscriptionArgs);
180
180
  }
181
+ async unSubscribeMultiple(url, messageHashes, topic, subscriptionHashes, params = {}, subscription = undefined) {
182
+ const requestId = this.requestId().toString();
183
+ const request = {
184
+ 'id': requestId,
185
+ 'type': 'unsubscribe',
186
+ 'topic': topic,
187
+ 'response': true,
188
+ };
189
+ const message = this.extend(request, params);
190
+ if (subscription !== undefined) {
191
+ subscription[requestId] = requestId;
192
+ }
193
+ const client = this.client(url);
194
+ for (let i = 0; i < subscriptionHashes.length; i++) {
195
+ const subscriptionHash = subscriptionHashes[i];
196
+ if (!(subscriptionHash in client.subscriptions)) {
197
+ client.subscriptions[requestId] = subscriptionHash;
198
+ }
199
+ }
200
+ return await this.watchMultiple(url, messageHashes, message, subscriptionHashes, subscription);
201
+ }
181
202
  async watchTicker(symbol, params = {}) {
182
203
  /**
183
204
  * @method
@@ -521,7 +542,7 @@ export default class kucoinfutures extends kucoinfuturesRest {
521
542
  async watchTradesForSymbols(symbols, since = undefined, limit = undefined, params = {}) {
522
543
  /**
523
544
  * @method
524
- * @name kucoinfutures#watchTrades
545
+ * @name kucoinfutures#watchTradesForSymbols
525
546
  * @description get the list of most recent trades for a particular symbol
526
547
  * @param {string} symbol unified symbol of the market to fetch trades for
527
548
  * @param {int} [since] timestamp in ms of the earliest trade to fetch
@@ -555,6 +576,49 @@ export default class kucoinfutures extends kucoinfuturesRest {
555
576
  }
556
577
  return this.filterBySinceLimit(trades, since, limit, 'timestamp', true);
557
578
  }
579
+ async unWatchTrades(symbol, params = {}) {
580
+ /**
581
+ * @method
582
+ * @name kucoinfutures#unWatchTrades
583
+ * @description unWatches trades stream
584
+ * @see https://docs.kucoin.com/futures/#execution-data
585
+ * @param {string} symbol unified symbol of the market to fetch trades for
586
+ * @param {object} [params] extra parameters specific to the exchange API endpoint
587
+ * @returns {object[]} a list of [trade structures]{@link https://docs.ccxt.com/#/?id=public-trades}
588
+ */
589
+ return await this.unWatchTradesForSymbols([symbol], params);
590
+ }
591
+ async unWatchTradesForSymbols(symbols, params = {}) {
592
+ /**
593
+ * @method
594
+ * @name kucoinfutures#unWatchTradesForSymbols
595
+ * @description get the list of most recent trades for a particular symbol
596
+ * @param {string} symbol unified symbol of the market to fetch trades for
597
+ * @param {object} [params] extra parameters specific to the exchange API endpoint
598
+ * @returns {object[]} a list of [trade structures]{@link https://docs.ccxt.com/#/?id=public-trades}
599
+ */
600
+ await this.loadMarkets();
601
+ symbols = this.marketSymbols(symbols, undefined, false);
602
+ const url = await this.negotiate(false);
603
+ symbols = this.marketSymbols(symbols);
604
+ const marketIds = this.marketIds(symbols);
605
+ const topic = '/contractMarket/execution:' + marketIds.join(',');
606
+ const subscriptionHashes = [];
607
+ const messageHashes = [];
608
+ for (let i = 0; i < symbols.length; i++) {
609
+ const symbol = symbols[i];
610
+ messageHashes.push('unsubscribe:trades:' + symbol);
611
+ subscriptionHashes.push('trades:' + symbol);
612
+ }
613
+ const subscription = {
614
+ 'messageHashes': messageHashes,
615
+ 'subMessageHashes': subscriptionHashes,
616
+ 'topic': 'trades',
617
+ 'unsubscribe': true,
618
+ 'symbols': symbols,
619
+ };
620
+ return await this.unSubscribeMultiple(url, messageHashes, topic, messageHashes, params, subscription);
621
+ }
558
622
  handleTrade(client, message) {
559
623
  //
560
624
  // {
@@ -689,6 +753,7 @@ export default class kucoinfutures extends kucoinfuturesRest {
689
753
  * @method
690
754
  * @name kucoinfutures#watchOrderBookForSymbols
691
755
  * @description watches information on open orders with bid (buy) and ask (sell) prices, volumes and other data
756
+ * @see https://docs.kucoin.com/futures/#level-2-market-data
692
757
  * @param {string[]} symbols unified array of symbols
693
758
  * @param {int} [limit] the maximum amount of order book entries to return
694
759
  * @param {object} [params] extra parameters specific to the exchange API endpoint
@@ -722,6 +787,48 @@ export default class kucoinfutures extends kucoinfuturesRest {
722
787
  const orderbook = await this.subscribeMultiple(url, messageHashes, topic, subscriptionHashes, subscriptionArgs, params);
723
788
  return orderbook.limit();
724
789
  }
790
+ async unWatchOrderBook(symbol, params = {}) {
791
+ /**
792
+ * @method
793
+ * @name kucoinfutures#unWatchOrderBook
794
+ * @description unWatches information on open orders with bid (buy) and ask (sell) prices, volumes and other data
795
+ * @see https://docs.kucoin.com/futures/#level-2-market-data
796
+ * @param {string} symbol unified symbol of the market to fetch the order book for
797
+ * @param {object} [params] extra parameters specific to the exchange API endpoint
798
+ * @returns {object} A dictionary of [order book structures]{@link https://docs.ccxt.com/#/?id=order-book-structure} indexed by market symbols
799
+ */
800
+ return await this.unWatchOrderBookForSymbols([symbol], params);
801
+ }
802
+ async unWatchOrderBookForSymbols(symbols, params = {}) {
803
+ /**
804
+ * @method
805
+ * @name kucoinfutures#unWatchOrderBookForSymbols
806
+ * @description unWatches information on open orders with bid (buy) and ask (sell) prices, volumes and other data
807
+ * @param {string[]} symbols unified array of symbols
808
+ * @param {object} [params] extra parameters specific to the exchange API endpoint
809
+ * @returns {object} A dictionary of [order book structures]{@link https://docs.ccxt.com/#/?id=order-book-structure} indexed by market symbols
810
+ */
811
+ await this.loadMarkets();
812
+ symbols = this.marketSymbols(symbols);
813
+ const marketIds = this.marketIds(symbols);
814
+ const url = await this.negotiate(false);
815
+ const topic = '/contractMarket/level2:' + marketIds.join(',');
816
+ const subscriptionHashes = [];
817
+ const messageHashes = [];
818
+ for (let i = 0; i < symbols.length; i++) {
819
+ const symbol = symbols[i];
820
+ messageHashes.push('unsubscribe:orderbook:' + symbol);
821
+ subscriptionHashes.push('orderbook:' + symbol);
822
+ }
823
+ const subscription = {
824
+ 'messageHashes': messageHashes,
825
+ 'symbols': symbols,
826
+ 'unsubscribe': true,
827
+ 'topic': 'orderbook',
828
+ 'subMessageHashes': subscriptionHashes,
829
+ };
830
+ return await this.unSubscribeMultiple(url, messageHashes, topic, messageHashes, params, subscription);
831
+ }
725
832
  handleDelta(orderbook, delta) {
726
833
  orderbook['nonce'] = this.safeInteger(delta, 'sequence');
727
834
  const timestamp = this.safeInteger(delta, 'timestamp');
@@ -1139,6 +1246,36 @@ export default class kucoinfutures extends kucoinfuturesRest {
1139
1246
  }
1140
1247
  this.handleErrors(undefined, undefined, client.url, undefined, undefined, data, message, undefined, undefined);
1141
1248
  }
1249
+ handleSubscriptionStatus(client, message) {
1250
+ //
1251
+ // {
1252
+ // "id": "1578090438322",
1253
+ // "type": "ack"
1254
+ // }
1255
+ //
1256
+ const id = this.safeString(message, 'id');
1257
+ if (!(id in client.subscriptions)) {
1258
+ return;
1259
+ }
1260
+ const subscriptionHash = this.safeString(client.subscriptions, id);
1261
+ const subscription = this.safeValue(client.subscriptions, subscriptionHash);
1262
+ delete client.subscriptions[id];
1263
+ const method = this.safeValue(subscription, 'method');
1264
+ if (method !== undefined) {
1265
+ method.call(this, client, message, subscription);
1266
+ }
1267
+ const isUnSub = this.safeBool(subscription, 'unsubscribe', false);
1268
+ if (isUnSub) {
1269
+ const messageHashes = this.safeList(subscription, 'messageHashes', []);
1270
+ const subMessageHashes = this.safeList(subscription, 'subMessageHashes', []);
1271
+ for (let i = 0; i < messageHashes.length; i++) {
1272
+ const messageHash = messageHashes[i];
1273
+ const subHash = subMessageHashes[i];
1274
+ this.cleanUnsubscription(client, subHash, messageHash);
1275
+ }
1276
+ this.cleanCache(subscription);
1277
+ }
1278
+ }
1142
1279
  handleMessage(client, message) {
1143
1280
  const type = this.safeString(message, 'type');
1144
1281
  const methods = {
@@ -1147,6 +1284,7 @@ export default class kucoinfutures extends kucoinfuturesRest {
1147
1284
  'message': this.handleSubject,
1148
1285
  'pong': this.handlePong,
1149
1286
  'error': this.handleErrorMessage,
1287
+ 'ack': this.handleSubscriptionStatus,
1150
1288
  };
1151
1289
  const method = this.safeValue(methods, type);
1152
1290
  if (method !== undefined) {
@@ -1,10 +1,12 @@
1
1
  import mexcRest from '../mexc.js';
2
- import type { Int, OHLCV, Str, OrderBook, Order, Trade, Ticker, Balances } from '../base/types.js';
2
+ import type { Int, OHLCV, Str, OrderBook, Order, Trade, Ticker, Balances, Strings, Tickers } from '../base/types.js';
3
3
  import Client from '../base/ws/Client.js';
4
4
  export default class mexc extends mexcRest {
5
5
  describe(): any;
6
6
  watchTicker(symbol: string, params?: {}): Promise<Ticker>;
7
7
  handleTicker(client: Client, message: any): void;
8
+ watchTickers(symbols?: Strings, params?: {}): Promise<Tickers>;
9
+ handleTickers(client: Client, message: any): void;
8
10
  parseWsTicker(ticker: any, market?: any): Ticker;
9
11
  watchSpotPublic(channel: any, messageHash: any, params?: {}): Promise<any>;
10
12
  watchSpotPrivate(channel: any, messageHash: any, params?: {}): Promise<any>;
@@ -30,7 +30,7 @@ export default class mexc extends mexcRest {
30
30
  'watchOrderBook': true,
31
31
  'watchOrders': true,
32
32
  'watchTicker': true,
33
- 'watchTickers': false,
33
+ 'watchTickers': true,
34
34
  'watchTrades': true,
35
35
  'watchTradesForSymbols': false,
36
36
  },
@@ -128,6 +128,84 @@ export default class mexc extends mexcRest {
128
128
  const messageHash = 'ticker:' + symbol;
129
129
  client.resolve(ticker, messageHash);
130
130
  }
131
+ async watchTickers(symbols = undefined, params = {}) {
132
+ /**
133
+ * @method
134
+ * @name mexc#watchTickers
135
+ * @description watches a price ticker, a statistical calculation with the information calculated over the past 24 hours for all markets of a specific list
136
+ * @see https://mexcdevelop.github.io/apidocs/spot_v3_en/#individual-symbol-book-ticker-streams
137
+ * @see https://mexcdevelop.github.io/apidocs/contract_v1_en/#public-channels
138
+ * @param {string[]} symbols unified symbol of the market to fetch the ticker for
139
+ * @param {object} [params] extra parameters specific to the exchange API endpoint
140
+ * @returns {object} a [ticker structure]{@link https://docs.ccxt.com/#/?id=ticker-structure}
141
+ */
142
+ await this.loadMarkets();
143
+ symbols = this.marketSymbols(symbols, undefined, false);
144
+ const messageHashes = [];
145
+ const marketIds = this.marketIds(symbols);
146
+ const firstMarket = this.market(symbols[0]);
147
+ const isSpot = firstMarket['spot'];
148
+ const url = (isSpot) ? this.urls['api']['ws']['spot'] : this.urls['api']['ws']['swap'];
149
+ const request = {};
150
+ if (isSpot) {
151
+ const topics = [];
152
+ for (let i = 0; i < marketIds.length; i++) {
153
+ const marketId = marketIds[i];
154
+ messageHashes.push('ticker:' + symbols[i]);
155
+ topics.push('spot@public.bookTicker.v3.api@' + marketId);
156
+ }
157
+ request['method'] = 'SUBSCRIPTION';
158
+ request['params'] = topics;
159
+ }
160
+ else {
161
+ request['method'] = 'sub.tickers';
162
+ request['params'] = {};
163
+ messageHashes.push('ticker');
164
+ }
165
+ const ticker = await this.watchMultiple(url, messageHashes, this.extend(request, params), messageHashes);
166
+ if (isSpot && this.newUpdates) {
167
+ const result = {};
168
+ result[ticker['symbol']] = ticker;
169
+ return result;
170
+ }
171
+ return this.filterByArray(this.tickers, 'symbol', symbols);
172
+ }
173
+ handleTickers(client, message) {
174
+ //
175
+ // {
176
+ // "channel": "push.tickers",
177
+ // "data": [
178
+ // {
179
+ // "symbol": "ETH_USDT",
180
+ // "lastPrice": 2324.5,
181
+ // "riseFallRate": 0.0356,
182
+ // "fairPrice": 2324.32,
183
+ // "indexPrice": 2325.44,
184
+ // "volume24": 25868309,
185
+ // "amount24": 591752573.9792,
186
+ // "maxBidPrice": 2557.98,
187
+ // "minAskPrice": 2092.89,
188
+ // "lower24Price": 2239.39,
189
+ // "high24Price": 2332.59,
190
+ // "timestamp": 1725872514111
191
+ // }
192
+ // ],
193
+ // "ts": 1725872514111
194
+ // }
195
+ //
196
+ const data = this.safeList(message, 'data');
197
+ const topic = 'ticker';
198
+ const result = [];
199
+ for (let i = 0; i < data.length; i++) {
200
+ const ticker = this.parseTicker(data[i]);
201
+ const symbol = ticker['symbol'];
202
+ this.tickers[symbol] = ticker;
203
+ result.push(ticker);
204
+ const messageHash = topic + ':' + symbol;
205
+ client.resolve(ticker, messageHash);
206
+ }
207
+ client.resolve(result, topic);
208
+ }
131
209
  parseWsTicker(ticker, market = undefined) {
132
210
  //
133
211
  // spot
@@ -1135,8 +1213,8 @@ export default class mexc extends mexcRest {
1135
1213
  // "code": 0,
1136
1214
  // "msg": "spot@public.increase.depth.v3.api@BTCUSDT"
1137
1215
  // }
1138
- //
1139
- const msg = this.safeString(message, 'msg');
1216
+ // Set the default to an empty string if the message is empty during the test.
1217
+ const msg = this.safeString(message, 'msg', '');
1140
1218
  if (msg === 'PONG') {
1141
1219
  this.handlePong(client, message);
1142
1220
  }
@@ -1180,6 +1258,7 @@ export default class mexc extends mexcRest {
1180
1258
  'push.kline': this.handleOHLCV,
1181
1259
  'public.bookTicker.v3.api': this.handleTicker,
1182
1260
  'push.ticker': this.handleTicker,
1261
+ 'push.tickers': this.handleTickers,
1183
1262
  'public.increase.depth.v3.api': this.handleOrderBook,
1184
1263
  'push.depth': this.handleOrderBook,
1185
1264
  'private.orders.v3.api': this.handleOrder,
package/js/src/pro/okx.js CHANGED
@@ -6,7 +6,7 @@
6
6
 
7
7
  // ---------------------------------------------------------------------------
8
8
  import okxRest from '../okx.js';
9
- import { ArgumentsRequired, BadRequest, ExchangeError, ChecksumError, AuthenticationError, InvalidNonce, UnsubscribeError } from '../base/errors.js';
9
+ import { ArgumentsRequired, BadRequest, ExchangeError, ChecksumError, AuthenticationError, InvalidNonce } from '../base/errors.js';
10
10
  import { ArrayCache, ArrayCacheByTimestamp, ArrayCacheBySymbolById, ArrayCacheBySymbolBySide } from '../base/ws/Cache.js';
11
11
  import { sha256 } from '../static_dependencies/noble-hashes/sha256.js';
12
12
  // ---------------------------------------------------------------------------
@@ -1445,8 +1445,11 @@ export default class okx extends okxRest {
1445
1445
  },
1446
1446
  ],
1447
1447
  };
1448
- const message = this.extend(request, params);
1449
- this.watch(url, messageHash, message, messageHash);
1448
+ // Only add params['access'] to prevent sending custom parameters, such as extraParams.
1449
+ if ('access' in params) {
1450
+ request['access'] = params['access'];
1451
+ }
1452
+ this.watch(url, messageHash, request, messageHash);
1450
1453
  }
1451
1454
  return await future;
1452
1455
  }
@@ -1616,7 +1619,7 @@ export default class okx extends okxRest {
1616
1619
  'channel': 'positions',
1617
1620
  'instType': 'ANY',
1618
1621
  };
1619
- const args = [arg];
1622
+ const args = [this.extend(arg, params)];
1620
1623
  const nonSymbolRequest = {
1621
1624
  'op': 'subscribe',
1622
1625
  'args': args,
@@ -2336,64 +2339,36 @@ export default class okx extends okxRest {
2336
2339
  handleUnSubscriptionTrades(client, symbol) {
2337
2340
  const subMessageHash = 'trades:' + symbol;
2338
2341
  const messageHash = 'unsubscribe:trades:' + symbol;
2339
- if (subMessageHash in client.subscriptions) {
2340
- delete client.subscriptions[subMessageHash];
2341
- }
2342
- if (messageHash in client.subscriptions) {
2343
- delete client.subscriptions[messageHash];
2342
+ this.cleanUnsubscription(client, subMessageHash, messageHash);
2343
+ if (symbol in this.trades) {
2344
+ delete this.trades[symbol];
2344
2345
  }
2345
- delete this.trades[symbol];
2346
- const error = new UnsubscribeError(this.id + ' ' + subMessageHash);
2347
- client.reject(error, subMessageHash);
2348
- client.resolve(true, messageHash);
2349
2346
  }
2350
2347
  handleUnsubscriptionOrderBook(client, symbol, channel) {
2351
2348
  const subMessageHash = channel + ':' + symbol;
2352
2349
  const messageHash = 'unsubscribe:orderbook:' + symbol;
2353
- if (subMessageHash in client.subscriptions) {
2354
- delete client.subscriptions[subMessageHash];
2350
+ this.cleanUnsubscription(client, subMessageHash, messageHash);
2351
+ if (symbol in this.orderbooks) {
2352
+ delete this.orderbooks[symbol];
2355
2353
  }
2356
- if (messageHash in client.subscriptions) {
2357
- delete client.subscriptions[messageHash];
2358
- }
2359
- delete this.orderbooks[symbol];
2360
- const error = new UnsubscribeError(this.id + ' ' + subMessageHash);
2361
- client.reject(error, subMessageHash);
2362
- client.resolve(true, messageHash);
2363
2354
  }
2364
2355
  handleUnsubscriptionOHLCV(client, symbol, channel) {
2365
2356
  const tf = channel.replace('candle', '');
2366
2357
  const timeframe = this.findTimeframe(tf);
2367
2358
  const subMessageHash = 'multi:' + channel + ':' + symbol;
2368
2359
  const messageHash = 'unsubscribe:' + subMessageHash;
2369
- if (subMessageHash in client.subscriptions) {
2370
- delete client.subscriptions[subMessageHash];
2371
- }
2372
- if (messageHash in client.subscriptions) {
2373
- delete client.subscriptions[messageHash];
2374
- }
2360
+ this.cleanUnsubscription(client, subMessageHash, messageHash);
2375
2361
  if (timeframe in this.ohlcvs[symbol]) {
2376
2362
  delete this.ohlcvs[symbol][timeframe];
2377
2363
  }
2378
- const error = new UnsubscribeError(this.id + ' ' + subMessageHash);
2379
- client.reject(error, subMessageHash);
2380
- client.resolve(true, messageHash);
2381
2364
  }
2382
2365
  handleUnsubscriptionTicker(client, symbol, channel) {
2383
2366
  const subMessageHash = channel + '::' + symbol;
2384
2367
  const messageHash = 'unsubscribe:ticker:' + symbol;
2385
- if (subMessageHash in client.subscriptions) {
2386
- delete client.subscriptions[subMessageHash];
2387
- }
2388
- if (messageHash in client.subscriptions) {
2389
- delete client.subscriptions[messageHash];
2390
- }
2368
+ this.cleanUnsubscription(client, subMessageHash, messageHash);
2391
2369
  if (symbol in this.tickers) {
2392
2370
  delete this.tickers[symbol];
2393
2371
  }
2394
- const error = new UnsubscribeError(this.id + ' ' + subMessageHash);
2395
- client.reject(error, subMessageHash);
2396
- client.resolve(true, messageHash);
2397
2372
  }
2398
2373
  handleUnsubscription(client, message) {
2399
2374
  //
@@ -18,6 +18,9 @@ export default class oxfun extends oxfunRest {
18
18
  watchTicker(symbol: string, params?: {}): Promise<Ticker>;
19
19
  watchTickers(symbols?: Strings, params?: {}): Promise<Tickers>;
20
20
  handleTicker(client: Client, message: any): void;
21
+ watchBidsAsks(symbols?: Strings, params?: {}): Promise<Tickers>;
22
+ handleBidAsk(client: Client, message: any): void;
23
+ parseWsBidAsk(ticker: any, market?: any): Ticker;
21
24
  watchBalance(params?: {}): Promise<Balances>;
22
25
  handleBalance(client: any, message: any): void;
23
26
  watchPositions(symbols?: Strings, since?: Int, limit?: Int, params?: {}): Promise<Position[]>;
@@ -25,6 +25,7 @@ export default class oxfun extends oxfunRest {
25
25
  'watchMyTrades': false,
26
26
  'watchTicker': true,
27
27
  'watchTickers': true,
28
+ 'watchBidsAsks': true,
28
29
  'watchBalance': true,
29
30
  'createOrderWs': true,
30
31
  'editOrderWs': true,
@@ -499,6 +500,77 @@ export default class oxfun extends oxfunRest {
499
500
  client.resolve(ticker, messageHash);
500
501
  }
501
502
  }
503
+ async watchBidsAsks(symbols = undefined, params = {}) {
504
+ /**
505
+ * @method
506
+ * @name oxfun#watchBidsAsks
507
+ * @see https://docs.ox.fun/?json#best-bid-ask
508
+ * @description watches best bid & ask for symbols
509
+ * @param {string[]} symbols unified symbol of the market to fetch the ticker for
510
+ * @param {object} [params] extra parameters specific to the exchange API endpoint
511
+ * @returns {object} a [ticker structure]{@link https://docs.ccxt.com/#/?id=ticker-structure}
512
+ */
513
+ await this.loadMarkets();
514
+ symbols = this.marketSymbols(symbols, undefined, false);
515
+ const messageHashes = [];
516
+ const args = [];
517
+ for (let i = 0; i < symbols.length; i++) {
518
+ const market = this.market(symbols[i]);
519
+ args.push('bestBidAsk:' + market['id']);
520
+ messageHashes.push('bidask:' + market['symbol']);
521
+ }
522
+ const newTickers = await this.subscribeMultiple(messageHashes, args, params);
523
+ if (this.newUpdates) {
524
+ const tickers = {};
525
+ tickers[newTickers['symbol']] = newTickers;
526
+ return tickers;
527
+ }
528
+ return this.filterByArray(this.bidsasks, 'symbol', symbols);
529
+ }
530
+ handleBidAsk(client, message) {
531
+ //
532
+ // {
533
+ // "table": "bestBidAsk",
534
+ // "data": {
535
+ // "ask": [
536
+ // 19045.0,
537
+ // 1.0
538
+ // ],
539
+ // "checksum": 3790706311,
540
+ // "marketCode": "BTC-USD-SWAP-LIN",
541
+ // "bid": [
542
+ // 19015.0,
543
+ // 1.0
544
+ // ],
545
+ // "timestamp": "1665456882928"
546
+ // }
547
+ // }
548
+ //
549
+ const data = this.safeDict(message, 'data', {});
550
+ const parsedTicker = this.parseWsBidAsk(data);
551
+ const symbol = parsedTicker['symbol'];
552
+ this.bidsasks[symbol] = parsedTicker;
553
+ const messageHash = 'bidask:' + symbol;
554
+ client.resolve(parsedTicker, messageHash);
555
+ }
556
+ parseWsBidAsk(ticker, market = undefined) {
557
+ const marketId = this.safeString(ticker, 'marketCode');
558
+ market = this.safeMarket(marketId, market);
559
+ const symbol = this.safeString(market, 'symbol');
560
+ const timestamp = this.safeInteger(ticker, 'timestamp');
561
+ const ask = this.safeList(ticker, 'ask', []);
562
+ const bid = this.safeList(ticker, 'bid', []);
563
+ return this.safeTicker({
564
+ 'symbol': symbol,
565
+ 'timestamp': timestamp,
566
+ 'datetime': this.iso8601(timestamp),
567
+ 'ask': this.safeNumber(ask, 0),
568
+ 'askVolume': this.safeNumber(ask, 1),
569
+ 'bid': this.safeNumber(bid, 0),
570
+ 'bidVolume': this.safeNumber(bid, 1),
571
+ 'info': ticker,
572
+ }, market);
573
+ }
502
574
  async watchBalance(params = {}) {
503
575
  /**
504
576
  * @method
@@ -1022,6 +1094,9 @@ export default class oxfun extends oxfunRest {
1022
1094
  if (table.indexOf('order') > -1) {
1023
1095
  this.handleOrders(client, message);
1024
1096
  }
1097
+ if (table === 'bestBidAsk') {
1098
+ this.handleBidAsk(client, message);
1099
+ }
1025
1100
  }
1026
1101
  else {
1027
1102
  if (event === 'login') {
@@ -1,5 +1,5 @@
1
1
  import phemexRest from '../phemex.js';
2
- import type { Int, Str, OrderBook, Order, Trade, Ticker, OHLCV, Balances, Dict } from '../base/types.js';
2
+ import type { Int, Str, OrderBook, Order, Trade, Ticker, OHLCV, Balances, Dict, Strings, Tickers } from '../base/types.js';
3
3
  import Client from '../base/ws/Client.js';
4
4
  export default class phemex extends phemexRest {
5
5
  describe(): any;
@@ -16,6 +16,7 @@ export default class phemex extends phemexRest {
16
16
  handleTrades(client: Client, message: any): void;
17
17
  handleOHLCV(client: Client, message: any): void;
18
18
  watchTicker(symbol: string, params?: {}): Promise<Ticker>;
19
+ watchTickers(symbols?: Strings, params?: {}): Promise<Tickers>;
19
20
  watchTrades(symbol: string, since?: Int, limit?: Int, params?: {}): Promise<Trade[]>;
20
21
  watchOrderBook(symbol: string, limit?: Int, params?: {}): Promise<OrderBook>;
21
22
  watchOHLCV(symbol: string, timeframe?: string, since?: Int, limit?: Int, params?: {}): Promise<OHLCV[]>;
@@ -17,7 +17,7 @@ export default class phemex extends phemexRest {
17
17
  'has': {
18
18
  'ws': true,
19
19
  'watchTicker': true,
20
- 'watchTickers': false,
20
+ 'watchTickers': true,
21
21
  'watchTrades': true,
22
22
  'watchMyTrades': true,
23
23
  'watchOrders': true,
@@ -526,6 +526,50 @@ export default class phemex extends phemexRest {
526
526
  const request = this.deepExtend(subscribe, params);
527
527
  return await this.watch(url, messageHash, request, subscriptionHash);
528
528
  }
529
+ async watchTickers(symbols = undefined, params = {}) {
530
+ /**
531
+ * @method
532
+ * @name phemex#watchTickers
533
+ * @see https://github.com/phemex/phemex-api-docs/blob/master/Public-Hedged-Perpetual-API.md#subscribe-24-hours-ticker
534
+ * @see https://github.com/phemex/phemex-api-docs/blob/master/Public-Contract-API-en.md#subscribe-24-hours-ticker
535
+ * @see https://github.com/phemex/phemex-api-docs/blob/master/Public-Spot-API-en.md#subscribe-24-hours-ticker
536
+ * @description watches a price ticker, a statistical calculation with the information calculated over the past 24 hours for all markets of a specific list
537
+ * @param {string[]} [symbols] unified symbol of the market to fetch the ticker for
538
+ * @param {object} [params] extra parameters specific to the exchange API endpoint
539
+ * @param {string} [params.channel] the channel to subscribe to, tickers by default. Can be tickers, sprd-tickers, index-tickers, block-tickers
540
+ * @returns {object} a [ticker structure]{@link https://docs.ccxt.com/#/?id=ticker-structure}
541
+ */
542
+ await this.loadMarkets();
543
+ symbols = this.marketSymbols(symbols, undefined, false);
544
+ const first = symbols[0];
545
+ const market = this.market(first);
546
+ const isSwap = market['swap'];
547
+ const settleIsUSDT = market['settle'] === 'USDT';
548
+ let name = 'spot_market24h';
549
+ if (isSwap) {
550
+ name = settleIsUSDT ? 'perp_market24h_pack_p' : 'market24h';
551
+ }
552
+ const url = this.urls['api']['ws'];
553
+ const requestId = this.requestId();
554
+ const subscriptionHash = name + '.subscribe';
555
+ const messageHashes = [];
556
+ for (let i = 0; i < symbols.length; i++) {
557
+ messageHashes.push('ticker:' + symbols[i]);
558
+ }
559
+ const subscribe = {
560
+ 'method': subscriptionHash,
561
+ 'id': requestId,
562
+ 'params': [],
563
+ };
564
+ const request = this.deepExtend(subscribe, params);
565
+ const ticker = await this.watchMultiple(url, messageHashes, request, messageHashes);
566
+ if (this.newUpdates) {
567
+ const result = {};
568
+ result[ticker['symbol']] = ticker;
569
+ return result;
570
+ }
571
+ return this.filterByArray(this.tickers, 'symbol', symbols);
572
+ }
529
573
  async watchTrades(symbol, since = undefined, limit = undefined, params = {}) {
530
574
  /**
531
575
  * @method
@@ -12,6 +12,9 @@ export default class woofipro extends woofiproRest {
12
12
  handleTicker(client: Client, message: any): any;
13
13
  watchTickers(symbols?: Strings, params?: {}): Promise<Tickers>;
14
14
  handleTickers(client: Client, message: any): void;
15
+ watchBidsAsks(symbols?: Strings, params?: {}): Promise<Tickers>;
16
+ handleBidAsk(client: Client, message: any): void;
17
+ parseWsBidAsk(ticker: any, market?: any): Ticker;
15
18
  watchOHLCV(symbol: string, timeframe?: string, since?: Int, limit?: Int, params?: {}): Promise<OHLCV[]>;
16
19
  handleOHLCV(client: Client, message: any): void;
17
20
  watchTrades(symbol: string, since?: Int, limit?: Int, params?: {}): Promise<Trade[]>;