ccxt 4.4.6__py2.py3-none-any.whl → 4.4.8__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/mexc.py CHANGED
@@ -54,8 +54,8 @@ class mexc(Exchange, ImplicitAPI):
54
54
  'closePosition': False,
55
55
  'createDepositAddress': True,
56
56
  'createMarketBuyOrderWithCost': True,
57
- 'createMarketOrderWithCost': False,
58
- 'createMarketSellOrderWithCost': False,
57
+ 'createMarketOrderWithCost': True,
58
+ 'createMarketSellOrderWithCost': True,
59
59
  'createOrder': True,
60
60
  'createOrders': True,
61
61
  'createPostOnlyOrder': True,
@@ -144,7 +144,7 @@ class mexc(Exchange, ImplicitAPI):
144
144
  'repayCrossMargin': False,
145
145
  'repayIsolatedMargin': False,
146
146
  'setLeverage': True,
147
- 'setMarginMode': None,
147
+ 'setMarginMode': True,
148
148
  'setPositionMode': True,
149
149
  'signIn': None,
150
150
  'transfer': None,
@@ -430,7 +430,6 @@ class mexc(Exchange, ImplicitAPI):
430
430
  'options': {
431
431
  'adjustForTimeDifference': False,
432
432
  'timeDifference': 0,
433
- 'createMarketBuyOrderRequiresPrice': True,
434
433
  'unavailableContracts': {
435
434
  'BTC/USDT:USDT': True,
436
435
  'LTC/USDT:USDT': True,
@@ -479,12 +478,14 @@ class mexc(Exchange, ImplicitAPI):
479
478
  'LTC': 'LTC',
480
479
  },
481
480
  'networks': {
481
+ 'ZKSYNC': 'ZKSYNCERA',
482
482
  'TRC20': 'TRX',
483
483
  'TON': 'TONCOIN',
484
484
  'AVAXC': 'AVAX_CCHAIN',
485
485
  'ERC20': 'ETH',
486
486
  'ACA': 'ACALA',
487
487
  'BEP20': 'BSC',
488
+ 'OPTIMISM': 'OP',
488
489
  # 'ADA': 'Cardano(ADA)',
489
490
  # 'AE': 'AE',
490
491
  # 'ALGO': 'Algorand(ALGO)',
@@ -2018,8 +2019,24 @@ class mexc(Exchange, ImplicitAPI):
2018
2019
  market = self.market(symbol)
2019
2020
  if not market['spot']:
2020
2021
  raise NotSupported(self.id + ' createMarketBuyOrderWithCost() supports spot orders only')
2021
- params['createMarketBuyOrderRequiresPrice'] = False
2022
- return self.create_order(symbol, 'market', 'buy', cost, None, params)
2022
+ params['cost'] = cost
2023
+ return self.create_order(symbol, 'market', 'buy', 0, None, params)
2024
+
2025
+ def create_market_sell_order_with_cost(self, symbol: str, cost: float, params={}):
2026
+ """
2027
+ create a market sell order by providing the symbol and cost
2028
+ :see: https://mexcdevelop.github.io/apidocs/spot_v3_en/#new-order
2029
+ :param str symbol: unified symbol of the market to create an order in
2030
+ :param float cost: how much you want to trade in units of the quote currency
2031
+ :param dict [params]: extra parameters specific to the exchange API endpoint
2032
+ :returns dict: an `order structure <https://docs.ccxt.com/#/?id=order-structure>`
2033
+ """
2034
+ self.load_markets()
2035
+ market = self.market(symbol)
2036
+ if not market['spot']:
2037
+ raise NotSupported(self.id + ' createMarketBuyOrderWithCost() supports spot orders only')
2038
+ params['cost'] = cost
2039
+ return self.create_order(symbol, 'market', 'sell', 0, None, params)
2023
2040
 
2024
2041
  def create_order(self, symbol: str, type: OrderType, side: OrderSide, amount: float, price: Num = None, params={}):
2025
2042
  """
@@ -2043,6 +2060,7 @@ class mexc(Exchange, ImplicitAPI):
2043
2060
  :param long [params.positionId]: *contract only* it is recommended to hasattr(self, fill) parameter when closing a position
2044
2061
  :param str [params.externalOid]: *contract only* external order ID
2045
2062
  :param int [params.positionMode]: *contract only* 1:hedge, 2:one-way, default: the user's current config
2063
+ :param boolean [params.test]: *spot only* whether to use the test endpoint or not, default is False
2046
2064
  :returns dict: an `order structure <https://docs.ccxt.com/#/?id=order-structure>`
2047
2065
  """
2048
2066
  self.load_markets()
@@ -2061,22 +2079,21 @@ class mexc(Exchange, ImplicitAPI):
2061
2079
  'side': orderSide,
2062
2080
  'type': type.upper(),
2063
2081
  }
2064
- if orderSide == 'BUY' and type == 'market':
2065
- createMarketBuyOrderRequiresPrice = True
2066
- createMarketBuyOrderRequiresPrice, params = self.handle_option_and_params(params, 'createOrder', 'createMarketBuyOrderRequiresPrice', True)
2082
+ if type == 'market':
2067
2083
  cost = self.safe_number_2(params, 'cost', 'quoteOrderQty')
2068
2084
  params = self.omit(params, 'cost')
2069
2085
  if cost is not None:
2070
2086
  amount = cost
2071
- elif createMarketBuyOrderRequiresPrice:
2087
+ request['quoteOrderQty'] = self.cost_to_precision(symbol, amount)
2088
+ else:
2072
2089
  if price is None:
2073
- raise InvalidOrder(self.id + ' createOrder() requires the price argument for market buy orders to calculate the total cost to spend(amount * price), alternatively set the createMarketBuyOrderRequiresPrice option or param to False and pass the cost to spend in the amount argument')
2090
+ request['quantity'] = self.amount_to_precision(symbol, amount)
2074
2091
  else:
2075
2092
  amountString = self.number_to_string(amount)
2076
2093
  priceString = self.number_to_string(price)
2077
2094
  quoteAmount = Precise.string_mul(amountString, priceString)
2078
2095
  amount = quoteAmount
2079
- request['quoteOrderQty'] = self.cost_to_precision(symbol, amount)
2096
+ request['quoteOrderQty'] = self.cost_to_precision(symbol, amount)
2080
2097
  else:
2081
2098
  request['quantity'] = self.amount_to_precision(symbol, amount)
2082
2099
  if price is not None:
@@ -2111,8 +2128,14 @@ class mexc(Exchange, ImplicitAPI):
2111
2128
  :returns dict: an `order structure <https://docs.ccxt.com/#/?id=order-structure>`
2112
2129
  """
2113
2130
  self.load_markets()
2131
+ test = self.safe_bool(params, 'test', False)
2132
+ params = self.omit(params, 'test')
2114
2133
  request = self.create_spot_order_request(market, type, side, amount, price, marginMode, params)
2115
- response = self.spotPrivatePostOrder(self.extend(request, params))
2134
+ response = None
2135
+ if test:
2136
+ response = self.spotPrivatePostOrderTest(request)
2137
+ else:
2138
+ response = self.spotPrivatePostOrder(request)
2116
2139
  #
2117
2140
  # spot
2118
2141
  #
@@ -2426,6 +2449,9 @@ class mexc(Exchange, ImplicitAPI):
2426
2449
  def fetch_orders(self, symbol: Str = None, since: Int = None, limit: Int = None, params={}) -> List[Order]:
2427
2450
  """
2428
2451
  fetches information on multiple orders made by the user
2452
+ :see: https://mexcdevelop.github.io/apidocs/spot_v3_en/#all-orders
2453
+ :see: https://mexcdevelop.github.io/apidocs/contract_v1_en/#get-all-of-the-user-39-s-historical-orders
2454
+ :see: https://mexcdevelop.github.io/apidocs/contract_v1_en/#gets-the-trigger-order-list
2429
2455
  :param str symbol: unified market symbol of the market orders were made in
2430
2456
  :param int [since]: the earliest time in ms to fetch orders for
2431
2457
  :param int [limit]: the maximum number of order structures to retrieve
@@ -2639,6 +2665,9 @@ class mexc(Exchange, ImplicitAPI):
2639
2665
  def fetch_open_orders(self, symbol: Str = None, since: Int = None, limit: Int = None, params={}) -> List[Order]:
2640
2666
  """
2641
2667
  fetch all unfilled currently open orders
2668
+ :see: https://mexcdevelop.github.io/apidocs/spot_v3_en/#current-open-orders
2669
+ :see: https://mexcdevelop.github.io/apidocs/contract_v1_en/#get-all-of-the-user-39-s-historical-orders
2670
+ :see: https://mexcdevelop.github.io/apidocs/contract_v1_en/#gets-the-trigger-order-list
2642
2671
  :param str symbol: unified market symbol
2643
2672
  :param int [since]: the earliest time in ms to fetch open orders for
2644
2673
  :param int [limit]: the maximum number of open orders structures to retrieve
@@ -2721,6 +2750,9 @@ class mexc(Exchange, ImplicitAPI):
2721
2750
  def fetch_closed_orders(self, symbol: Str = None, since: Int = None, limit: Int = None, params={}) -> List[Order]:
2722
2751
  """
2723
2752
  fetches information on multiple closed orders made by the user
2753
+ :see: https://mexcdevelop.github.io/apidocs/spot_v3_en/#all-orders
2754
+ :see: https://mexcdevelop.github.io/apidocs/contract_v1_en/#get-all-of-the-user-39-s-historical-orders
2755
+ :see: https://mexcdevelop.github.io/apidocs/contract_v1_en/#gets-the-trigger-order-list
2724
2756
  :param str symbol: unified market symbol of the market orders were made in
2725
2757
  :param int [since]: the earliest time in ms to fetch orders for
2726
2758
  :param int [limit]: the maximum number of order structures to retrieve
@@ -2732,6 +2764,9 @@ class mexc(Exchange, ImplicitAPI):
2732
2764
  def fetch_canceled_orders(self, symbol: Str = None, since: Int = None, limit: Int = None, params={}):
2733
2765
  """
2734
2766
  fetches information on multiple canceled orders made by the user
2767
+ :see: https://mexcdevelop.github.io/apidocs/spot_v3_en/#all-orders
2768
+ :see: https://mexcdevelop.github.io/apidocs/contract_v1_en/#get-all-of-the-user-39-s-historical-orders
2769
+ :see: https://mexcdevelop.github.io/apidocs/contract_v1_en/#gets-the-trigger-order-list
2735
2770
  :param str symbol: unified market symbol of the market orders were made in
2736
2771
  :param int [since]: timestamp in ms of the earliest order, default is None
2737
2772
  :param int [limit]: max number of orders to return, default is None
@@ -5285,6 +5320,43 @@ class mexc(Exchange, ImplicitAPI):
5285
5320
  positions = self.parse_positions(data, symbols, params)
5286
5321
  return self.filter_by_since_limit(positions, since, limit)
5287
5322
 
5323
+ def set_margin_mode(self, marginMode: str, symbol: Str = None, params={}):
5324
+ """
5325
+ set margin mode to 'cross' or 'isolated'
5326
+ :see: https://mexcdevelop.github.io/apidocs/contract_v1_en/#switch-leverage
5327
+ :param str marginMode: 'cross' or 'isolated'
5328
+ :param str [symbol]: required when there is no position, else provide params["positionId"]
5329
+ :param dict [params]: extra parameters specific to the exchange API endpoint
5330
+ :param str [params.positionId]: required when a position is set
5331
+ :param str [params.direction]: "long" or "short" required when there is no position
5332
+ :returns dict: response from the exchange
5333
+ """
5334
+ self.load_markets()
5335
+ market = self.market(symbol)
5336
+ if market['spot']:
5337
+ raise BadSymbol(self.id + ' setMarginMode() supports contract markets only')
5338
+ marginMode = marginMode.lower()
5339
+ if marginMode != 'isolated' and marginMode != 'cross':
5340
+ raise BadRequest(self.id + ' setMarginMode() marginMode argument should be isolated or cross')
5341
+ leverage = self.safe_integer(params, 'leverage')
5342
+ if leverage is None:
5343
+ raise ArgumentsRequired(self.id + ' setMarginMode() requires a leverage parameter')
5344
+ direction = self.safe_string_lower_2(params, 'direction', 'positionId')
5345
+ request: dict = {
5346
+ 'leverage': leverage,
5347
+ 'openType': 1 if (marginMode == 'isolated') else 2,
5348
+ }
5349
+ if symbol is not None:
5350
+ request['symbol'] = market['id']
5351
+ if direction is not None:
5352
+ request['positionType'] = 2 if (direction == 'short') else 1
5353
+ params = self.omit(params, 'direction')
5354
+ response = self.contractPrivatePostPositionChangeLeverage(self.extend(request, params))
5355
+ #
5356
+ # {success: True, code: '0'}
5357
+ #
5358
+ return self.parse_leverage(response, market)
5359
+
5288
5360
  def nonce(self):
5289
5361
  return self.milliseconds() - self.safe_integer(self.options, 'timeDifference', 0)
5290
5362
 
ccxt/okx.py CHANGED
@@ -290,6 +290,7 @@ class okx(Exchange, ImplicitAPI):
290
290
  'copytrading/public-preference-currency': 4,
291
291
  'copytrading/public-current-subpositions': 4,
292
292
  'copytrading/public-subpositions-history': 4,
293
+ 'support/announcements-types': 20,
293
294
  },
294
295
  },
295
296
  'private': {
@@ -435,6 +436,7 @@ class okx(Exchange, ImplicitAPI):
435
436
  # affiliate
436
437
  'affiliate/invitee/detail': 1,
437
438
  'users/partner/if-rebate': 1,
439
+ 'support/announcements': 4,
438
440
  },
439
441
  'post': {
440
442
  # rfq
@@ -814,6 +816,8 @@ class okx(Exchange, ImplicitAPI):
814
816
  # SPOT/MARGIN error codes 54000-54999
815
817
  '54000': ExchangeError, # Margin transactions unavailable
816
818
  '54001': ExchangeError, # Only Multi-currency margin account can be set to borrow coins automatically
819
+ '54008': InvalidOrder, # This operation is disabled by the 'mass cancel order' endpoint. Please enable it using self endpoint.
820
+ '54009': InvalidOrder, # The range of {param0} should be [{param1}, {param2}].
817
821
  '54011': InvalidOrder, # 200 Pre-market trading contracts are only allowed to reduce the number of positions within 1 hour before delivery. Please modify or cancel the order.
818
822
  # Trading bot Error Code from 55100 to 55999
819
823
  '55100': InvalidOrder, # Take profit % should be within the range of {parameter1}-{parameter2}
ccxt/pro/__init__.py CHANGED
@@ -4,7 +4,7 @@
4
4
 
5
5
  # ----------------------------------------------------------------------------
6
6
 
7
- __version__ = '4.4.6'
7
+ __version__ = '4.4.8'
8
8
 
9
9
  # ----------------------------------------------------------------------------
10
10
 
ccxt/pro/bitget.py CHANGED
@@ -42,6 +42,7 @@ class bitget(ccxt.async_support.bitget):
42
42
  'watchOrders': True,
43
43
  'watchTicker': True,
44
44
  'watchTickers': True,
45
+ 'watchBidsAsks': True,
45
46
  'watchTrades': True,
46
47
  'watchTradesForSymbols': True,
47
48
  'watchPositions': True,
@@ -209,6 +210,7 @@ class bitget(ccxt.async_support.bitget):
209
210
  # "ts": 1701842994341
210
211
  # }
211
212
  #
213
+ self.handle_bid_ask(client, message)
212
214
  ticker = self.parse_ws_ticker(message)
213
215
  symbol = ticker['symbol']
214
216
  self.tickers[symbol] = ticker
@@ -320,6 +322,66 @@ class bitget(ccxt.async_support.bitget):
320
322
  'info': ticker,
321
323
  }, market)
322
324
 
325
+ async def watch_bids_asks(self, symbols: Strings = None, params={}) -> Tickers:
326
+ """
327
+ :see: https://www.bitget.com/api-doc/spot/websocket/public/Tickers-Channel
328
+ :see: https://www.bitget.com/api-doc/contract/websocket/public/Tickers-Channel
329
+ watches best bid & ask for symbols
330
+ :param str[] symbols: unified symbol of the market to fetch the ticker for
331
+ :param dict [params]: extra parameters specific to the exchange API endpoint
332
+ :returns dict: a `ticker structure <https://docs.ccxt.com/#/?id=ticker-structure>`
333
+ """
334
+ await self.load_markets()
335
+ symbols = self.market_symbols(symbols, None, False)
336
+ market = self.market(symbols[0])
337
+ instType = None
338
+ instType, params = self.get_inst_type(market, params)
339
+ topics = []
340
+ messageHashes = []
341
+ for i in range(0, len(symbols)):
342
+ symbol = symbols[i]
343
+ marketInner = self.market(symbol)
344
+ args: dict = {
345
+ 'instType': instType,
346
+ 'channel': 'ticker',
347
+ 'instId': marketInner['id'],
348
+ }
349
+ topics.append(args)
350
+ messageHashes.append('bidask:' + symbol)
351
+ tickers = await self.watch_public_multiple(messageHashes, topics, params)
352
+ if self.newUpdates:
353
+ result: dict = {}
354
+ result[tickers['symbol']] = tickers
355
+ return result
356
+ return self.filter_by_array(self.bidsasks, 'symbol', symbols)
357
+
358
+ def handle_bid_ask(self, client: Client, message):
359
+ ticker = self.parse_ws_bid_ask(message)
360
+ symbol = ticker['symbol']
361
+ self.bidsasks[symbol] = ticker
362
+ messageHash = 'bidask:' + symbol
363
+ client.resolve(ticker, messageHash)
364
+
365
+ def parse_ws_bid_ask(self, message, market=None):
366
+ arg = self.safe_value(message, 'arg', {})
367
+ data = self.safe_value(message, 'data', [])
368
+ ticker = self.safe_value(data, 0, {})
369
+ timestamp = self.safe_integer(ticker, 'ts')
370
+ instType = self.safe_string(arg, 'instType')
371
+ marketType = 'spot' if (instType == 'SPOT') else 'contract'
372
+ marketId = self.safe_string(ticker, 'instId')
373
+ market = self.safe_market(marketId, market, None, marketType)
374
+ return self.safe_ticker({
375
+ 'symbol': market['symbol'],
376
+ 'timestamp': timestamp,
377
+ 'datetime': self.iso8601(timestamp),
378
+ 'ask': self.safe_string(ticker, 'askPr'),
379
+ 'askVolume': self.safe_string(ticker, 'askSz'),
380
+ 'bid': self.safe_string(ticker, 'bidPr'),
381
+ 'bidVolume': self.safe_string(ticker, 'bidSz'),
382
+ 'info': ticker,
383
+ }, market)
384
+
323
385
  async def watch_ohlcv(self, symbol: str, timeframe='1m', since: Int = None, limit: Int = None, params={}) -> List[list]:
324
386
  """
325
387
  watches historical candlestick data containing the open, high, low, close price, and the volume of a market
ccxt/pro/bitmart.py CHANGED
@@ -31,6 +31,7 @@ class bitmart(ccxt.async_support.bitmart):
31
31
  'watchBalance': True,
32
32
  'watchTicker': True,
33
33
  'watchTickers': True,
34
+ 'watchBidsAsks': True,
34
35
  'watchOrderBook': True,
35
36
  'watchOrderBookForSymbols': True,
36
37
  'watchOrders': True,
@@ -314,6 +315,7 @@ class bitmart(ccxt.async_support.bitmart):
314
315
 
315
316
  async def watch_tickers(self, symbols: Strings = None, params={}) -> Tickers:
316
317
  """
318
+ :see: https://developer-pro.bitmart.com/en/spot/#public-ticker-channel
317
319
  :see: https://developer-pro.bitmart.com/en/futuresv2/#public-ticker-channel
318
320
  watches a price ticker, a statistical calculation with the information calculated over the past 24 hours for all markets of a specific list
319
321
  :param str[] symbols: unified symbol of the market to fetch the ticker for
@@ -331,6 +333,75 @@ class bitmart(ccxt.async_support.bitmart):
331
333
  return tickers
332
334
  return self.filter_by_array(self.tickers, 'symbol', symbols)
333
335
 
336
+ async def watch_bids_asks(self, symbols: Strings = None, params={}) -> Tickers:
337
+ """
338
+ :see: https://developer-pro.bitmart.com/en/spot/#public-ticker-channel
339
+ :see: https://developer-pro.bitmart.com/en/futuresv2/#public-ticker-channel
340
+ watches best bid & ask for symbols
341
+ :param str[] symbols: unified symbol of the market to fetch the ticker for
342
+ :param dict [params]: extra parameters specific to the exchange API endpoint
343
+ :returns dict: a `ticker structure <https://docs.ccxt.com/#/?id=ticker-structure>`
344
+ """
345
+ await self.load_markets()
346
+ symbols = self.market_symbols(symbols, None, False)
347
+ firstMarket = self.get_market_from_symbols(symbols)
348
+ marketType = None
349
+ marketType, params = self.handle_market_type_and_params('watchBidsAsks', firstMarket, params)
350
+ url = self.implode_hostname(self.urls['api']['ws'][marketType]['public'])
351
+ channelType = 'spot' if (marketType == 'spot') else 'futures'
352
+ actionType = 'op' if (marketType == 'spot') else 'action'
353
+ rawSubscriptions = []
354
+ messageHashes = []
355
+ for i in range(0, len(symbols)):
356
+ market = self.market(symbols[i])
357
+ rawSubscriptions.append(channelType + '/ticker:' + market['id'])
358
+ messageHashes.append('bidask:' + symbols[i])
359
+ if marketType != 'spot':
360
+ rawSubscriptions = [channelType + '/ticker']
361
+ request: dict = {
362
+ 'args': rawSubscriptions,
363
+ }
364
+ request[actionType] = 'subscribe'
365
+ newTickers = await self.watch_multiple(url, messageHashes, request, rawSubscriptions)
366
+ if self.newUpdates:
367
+ tickers: dict = {}
368
+ tickers[newTickers['symbol']] = newTickers
369
+ return tickers
370
+ return self.filter_by_array(self.bidsasks, 'symbol', symbols)
371
+
372
+ def handle_bid_ask(self, client: Client, message):
373
+ table = self.safe_string(message, 'table')
374
+ isSpot = (table is not None)
375
+ rawTickers = []
376
+ if isSpot:
377
+ rawTickers = self.safe_list(message, 'data', [])
378
+ else:
379
+ rawTickers = [self.safe_value(message, 'data', {})]
380
+ if not len(rawTickers):
381
+ return
382
+ for i in range(0, len(rawTickers)):
383
+ ticker = self.parse_ws_bid_ask(rawTickers[i])
384
+ symbol = ticker['symbol']
385
+ self.bidsasks[symbol] = ticker
386
+ messageHash = 'bidask:' + symbol
387
+ client.resolve(ticker, messageHash)
388
+
389
+ def parse_ws_bid_ask(self, ticker, market=None):
390
+ marketId = self.safe_string(ticker, 'symbol')
391
+ market = self.safe_market(marketId, market)
392
+ symbol = self.safe_string(market, 'symbol')
393
+ timestamp = self.safe_integer(ticker, 'ms_t')
394
+ return self.safe_ticker({
395
+ 'symbol': symbol,
396
+ 'timestamp': timestamp,
397
+ 'datetime': self.iso8601(timestamp),
398
+ 'ask': self.safe_string_2(ticker, 'ask_px', 'ask_price'),
399
+ 'askVolume': self.safe_string_2(ticker, 'ask_sz', 'ask_vol'),
400
+ 'bid': self.safe_string_2(ticker, 'bid_px', 'bid_price'),
401
+ 'bidVolume': self.safe_string_2(ticker, 'bid_sz', 'bid_vol'),
402
+ 'info': ticker,
403
+ }, market)
404
+
334
405
  async def watch_orders(self, symbol: Str = None, since: Int = None, limit: Int = None, params={}) -> List[Order]:
335
406
  """
336
407
  watches information on multiple orders made by the user
@@ -878,6 +949,7 @@ class bitmart(ccxt.async_support.bitmart):
878
949
  # }
879
950
  # }
880
951
  #
952
+ self.handle_bid_ask(client, message)
881
953
  table = self.safe_string(message, 'table')
882
954
  isSpot = (table is not None)
883
955
  rawTickers = []
ccxt/pro/bitvavo.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, Num, Order, OrderBook, OrderSide, OrderType, Str, Ticker, Trade, TradingFees
9
+ from ccxt.base.types import Balances, Int, Num, Order, OrderBook, OrderSide, OrderType, Str, Strings, Ticker, Tickers, Trade, TradingFees
10
10
  from ccxt.async_support.base.ws.client import Client
11
11
  from typing import List
12
12
  from ccxt.base.errors import ExchangeError
@@ -25,6 +25,8 @@ class bitvavo(ccxt.async_support.bitvavo):
25
25
  'watchOrderBook': True,
26
26
  'watchTrades': True,
27
27
  'watchTicker': True,
28
+ 'watchTickers': True,
29
+ 'watchBidsAsks': True,
28
30
  'watchOHLCV': True,
29
31
  'watchOrders': True,
30
32
  'watchMyTrades': True,
@@ -85,15 +87,51 @@ class bitvavo(ccxt.async_support.bitvavo):
85
87
  message = self.extend(request, params)
86
88
  return await self.watch(url, messageHash, message, messageHash)
87
89
 
90
+ async def watch_public_multiple(self, methodName, channelName: str, symbols, params={}):
91
+ await self.load_markets()
92
+ symbols = self.market_symbols(symbols)
93
+ messageHashes = [methodName]
94
+ args = []
95
+ for i in range(0, len(symbols)):
96
+ market = self.market(symbols[i])
97
+ args.append(market['id'])
98
+ url = self.urls['api']['ws']
99
+ request: dict = {
100
+ 'action': 'subscribe',
101
+ 'channels': [
102
+ {
103
+ 'name': channelName,
104
+ 'markets': args,
105
+ },
106
+ ],
107
+ }
108
+ message = self.extend(request, params)
109
+ return await self.watch_multiple(url, messageHashes, message, messageHashes)
110
+
88
111
  async def watch_ticker(self, symbol: str, params={}) -> Ticker:
89
112
  """
90
113
  watches a price ticker, a statistical calculation with the information calculated over the past 24 hours for a specific market
114
+ :see: https://docs.bitvavo.com/#tag/Market-data-subscription-WebSocket/paths/~1subscribeTicker24h/post
91
115
  :param str symbol: unified symbol of the market to fetch the ticker for
92
116
  :param dict [params]: extra parameters specific to the exchange API endpoint
93
117
  :returns dict: a `ticker structure <https://docs.ccxt.com/#/?id=ticker-structure>`
94
118
  """
95
119
  return await self.watch_public('ticker24h', symbol, params)
96
120
 
121
+ async def watch_tickers(self, symbols: Strings = None, params={}) -> Tickers:
122
+ """
123
+ watches a price ticker, a statistical calculation with the information calculated over the past 24 hours for all markets of a specific list
124
+ :see: https://docs.bitvavo.com/#tag/Market-data-subscription-WebSocket/paths/~1subscribeTicker24h/post
125
+ :param str[] [symbols]: unified symbol of the market to fetch the ticker for
126
+ :param dict [params]: extra parameters specific to the exchange API endpoint
127
+ :returns dict: a `ticker structure <https://docs.ccxt.com/#/?id=ticker-structure>`
128
+ """
129
+ await self.load_markets()
130
+ symbols = self.market_symbols(symbols, None, False)
131
+ channel = 'ticker24h'
132
+ tickers = await self.watch_public_multiple(channel, channel, symbols, params)
133
+ return self.filter_by_array(tickers, 'symbol', symbols)
134
+
97
135
  def handle_ticker(self, client: Client, message):
98
136
  #
99
137
  # {
@@ -116,8 +154,10 @@ class bitvavo(ccxt.async_support.bitvavo):
116
154
  # ]
117
155
  # }
118
156
  #
157
+ self.handle_bid_ask(client, message)
119
158
  event = self.safe_string(message, 'event')
120
159
  tickers = self.safe_value(message, 'data', [])
160
+ result = []
121
161
  for i in range(0, len(tickers)):
122
162
  data = tickers[i]
123
163
  marketId = self.safe_string(data, 'market')
@@ -126,8 +166,53 @@ class bitvavo(ccxt.async_support.bitvavo):
126
166
  ticker = self.parse_ticker(data, market)
127
167
  symbol = ticker['symbol']
128
168
  self.tickers[symbol] = ticker
169
+ result.append(ticker)
129
170
  client.resolve(ticker, messageHash)
130
- return message
171
+ client.resolve(result, event)
172
+
173
+ async def watch_bids_asks(self, symbols: Strings = None, params={}) -> Tickers:
174
+ """
175
+ watches best bid & ask for symbols
176
+ :see: https://docs.bitvavo.com/#tag/Market-data-subscription-WebSocket/paths/~1subscribeTicker24h/post
177
+ :param str[] symbols: unified symbol of the market to fetch the ticker for
178
+ :param dict [params]: extra parameters specific to the exchange API endpoint
179
+ :returns dict: a `ticker structure <https://docs.ccxt.com/#/?id=ticker-structure>`
180
+ """
181
+ await self.load_markets()
182
+ symbols = self.market_symbols(symbols, None, False)
183
+ channel = 'ticker24h'
184
+ tickers = await self.watch_public_multiple('bidask', channel, symbols, params)
185
+ return self.filter_by_array(tickers, 'symbol', symbols)
186
+
187
+ def handle_bid_ask(self, client: Client, message):
188
+ event = 'bidask'
189
+ tickers = self.safe_value(message, 'data', [])
190
+ result = []
191
+ for i in range(0, len(tickers)):
192
+ data = tickers[i]
193
+ ticker = self.parse_ws_bid_ask(data)
194
+ symbol = ticker['symbol']
195
+ self.bidsasks[symbol] = ticker
196
+ result.append(ticker)
197
+ messageHash = event + ':' + symbol
198
+ client.resolve(ticker, messageHash)
199
+ client.resolve(result, event)
200
+
201
+ def parse_ws_bid_ask(self, ticker, market=None):
202
+ marketId = self.safe_string(ticker, 'market')
203
+ market = self.safe_market(marketId, None, '-')
204
+ symbol = self.safe_string(market, 'symbol')
205
+ timestamp = self.safe_integer(ticker, 'timestamp')
206
+ return self.safe_ticker({
207
+ 'symbol': symbol,
208
+ 'timestamp': timestamp,
209
+ 'datetime': self.iso8601(timestamp),
210
+ 'ask': self.safe_number(ticker, 'ask'),
211
+ 'askVolume': self.safe_number(ticker, 'askSize'),
212
+ 'bid': self.safe_number(ticker, 'bid'),
213
+ 'bidVolume': self.safe_number(ticker, 'bidSize'),
214
+ 'info': ticker,
215
+ }, market)
131
216
 
132
217
  async def watch_trades(self, symbol: str, since: Int = None, limit: Int = None, params={}) -> List[Trade]:
133
218
  """
ccxt/pro/blofin.py CHANGED
@@ -26,6 +26,7 @@ class blofin(ccxt.async_support.blofin):
26
26
  'watchOrderBookForSymbols': True,
27
27
  'watchTicker': True,
28
28
  'watchTickers': True,
29
+ 'watchBidsAsks': True,
29
30
  'watchOHLCV': True,
30
31
  'watchOHLCVForSymbols': True,
31
32
  'watchOrders': True,
@@ -256,6 +257,7 @@ class blofin(ccxt.async_support.blofin):
256
257
  # ],
257
258
  # }
258
259
  #
260
+ self.handle_bid_ask(client, message)
259
261
  arg = self.safe_dict(message, 'arg')
260
262
  channelName = self.safe_string(arg, 'channel')
261
263
  data = self.safe_list(message, 'data')
@@ -269,6 +271,63 @@ class blofin(ccxt.async_support.blofin):
269
271
  def parse_ws_ticker(self, ticker, market: Market = None) -> Ticker:
270
272
  return self.parse_ticker(ticker, market)
271
273
 
274
+ async def watch_bids_asks(self, symbols: Strings = None, params={}) -> Tickers:
275
+ """
276
+ watches best bid & ask for symbols
277
+ :see: https://docs.blofin.com/index.html#ws-tickers-channel
278
+ :param str[] symbols: unified symbol of the market to fetch the ticker for
279
+ :param dict [params]: extra parameters specific to the exchange API endpoint
280
+ :returns dict: a `ticker structure <https://docs.ccxt.com/#/?id=ticker-structure>`
281
+ """
282
+ await self.load_markets()
283
+ symbols = self.market_symbols(symbols, None, False)
284
+ firstMarket = self.market(symbols[0])
285
+ channel = 'tickers'
286
+ marketType = None
287
+ marketType, params = self.handle_market_type_and_params('watchBidsAsks', firstMarket, params)
288
+ url = self.implode_hostname(self.urls['api']['ws'][marketType]['public'])
289
+ messageHashes = []
290
+ args = []
291
+ for i in range(0, len(symbols)):
292
+ market = self.market(symbols[i])
293
+ messageHashes.append('bidask:' + market['symbol'])
294
+ args.append({
295
+ 'channel': channel,
296
+ 'instId': market['id'],
297
+ })
298
+ request = self.get_subscription_request(args)
299
+ ticker = await self.watch_multiple(url, messageHashes, self.deep_extend(request, params), messageHashes)
300
+ if self.newUpdates:
301
+ tickers = {}
302
+ tickers[ticker['symbol']] = ticker
303
+ return tickers
304
+ return self.filter_by_array(self.bidsasks, 'symbol', symbols)
305
+
306
+ def handle_bid_ask(self, client: Client, message):
307
+ data = self.safe_list(message, 'data')
308
+ for i in range(0, len(data)):
309
+ ticker = self.parse_ws_bid_ask(data[i])
310
+ symbol = ticker['symbol']
311
+ messageHash = 'bidask:' + symbol
312
+ self.bidsasks[symbol] = ticker
313
+ client.resolve(ticker, messageHash)
314
+
315
+ def parse_ws_bid_ask(self, ticker, market=None):
316
+ marketId = self.safe_string(ticker, 'instId')
317
+ market = self.safe_market(marketId, market, '-')
318
+ symbol = self.safe_string(market, 'symbol')
319
+ timestamp = self.safe_integer(ticker, 'ts')
320
+ return self.safe_ticker({
321
+ 'symbol': symbol,
322
+ 'timestamp': timestamp,
323
+ 'datetime': self.iso8601(timestamp),
324
+ 'ask': self.safe_string(ticker, 'askPrice'),
325
+ 'askVolume': self.safe_string(ticker, 'askSize'),
326
+ 'bid': self.safe_string(ticker, 'bidPrice'),
327
+ 'bidVolume': self.safe_string(ticker, 'bidSize'),
328
+ 'info': ticker,
329
+ }, market)
330
+
272
331
  async def watch_ohlcv(self, symbol: str, timeframe='1m', since: Int = None, limit: Int = None, params={}) -> List[list]:
273
332
  """
274
333
  watches historical candlestick data containing the open, high, low, and close price, and the volume of a market