ccxt 4.4.7__py2.py3-none-any.whl → 4.4.9__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.
Files changed (47) hide show
  1. ccxt/__init__.py +1 -1
  2. ccxt/abstract/bigone.py +1 -1
  3. ccxt/abstract/binance.py +1 -0
  4. ccxt/abstract/binancecoinm.py +1 -0
  5. ccxt/abstract/binanceus.py +1 -0
  6. ccxt/abstract/binanceusdm.py +1 -0
  7. ccxt/abstract/bybit.py +5 -0
  8. ccxt/abstract/kucoinfutures.py +5 -0
  9. ccxt/abstract/okx.py +2 -0
  10. ccxt/async_support/__init__.py +1 -1
  11. ccxt/async_support/base/exchange.py +1 -1
  12. ccxt/async_support/bigone.py +35 -86
  13. ccxt/async_support/binance.py +49 -12
  14. ccxt/async_support/bingx.py +7 -2
  15. ccxt/async_support/bitget.py +3 -0
  16. ccxt/async_support/bybit.py +354 -2
  17. ccxt/async_support/gate.py +30 -1
  18. ccxt/async_support/htx.py +22 -0
  19. ccxt/async_support/kucoin.py +1 -0
  20. ccxt/async_support/kucoinfutures.py +152 -3
  21. ccxt/async_support/okx.py +4 -0
  22. ccxt/base/exchange.py +1 -1
  23. ccxt/bigone.py +35 -86
  24. ccxt/binance.py +49 -12
  25. ccxt/bingx.py +7 -2
  26. ccxt/bitget.py +3 -0
  27. ccxt/bybit.py +354 -2
  28. ccxt/gate.py +30 -1
  29. ccxt/htx.py +22 -0
  30. ccxt/kucoin.py +1 -0
  31. ccxt/kucoinfutures.py +152 -3
  32. ccxt/okx.py +4 -0
  33. ccxt/pro/__init__.py +1 -1
  34. ccxt/pro/binance.py +3 -3
  35. ccxt/pro/bitmart.py +72 -0
  36. ccxt/pro/bitvavo.py +87 -2
  37. ccxt/pro/blofin.py +59 -0
  38. ccxt/pro/hitbtc.py +112 -44
  39. ccxt/pro/hollaex.py +5 -0
  40. ccxt/pro/okx.py +12 -1
  41. ccxt/pro/p2b.py +33 -2
  42. ccxt/pro/whitebit.py +29 -1
  43. {ccxt-4.4.7.dist-info → ccxt-4.4.9.dist-info}/METADATA +4 -4
  44. {ccxt-4.4.7.dist-info → ccxt-4.4.9.dist-info}/RECORD +47 -47
  45. {ccxt-4.4.7.dist-info → ccxt-4.4.9.dist-info}/LICENSE.txt +0 -0
  46. {ccxt-4.4.7.dist-info → ccxt-4.4.9.dist-info}/WHEEL +0 -0
  47. {ccxt-4.4.7.dist-info → ccxt-4.4.9.dist-info}/top_level.txt +0 -0
ccxt/kucoinfutures.py CHANGED
@@ -5,7 +5,7 @@
5
5
 
6
6
  from ccxt.kucoin import kucoin
7
7
  from ccxt.abstract.kucoinfutures import ImplicitAPI
8
- from ccxt.base.types import Balances, Currency, Int, LeverageTier, MarginModification, Market, Num, Order, OrderBook, OrderRequest, OrderSide, OrderType, Str, Strings, Ticker, Tickers, Trade, TradingFeeInterface, Transaction, TransferEntry
8
+ from ccxt.base.types import Balances, Currency, Int, Leverage, LeverageTier, MarginMode, MarginModification, Market, Num, Order, OrderBook, OrderRequest, OrderSide, OrderType, Str, Strings, Ticker, Tickers, Trade, TradingFeeInterface, Transaction, TransferEntry
9
9
  from typing import List
10
10
  from ccxt.base.errors import AuthenticationError
11
11
  from ccxt.base.errors import PermissionDenied
@@ -80,9 +80,10 @@ class kucoinfutures(kucoin, ImplicitAPI):
80
80
  'fetchIsolatedBorrowRates': False,
81
81
  'fetchL3OrderBook': True,
82
82
  'fetchLedger': True,
83
+ 'fetchLeverage': True,
83
84
  'fetchLeverageTiers': False,
84
85
  'fetchMarginAdjustmentHistory': False,
85
- 'fetchMarginMode': False,
86
+ 'fetchMarginMode': True,
86
87
  'fetchMarketLeverageTiers': True,
87
88
  'fetchMarkets': True,
88
89
  'fetchMarkOHLCV': False,
@@ -106,7 +107,7 @@ class kucoinfutures(kucoin, ImplicitAPI):
106
107
  'fetchTransactionFee': False,
107
108
  'fetchWithdrawals': True,
108
109
  'setLeverage': False,
109
- 'setMarginMode': False,
110
+ 'setMarginMode': True,
110
111
  'transfer': True,
111
112
  'withdraw': None,
112
113
  },
@@ -183,12 +184,15 @@ class kucoinfutures(kucoin, ImplicitAPI):
183
184
  'trade-fees': 1,
184
185
  'history-positions': 1,
185
186
  'getMaxOpenSize': 1,
187
+ 'getCrossUserLeverage': 1,
188
+ 'position/getMarginMode': 1,
186
189
  },
187
190
  'post': {
188
191
  'withdrawals': 1,
189
192
  'transfer-out': 1, # v2
190
193
  'transfer-in': 1,
191
194
  'orders': 1.33,
195
+ 'st-orders': 1.33,
192
196
  'orders/test': 1.33,
193
197
  'position/margin/auto-deposit-status': 1,
194
198
  'position/margin/deposit-margin': 1,
@@ -196,6 +200,8 @@ class kucoinfutures(kucoin, ImplicitAPI):
196
200
  'bullet-private': 1,
197
201
  'sub/api-key': 1,
198
202
  'sub/api-key/update': 1,
203
+ 'changeCrossUserLeverage': 1,
204
+ 'position/changeMarginMode': 1,
199
205
  },
200
206
  'delete': {
201
207
  'withdrawals/{withdrawalId}': 1,
@@ -330,9 +336,13 @@ class kucoinfutures(kucoin, ImplicitAPI):
330
336
  'futuresPrivate': {
331
337
  'GET': {
332
338
  'getMaxOpenSize': 'v2',
339
+ 'getCrossUserLeverage': 'v2',
340
+ 'position/getMarginMode': 'v2',
333
341
  },
334
342
  'POST': {
335
343
  'transfer-out': 'v2',
344
+ 'changeCrossUserLeverage': 'v2',
345
+ 'position/changeMarginMode': 'v2',
336
346
  },
337
347
  },
338
348
  'futuresPublic': {
@@ -2721,3 +2731,142 @@ class kucoinfutures(kucoin, ImplicitAPI):
2721
2731
  'percentage': True,
2722
2732
  'tierBased': True,
2723
2733
  }
2734
+
2735
+ def fetch_margin_mode(self, symbol: str, params={}) -> MarginMode:
2736
+ """
2737
+ fetches the margin mode of a trading pair
2738
+ :see: https://www.kucoin.com/docs/rest/futures-trading/positions/get-margin-mode
2739
+ :param str symbol: unified symbol of the market to fetch the margin mode for
2740
+ :param dict [params]: extra parameters specific to the exchange API endpoint
2741
+ :returns dict: a `margin mode structure <https://docs.ccxt.com/#/?id=margin-mode-structure>`
2742
+ """
2743
+ self.load_markets()
2744
+ market = self.market(symbol)
2745
+ request: dict = {
2746
+ 'symbol': market['id'],
2747
+ }
2748
+ response = self.futuresPrivateGetPositionGetMarginMode(self.extend(request, params))
2749
+ #
2750
+ # {
2751
+ # "code": "200000",
2752
+ # "data": {
2753
+ # "symbol": "XBTUSDTM",
2754
+ # "marginMode": "ISOLATED"
2755
+ # }
2756
+ # }
2757
+ #
2758
+ data = self.safe_dict(response, 'data', {})
2759
+ return self.parse_margin_mode(data, market)
2760
+
2761
+ def parse_margin_mode(self, marginMode: dict, market=None) -> MarginMode:
2762
+ marginType = self.safe_string(marginMode, 'marginMode')
2763
+ marginType = 'isolated' if (marginType == 'ISOLATED') else 'cross'
2764
+ return {
2765
+ 'info': marginMode,
2766
+ 'symbol': market['symbol'],
2767
+ 'marginMode': marginType,
2768
+ }
2769
+
2770
+ def set_margin_mode(self, marginMode: str, symbol: Str = None, params={}):
2771
+ """
2772
+ set margin mode to 'cross' or 'isolated'
2773
+ :see: https://www.kucoin.com/docs/rest/futures-trading/positions/modify-margin-mode
2774
+ :param str marginMode: 'cross' or 'isolated'
2775
+ :param str symbol: unified market symbol
2776
+ :param dict [params]: extra parameters specific to the exchange API endpoint
2777
+ :returns dict: response from the exchange
2778
+ """
2779
+ if symbol is None:
2780
+ raise ArgumentsRequired(self.id + ' setMarginMode() requires a symbol argument')
2781
+ self.check_required_argument('setMarginMode', marginMode, 'marginMode', ['cross', 'isolated'])
2782
+ self.load_markets()
2783
+ market = self.market(symbol)
2784
+ request: dict = {
2785
+ 'symbol': market['id'],
2786
+ 'marginMode': marginMode.upper(),
2787
+ }
2788
+ response = self.futuresPrivatePostPositionChangeMarginMode(self.extend(request, params))
2789
+ #
2790
+ # {
2791
+ # "code": "200000",
2792
+ # "data": {
2793
+ # "symbol": "XBTUSDTM",
2794
+ # "marginMode": "ISOLATED"
2795
+ # }
2796
+ # }
2797
+ #
2798
+ data = self.safe_dict(response, 'data', {})
2799
+ return self.parse_margin_mode(data, market)
2800
+
2801
+ def fetch_leverage(self, symbol: str, params={}) -> Leverage:
2802
+ """
2803
+ fetch the set leverage for a market
2804
+ :see: https://www.kucoin.com/docs/rest/futures-trading/positions/get-cross-margin-leverage
2805
+ :param str symbol: unified market symbol
2806
+ :param dict [params]: extra parameters specific to the exchange API endpoint
2807
+ :returns dict: a `leverage structure <https://docs.ccxt.com/#/?id=leverage-structure>`
2808
+ """
2809
+ marginMode = None
2810
+ marginMode, params = self.handle_margin_mode_and_params(symbol, params)
2811
+ if marginMode != 'cross':
2812
+ raise NotSupported(self.id + ' fetchLeverage() currently supports only params["marginMode"] = "cross"')
2813
+ self.load_markets()
2814
+ market = self.market(symbol)
2815
+ request: dict = {
2816
+ 'symbol': market['id'],
2817
+ }
2818
+ response = self.futuresPrivateGetGetCrossUserLeverage(self.extend(request, params))
2819
+ #
2820
+ # {
2821
+ # "code": "200000",
2822
+ # "data": {
2823
+ # "symbol": "XBTUSDTM",
2824
+ # "leverage": "3"
2825
+ # }
2826
+ # }
2827
+ #
2828
+ data = self.safe_dict(response, 'data', {})
2829
+ parsed = self.parse_leverage(data, market)
2830
+ return self.extend(parsed, {
2831
+ 'marginMode': marginMode,
2832
+ })
2833
+
2834
+ def set_leverage(self, leverage: Int, symbol: Str = None, params={}):
2835
+ """
2836
+ set the level of leverage for a market
2837
+ :see: https://www.kucoin.com/docs/rest/futures-trading/positions/modify-cross-margin-leverage
2838
+ :param float leverage: the rate of leverage
2839
+ :param str symbol: unified market symbol
2840
+ :param dict [params]: extra parameters specific to the exchange API endpoint
2841
+ :returns dict: response from the exchange
2842
+ """
2843
+ marginMode = None
2844
+ marginMode, params = self.handle_margin_mode_and_params(symbol, params)
2845
+ if marginMode != 'cross':
2846
+ raise NotSupported(self.id + ' setLeverage() currently supports only params["marginMode"] = "cross"')
2847
+ self.load_markets()
2848
+ market = self.market(symbol)
2849
+ request: dict = {
2850
+ 'symbol': market['id'],
2851
+ 'leverage': str(leverage),
2852
+ }
2853
+ response = self.futuresPrivatePostChangeCrossUserLeverage(self.extend(request, params))
2854
+ #
2855
+ # {
2856
+ # "code": "200000",
2857
+ # "data": True
2858
+ # }
2859
+ #
2860
+ return self.parse_leverage(response, market)
2861
+
2862
+ def parse_leverage(self, leverage: dict, market: Market = None) -> Leverage:
2863
+ marketId = self.safe_string(leverage, 'symbol')
2864
+ market = self.safe_market(marketId, market)
2865
+ leverageNum = self.safe_integer(leverage, 'leverage')
2866
+ return {
2867
+ 'info': leverage,
2868
+ 'symbol': market['symbol'],
2869
+ 'marginMode': None,
2870
+ 'longLeverage': leverageNum,
2871
+ 'shortLeverage': leverageNum,
2872
+ }
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.7'
7
+ __version__ = '4.4.9'
8
8
 
9
9
  # ----------------------------------------------------------------------------
10
10
 
ccxt/pro/binance.py CHANGED
@@ -2417,13 +2417,13 @@ class binance(ccxt.async_support.binance):
2417
2417
  subType, params = self.handle_sub_type_and_params('watchBalance', None, params)
2418
2418
  isPortfolioMargin = None
2419
2419
  isPortfolioMargin, params = self.handle_option_and_params_2(params, 'watchBalance', 'papi', 'portfolioMargin', False)
2420
- urlType = type
2421
- if isPortfolioMargin:
2422
- urlType = 'papi'
2423
2420
  if self.isLinear(type, subType):
2424
2421
  type = 'future'
2425
2422
  elif self.isInverse(type, subType):
2426
2423
  type = 'delivery'
2424
+ urlType = type
2425
+ if isPortfolioMargin:
2426
+ urlType = 'papi'
2427
2427
  url = self.urls['api']['ws'][urlType] + '/' + self.options[type]['listenKey']
2428
2428
  client = self.client(url)
2429
2429
  self.set_balance_cache(client, type, isPortfolioMargin)
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