ccxt 4.4.1__py2.py3-none-any.whl → 4.4.3__py2.py3-none-any.whl

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.
ccxt/pro/kucoin.py CHANGED
@@ -11,7 +11,6 @@ from typing import List
11
11
  from typing import Any
12
12
  from ccxt.base.errors import ExchangeError
13
13
  from ccxt.base.errors import ArgumentsRequired
14
- from ccxt.base.errors import UnsubscribeError
15
14
 
16
15
 
17
16
  class kucoin(ccxt.async_support.kucoin):
@@ -502,8 +501,6 @@ class kucoin(ccxt.async_support.kucoin):
502
501
  unWatches trades stream
503
502
  :see: https://www.kucoin.com/docs/websocket/spot-trading/public-channels/match-execution-data
504
503
  :param str symbol: unified symbol of the market to fetch trades for
505
- :param int [since]: timestamp in ms of the earliest trade to fetch
506
- :param int [limit]: the maximum amount of trades to fetch
507
504
  :param dict [params]: extra parameters specific to the exchange API endpoint
508
505
  :returns dict[]: a list of `trade structures <https://docs.ccxt.com/#/?id=public-trades>`
509
506
  """
@@ -851,46 +848,8 @@ class kucoin(ccxt.async_support.kucoin):
851
848
  for i in range(0, len(messageHashes)):
852
849
  messageHash = messageHashes[i]
853
850
  subHash = subMessageHashes[i]
854
- if messageHash in client.subscriptions:
855
- del client.subscriptions[messageHash]
856
- if subHash in client.subscriptions:
857
- del client.subscriptions[subHash]
858
- error = UnsubscribeError(self.id + ' ' + subHash)
859
- client.reject(error, subHash)
860
- client.resolve(True, messageHash)
861
- self.clean_cache(subscription)
862
-
863
- def clean_cache(self, subscription: dict):
864
- topic = self.safe_string(subscription, 'topic')
865
- symbols = self.safe_list(subscription, 'symbols', [])
866
- symbolsLength = len(symbols)
867
- if symbolsLength > 0:
868
- for i in range(0, len(symbols)):
869
- symbol = symbols[i]
870
- if topic == 'trades':
871
- if symbol in self.trades:
872
- del self.trades[symbol]
873
- elif topic == 'orderbook':
874
- if symbol in self.orderbooks:
875
- del self.orderbooks[symbol]
876
- elif topic == 'ticker':
877
- if symbol in self.tickers:
878
- del self.tickers[symbol]
879
- else:
880
- if topic == 'myTrades':
881
- # don't reset self.myTrades directly here
882
- # because in c# we need to use a different object
883
- keys = list(self.myTrades.keys())
884
- for i in range(0, len(keys)):
885
- del self.myTrades[keys[i]]
886
- elif topic == 'orders':
887
- orderSymbols = list(self.orders.keys())
888
- for i in range(0, len(orderSymbols)):
889
- del self.orders[orderSymbols[i]]
890
- elif topic == 'ticker':
891
- tickerSymbols = list(self.tickers.keys())
892
- for i in range(0, len(tickerSymbols)):
893
- del self.tickers[tickerSymbols[i]]
851
+ self.clean_unsubscription(client, subHash, messageHash)
852
+ self.clean_cache(subscription)
894
853
 
895
854
  def handle_system_status(self, client: Client, message):
896
855
  #
ccxt/pro/kucoinfutures.py CHANGED
@@ -8,6 +8,7 @@ from ccxt.async_support.base.ws.cache import ArrayCache, ArrayCacheBySymbolById,
8
8
  from ccxt.base.types import Balances, Int, Order, OrderBook, Position, Str, Strings, Ticker, Tickers, Trade
9
9
  from ccxt.async_support.base.ws.client import Client
10
10
  from typing import List
11
+ from typing import Any
11
12
  from ccxt.base.errors import ExchangeError
12
13
  from ccxt.base.errors import ArgumentsRequired
13
14
 
@@ -175,6 +176,24 @@ class kucoinfutures(ccxt.async_support.kucoinfutures):
175
176
  }
176
177
  return await self.watch_multiple(url, messageHashes, self.extend(request, params), subscriptionHashes, subscriptionArgs)
177
178
 
179
+ async def un_subscribe_multiple(self, url, messageHashes, topic, subscriptionHashes, params={}, subscription: dict = None):
180
+ requestId = str(self.request_id())
181
+ request: dict = {
182
+ 'id': requestId,
183
+ 'type': 'unsubscribe',
184
+ 'topic': topic,
185
+ 'response': True,
186
+ }
187
+ message = self.extend(request, params)
188
+ if subscription is not None:
189
+ subscription[requestId] = requestId
190
+ client = self.client(url)
191
+ for i in range(0, len(subscriptionHashes)):
192
+ subscriptionHash = subscriptionHashes[i]
193
+ if not (subscriptionHash in client.subscriptions):
194
+ client.subscriptions[requestId] = subscriptionHash
195
+ return await self.watch_multiple(url, messageHashes, message, subscriptionHashes, subscription)
196
+
178
197
  async def watch_ticker(self, symbol: str, params={}) -> Ticker:
179
198
  """
180
199
  watches a price ticker, a statistical calculation with the information calculated over the past 24 hours for a specific market
@@ -526,6 +545,44 @@ class kucoinfutures(ccxt.async_support.kucoinfutures):
526
545
  limit = trades.getLimit(tradeSymbol, limit)
527
546
  return self.filter_by_since_limit(trades, since, limit, 'timestamp', True)
528
547
 
548
+ async def un_watch_trades(self, symbol: str, params={}) -> Any:
549
+ """
550
+ unWatches trades stream
551
+ :see: https://docs.kucoin.com/futures/#execution-data
552
+ :param str symbol: unified symbol of the market to fetch trades for
553
+ :param dict [params]: extra parameters specific to the exchange API endpoint
554
+ :returns dict[]: a list of `trade structures <https://docs.ccxt.com/#/?id=public-trades>`
555
+ """
556
+ return await self.un_watch_trades_for_symbols([symbol], params)
557
+
558
+ async def un_watch_trades_for_symbols(self, symbols: List[str], params={}) -> Any:
559
+ """
560
+ get the list of most recent trades for a particular symbol
561
+ :param str symbol: unified symbol of the market to fetch trades for
562
+ :param dict [params]: extra parameters specific to the exchange API endpoint
563
+ :returns dict[]: a list of `trade structures <https://docs.ccxt.com/#/?id=public-trades>`
564
+ """
565
+ await self.load_markets()
566
+ symbols = self.market_symbols(symbols, None, False)
567
+ url = await self.negotiate(False)
568
+ symbols = self.market_symbols(symbols)
569
+ marketIds = self.market_ids(symbols)
570
+ topic = '/contractMarket/execution:' + ','.join(marketIds)
571
+ subscriptionHashes = []
572
+ messageHashes = []
573
+ for i in range(0, len(symbols)):
574
+ symbol = symbols[i]
575
+ messageHashes.append('unsubscribe:trades:' + symbol)
576
+ subscriptionHashes.append('trades:' + symbol)
577
+ subscription = {
578
+ 'messageHashes': messageHashes,
579
+ 'subMessageHashes': subscriptionHashes,
580
+ 'topic': 'trades',
581
+ 'unsubscribe': True,
582
+ 'symbols': symbols,
583
+ }
584
+ return await self.un_subscribe_multiple(url, messageHashes, topic, messageHashes, params, subscription)
585
+
529
586
  def handle_trade(self, client: Client, message):
530
587
  #
531
588
  # {
@@ -651,6 +708,7 @@ class kucoinfutures(ccxt.async_support.kucoinfutures):
651
708
  async def watch_order_book_for_symbols(self, symbols: List[str], limit: Int = None, params={}) -> OrderBook:
652
709
  """
653
710
  watches information on open orders with bid(buy) and ask(sell) prices, volumes and other data
711
+ :see: https://docs.kucoin.com/futures/#level-2-market-data
654
712
  :param str[] symbols: unified array of symbols
655
713
  :param int [limit]: the maximum amount of order book entries to return
656
714
  :param dict [params]: extra parameters specific to the exchange API endpoint
@@ -680,6 +738,43 @@ class kucoinfutures(ccxt.async_support.kucoinfutures):
680
738
  orderbook = await self.subscribe_multiple(url, messageHashes, topic, subscriptionHashes, subscriptionArgs, params)
681
739
  return orderbook.limit()
682
740
 
741
+ async def un_watch_order_book(self, symbol: str, params={}) -> Any:
742
+ """
743
+ unWatches information on open orders with bid(buy) and ask(sell) prices, volumes and other data
744
+ :see: https://docs.kucoin.com/futures/#level-2-market-data
745
+ :param str symbol: unified symbol of the market to fetch the order book for
746
+ :param dict [params]: extra parameters specific to the exchange API endpoint
747
+ :returns dict: A dictionary of `order book structures <https://docs.ccxt.com/#/?id=order-book-structure>` indexed by market symbols
748
+ """
749
+ return await self.un_watch_order_book_for_symbols([symbol], params)
750
+
751
+ async def un_watch_order_book_for_symbols(self, symbols: List[str], params={}) -> Any:
752
+ """
753
+ unWatches information on open orders with bid(buy) and ask(sell) prices, volumes and other data
754
+ :param str[] symbols: unified array of symbols
755
+ :param dict [params]: extra parameters specific to the exchange API endpoint
756
+ :returns dict: A dictionary of `order book structures <https://docs.ccxt.com/#/?id=order-book-structure>` indexed by market symbols
757
+ """
758
+ await self.load_markets()
759
+ symbols = self.market_symbols(symbols)
760
+ marketIds = self.market_ids(symbols)
761
+ url = await self.negotiate(False)
762
+ topic = '/contractMarket/level2:' + ','.join(marketIds)
763
+ subscriptionHashes = []
764
+ messageHashes = []
765
+ for i in range(0, len(symbols)):
766
+ symbol = symbols[i]
767
+ messageHashes.append('unsubscribe:orderbook:' + symbol)
768
+ subscriptionHashes.append('orderbook:' + symbol)
769
+ subscription = {
770
+ 'messageHashes': messageHashes,
771
+ 'symbols': symbols,
772
+ 'unsubscribe': True,
773
+ 'topic': 'orderbook',
774
+ 'subMessageHashes': subscriptionHashes,
775
+ }
776
+ return await self.un_subscribe_multiple(url, messageHashes, topic, messageHashes, params, subscription)
777
+
683
778
  def handle_delta(self, orderbook, delta):
684
779
  orderbook['nonce'] = self.safe_integer(delta, 'sequence')
685
780
  timestamp = self.safe_integer(delta, 'timestamp')
@@ -1067,6 +1162,32 @@ class kucoinfutures(ccxt.async_support.kucoinfutures):
1067
1162
  self.options['urls'][type] = None
1068
1163
  self.handle_errors(None, None, client.url, None, None, data, message, None, None)
1069
1164
 
1165
+ def handle_subscription_status(self, client: Client, message):
1166
+ #
1167
+ # {
1168
+ # "id": "1578090438322",
1169
+ # "type": "ack"
1170
+ # }
1171
+ #
1172
+ id = self.safe_string(message, 'id')
1173
+ if not (id in client.subscriptions):
1174
+ return
1175
+ subscriptionHash = self.safe_string(client.subscriptions, id)
1176
+ subscription = self.safe_value(client.subscriptions, subscriptionHash)
1177
+ del client.subscriptions[id]
1178
+ method = self.safe_value(subscription, 'method')
1179
+ if method is not None:
1180
+ method(client, message, subscription)
1181
+ isUnSub = self.safe_bool(subscription, 'unsubscribe', False)
1182
+ if isUnSub:
1183
+ messageHashes = self.safe_list(subscription, 'messageHashes', [])
1184
+ subMessageHashes = self.safe_list(subscription, 'subMessageHashes', [])
1185
+ for i in range(0, len(messageHashes)):
1186
+ messageHash = messageHashes[i]
1187
+ subHash = subMessageHashes[i]
1188
+ self.clean_unsubscription(client, subHash, messageHash)
1189
+ self.clean_cache(subscription)
1190
+
1070
1191
  def handle_message(self, client: Client, message):
1071
1192
  type = self.safe_string(message, 'type')
1072
1193
  methods: dict = {
@@ -1075,6 +1196,7 @@ class kucoinfutures(ccxt.async_support.kucoinfutures):
1075
1196
  'message': self.handle_subject,
1076
1197
  'pong': self.handle_pong,
1077
1198
  'error': self.handle_error_message,
1199
+ 'ack': self.handle_subscription_status,
1078
1200
  }
1079
1201
  method = self.safe_value(methods, type)
1080
1202
  if method is not None:
ccxt/pro/mexc.py CHANGED
@@ -6,7 +6,7 @@
6
6
  import ccxt.async_support
7
7
  from ccxt.async_support.base.ws.cache import ArrayCache, ArrayCacheBySymbolById, ArrayCacheByTimestamp
8
8
  import hashlib
9
- from ccxt.base.types import Balances, Int, Order, OrderBook, Str, Ticker, Trade
9
+ from ccxt.base.types import Balances, Int, Order, OrderBook, Str, Strings, Ticker, Tickers, Trade
10
10
  from ccxt.async_support.base.ws.client import Client
11
11
  from typing import List
12
12
  from ccxt.base.errors import AuthenticationError
@@ -33,7 +33,7 @@ class mexc(ccxt.async_support.mexc):
33
33
  'watchOrderBook': True,
34
34
  'watchOrders': True,
35
35
  'watchTicker': True,
36
- 'watchTickers': False,
36
+ 'watchTickers': True,
37
37
  'watchTrades': True,
38
38
  'watchTradesForSymbols': False,
39
39
  },
@@ -126,6 +126,77 @@ class mexc(ccxt.async_support.mexc):
126
126
  messageHash = 'ticker:' + symbol
127
127
  client.resolve(ticker, messageHash)
128
128
 
129
+ async def watch_tickers(self, symbols: Strings = None, params={}) -> Tickers:
130
+ """
131
+ watches a price ticker, a statistical calculation with the information calculated over the past 24 hours for all markets of a specific list
132
+ :see: https://mexcdevelop.github.io/apidocs/spot_v3_en/#individual-symbol-book-ticker-streams
133
+ :see: https://mexcdevelop.github.io/apidocs/contract_v1_en/#public-channels
134
+ :param str[] symbols: unified symbol of the market to fetch the ticker for
135
+ :param dict [params]: extra parameters specific to the exchange API endpoint
136
+ :returns dict: a `ticker structure <https://docs.ccxt.com/#/?id=ticker-structure>`
137
+ """
138
+ await self.load_markets()
139
+ symbols = self.market_symbols(symbols, None, False)
140
+ messageHashes = []
141
+ marketIds = self.market_ids(symbols)
142
+ firstMarket = self.market(symbols[0])
143
+ isSpot = firstMarket['spot']
144
+ url = self.urls['api']['ws']['spot'] if (isSpot) else self.urls['api']['ws']['swap']
145
+ request: dict = {}
146
+ if isSpot:
147
+ topics = []
148
+ for i in range(0, len(marketIds)):
149
+ marketId = marketIds[i]
150
+ messageHashes.append('ticker:' + symbols[i])
151
+ topics.append('spot@public.bookTicker.v3.api@' + marketId)
152
+ request['method'] = 'SUBSCRIPTION'
153
+ request['params'] = topics
154
+ else:
155
+ request['method'] = 'sub.tickers'
156
+ request['params'] = {}
157
+ messageHashes.append('ticker')
158
+ ticker = await self.watch_multiple(url, messageHashes, self.extend(request, params), messageHashes)
159
+ if isSpot and self.newUpdates:
160
+ result: dict = {}
161
+ result[ticker['symbol']] = ticker
162
+ return result
163
+ return self.filter_by_array(self.tickers, 'symbol', symbols)
164
+
165
+ def handle_tickers(self, client: Client, message):
166
+ #
167
+ # {
168
+ # "channel": "push.tickers",
169
+ # "data": [
170
+ # {
171
+ # "symbol": "ETH_USDT",
172
+ # "lastPrice": 2324.5,
173
+ # "riseFallRate": 0.0356,
174
+ # "fairPrice": 2324.32,
175
+ # "indexPrice": 2325.44,
176
+ # "volume24": 25868309,
177
+ # "amount24": 591752573.9792,
178
+ # "maxBidPrice": 2557.98,
179
+ # "minAskPrice": 2092.89,
180
+ # "lower24Price": 2239.39,
181
+ # "high24Price": 2332.59,
182
+ # "timestamp": 1725872514111
183
+ # }
184
+ # ],
185
+ # "ts": 1725872514111
186
+ # }
187
+ #
188
+ data = self.safe_list(message, 'data')
189
+ topic = 'ticker'
190
+ result = []
191
+ for i in range(0, len(data)):
192
+ ticker = self.parse_ticker(data[i])
193
+ symbol = ticker['symbol']
194
+ self.tickers[symbol] = ticker
195
+ result.append(ticker)
196
+ messageHash = topic + ':' + symbol
197
+ client.resolve(ticker, messageHash)
198
+ client.resolve(result, topic)
199
+
129
200
  def parse_ws_ticker(self, ticker, market=None):
130
201
  #
131
202
  # spot
@@ -1073,8 +1144,8 @@ class mexc(ccxt.async_support.mexc):
1073
1144
  # "code": 0,
1074
1145
  # "msg": "spot@public.increase.depth.v3.api@BTCUSDT"
1075
1146
  # }
1076
- #
1077
- msg = self.safe_string(message, 'msg')
1147
+ # Set the default to an empty string if the message is empty during the test.
1148
+ msg = self.safe_string(message, 'msg', '')
1078
1149
  if msg == 'PONG':
1079
1150
  self.handle_pong(client, message)
1080
1151
  elif msg.find('@') > -1:
@@ -1110,6 +1181,7 @@ class mexc(ccxt.async_support.mexc):
1110
1181
  'push.kline': self.handle_ohlcv,
1111
1182
  'public.bookTicker.v3.api': self.handle_ticker,
1112
1183
  'push.ticker': self.handle_ticker,
1184
+ 'push.tickers': self.handle_tickers,
1113
1185
  'public.increase.depth.v3.api': self.handle_order_book,
1114
1186
  'push.depth': self.handle_order_book,
1115
1187
  'private.orders.v3.api': self.handle_order,
ccxt/pro/okx.py CHANGED
@@ -16,7 +16,6 @@ from ccxt.base.errors import ArgumentsRequired
16
16
  from ccxt.base.errors import BadRequest
17
17
  from ccxt.base.errors import InvalidNonce
18
18
  from ccxt.base.errors import ChecksumError
19
- from ccxt.base.errors import UnsubscribeError
20
19
 
21
20
 
22
21
  class okx(ccxt.async_support.okx):
@@ -1341,8 +1340,10 @@ class okx(ccxt.async_support.okx):
1341
1340
  },
1342
1341
  ],
1343
1342
  }
1344
- message = self.extend(request, params)
1345
- self.watch(url, messageHash, message, messageHash)
1343
+ # Only add params['access'] to prevent sending custom parameters, such.
1344
+ if 'access' in params:
1345
+ request['access'] = params['access']
1346
+ self.watch(url, messageHash, request, messageHash)
1346
1347
  return await future
1347
1348
 
1348
1349
  async def watch_balance(self, params={}) -> Balances:
@@ -1500,7 +1501,7 @@ class okx(ccxt.async_support.okx):
1500
1501
  'channel': 'positions',
1501
1502
  'instType': 'ANY',
1502
1503
  }
1503
- args = [arg]
1504
+ args = [self.extend(arg, params)]
1504
1505
  nonSymbolRequest: dict = {
1505
1506
  'op': 'subscribe',
1506
1507
  'args': args,
@@ -2162,54 +2163,32 @@ class okx(ccxt.async_support.okx):
2162
2163
  def handle_un_subscription_trades(self, client: Client, symbol: str):
2163
2164
  subMessageHash = 'trades:' + symbol
2164
2165
  messageHash = 'unsubscribe:trades:' + symbol
2165
- if subMessageHash in client.subscriptions:
2166
- del client.subscriptions[subMessageHash]
2167
- if messageHash in client.subscriptions:
2168
- del client.subscriptions[messageHash]
2169
- del self.trades[symbol]
2170
- error = UnsubscribeError(self.id + ' ' + subMessageHash)
2171
- client.reject(error, subMessageHash)
2172
- client.resolve(True, messageHash)
2166
+ self.clean_unsubscription(client, subMessageHash, messageHash)
2167
+ if symbol in self.trades:
2168
+ del self.trades[symbol]
2173
2169
 
2174
2170
  def handle_unsubscription_order_book(self, client: Client, symbol: str, channel: str):
2175
2171
  subMessageHash = channel + ':' + symbol
2176
2172
  messageHash = 'unsubscribe:orderbook:' + symbol
2177
- if subMessageHash in client.subscriptions:
2178
- del client.subscriptions[subMessageHash]
2179
- if messageHash in client.subscriptions:
2180
- del client.subscriptions[messageHash]
2181
- del self.orderbooks[symbol]
2182
- error = UnsubscribeError(self.id + ' ' + subMessageHash)
2183
- client.reject(error, subMessageHash)
2184
- client.resolve(True, messageHash)
2173
+ self.clean_unsubscription(client, subMessageHash, messageHash)
2174
+ if symbol in self.orderbooks:
2175
+ del self.orderbooks[symbol]
2185
2176
 
2186
2177
  def handle_unsubscription_ohlcv(self, client: Client, symbol: str, channel: str):
2187
2178
  tf = channel.replace('candle', '')
2188
2179
  timeframe = self.find_timeframe(tf)
2189
2180
  subMessageHash = 'multi:' + channel + ':' + symbol
2190
2181
  messageHash = 'unsubscribe:' + subMessageHash
2191
- if subMessageHash in client.subscriptions:
2192
- del client.subscriptions[subMessageHash]
2193
- if messageHash in client.subscriptions:
2194
- del client.subscriptions[messageHash]
2182
+ self.clean_unsubscription(client, subMessageHash, messageHash)
2195
2183
  if timeframe in self.ohlcvs[symbol]:
2196
2184
  del self.ohlcvs[symbol][timeframe]
2197
- error = UnsubscribeError(self.id + ' ' + subMessageHash)
2198
- client.reject(error, subMessageHash)
2199
- client.resolve(True, messageHash)
2200
2185
 
2201
2186
  def handle_unsubscription_ticker(self, client: Client, symbol: str, channel):
2202
2187
  subMessageHash = channel + '::' + symbol
2203
2188
  messageHash = 'unsubscribe:ticker:' + symbol
2204
- if subMessageHash in client.subscriptions:
2205
- del client.subscriptions[subMessageHash]
2206
- if messageHash in client.subscriptions:
2207
- del client.subscriptions[messageHash]
2189
+ self.clean_unsubscription(client, subMessageHash, messageHash)
2208
2190
  if symbol in self.tickers:
2209
2191
  del self.tickers[symbol]
2210
- error = UnsubscribeError(self.id + ' ' + subMessageHash)
2211
- client.reject(error, subMessageHash)
2212
- client.resolve(True, messageHash)
2213
2192
 
2214
2193
  def handle_unsubscription(self, client: Client, message):
2215
2194
  #
ccxt/pro/oxfun.py CHANGED
@@ -30,6 +30,7 @@ class oxfun(ccxt.async_support.oxfun):
30
30
  'watchMyTrades': False,
31
31
  'watchTicker': True,
32
32
  'watchTickers': True,
33
+ 'watchBidsAsks': True,
33
34
  'watchBalance': True,
34
35
  'createOrderWs': True,
35
36
  'editOrderWs': True,
@@ -466,6 +467,73 @@ class oxfun(ccxt.async_support.oxfun):
466
467
  self.tickers[symbol] = ticker
467
468
  client.resolve(ticker, messageHash)
468
469
 
470
+ async def watch_bids_asks(self, symbols: Strings = None, params={}) -> Tickers:
471
+ """
472
+ :see: https://docs.ox.fun/?json#best-bid-ask
473
+ watches best bid & ask for symbols
474
+ :param str[] symbols: unified symbol of the market to fetch the ticker for
475
+ :param dict [params]: extra parameters specific to the exchange API endpoint
476
+ :returns dict: a `ticker structure <https://docs.ccxt.com/#/?id=ticker-structure>`
477
+ """
478
+ await self.load_markets()
479
+ symbols = self.market_symbols(symbols, None, False)
480
+ messageHashes = []
481
+ args = []
482
+ for i in range(0, len(symbols)):
483
+ market = self.market(symbols[i])
484
+ args.append('bestBidAsk:' + market['id'])
485
+ messageHashes.append('bidask:' + market['symbol'])
486
+ newTickers = await self.subscribe_multiple(messageHashes, args, params)
487
+ if self.newUpdates:
488
+ tickers: dict = {}
489
+ tickers[newTickers['symbol']] = newTickers
490
+ return tickers
491
+ return self.filter_by_array(self.bidsasks, 'symbol', symbols)
492
+
493
+ def handle_bid_ask(self, client: Client, message):
494
+ #
495
+ # {
496
+ # "table": "bestBidAsk",
497
+ # "data": {
498
+ # "ask": [
499
+ # 19045.0,
500
+ # 1.0
501
+ # ],
502
+ # "checksum": 3790706311,
503
+ # "marketCode": "BTC-USD-SWAP-LIN",
504
+ # "bid": [
505
+ # 19015.0,
506
+ # 1.0
507
+ # ],
508
+ # "timestamp": "1665456882928"
509
+ # }
510
+ # }
511
+ #
512
+ data = self.safe_dict(message, 'data', {})
513
+ parsedTicker = self.parse_ws_bid_ask(data)
514
+ symbol = parsedTicker['symbol']
515
+ self.bidsasks[symbol] = parsedTicker
516
+ messageHash = 'bidask:' + symbol
517
+ client.resolve(parsedTicker, messageHash)
518
+
519
+ def parse_ws_bid_ask(self, ticker, market=None):
520
+ marketId = self.safe_string(ticker, 'marketCode')
521
+ market = self.safe_market(marketId, market)
522
+ symbol = self.safe_string(market, 'symbol')
523
+ timestamp = self.safe_integer(ticker, 'timestamp')
524
+ ask = self.safe_list(ticker, 'ask', [])
525
+ bid = self.safe_list(ticker, 'bid', [])
526
+ return self.safe_ticker({
527
+ 'symbol': symbol,
528
+ 'timestamp': timestamp,
529
+ 'datetime': self.iso8601(timestamp),
530
+ 'ask': self.safe_number(ask, 0),
531
+ 'askVolume': self.safe_number(ask, 1),
532
+ 'bid': self.safe_number(bid, 0),
533
+ 'bidVolume': self.safe_number(bid, 1),
534
+ 'info': ticker,
535
+ }, market)
536
+
469
537
  async def watch_balance(self, params={}) -> Balances:
470
538
  """
471
539
  :see: https://docs.ox.fun/?json#balance-channel
@@ -943,6 +1011,8 @@ class oxfun(ccxt.async_support.oxfun):
943
1011
  self.handle_positions(client, message)
944
1012
  if table.find('order') > -1:
945
1013
  self.handle_orders(client, message)
1014
+ if table == 'bestBidAsk':
1015
+ self.handle_bid_ask(client, message)
946
1016
  else:
947
1017
  if event == 'login':
948
1018
  self.handle_authentication_message(client, message)
ccxt/pro/phemex.py CHANGED
@@ -6,7 +6,7 @@
6
6
  import ccxt.async_support
7
7
  from ccxt.async_support.base.ws.cache import ArrayCache, ArrayCacheBySymbolById, ArrayCacheByTimestamp
8
8
  import hashlib
9
- from ccxt.base.types import Balances, Int, Order, OrderBook, Str, Ticker, Trade
9
+ from ccxt.base.types import Balances, Int, Order, OrderBook, Str, Strings, Ticker, Tickers, Trade
10
10
  from ccxt.async_support.base.ws.client import Client
11
11
  from typing import List
12
12
  from ccxt.base.errors import AuthenticationError
@@ -20,7 +20,7 @@ class phemex(ccxt.async_support.phemex):
20
20
  'has': {
21
21
  'ws': True,
22
22
  'watchTicker': True,
23
- 'watchTickers': False, # for now
23
+ 'watchTickers': True,
24
24
  'watchTrades': True,
25
25
  'watchMyTrades': True,
26
26
  'watchOrders': True,
@@ -504,6 +504,45 @@ class phemex(ccxt.async_support.phemex):
504
504
  request = self.deep_extend(subscribe, params)
505
505
  return await self.watch(url, messageHash, request, subscriptionHash)
506
506
 
507
+ async def watch_tickers(self, symbols: Strings = None, params={}) -> Tickers:
508
+ """
509
+ :see: https://github.com/phemex/phemex-api-docs/blob/master/Public-Hedged-Perpetual-API.md#subscribe-24-hours-ticker
510
+ :see: https://github.com/phemex/phemex-api-docs/blob/master/Public-Contract-API-en.md#subscribe-24-hours-ticker
511
+ :see: https://github.com/phemex/phemex-api-docs/blob/master/Public-Spot-API-en.md#subscribe-24-hours-ticker
512
+ watches a price ticker, a statistical calculation with the information calculated over the past 24 hours for all markets of a specific list
513
+ :param str[] [symbols]: unified symbol of the market to fetch the ticker for
514
+ :param dict [params]: extra parameters specific to the exchange API endpoint
515
+ :param str [params.channel]: the channel to subscribe to, tickers by default. Can be tickers, sprd-tickers, index-tickers, block-tickers
516
+ :returns dict: a `ticker structure <https://docs.ccxt.com/#/?id=ticker-structure>`
517
+ """
518
+ await self.load_markets()
519
+ symbols = self.market_symbols(symbols, None, False)
520
+ first = symbols[0]
521
+ market = self.market(first)
522
+ isSwap = market['swap']
523
+ settleIsUSDT = market['settle'] == 'USDT'
524
+ name = 'spot_market24h'
525
+ if isSwap:
526
+ name = 'perp_market24h_pack_p' if settleIsUSDT else 'market24h'
527
+ url = self.urls['api']['ws']
528
+ requestId = self.request_id()
529
+ subscriptionHash = name + '.subscribe'
530
+ messageHashes = []
531
+ for i in range(0, len(symbols)):
532
+ messageHashes.append('ticker:' + symbols[i])
533
+ subscribe: dict = {
534
+ 'method': subscriptionHash,
535
+ 'id': requestId,
536
+ 'params': [],
537
+ }
538
+ request = self.deep_extend(subscribe, params)
539
+ ticker = await self.watch_multiple(url, messageHashes, request, messageHashes)
540
+ if self.newUpdates:
541
+ result: dict = {}
542
+ result[ticker['symbol']] = ticker
543
+ return result
544
+ return self.filter_by_array(self.tickers, 'symbol', symbols)
545
+
507
546
  async def watch_trades(self, symbol: str, since: Int = None, limit: Int = None, params={}) -> List[Trade]:
508
547
  """
509
548
  :see: https://github.com/phemex/phemex-api-docs/blob/master/Public-Hedged-Perpetual-API.md#subscribe-trade