ccxt 4.3.72__py2.py3-none-any.whl → 4.3.74__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.

Potentially problematic release.


This version of ccxt might be problematic. Click here for more details.

ccxt/pro/alpaca.py CHANGED
@@ -56,6 +56,7 @@ class alpaca(ccxt.async_support.alpaca):
56
56
  async def watch_ticker(self, symbol: str, params={}) -> Ticker:
57
57
  """
58
58
  watches a price ticker, a statistical calculation with the information calculated over the past 24 hours for a specific market
59
+ :see: https://docs.alpaca.markets/docs/real-time-crypto-pricing-data#quotes
59
60
  :param str symbol: unified symbol of the market to fetch the ticker for
60
61
  :param dict [params]: extra parameters specific to the exchange API endpoint
61
62
  :returns dict: a `ticker structure <https://docs.ccxt.com/#/?id=ticker-structure>`
@@ -129,6 +130,7 @@ class alpaca(ccxt.async_support.alpaca):
129
130
  async def watch_ohlcv(self, symbol: str, timeframe='1m', since: Int = None, limit: Int = None, params={}) -> List[list]:
130
131
  """
131
132
  watches historical candlestick data containing the open, high, low, and close price, and the volume of a market
133
+ :see: https://docs.alpaca.markets/docs/real-time-crypto-pricing-data#bars
132
134
  :param str symbol: unified symbol of the market to fetch OHLCV data for
133
135
  :param str timeframe: the length of time each candle represents
134
136
  :param int [since]: timestamp in ms of the earliest candle to fetch
@@ -181,6 +183,7 @@ class alpaca(ccxt.async_support.alpaca):
181
183
  async def watch_order_book(self, symbol: str, limit: Int = None, params={}) -> OrderBook:
182
184
  """
183
185
  watches information on open orders with bid(buy) and ask(sell) prices, volumes and other data
186
+ :see: https://docs.alpaca.markets/docs/real-time-crypto-pricing-data#orderbooks
184
187
  :param str symbol: unified symbol of the market to fetch the order book for
185
188
  :param int [limit]: the maximum amount of order book entries to return.
186
189
  :param dict [params]: extra parameters specific to the exchange API endpoint
@@ -254,6 +257,7 @@ class alpaca(ccxt.async_support.alpaca):
254
257
  async def watch_trades(self, symbol: str, since: Int = None, limit: Int = None, params={}) -> List[Trade]:
255
258
  """
256
259
  watches information on multiple trades made in a market
260
+ :see: https://docs.alpaca.markets/docs/real-time-crypto-pricing-data#trades
257
261
  :param str symbol: unified market symbol of the market trades were made in
258
262
  :param int [since]: the earliest time in ms to fetch orders for
259
263
  :param int [limit]: the maximum number of trade structures to retrieve
@@ -302,6 +306,7 @@ class alpaca(ccxt.async_support.alpaca):
302
306
  async def watch_my_trades(self, symbol: Str = None, since: Int = None, limit: Int = None, params={}) -> List[Trade]:
303
307
  """
304
308
  watches information on multiple trades made by the user
309
+ :see: https://docs.alpaca.markets/docs/websocket-streaming#trade-updates
305
310
  :param str symbol: unified market symbol of the market trades were made in
306
311
  :param int [since]: the earliest time in ms to fetch trades for
307
312
  :param int [limit]: the maximum number of trade structures to retrieve
ccxt/pro/binance.py CHANGED
@@ -864,10 +864,15 @@ class binance(ccxt.async_support.binance):
864
864
  async def watch_trades_for_symbols(self, symbols: List[str], since: Int = None, limit: Int = None, params={}) -> List[Trade]:
865
865
  """
866
866
  get the list of most recent trades for a list of symbols
867
+ :see: https://binance-docs.github.io/apidocs/spot/en/#aggregate-trade-streams
868
+ :see: https://binance-docs.github.io/apidocs/spot/en/#trade-streams
869
+ :see: https://binance-docs.github.io/apidocs/futures/en/#aggregate-trade-streams
870
+ :see: https://binance-docs.github.io/apidocs/delivery/en/#aggregate-trade-streams
867
871
  :param str[] symbols: unified symbol of the market to fetch trades for
868
872
  :param int [since]: timestamp in ms of the earliest trade to fetch
869
873
  :param int [limit]: the maximum amount of trades to fetch
870
874
  :param dict [params]: extra parameters specific to the exchange API endpoint
875
+ :param str [params.name]: the name of the method to call, 'trade' or 'aggTrade', default is 'trade'
871
876
  :returns dict[]: a list of `trade structures <https://docs.ccxt.com/#/?id=public-trades>`
872
877
  """
873
878
  await self.load_markets()
@@ -878,8 +883,9 @@ class binance(ccxt.async_support.binance):
878
883
  if symbolsLength > 200:
879
884
  raise BadRequest(self.id + ' watchTradesForSymbols() accepts 200 symbols at most. To watch more symbols call watchTradesForSymbols() multiple times')
880
885
  streamHash += '::' + ','.join(symbols)
881
- options = self.safe_value(self.options, 'watchTradesForSymbols', {})
882
- name = self.safe_string(options, 'name', 'trade')
886
+ name = None
887
+ name, params = self.handle_option_and_params(params, 'watchTradesForSymbols', 'name', 'trade')
888
+ params = self.omit(params, 'callerMethodName')
883
889
  firstMarket = self.market(symbols[0])
884
890
  type = firstMarket['type']
885
891
  if firstMarket['contract']:
@@ -914,12 +920,18 @@ class binance(ccxt.async_support.binance):
914
920
  async def watch_trades(self, symbol: str, since: Int = None, limit: Int = None, params={}) -> List[Trade]:
915
921
  """
916
922
  get the list of most recent trades for a particular symbol
923
+ :see: https://binance-docs.github.io/apidocs/spot/en/#aggregate-trade-streams
924
+ :see: https://binance-docs.github.io/apidocs/spot/en/#trade-streams
925
+ :see: https://binance-docs.github.io/apidocs/futures/en/#aggregate-trade-streams
926
+ :see: https://binance-docs.github.io/apidocs/delivery/en/#aggregate-trade-streams
917
927
  :param str symbol: unified symbol of the market to fetch trades for
918
928
  :param int [since]: timestamp in ms of the earliest trade to fetch
919
929
  :param int [limit]: the maximum amount of trades to fetch
920
930
  :param dict [params]: extra parameters specific to the exchange API endpoint
931
+ :param str [params.name]: the name of the method to call, 'trade' or 'aggTrade', default is 'trade'
921
932
  :returns dict[]: a list of `trade structures <https://docs.ccxt.com/#/?id=public-trades>`
922
933
  """
934
+ params['callerMethodName'] = 'watchTrades'
923
935
  return await self.watch_trades_for_symbols([symbol], since, limit, params)
924
936
 
925
937
  def parse_ws_trade(self, trade, market=None) -> Trade:
@@ -1098,11 +1110,15 @@ class binance(ccxt.async_support.binance):
1098
1110
  async def watch_ohlcv(self, symbol: str, timeframe='1m', since: Int = None, limit: Int = None, params={}) -> List[list]:
1099
1111
  """
1100
1112
  watches historical candlestick data containing the open, high, low, and close price, and the volume of a market
1113
+ :see: https://binance-docs.github.io/apidocs/spot/en/#kline-candlestick-data
1114
+ :see: https://binance-docs.github.io/apidocs/futures/en/#kline-candlestick-data
1115
+ :see: https://binance-docs.github.io/apidocs/delivery/en/#kline-candlestick-data
1101
1116
  :param str symbol: unified symbol of the market to fetch OHLCV data for
1102
1117
  :param str timeframe: the length of time each candle represents
1103
1118
  :param int [since]: timestamp in ms of the earliest candle to fetch
1104
1119
  :param int [limit]: the maximum amount of candles to fetch
1105
1120
  :param dict [params]: extra parameters specific to the exchange API endpoint
1121
+ :param dict [params.timezone]: if provided, kline intervals are interpreted in that timezone instead of UTC, example '+08:00'
1106
1122
  :returns int[][]: A list of candles ordered, open, high, low, close, volume
1107
1123
  """
1108
1124
  params['callerMethodName'] = 'watchOHLCV'
@@ -1112,10 +1128,14 @@ class binance(ccxt.async_support.binance):
1112
1128
  async def watch_ohlcv_for_symbols(self, symbolsAndTimeframes: List[List[str]], since: Int = None, limit: Int = None, params={}):
1113
1129
  """
1114
1130
  watches historical candlestick data containing the open, high, low, and close price, and the volume of a market
1131
+ :see: https://binance-docs.github.io/apidocs/spot/en/#kline-candlestick-data
1132
+ :see: https://binance-docs.github.io/apidocs/futures/en/#kline-candlestick-data
1133
+ :see: https://binance-docs.github.io/apidocs/delivery/en/#kline-candlestick-data
1115
1134
  :param str[][] symbolsAndTimeframes: array of arrays containing unified symbols and timeframes to fetch OHLCV data for, example [['BTC/USDT', '1m'], ['LTC/USDT', '5m']]
1116
1135
  :param int [since]: timestamp in ms of the earliest candle to fetch
1117
1136
  :param int [limit]: the maximum amount of candles to fetch
1118
1137
  :param dict [params]: extra parameters specific to the exchange API endpoint
1138
+ :param dict [params.timezone]: if provided, kline intervals are interpreted in that timezone instead of UTC, example '+08:00'
1119
1139
  :returns int[][]: A list of candles ordered, open, high, low, close, volume
1120
1140
  """
1121
1141
  await self.load_markets()
@@ -1127,6 +1147,10 @@ class binance(ccxt.async_support.binance):
1127
1147
  type = firstMarket['type']
1128
1148
  if firstMarket['contract']:
1129
1149
  type = 'future' if firstMarket['linear'] else 'delivery'
1150
+ isSpot = (type == 'spot')
1151
+ timezone = None
1152
+ timezone, params = self.handle_param_string(params, 'timezone', None)
1153
+ isUtc8 = (timezone is not None) and ((timezone == '+08:00') or Precise.string_eq(timezone, '8'))
1130
1154
  rawHashes = []
1131
1155
  messageHashes = []
1132
1156
  for i in range(0, len(symbolsAndTimeframes)):
@@ -1139,7 +1163,10 @@ class binance(ccxt.async_support.binance):
1139
1163
  if klineType == 'indexPriceKline':
1140
1164
  # weird behavior for index price kline we can't use the perp suffix
1141
1165
  marketId = marketId.replace('_perp', '')
1142
- rawHashes.append(marketId + '@' + klineType + '_' + interval)
1166
+ shouldUseUTC8 = (isUtc8 and isSpot)
1167
+ suffix = '@+08:00'
1168
+ utcSuffix = suffix if shouldUseUTC8 else ''
1169
+ rawHashes.append(marketId + '@' + klineType + '_' + interval + utcSuffix)
1143
1170
  messageHashes.append('ohlcv::' + symbolString + '::' + timeframeString)
1144
1171
  url = self.urls['api']['ws'][type] + '/' + self.stream(type, 'multipleOHLCV')
1145
1172
  requestId = self.request_id(url)
@@ -1151,6 +1178,7 @@ class binance(ccxt.async_support.binance):
1151
1178
  subscribe = {
1152
1179
  'id': requestId,
1153
1180
  }
1181
+ params = self.omit(params, 'callerMethodName')
1154
1182
  symbol, timeframe, candles = await self.watch_multiple(url, messageHashes, self.extend(request, params), messageHashes, subscribe)
1155
1183
  if self.newUpdates:
1156
1184
  limit = candles.getLimit(symbol, limit)
ccxt/pro/bitfinex.py CHANGED
@@ -62,6 +62,7 @@ class bitfinex(ccxt.async_support.bitfinex):
62
62
  async def watch_trades(self, symbol: str, since: Int = None, limit: Int = None, params={}) -> List[Trade]:
63
63
  """
64
64
  get the list of most recent trades for a particular symbol
65
+ :see: https://docs.bitfinex.com/v1/reference/ws-public-trades
65
66
  :param str symbol: unified symbol of the market to fetch trades for
66
67
  :param int [since]: timestamp in ms of the earliest trade to fetch
67
68
  :param int [limit]: the maximum amount of trades to fetch
@@ -78,6 +79,7 @@ class bitfinex(ccxt.async_support.bitfinex):
78
79
  async def watch_ticker(self, symbol: str, params={}) -> Ticker:
79
80
  """
80
81
  watches a price ticker, a statistical calculation with the information calculated over the past 24 hours for a specific market
82
+ :see: https://docs.bitfinex.com/v1/reference/ws-public-ticker
81
83
  :param str symbol: unified symbol of the market to fetch the ticker for
82
84
  :param dict [params]: extra parameters specific to the exchange API endpoint
83
85
  :returns dict: a `ticker structure <https://docs.ccxt.com/#/?id=ticker-structure>`
@@ -238,6 +240,7 @@ class bitfinex(ccxt.async_support.bitfinex):
238
240
  async def watch_order_book(self, symbol: str, limit: Int = None, params={}) -> OrderBook:
239
241
  """
240
242
  watches information on open orders with bid(buy) and ask(sell) prices, volumes and other data
243
+ :see: https://docs.bitfinex.com/v1/reference/ws-public-order-books
241
244
  :param str symbol: unified symbol of the market to fetch the order book for
242
245
  :param int [limit]: the maximum amount of order book entries to return
243
246
  :param dict [params]: extra parameters specific to the exchange API endpoint
@@ -430,6 +433,8 @@ class bitfinex(ccxt.async_support.bitfinex):
430
433
  async def watch_orders(self, symbol: Str = None, since: Int = None, limit: Int = None, params={}) -> List[Order]:
431
434
  """
432
435
  watches information on multiple orders made by the user
436
+ :see: https://docs.bitfinex.com/v1/reference/ws-auth-order-updates
437
+ :see: https://docs.bitfinex.com/v1/reference/ws-auth-order-snapshots
433
438
  :param str symbol: unified market symbol of the market orders were made in
434
439
  :param int [since]: the earliest time in ms to fetch orders for
435
440
  :param int [limit]: the maximum number of order structures to retrieve
ccxt/pro/kucoin.py CHANGED
@@ -1024,6 +1024,10 @@ class kucoin(ccxt.async_support.kucoin):
1024
1024
  tradeId = self.safe_string(trade, 'tradeId')
1025
1025
  price = self.safe_string(trade, 'matchPrice')
1026
1026
  amount = self.safe_string(trade, 'matchSize')
1027
+ if price is None:
1028
+ # /spot/tradeFills
1029
+ price = self.safe_string(trade, 'price')
1030
+ amount = self.safe_string(trade, 'size')
1027
1031
  order = self.safe_string(trade, 'orderId')
1028
1032
  timestamp = self.safe_integer_product_2(trade, 'ts', 'time', 0.000001)
1029
1033
  feeCurrency = market['quote']
ccxt/pro/woo.py CHANGED
@@ -90,32 +90,48 @@ class woo(ccxt.async_support.woo):
90
90
 
91
91
  async def watch_order_book(self, symbol: str, limit: Int = None, params={}) -> OrderBook:
92
92
  """
93
+ :see: https://docs.woo.org/#orderbookupdate
93
94
  :see: https://docs.woo.org/#orderbook
94
95
  watches information on open orders with bid(buy) and ask(sell) prices, volumes and other data
95
96
  :param str symbol: unified symbol of the market to fetch the order book for
96
97
  :param int [limit]: the maximum amount of order book entries to return.
97
98
  :param dict [params]: extra parameters specific to the exchange API endpoint
99
+ :param str [params.method]: either(default) 'orderbook' or 'orderbookupdate', default is 'orderbook'
98
100
  :returns dict: A dictionary of `order book structures <https://docs.ccxt.com/#/?id=order-book-structure>` indexed by market symbols
99
101
  """
100
102
  await self.load_markets()
101
- name = 'orderbook'
103
+ method = None
104
+ method, params = self.handle_option_and_params(params, 'watchOrderBook', 'method', 'orderbook')
102
105
  market = self.market(symbol)
103
- topic = market['id'] + '@' + name
106
+ topic = market['id'] + '@' + method
107
+ urlUid = '/' + self.uid if (self.uid) else ''
108
+ url = self.urls['api']['ws']['public'] + urlUid
109
+ requestId = self.request_id(url)
104
110
  request: dict = {
105
111
  'event': 'subscribe',
106
112
  'topic': topic,
113
+ 'id': requestId,
107
114
  }
108
- message = self.extend(request, params)
109
- orderbook = await self.watch_public(topic, message)
115
+ subscription: dict = {
116
+ 'id': str(requestId),
117
+ 'name': method,
118
+ 'symbol': symbol,
119
+ 'limit': limit,
120
+ 'params': params,
121
+ }
122
+ if method == 'orderbookupdate':
123
+ subscription['method'] = self.handle_order_book_subscription
124
+ orderbook = await self.watch(url, topic, self.extend(request, params), topic, subscription)
110
125
  return orderbook.limit()
111
126
 
112
127
  def handle_order_book(self, client: Client, message):
113
128
  #
114
129
  # {
115
- # "topic": "PERP_BTC_USDT@orderbook",
116
- # "ts": 1650121915308,
130
+ # "topic": "PERP_BTC_USDT@orderbookupdate",
131
+ # "ts": 1722500373999,
117
132
  # "data": {
118
133
  # "symbol": "PERP_BTC_USDT",
134
+ # "prevTs": 1722500373799,
119
135
  # "bids": [
120
136
  # [
121
137
  # 0.30891,
@@ -136,13 +152,89 @@ class woo(ccxt.async_support.woo):
136
152
  market = self.safe_market(marketId)
137
153
  symbol = market['symbol']
138
154
  topic = self.safe_string(message, 'topic')
139
- if not (symbol in self.orderbooks):
140
- self.orderbooks[symbol] = self.order_book({})
141
- orderbook = self.orderbooks[symbol]
155
+ method = self.safe_string(topic.split('@'), 1)
156
+ if method == 'orderbookupdate':
157
+ if not (symbol in self.orderbooks):
158
+ return
159
+ orderbook = self.orderbooks[symbol]
160
+ timestamp = self.safe_integer(orderbook, 'timestamp')
161
+ if timestamp is None:
162
+ orderbook.cache.append(message)
163
+ else:
164
+ try:
165
+ ts = self.safe_integer(message, 'ts')
166
+ if ts > timestamp:
167
+ self.handle_order_book_message(client, message, orderbook)
168
+ client.resolve(orderbook, topic)
169
+ except Exception as e:
170
+ del self.orderbooks[symbol]
171
+ del client.subscriptions[topic]
172
+ client.reject(e, topic)
173
+ else:
174
+ if not (symbol in self.orderbooks):
175
+ defaultLimit = self.safe_integer(self.options, 'watchOrderBookLimit', 1000)
176
+ subscription = client.subscriptions[topic]
177
+ limit = self.safe_integer(subscription, 'limit', defaultLimit)
178
+ self.orderbooks[symbol] = self.order_book({}, limit)
179
+ orderbook = self.orderbooks[symbol]
180
+ timestamp = self.safe_integer(message, 'ts')
181
+ snapshot = self.parse_order_book(data, symbol, timestamp, 'bids', 'asks')
182
+ orderbook.reset(snapshot)
183
+ client.resolve(orderbook, topic)
184
+
185
+ def handle_order_book_subscription(self, client: Client, message, subscription):
186
+ defaultLimit = self.safe_integer(self.options, 'watchOrderBookLimit', 1000)
187
+ limit = self.safe_integer(subscription, 'limit', defaultLimit)
188
+ symbol = self.safe_string(subscription, 'symbol') # watchOrderBook
189
+ if symbol in self.orderbooks:
190
+ del self.orderbooks[symbol]
191
+ self.orderbooks[symbol] = self.order_book({}, limit)
192
+ self.spawn(self.fetch_order_book_snapshot, client, message, subscription)
193
+
194
+ async def fetch_order_book_snapshot(self, client, message, subscription):
195
+ symbol = self.safe_string(subscription, 'symbol')
196
+ messageHash = self.safe_string(message, 'topic')
197
+ try:
198
+ defaultLimit = self.safe_integer(self.options, 'watchOrderBookLimit', 1000)
199
+ limit = self.safe_integer(subscription, 'limit', defaultLimit)
200
+ params = self.safe_value(subscription, 'params')
201
+ snapshot = await self.fetch_rest_order_book_safe(symbol, limit, params)
202
+ if self.safe_value(self.orderbooks, symbol) is None:
203
+ # if the orderbook is dropped before the snapshot is received
204
+ return
205
+ orderbook = self.orderbooks[symbol]
206
+ orderbook.reset(snapshot)
207
+ messages = orderbook.cache
208
+ for i in range(0, len(messages)):
209
+ messageItem = messages[i]
210
+ ts = self.safe_integer(messageItem, 'ts')
211
+ if ts < orderbook['timestamp']:
212
+ continue
213
+ else:
214
+ self.handle_order_book_message(client, messageItem, orderbook)
215
+ self.orderbooks[symbol] = orderbook
216
+ client.resolve(orderbook, messageHash)
217
+ except Exception as e:
218
+ del client.subscriptions[messageHash]
219
+ client.reject(e, messageHash)
220
+
221
+ def handle_order_book_message(self, client: Client, message, orderbook):
222
+ data = self.safe_dict(message, 'data')
223
+ self.handle_deltas(orderbook['asks'], self.safe_value(data, 'asks', []))
224
+ self.handle_deltas(orderbook['bids'], self.safe_value(data, 'bids', []))
142
225
  timestamp = self.safe_integer(message, 'ts')
143
- snapshot = self.parse_order_book(data, symbol, timestamp, 'bids', 'asks')
144
- orderbook.reset(snapshot)
145
- client.resolve(orderbook, topic)
226
+ orderbook['timestamp'] = timestamp
227
+ orderbook['datetime'] = self.iso8601(timestamp)
228
+ return orderbook
229
+
230
+ def handle_delta(self, bookside, delta):
231
+ price = self.safe_float_2(delta, 'price', 0)
232
+ amount = self.safe_float_2(delta, 'quantity', 1)
233
+ bookside.store(price, amount)
234
+
235
+ def handle_deltas(self, bookside, deltas):
236
+ for i in range(0, len(deltas)):
237
+ self.handle_delta(bookside, deltas[i])
146
238
 
147
239
  async def watch_ticker(self, symbol: str, params={}) -> Ticker:
148
240
  """
@@ -998,6 +1090,7 @@ class woo(ccxt.async_support.woo):
998
1090
  'pong': self.handle_pong,
999
1091
  'subscribe': self.handle_subscribe,
1000
1092
  'orderbook': self.handle_order_book,
1093
+ 'orderbookupdate': self.handle_order_book,
1001
1094
  'ticker': self.handle_ticker,
1002
1095
  'tickers': self.handle_tickers,
1003
1096
  'kline': self.handle_ohlcv,
@@ -1056,6 +1149,12 @@ class woo(ccxt.async_support.woo):
1056
1149
  # "ts": 1657117712212
1057
1150
  # }
1058
1151
  #
1152
+ id = self.safe_string(message, 'id')
1153
+ subscriptionsById = self.index_by(client.subscriptions, 'id')
1154
+ subscription = self.safe_value(subscriptionsById, id, {})
1155
+ method = self.safe_value(subscription, 'method')
1156
+ if method is not None:
1157
+ method(client, message, subscription)
1059
1158
  return message
1060
1159
 
1061
1160
  def handle_auth(self, client: Client, message):
ccxt/test/tests_async.py CHANGED
@@ -3,7 +3,7 @@
3
3
  import asyncio
4
4
 
5
5
 
6
- from tests_helpers import AuthenticationError, NotSupported, InvalidProxySettings, ExchangeNotAvailable, OperationFailed, OnMaintenance, get_cli_arg_value, baseMainTestClass, dump, json_parse, json_stringify, convert_ascii, io_file_exists, io_file_read, io_dir_read, call_method, call_method_sync, call_exchange_method_dynamically, call_exchange_method_dynamically_sync, exception_message, exit_script, get_exchange_prop, set_exchange_prop, init_exchange, get_test_files_sync, get_test_files, set_fetch_response, is_null_value, close # noqa: F401
6
+ from tests_helpers import AuthenticationError, NotSupported, InvalidProxySettings, ExchangeNotAvailable, OperationFailed, OnMaintenance, get_cli_arg_value, baseMainTestClass, dump, json_parse, json_stringify, convert_ascii, io_file_exists, io_file_read, io_dir_read, call_method, call_method_sync, call_exchange_method_dynamically, call_exchange_method_dynamically_sync, get_root_exception, exception_message, exit_script, get_exchange_prop, set_exchange_prop, init_exchange, get_test_files_sync, get_test_files, set_fetch_response, is_null_value, close # noqa: F401
7
7
 
8
8
  class testMainClass(baseMainTestClass):
9
9
  def parse_cli_args(self):
@@ -261,7 +261,8 @@ class testMainClass(baseMainTestClass):
261
261
  try:
262
262
  await self.test_method(method_name, exchange, args, is_public)
263
263
  return True
264
- except Exception as e:
264
+ except Exception as ex:
265
+ e = get_root_exception(ex)
265
266
  is_load_markets = (method_name == 'loadMarkets')
266
267
  is_auth_error = (isinstance(e, AuthenticationError))
267
268
  is_not_supported = (isinstance(e, NotSupported))
@@ -217,6 +217,9 @@ def exception_message(exc):
217
217
  message = message[0:LOG_CHARS_LENGTH]
218
218
  return message
219
219
 
220
+ # stub for c#
221
+ def get_root_exception(exc):
222
+ return exc
220
223
 
221
224
  def exit_script(code=0):
222
225
  exit(code)
ccxt/test/tests_sync.py CHANGED
@@ -1,6 +1,6 @@
1
1
  # -*- coding: utf-8 -*-
2
2
 
3
- from tests_helpers import AuthenticationError, NotSupported, InvalidProxySettings, ExchangeNotAvailable, OperationFailed, OnMaintenance, get_cli_arg_value, baseMainTestClass, dump, json_parse, json_stringify, convert_ascii, io_file_exists, io_file_read, io_dir_read, call_method, call_method_sync, call_exchange_method_dynamically, call_exchange_method_dynamically_sync, exception_message, exit_script, get_exchange_prop, set_exchange_prop, init_exchange, get_test_files_sync, get_test_files, set_fetch_response, is_null_value, close # noqa: F401
3
+ from tests_helpers import AuthenticationError, NotSupported, InvalidProxySettings, ExchangeNotAvailable, OperationFailed, OnMaintenance, get_cli_arg_value, baseMainTestClass, dump, json_parse, json_stringify, convert_ascii, io_file_exists, io_file_read, io_dir_read, call_method, call_method_sync, call_exchange_method_dynamically, call_exchange_method_dynamically_sync, get_root_exception, exception_message, exit_script, get_exchange_prop, set_exchange_prop, init_exchange, get_test_files_sync, get_test_files, set_fetch_response, is_null_value, close # noqa: F401
4
4
 
5
5
  class testMainClass(baseMainTestClass):
6
6
  def parse_cli_args(self):
@@ -258,7 +258,8 @@ class testMainClass(baseMainTestClass):
258
258
  try:
259
259
  self.test_method(method_name, exchange, args, is_public)
260
260
  return True
261
- except Exception as e:
261
+ except Exception as ex:
262
+ e = get_root_exception(ex)
262
263
  is_load_markets = (method_name == 'loadMarkets')
263
264
  is_auth_error = (isinstance(e, AuthenticationError))
264
265
  is_not_supported = (isinstance(e, NotSupported))
ccxt/woo.py CHANGED
@@ -155,7 +155,7 @@ class woo(Exchange, ImplicitAPI):
155
155
  'https://support.woo.org/hc/en-001/articles/4404611795353--Trading-Fees',
156
156
  ],
157
157
  'referral': {
158
- 'url': 'https://x.woo.org/register?ref=YWOWC96B',
158
+ 'url': 'https://x.woo.org/register?ref=DIJT0CNL',
159
159
  'discount': 0.35,
160
160
  },
161
161
  },
ccxt/yobit.py CHANGED
@@ -541,31 +541,7 @@ class yobit(Exchange, ImplicitAPI):
541
541
  'info': ticker,
542
542
  }, market)
543
543
 
544
- def fetch_tickers(self, symbols: Strings = None, params={}) -> Tickers:
545
- """
546
- :see: https://yobit.net/en/api
547
- fetches price tickers for multiple markets, statistical information calculated over the past 24 hours for each market
548
- :param str[]|None symbols: unified symbols of the markets to fetch the ticker for, all market tickers are returned if not assigned
549
- :param dict [params]: extra parameters specific to the exchange API endpoint
550
- :returns dict: a dictionary of `ticker structures <https://docs.ccxt.com/#/?id=ticker-structure>`
551
- """
552
- if symbols is None:
553
- raise ArgumentsRequired(self.id + ' fetchTickers() requires "symbols" argument')
554
- self.load_markets()
555
- symbols = self.market_symbols(symbols)
556
- ids = None
557
- if symbols is None:
558
- ids = self.ids
559
- else:
560
- ids = self.market_ids(symbols)
561
- idsLength: number = len(ids)
562
- idsString = '-'.join(ids)
563
- maxLength = self.safe_integer(self.options, 'maxUrlLength', 2048)
564
- # max URL length is 2048 symbols, including http schema, hostname, tld, etc...
565
- lenghtOfBaseUrl = 30 # the url including api-base and endpoint dir is 30 chars
566
- actualLength = len(idsString) + lenghtOfBaseUrl
567
- if actualLength > maxLength:
568
- raise ArgumentsRequired(self.id + ' fetchTickers() is being requested for ' + str(idsLength) + ' markets(which has an URL length of ' + str(actualLength) + ' characters), but it exceedes max URL length(' + str(maxLength) + '), please pass limisted symbols array to fetchTickers to fit in one request')
544
+ def fetch_tickers_helper(self, idsString: str, params={}) -> Tickers:
569
545
  request: dict = {
570
546
  'pair': idsString,
571
547
  }
@@ -578,7 +554,53 @@ class yobit(Exchange, ImplicitAPI):
578
554
  market = self.safe_market(id)
579
555
  symbol = market['symbol']
580
556
  result[symbol] = self.parse_ticker(ticker, market)
581
- return self.filter_by_array_tickers(result, 'symbol', symbols)
557
+ return result
558
+
559
+ def fetch_tickers(self, symbols: Strings = None, params={}) -> Tickers:
560
+ """
561
+ :see: https://yobit.net/en/api
562
+ fetches price tickers for multiple markets, statistical information calculated over the past 24 hours for each market
563
+ :param str[]|None symbols: unified symbols of the markets to fetch the ticker for, all market tickers are returned if not assigned
564
+ :param dict [params]: extra parameters specific to the exchange API endpoint
565
+ :param dict [params.all]: you can set to `true` for convenience to fetch all tickers from self exchange by sending multiple requests
566
+ :returns dict: a dictionary of `ticker structures <https://docs.ccxt.com/#/?id=ticker-structure>`
567
+ """
568
+ allSymbols = None
569
+ allSymbols, params = self.handle_param_bool(params, 'all', False)
570
+ if symbols is None and not allSymbols:
571
+ raise ArgumentsRequired(self.id + ' fetchTickers() requires "symbols" argument or use `params["all"] = True` to send multiple requests for all markets')
572
+ self.load_markets()
573
+ promises = []
574
+ maxLength = self.safe_integer(self.options, 'maxUrlLength', 2048)
575
+ # max URL length is 2048 symbols, including http schema, hostname, tld, etc...
576
+ lenghtOfBaseUrl = 40 # safe space for the url including api-base and endpoint dir is 30 chars
577
+ if allSymbols:
578
+ symbols = self.symbols
579
+ ids = ''
580
+ for i in range(0, len(self.ids)):
581
+ id = self.ids[i]
582
+ prefix = '' if (ids == '') else '-'
583
+ ids += prefix + id
584
+ if len(ids) > maxLength:
585
+ promises.append(self.fetch_tickers_helper(ids, params))
586
+ ids = ''
587
+ if ids != '':
588
+ promises.append(self.fetch_tickers_helper(ids, params))
589
+ else:
590
+ symbols = self.market_symbols(symbols)
591
+ ids = self.market_ids(symbols)
592
+ idsLength: number = len(ids)
593
+ idsString = '-'.join(ids)
594
+ actualLength = len(idsString) + lenghtOfBaseUrl
595
+ if actualLength > maxLength:
596
+ raise ArgumentsRequired(self.id + ' fetchTickers() is being requested for ' + str(idsLength) + ' markets(which has an URL length of ' + str(actualLength) + ' characters), but it exceedes max URL length(' + str(maxLength) + '), please pass limisted symbols array to fetchTickers to fit in one request')
597
+ promises.append(self.fetch_tickers_helper(idsString, params))
598
+ resultAll = promises
599
+ finalResult = {}
600
+ for i in range(0, len(resultAll)):
601
+ result = self.filter_by_array_tickers(resultAll[i], 'symbol', symbols)
602
+ finalResult = self.extend(finalResult, result)
603
+ return finalResult
582
604
 
583
605
  def fetch_ticker(self, symbol: str, params={}) -> Ticker:
584
606
  """
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.1
2
2
  Name: ccxt
3
- Version: 4.3.72
3
+ Version: 4.3.74
4
4
  Summary: A JavaScript / TypeScript / Python / C# / PHP cryptocurrency trading library with support for 100+ exchanges
5
5
  Home-page: https://ccxt.com
6
6
  Author: Igor Kroitor
@@ -102,7 +102,7 @@ Current feature list:
102
102
  | [![kucoinfutures](https://user-images.githubusercontent.com/1294454/147508995-9e35030a-d046-43a1-a006-6fabd981b554.jpg)](https://futures.kucoin.com/?rcode=E5wkqe) | kucoinfutures | [KuCoin Futures](https://futures.kucoin.com/?rcode=E5wkqe) | [![API Version 1](https://img.shields.io/badge/1-lightgray)](https://docs.kucoin.com/futures) | cex | [![CCXT Certified](https://img.shields.io/badge/CCXT-Certified-green.svg)](https://github.com/ccxt/ccxt/wiki/Certification) | [![CCXT Pro](https://img.shields.io/badge/CCXT-Pro-black)](https://ccxt.pro) | |
103
103
  | [![mexc](https://user-images.githubusercontent.com/1294454/137283979-8b2a818d-8633-461b-bfca-de89e8c446b2.jpg)](https://www.mexc.com/register?inviteCode=mexc-1FQ1GNu1) | mexc | [MEXC Global](https://www.mexc.com/register?inviteCode=mexc-1FQ1GNu1) | [![API Version 3](https://img.shields.io/badge/3-lightgray)](https://mexcdevelop.github.io/apidocs/) | cex | [![CCXT Certified](https://img.shields.io/badge/CCXT-Certified-green.svg)](https://github.com/ccxt/ccxt/wiki/Certification) | [![CCXT Pro](https://img.shields.io/badge/CCXT-Pro-black)](https://ccxt.pro) | |
104
104
  | [![okx](https://user-images.githubusercontent.com/1294454/152485636-38b19e4a-bece-4dec-979a-5982859ffc04.jpg)](https://www.okx.com/join/CCXT2023) | okx | [OKX](https://www.okx.com/join/CCXT2023) | [![API Version 5](https://img.shields.io/badge/5-lightgray)](https://www.okx.com/docs-v5/en/) | cex | [![CCXT Certified](https://img.shields.io/badge/CCXT-Certified-green.svg)](https://github.com/ccxt/ccxt/wiki/Certification) | [![CCXT Pro](https://img.shields.io/badge/CCXT-Pro-black)](https://ccxt.pro) | [![Sign up with OKX using CCXT's referral link for a 20% discount!](https://img.shields.io/static/v1?label=Fee&message=%2d20%25&color=orange)](https://www.okx.com/join/CCXT2023) |
105
- | [![woo](https://user-images.githubusercontent.com/1294454/150730761-1a00e5e0-d28c-480f-9e65-089ce3e6ef3b.jpg)](https://x.woo.org/register?ref=YWOWC96B) | woo | [WOO X](https://x.woo.org/register?ref=YWOWC96B) | [![API Version 1](https://img.shields.io/badge/1-lightgray)](https://docs.woo.org/) | cex | [![CCXT Certified](https://img.shields.io/badge/CCXT-Certified-green.svg)](https://github.com/ccxt/ccxt/wiki/Certification) | [![CCXT Pro](https://img.shields.io/badge/CCXT-Pro-black)](https://ccxt.pro) | [![Sign up with WOO X using CCXT's referral link for a 35% discount!](https://img.shields.io/static/v1?label=Fee&message=%2d35%25&color=orange)](https://x.woo.org/register?ref=YWOWC96B) |
105
+ | [![woo](https://user-images.githubusercontent.com/1294454/150730761-1a00e5e0-d28c-480f-9e65-089ce3e6ef3b.jpg)](https://x.woo.org/register?ref=DIJT0CNL) | woo | [WOO X](https://x.woo.org/register?ref=DIJT0CNL) | [![API Version 1](https://img.shields.io/badge/1-lightgray)](https://docs.woo.org/) | cex | [![CCXT Certified](https://img.shields.io/badge/CCXT-Certified-green.svg)](https://github.com/ccxt/ccxt/wiki/Certification) | [![CCXT Pro](https://img.shields.io/badge/CCXT-Pro-black)](https://ccxt.pro) | [![Sign up with WOO X using CCXT's referral link for a 35% discount!](https://img.shields.io/static/v1?label=Fee&message=%2d35%25&color=orange)](https://x.woo.org/register?ref=DIJT0CNL) |
106
106
  | [![woofipro](https://github.com/ccxt/ccxt/assets/43336371/b1e7b348-a0fc-4605-8b7f-91176958fd69)](https://dex.woo.org/en/trade?ref=CCXT) | woofipro | [WOOFI PRO](https://dex.woo.org/en/trade?ref=CCXT) | [![API Version 1](https://img.shields.io/badge/1-lightgray)](https://orderly.network/docs/build-on-evm/building-on-evm) | dex | [![CCXT Certified](https://img.shields.io/badge/CCXT-Certified-green.svg)](https://github.com/ccxt/ccxt/wiki/Certification) | [![CCXT Pro](https://img.shields.io/badge/CCXT-Pro-black)](https://ccxt.pro) | [![Sign up with WOOFI PRO using CCXT's referral link for a 5% discount!](https://img.shields.io/static/v1?label=Fee&message=%2d5%25&color=orange)](https://dex.woo.org/en/trade?ref=CCXT) |
107
107
 
108
108
  ## Supported Cryptocurrency Exchanges
@@ -207,7 +207,7 @@ The CCXT library currently supports the following 102 cryptocurrency exchange ma
207
207
  | [![wavesexchange](https://user-images.githubusercontent.com/1294454/84547058-5fb27d80-ad0b-11ea-8711-78ac8b3c7f31.jpg)](https://wx.network) | wavesexchange | [Waves.Exchange](https://wx.network) | [![API Version *](https://img.shields.io/badge/*-lightgray)](https://docs.wx.network) | dex | | |
208
208
  | [![wazirx](https://user-images.githubusercontent.com/1294454/148647666-c109c20b-f8ac-472f-91c3-5f658cb90f49.jpeg)](https://wazirx.com/invite/k7rrnks5) | wazirx | [WazirX](https://wazirx.com/invite/k7rrnks5) | [![API Version 2](https://img.shields.io/badge/2-lightgray)](https://docs.wazirx.com/#public-rest-api-for-wazirx) | cex | | [![CCXT Pro](https://img.shields.io/badge/CCXT-Pro-black)](https://ccxt.pro) |
209
209
  | [![whitebit](https://user-images.githubusercontent.com/1294454/66732963-8eb7dd00-ee66-11e9-849b-10d9282bb9e0.jpg)](https://whitebit.com/referral/d9bdf40e-28f2-4b52-b2f9-cd1415d82963) | whitebit | [WhiteBit](https://whitebit.com/referral/d9bdf40e-28f2-4b52-b2f9-cd1415d82963) | [![API Version 4](https://img.shields.io/badge/4-lightgray)](https://github.com/whitebit-exchange/api-docs) | cex | | [![CCXT Pro](https://img.shields.io/badge/CCXT-Pro-black)](https://ccxt.pro) |
210
- | [![woo](https://user-images.githubusercontent.com/1294454/150730761-1a00e5e0-d28c-480f-9e65-089ce3e6ef3b.jpg)](https://x.woo.org/register?ref=YWOWC96B) | woo | [WOO X](https://x.woo.org/register?ref=YWOWC96B) | [![API Version 1](https://img.shields.io/badge/1-lightgray)](https://docs.woo.org/) | cex | [![CCXT Certified](https://img.shields.io/badge/CCXT-Certified-green.svg)](https://github.com/ccxt/ccxt/wiki/Certification) | [![CCXT Pro](https://img.shields.io/badge/CCXT-Pro-black)](https://ccxt.pro) |
210
+ | [![woo](https://user-images.githubusercontent.com/1294454/150730761-1a00e5e0-d28c-480f-9e65-089ce3e6ef3b.jpg)](https://x.woo.org/register?ref=DIJT0CNL) | woo | [WOO X](https://x.woo.org/register?ref=DIJT0CNL) | [![API Version 1](https://img.shields.io/badge/1-lightgray)](https://docs.woo.org/) | cex | [![CCXT Certified](https://img.shields.io/badge/CCXT-Certified-green.svg)](https://github.com/ccxt/ccxt/wiki/Certification) | [![CCXT Pro](https://img.shields.io/badge/CCXT-Pro-black)](https://ccxt.pro) |
211
211
  | [![woofipro](https://github.com/ccxt/ccxt/assets/43336371/b1e7b348-a0fc-4605-8b7f-91176958fd69)](https://dex.woo.org/en/trade?ref=CCXT) | woofipro | [WOOFI PRO](https://dex.woo.org/en/trade?ref=CCXT) | [![API Version 1](https://img.shields.io/badge/1-lightgray)](https://orderly.network/docs/build-on-evm/building-on-evm) | dex | [![CCXT Certified](https://img.shields.io/badge/CCXT-Certified-green.svg)](https://github.com/ccxt/ccxt/wiki/Certification) | [![CCXT Pro](https://img.shields.io/badge/CCXT-Pro-black)](https://ccxt.pro) |
212
212
  | [![xt](https://user-images.githubusercontent.com/14319357/232636712-466df2fc-560a-4ca4-aab2-b1d954a58e24.jpg)](https://www.xt.com/en/accounts/register?ref=9PTM9VW) | xt | [XT](https://www.xt.com/en/accounts/register?ref=9PTM9VW) | [![API Version 4](https://img.shields.io/badge/4-lightgray)](https://doc.xt.com/) | cex | | [![CCXT Pro](https://img.shields.io/badge/CCXT-Pro-black)](https://ccxt.pro) |
213
213
  | [![yobit](https://user-images.githubusercontent.com/1294454/27766910-cdcbfdae-5eea-11e7-9859-03fea873272d.jpg)](https://www.yobit.net) | yobit | [YoBit](https://www.yobit.net) | [![API Version 3](https://img.shields.io/badge/3-lightgray)](https://www.yobit.net/en/api/) | cex | | |
@@ -270,13 +270,13 @@ console.log(version, Object.keys(exchanges));
270
270
 
271
271
  All-in-one browser bundle (dependencies included), served from a CDN of your choice:
272
272
 
273
- * jsDelivr: https://cdn.jsdelivr.net/npm/ccxt@4.3.72/dist/ccxt.browser.min.js
274
- * unpkg: https://unpkg.com/ccxt@4.3.72/dist/ccxt.browser.min.js
273
+ * jsDelivr: https://cdn.jsdelivr.net/npm/ccxt@4.3.74/dist/ccxt.browser.min.js
274
+ * unpkg: https://unpkg.com/ccxt@4.3.74/dist/ccxt.browser.min.js
275
275
 
276
276
  CDNs are not updated in real-time and may have delays. Defaulting to the most recent version without specifying the version number is not recommended. Please, keep in mind that we are not responsible for the correct operation of those CDN servers.
277
277
 
278
278
  ```HTML
279
- <script type="text/javascript" src="https://cdn.jsdelivr.net/npm/ccxt@4.3.72/dist/ccxt.browser.min.js"></script>
279
+ <script type="text/javascript" src="https://cdn.jsdelivr.net/npm/ccxt@4.3.74/dist/ccxt.browser.min.js"></script>
280
280
  ```
281
281
 
282
282
  Creates a global `ccxt` object: