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

@@ -4,9 +4,9 @@
4
4
  # https://github.com/ccxt/ccxt/blob/master/CONTRIBUTING.md#how-to-contribute-code
5
5
 
6
6
  import ccxt.async_support
7
- from ccxt.async_support.base.ws.cache import ArrayCache
7
+ from ccxt.async_support.base.ws.cache import ArrayCache, ArrayCacheByTimestamp
8
8
  import hashlib
9
- from ccxt.base.types import Int, Market, OrderBook, Strings, Ticker, FundingRate, FundingRates, Trade
9
+ from ccxt.base.types import Int, Market, OrderBook, Strings, Ticker, Tickers, FundingRate, FundingRates, Trade
10
10
  from ccxt.async_support.base.ws.client import Client
11
11
  from typing import List
12
12
  from ccxt.base.errors import ExchangeError
@@ -27,12 +27,12 @@ class coinbaseinternational(ccxt.async_support.coinbaseinternational):
27
27
  'watchTicker': True,
28
28
  'watchBalance': False,
29
29
  'watchMyTrades': False,
30
- 'watchOHLCV': False,
30
+ 'watchOHLCV': True,
31
31
  'watchOHLCVForSymbols': False,
32
32
  'watchOrders': False,
33
33
  'watchOrdersForSymbols': False,
34
34
  'watchPositions': False,
35
- 'watchTickers': False,
35
+ 'watchTickers': True,
36
36
  'createOrderWs': False,
37
37
  'editOrderWs': False,
38
38
  'cancelOrderWs': False,
@@ -58,6 +58,14 @@ class coinbaseinternational(ccxt.async_support.coinbaseinternational):
58
58
  'tradesLimit': 1000,
59
59
  'ordersLimit': 1000,
60
60
  'myTradesLimit': 1000,
61
+ 'timeframes': {
62
+ '1m': 'CANDLES_ONE_MINUTE',
63
+ '5m': 'CANDLES_FIVE_MINUTES',
64
+ '30m': 'CANDLES_THIRTY_MINUTES',
65
+ '1h': 'CANDLES_ONE_HOUR',
66
+ '2h': 'CANDLES_TWO_HOURS',
67
+ '1d': 'CANDLES_ONE_DAY',
68
+ },
61
69
  },
62
70
  'exceptions': {
63
71
  'exact': {
@@ -80,15 +88,18 @@ class coinbaseinternational(ccxt.async_support.coinbaseinternational):
80
88
  self.check_required_credentials()
81
89
  market = None
82
90
  messageHash = name
83
- productIds = []
91
+ productIds = None
84
92
  if symbols is None:
85
- symbols = self.symbols
93
+ symbols = self.get_active_symbols()
86
94
  symbolsLength = len(symbols)
95
+ messageHashes = []
87
96
  if symbolsLength > 1:
88
97
  parsedSymbols = self.market_symbols(symbols)
89
98
  marketIds = self.market_ids(parsedSymbols)
90
99
  productIds = marketIds
91
- messageHash = messageHash + '::' + ','.join(parsedSymbols)
100
+ for i in range(0, len(parsedSymbols)):
101
+ messageHashes.append(name + '::' + parsedSymbols[i])
102
+ # messageHash = messageHash + '::' + ','.join(parsedSymbols)
92
103
  elif symbolsLength == 1:
93
104
  market = self.market(symbols[0])
94
105
  messageHash = name + '::' + market['symbol']
@@ -101,13 +112,17 @@ class coinbaseinternational(ccxt.async_support.coinbaseinternational):
101
112
  signature = self.hmac(self.encode(auth), self.base64_to_binary(self.secret), hashlib.sha256, 'base64')
102
113
  subscribe: dict = {
103
114
  'type': 'SUBSCRIBE',
104
- 'product_ids': productIds,
115
+ # 'product_ids': productIds,
105
116
  'channels': [name],
106
117
  'time': timestamp,
107
118
  'key': self.apiKey,
108
119
  'passphrase': self.password,
109
120
  'signature': signature,
110
121
  }
122
+ if productIds is not None:
123
+ subscribe['product_ids'] = productIds
124
+ if symbolsLength > 1:
125
+ return await self.watch_multiple(url, messageHashes, self.extend(subscribe, params), messageHashes)
111
126
  return await self.watch(url, messageHash, self.extend(subscribe, params), messageHash)
112
127
 
113
128
  async def subscribe_multiple(self, name: str, symbols: Strings = None, params={}):
@@ -184,6 +199,7 @@ class coinbaseinternational(ccxt.async_support.coinbaseinternational):
184
199
  :see: https://docs.cloud.coinbase.com/intx/docs/websocket-channels#instruments-channel
185
200
  :param str [symbol]: unified symbol of the market to fetch the ticker for
186
201
  :param dict [params]: extra parameters specific to the exchange API endpoint
202
+ :param str [params.channel]: the channel to watch, 'LEVEL1' or 'INSTRUMENTS', default is 'LEVEL1'
187
203
  :returns dict: a `ticker structure <https://docs.ccxt.com/#/?id=ticker-structure>`
188
204
  """
189
205
  await self.load_markets()
@@ -191,6 +207,35 @@ class coinbaseinternational(ccxt.async_support.coinbaseinternational):
191
207
  channel, params = self.handle_option_and_params(params, 'watchTicker', 'channel', 'LEVEL1')
192
208
  return await self.subscribe(channel, [symbol], params)
193
209
 
210
+ def get_active_symbols(self):
211
+ symbols = self.symbols
212
+ output = []
213
+ for i in range(0, len(symbols)):
214
+ symbol = symbols[i]
215
+ market = self.markets[symbol]
216
+ if market['active']:
217
+ output.append(symbol)
218
+ return output
219
+
220
+ async def watch_tickers(self, symbols: Strings = None, params={}) -> Tickers:
221
+ """
222
+ watches a price ticker, a statistical calculation with the information calculated over the past 24 hours for a specific market
223
+ :see: https://docs.cloud.coinbase.com/intx/docs/websocket-channels#instruments-channel
224
+ :param str[] [symbols]: unified symbol of the market to fetch the ticker for
225
+ :param dict [params]: extra parameters specific to the exchange API endpoint
226
+ :param str [params.channel]: the channel to watch, 'LEVEL1' or 'INSTRUMENTS', default is 'INSTLEVEL1UMENTS'
227
+ :returns dict: a `ticker structure <https://docs.ccxt.com/#/?id=ticker-structure>`
228
+ """
229
+ await self.load_markets()
230
+ channel = None
231
+ channel, params = self.handle_option_and_params(params, 'watchTickers', 'channel', 'LEVEL1')
232
+ ticker = await self.subscribe(channel, symbols, params)
233
+ if self.newUpdates:
234
+ result: dict = {}
235
+ result[ticker['symbol']] = ticker
236
+ return result
237
+ return self.filter_by_array(self.tickers, 'symbol', symbols)
238
+
194
239
  def handle_instrument(self, client: Client, message):
195
240
  #
196
241
  # {
@@ -248,6 +293,33 @@ class coinbaseinternational(ccxt.async_support.coinbaseinternational):
248
293
  # "channel":"INSTRUMENTS",
249
294
  # "type":"SNAPSHOT"
250
295
  # }
296
+ # instruments
297
+ # {
298
+ # sequence: 0,
299
+ # instrument_type: 'PERP',
300
+ # instrument_mode: 'standard',
301
+ # base_asset_name: 'BTC',
302
+ # quote_asset_name: 'USDC',
303
+ # base_increment: '0.0001',
304
+ # quote_increment: '0.1',
305
+ # avg_daily_quantity: '502.8845',
306
+ # avg_daily_volume: '3.1495242961566668E7',
307
+ # total30_day_quantity: '15086.535',
308
+ # total30_day_volume: '9.44857288847E8',
309
+ # total24_hour_quantity: '5.0',
310
+ # total24_hour_volume: '337016.5',
311
+ # base_imf: '0.1',
312
+ # min_quantity: '0.0001',
313
+ # position_size_limit: '800',
314
+ # funding_interval: '3600000000000',
315
+ # trading_state: 'trading',
316
+ # last_updated_time: '2024-07-30T15:00:00Z',
317
+ # default_initial_margin: '0.2',
318
+ # base_asset_multiplier: '1.0',
319
+ # channel: 'INSTRUMENTS',
320
+ # type: 'SNAPSHOT',
321
+ # time: '2024-07-30T15:26:56.766Z',
322
+ # }
251
323
  #
252
324
  marketId = self.safe_string(ticker, 'product_id')
253
325
  datetime = self.safe_string(ticker, 'time')
@@ -270,8 +342,8 @@ class coinbaseinternational(ccxt.async_support.coinbaseinternational):
270
342
  'change': None,
271
343
  'percentage': None,
272
344
  'average': None,
273
- 'baseVolume': self.safe_string(ticker, 'total_24_hour_quantity'),
274
- 'quoteVolume': self.safe_string(ticker, 'total_24_hour_volume'),
345
+ 'baseVolume': self.safe_string_2(ticker, 'total_24_hour_quantity', 'total24_hour_quantity'),
346
+ 'quoteVolume': self.safe_string_2(ticker, 'total_24_hour_volume', 'total24_hour_volume'),
275
347
  })
276
348
 
277
349
  def handle_ticker(self, client: Client, message):
@@ -343,6 +415,63 @@ class coinbaseinternational(ccxt.async_support.coinbaseinternational):
343
415
  'previousClose': None,
344
416
  })
345
417
 
418
+ async def watch_ohlcv(self, symbol: str, timeframe='1m', since: Int = None, limit: Int = None, params={}) -> List[list]:
419
+ """
420
+ watches historical candlestick data containing the open, high, low, close price, and the volume of a market
421
+ :see: https://docs.cdp.coinbase.com/intx/docs/websocket-channels#candles-channel
422
+ :param str symbol: unified symbol of the market to fetch OHLCV data for
423
+ :param str timeframe: the length of time each candle represents
424
+ :param int [since]: timestamp in ms of the earliest candle to fetch
425
+ :param int [limit]: the maximum amount of candles to fetch
426
+ :param dict [params]: extra parameters specific to the exchange API endpoint
427
+ :returns int[][]: A list of candles ordered, open, high, low, close, volume
428
+ """
429
+ await self.load_markets()
430
+ market = self.market(symbol)
431
+ symbol = market['symbol']
432
+ options = self.safe_dict(self.options, 'timeframes', {})
433
+ interval = self.safe_string(options, timeframe, timeframe)
434
+ ohlcv = await self.subscribe(interval, [symbol], params)
435
+ if self.newUpdates:
436
+ limit = ohlcv.getLimit(symbol, limit)
437
+ return self.filter_by_since_limit(ohlcv, since, limit, 0, True)
438
+
439
+ def handle_ohlcv(self, client: Client, message):
440
+ #
441
+ # {
442
+ # "sequence": 0,
443
+ # "product_id": "BTC-PERP",
444
+ # "channel": "CANDLES_ONE_MINUTE",
445
+ # "type": "SNAPSHOT",
446
+ # "candles": [
447
+ # {
448
+ # "time": "2023-05-10T14:58:47.000Z",
449
+ # "low": "28787.8",
450
+ # "high": "28788.8",
451
+ # "open": "28788.8",
452
+ # "close": "28787.8",
453
+ # "volume": "0.466"
454
+ # },
455
+ # ]
456
+ # }
457
+ #
458
+ messageHash = self.safe_string(message, 'channel')
459
+ marketId = self.safe_string(message, 'product_id')
460
+ market = self.safe_market(marketId)
461
+ symbol = market['symbol']
462
+ timeframe = self.find_timeframe(messageHash)
463
+ self.ohlcvs[symbol] = self.safe_value(self.ohlcvs, symbol, {})
464
+ if self.safe_value(self.ohlcvs[symbol], timeframe) is None:
465
+ limit = self.safe_integer(self.options, 'OHLCVLimit', 1000)
466
+ self.ohlcvs[symbol][timeframe] = ArrayCacheByTimestamp(limit)
467
+ stored = self.ohlcvs[symbol][timeframe]
468
+ data = self.safe_list(message, 'candles', [])
469
+ for i in range(0, len(data)):
470
+ tick = data[i]
471
+ parsed = self.parse_ohlcv(tick, market)
472
+ stored.append(parsed)
473
+ client.resolve(stored, messageHash + '::' + symbol)
474
+
346
475
  async def watch_trades(self, symbol: str, since: Int = None, limit: Int = None, params={}) -> List[Trade]:
347
476
  """
348
477
  get the list of most recent trades for a particular symbol
@@ -606,7 +735,7 @@ class coinbaseinternational(ccxt.async_support.coinbaseinternational):
606
735
  def handle_message(self, client, message):
607
736
  if self.handle_error_message(client, message):
608
737
  return
609
- channel = self.safe_string(message, 'channel')
738
+ channel = self.safe_string(message, 'channel', '')
610
739
  methods: dict = {
611
740
  'SUBSCRIPTIONS': self.handle_subscription_status,
612
741
  'INSTRUMENTS': self.handle_instrument,
@@ -620,6 +749,8 @@ class coinbaseinternational(ccxt.async_support.coinbaseinternational):
620
749
  if type == 'error':
621
750
  errorMessage = self.safe_string(message, 'message')
622
751
  raise ExchangeError(errorMessage)
752
+ if channel.find('CANDLES') > -1:
753
+ self.handle_ohlcv(client, message)
623
754
  method = self.safe_value(methods, channel)
624
755
  if method is not None:
625
756
  method(client, message)
ccxt/pro/cryptocom.py CHANGED
@@ -9,6 +9,7 @@ import hashlib
9
9
  from ccxt.base.types import Balances, Int, Num, Order, OrderBook, OrderSide, OrderType, Position, Str, Strings, Ticker, Trade
10
10
  from ccxt.async_support.base.ws.client import Client
11
11
  from typing import List
12
+ from ccxt.base.errors import ExchangeError
12
13
  from ccxt.base.errors import AuthenticationError
13
14
  from ccxt.base.errors import NetworkError
14
15
  from ccxt.base.errors import ChecksumError
@@ -827,6 +828,7 @@ class cryptocom(ccxt.async_support.cryptocom):
827
828
  # "message": "invalid channel {"channels":["trade.BTCUSD-PERP"]}"
828
829
  # }
829
830
  #
831
+ id = self.safe_string(message, 'id')
830
832
  errorCode = self.safe_string(message, 'code')
831
833
  try:
832
834
  if errorCode and errorCode != '0':
@@ -835,6 +837,7 @@ class cryptocom(ccxt.async_support.cryptocom):
835
837
  messageString = self.safe_value(message, 'message')
836
838
  if messageString is not None:
837
839
  self.throw_broadly_matched_exception(self.exceptions['broad'], messageString, feedback)
840
+ raise ExchangeError(feedback)
838
841
  return False
839
842
  except Exception as e:
840
843
  if isinstance(e, AuthenticationError):
@@ -843,7 +846,7 @@ class cryptocom(ccxt.async_support.cryptocom):
843
846
  if messageHash in client.subscriptions:
844
847
  del client.subscriptions[messageHash]
845
848
  else:
846
- client.reject(e)
849
+ client.reject(e, id)
847
850
  return True
848
851
 
849
852
  def handle_subscribe(self, client: Client, message):
ccxt/pro/hitbtc.py CHANGED
@@ -1137,7 +1137,8 @@ class hitbtc(ccxt.async_support.hitbtc):
1137
1137
  return message
1138
1138
 
1139
1139
  def handle_message(self, client: Client, message):
1140
- self.handle_error(client, message)
1140
+ if self.handle_error(client, message):
1141
+ return
1141
1142
  channel = self.safe_string_2(message, 'ch', 'method')
1142
1143
  if channel is not None:
1143
1144
  splitChannel = channel.split('/')
@@ -1206,11 +1207,22 @@ class hitbtc(ccxt.async_support.hitbtc):
1206
1207
  #
1207
1208
  error = self.safe_value(message, 'error')
1208
1209
  if error is not None:
1209
- code = self.safe_value(error, 'code')
1210
- errorMessage = self.safe_string(error, 'message')
1211
- description = self.safe_string(error, 'description')
1212
- feedback = self.id + ' ' + description
1213
- self.throw_exactly_matched_exception(self.exceptions['exact'], code, feedback)
1214
- self.throw_broadly_matched_exception(self.exceptions['broad'], errorMessage, feedback)
1215
- raise ExchangeError(feedback) # unknown message
1210
+ try:
1211
+ code = self.safe_value(error, 'code')
1212
+ errorMessage = self.safe_string(error, 'message')
1213
+ description = self.safe_string(error, 'description')
1214
+ feedback = self.id + ' ' + description
1215
+ self.throw_exactly_matched_exception(self.exceptions['exact'], code, feedback)
1216
+ self.throw_broadly_matched_exception(self.exceptions['broad'], errorMessage, feedback)
1217
+ raise ExchangeError(feedback) # unknown message
1218
+ except Exception as e:
1219
+ if isinstance(e, AuthenticationError):
1220
+ messageHash = 'authenticated'
1221
+ client.reject(e, messageHash)
1222
+ if messageHash in client.subscriptions:
1223
+ del client.subscriptions[messageHash]
1224
+ else:
1225
+ id = self.safe_string(message, 'id')
1226
+ client.reject(e, id)
1227
+ return True
1216
1228
  return None
ccxt/pro/okx.py CHANGED
@@ -1764,6 +1764,12 @@ class okx(ccxt.async_support.okx):
1764
1764
  self.throw_broadly_matched_exception(self.exceptions['broad'], messageString, feedback)
1765
1765
  raise ExchangeError(feedback)
1766
1766
  except Exception as e:
1767
+ # if the message contains an id, it means it is a response to a request
1768
+ # so we only reject that promise, instead of deleting all futures, destroying the authentication future
1769
+ id = self.safe_string(message, 'id')
1770
+ if id is not None:
1771
+ client.reject(e, id)
1772
+ return False
1767
1773
  client.reject(e)
1768
1774
  return False
1769
1775
  return message
ccxt/pro/poloniex.py CHANGED
@@ -1100,13 +1100,7 @@ class poloniex(ccxt.async_support.poloniex):
1100
1100
  if type == 'auth':
1101
1101
  self.handle_authenticate(client, message)
1102
1102
  elif type is None:
1103
- data = self.safe_value(message, 'data')
1104
- item = self.safe_value(data, 0)
1105
- orderId = self.safe_string(item, 'orderId')
1106
- if orderId == '0':
1107
- self.handle_error_message(client, item)
1108
- else:
1109
- self.handle_order_request(client, message)
1103
+ self.handle_order_request(client, message)
1110
1104
  else:
1111
1105
  data = self.safe_value(message, 'data', [])
1112
1106
  dataLength = len(data)
@@ -1131,12 +1125,40 @@ class poloniex(ccxt.async_support.poloniex):
1131
1125
  # "event": "error",
1132
1126
  # "message": "Platform in maintenance mode"
1133
1127
  # }
1128
+ # {
1129
+ # "id":"1722386782048",
1130
+ # "data":[
1131
+ # {
1132
+ # "orderId":0,
1133
+ # "clientOrderId":null,
1134
+ # "message":"available insufficient",
1135
+ # "code":21721
1136
+ # }
1137
+ # ]
1138
+ # }
1134
1139
  #
1140
+ id = self.safe_string(message, 'id')
1135
1141
  event = self.safe_string(message, 'event')
1136
- orderId = self.safe_string(message, 'orderId')
1142
+ data = self.safe_list(message, 'data')
1143
+ first = self.safe_dict(data, 0)
1144
+ orderId = self.safe_string(first, 'orderId')
1137
1145
  if (event == 'error') or (orderId == '0'):
1138
- error = self.safe_string(message, 'message')
1139
- raise ExchangeError(self.id + ' error: ' + self.json(error))
1146
+ try:
1147
+ error = self.safe_string(first, 'message')
1148
+ code = self.safe_string(first, 'code')
1149
+ feedback = self.id + ' ' + self.json(message)
1150
+ self.throw_exactly_matched_exception(self.exceptions['exact'], code, feedback)
1151
+ self.throw_broadly_matched_exception(self.exceptions['broad'], error, feedback)
1152
+ raise ExchangeError(feedback)
1153
+ except Exception as e:
1154
+ if isinstance(e, AuthenticationError):
1155
+ messageHash = 'authenticated'
1156
+ client.reject(e, messageHash)
1157
+ if messageHash in client.subscriptions:
1158
+ del client.subscriptions[messageHash]
1159
+ else:
1160
+ client.reject(e, id)
1161
+ return True
1140
1162
  return False
1141
1163
 
1142
1164
  def handle_authenticate(self, client: Client, message):
ccxt/pro/woo.py CHANGED
@@ -34,7 +34,7 @@ class woo(ccxt.async_support.woo):
34
34
  'api': {
35
35
  'ws': {
36
36
  'public': 'wss://wss.woo.org/ws/stream',
37
- 'private': 'wss://wss.woo.network/v2/ws/private/stream',
37
+ 'private': 'wss://wss.woo.org/v2/ws/private/stream',
38
38
  },
39
39
  },
40
40
  'test': {
@@ -79,7 +79,8 @@ class woo(ccxt.async_support.woo):
79
79
  return newValue
80
80
 
81
81
  async def watch_public(self, messageHash, message):
82
- url = self.urls['api']['ws']['public'] + '/' + self.uid
82
+ urlUid = '/' + self.uid if (self.uid) else ''
83
+ url = self.urls['api']['ws']['public'] + urlUid
83
84
  requestId = self.request_id(url)
84
85
  subscribe: dict = {
85
86
  'id': requestId,
@@ -456,7 +457,7 @@ class woo(ccxt.async_support.woo):
456
457
  marketId = self.safe_string(trade, 'symbol')
457
458
  market = self.safe_market(marketId, market)
458
459
  symbol = market['symbol']
459
- price = self.safe_string(trade, 'executedPrice', 'price')
460
+ price = self.safe_string_2(trade, 'executedPrice', 'price')
460
461
  amount = self.safe_string_2(trade, 'executedQuantity', 'size')
461
462
  cost = Precise.string_mul(price, amount)
462
463
  side = self.safe_string_lower(trade, 'side')
@@ -492,7 +493,7 @@ class woo(ccxt.async_support.woo):
492
493
  def check_required_uid(self, error=True):
493
494
  if not self.uid:
494
495
  if error:
495
- raise AuthenticationError(self.id + ' requires `uid` credential')
496
+ raise AuthenticationError(self.id + ' requires `uid` credential(woox calls it `application_id`)')
496
497
  else:
497
498
  return False
498
499
  return True
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.1
2
2
  Name: ccxt
3
- Version: 4.3.69
3
+ Version: 4.3.70
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
@@ -139,7 +139,7 @@ The CCXT library currently supports the following 101 cryptocurrency exchange ma
139
139
  | [![bitvavo](https://user-images.githubusercontent.com/1294454/169202626-bd130fc5-fcf9-41bb-8d97-6093225c73cd.jpg)](https://bitvavo.com/?a=24F34952F7) | bitvavo | [Bitvavo](https://bitvavo.com/?a=24F34952F7) | [![API Version 2](https://img.shields.io/badge/2-lightgray)](https://docs.bitvavo.com/) | cex | | [![CCXT Pro](https://img.shields.io/badge/CCXT-Pro-black)](https://ccxt.pro) |
140
140
  | [![bl3p](https://user-images.githubusercontent.com/1294454/28501752-60c21b82-6feb-11e7-818b-055ee6d0e754.jpg)](https://bl3p.eu) | bl3p | [BL3P](https://bl3p.eu) | [![API Version 1](https://img.shields.io/badge/1-lightgray)](https://github.com/BitonicNL/bl3p-api/tree/master/docs) | cex | | |
141
141
  | [![blockchaincom](https://user-images.githubusercontent.com/1294454/147515585-1296e91b-7398-45e5-9d32-f6121538533f.jpeg)](https://blockchain.com) | blockchaincom | [Blockchain.com](https://blockchain.com) | [![API Version 3](https://img.shields.io/badge/3-lightgray)](https://api.blockchain.com/v3) | cex | | [![CCXT Pro](https://img.shields.io/badge/CCXT-Pro-black)](https://ccxt.pro) |
142
- | [![blofin](https://github.com/ccxt/ccxt/assets/43336371/255a7b29-341f-4d20-8342-fbfae4932807)](https://blofin.com/register?referral_code=jBd8U1) | blofin | [BloFin](https://blofin.com/register?referral_code=jBd8U1) | [![API Version 1](https://img.shields.io/badge/1-lightgray)](https://blofin.com/docs) | cex | | |
142
+ | [![blofin](https://github.com/ccxt/ccxt/assets/43336371/255a7b29-341f-4d20-8342-fbfae4932807)](https://blofin.com/register?referral_code=jBd8U1) | blofin | [BloFin](https://blofin.com/register?referral_code=jBd8U1) | [![API Version 1](https://img.shields.io/badge/1-lightgray)](https://blofin.com/docs) | cex | | [![CCXT Pro](https://img.shields.io/badge/CCXT-Pro-black)](https://ccxt.pro) |
143
143
  | [![btcalpha](https://user-images.githubusercontent.com/1294454/42625213-dabaa5da-85cf-11e8-8f99-aa8f8f7699f0.jpg)](https://btc-alpha.com/?r=123788) | btcalpha | [BTC-Alpha](https://btc-alpha.com/?r=123788) | [![API Version 1](https://img.shields.io/badge/1-lightgray)](https://btc-alpha.github.io/api-docs) | cex | | |
144
144
  | [![btcbox](https://user-images.githubusercontent.com/51840849/87327317-98c55400-c53c-11ea-9a11-81f7d951cc74.jpg)](https://www.btcbox.co.jp/) | btcbox | [BtcBox](https://www.btcbox.co.jp/) | [![API Version 1](https://img.shields.io/badge/1-lightgray)](https://blog.btcbox.jp/en/archives/8762) | cex | | |
145
145
  | [![btcmarkets](https://user-images.githubusercontent.com/51840849/89731817-b3fb8480-da52-11ea-817f-783b08aaf32b.jpg)](https://btcmarkets.net) | btcmarkets | [BTC Markets](https://btcmarkets.net) | [![API Version 3](https://img.shields.io/badge/3-lightgray)](https://api.btcmarkets.net/doc/v3) | cex | | |
@@ -269,13 +269,13 @@ console.log(version, Object.keys(exchanges));
269
269
 
270
270
  All-in-one browser bundle (dependencies included), served from a CDN of your choice:
271
271
 
272
- * jsDelivr: https://cdn.jsdelivr.net/npm/ccxt@4.3.69/dist/ccxt.browser.min.js
273
- * unpkg: https://unpkg.com/ccxt@4.3.69/dist/ccxt.browser.min.js
272
+ * jsDelivr: https://cdn.jsdelivr.net/npm/ccxt@4.3.70/dist/ccxt.browser.min.js
273
+ * unpkg: https://unpkg.com/ccxt@4.3.70/dist/ccxt.browser.min.js
274
274
 
275
275
  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.
276
276
 
277
277
  ```HTML
278
- <script type="text/javascript" src="https://cdn.jsdelivr.net/npm/ccxt@4.3.69/dist/ccxt.browser.min.js"></script>
278
+ <script type="text/javascript" src="https://cdn.jsdelivr.net/npm/ccxt@4.3.70/dist/ccxt.browser.min.js"></script>
279
279
  ```
280
280
 
281
281
  Creates a global `ccxt` object: