ccxt 4.4.61__py2.py3-none-any.whl → 4.4.63__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.
@@ -74,7 +74,9 @@ class bybit(Exchange, ImplicitAPI):
74
74
  'createTrailingAmountOrder': True,
75
75
  'createTriggerOrder': True,
76
76
  'editOrder': True,
77
+ 'editOrders': True,
77
78
  'fetchBalance': True,
79
+ 'fetchBidsAsks': 'emulated',
78
80
  'fetchBorrowInterest': False, # temporarily disabled, doesn't work
79
81
  'fetchBorrowRateHistories': False,
80
82
  'fetchBorrowRateHistory': False,
@@ -255,6 +257,7 @@ class bybit(Exchange, ImplicitAPI):
255
257
  'v5/spot-lever-token/reference': 5,
256
258
  # spot margin trade
257
259
  'v5/spot-margin-trade/data': 5,
260
+ 'v5/spot-margin-trade/collateral': 5,
258
261
  'v5/spot-cross-margin-trade/data': 5,
259
262
  'v5/spot-cross-margin-trade/pledge-token': 5,
260
263
  'v5/spot-cross-margin-trade/borrow-token': 5,
@@ -1240,6 +1243,9 @@ class bybit(Exchange, ImplicitAPI):
1240
1243
  'fetchOHLCV': {
1241
1244
  'limit': 1000,
1242
1245
  },
1246
+ 'editOrders': {
1247
+ 'max': 10,
1248
+ },
1243
1249
  },
1244
1250
  'spot': {
1245
1251
  'extends': 'default',
@@ -1340,7 +1346,7 @@ class bybit(Exchange, ImplicitAPI):
1340
1346
  # so we're assuming UTA is enabled
1341
1347
  self.options['enableUnifiedMargin'] = False
1342
1348
  self.options['enableUnifiedAccount'] = True
1343
- self.options['unifiedMarginStatus'] = 3
1349
+ self.options['unifiedMarginStatus'] = 6
1344
1350
  return [self.options['enableUnifiedMargin'], self.options['enableUnifiedAccount']]
1345
1351
  rawPromises = [self.privateGetV5UserQueryApi(params), self.privateGetV5AccountInfo(params)]
1346
1352
  promises = await asyncio.gather(*rawPromises)
@@ -1405,7 +1411,7 @@ class bybit(Exchange, ImplicitAPI):
1405
1411
  accountResult = self.safe_dict(accountInfo, 'result', {})
1406
1412
  self.options['enableUnifiedMargin'] = self.safe_integer(result, 'unified') == 1
1407
1413
  self.options['enableUnifiedAccount'] = self.safe_integer(result, 'uta') == 1
1408
- self.options['unifiedMarginStatus'] = self.safe_integer(accountResult, 'unifiedMarginStatus', 3) # default to uta.1 if not found
1414
+ self.options['unifiedMarginStatus'] = self.safe_integer(accountResult, 'unifiedMarginStatus', 6) # default to uta 2.0 pro if not found
1409
1415
  return [self.options['enableUnifiedMargin'], self.options['enableUnifiedAccount']]
1410
1416
 
1411
1417
  async def upgrade_unified_trade_account(self, params={}):
@@ -2463,6 +2469,20 @@ class bybit(Exchange, ImplicitAPI):
2463
2469
  tickerList = self.safe_list(result, 'list', [])
2464
2470
  return self.parse_tickers(tickerList, parsedSymbols)
2465
2471
 
2472
+ async def fetch_bids_asks(self, symbols: Strings = None, params={}):
2473
+ """
2474
+ fetches the bid and ask price and volume for multiple markets
2475
+
2476
+ https://bybit-exchange.github.io/docs/v5/market/tickers
2477
+
2478
+ :param str[]|None symbols: unified symbols of the markets to fetch the bids and asks for, all markets are returned if not assigned
2479
+ :param dict [params]: extra parameters specific to the exchange API endpoint
2480
+ :param str [params.subType]: *contract only* 'linear', 'inverse'
2481
+ :param str [params.baseCoin]: *option only* base coin, default is 'BTC'
2482
+ :returns dict: a dictionary of `ticker structures <https://docs.ccxt.com/#/?id=ticker-structure>`
2483
+ """
2484
+ return await self.fetch_tickers(symbols, params)
2485
+
2466
2486
  def parse_ohlcv(self, ohlcv, market: Market = None) -> list:
2467
2487
  #
2468
2488
  # [
@@ -3284,7 +3304,7 @@ class bybit(Exchange, ImplicitAPI):
3284
3304
  isInverse = (type == 'inverse')
3285
3305
  isFunding = (lowercaseRawType == 'fund') or (lowercaseRawType == 'funding')
3286
3306
  if isUnifiedAccount:
3287
- unifiedMarginStatus = self.safe_integer(self.options, 'unifiedMarginStatus', 3)
3307
+ unifiedMarginStatus = self.safe_integer(self.options, 'unifiedMarginStatus', 6)
3288
3308
  if unifiedMarginStatus < 5:
3289
3309
  # it's not uta.20 where inverse are unified
3290
3310
  if isInverse:
@@ -3985,13 +4005,15 @@ class bybit(Exchange, ImplicitAPI):
3985
4005
  price = self.safe_value(rawOrder, 'price')
3986
4006
  orderParams = self.safe_dict(rawOrder, 'params', {})
3987
4007
  orderRequest = self.create_order_request(marketId, type, side, amount, price, orderParams, isUta)
4008
+ del orderRequest['category']
3988
4009
  ordersRequests.append(orderRequest)
3989
4010
  symbols = self.market_symbols(orderSymbols, None, False, True, True)
3990
4011
  market = self.market(symbols[0])
4012
+ unifiedMarginStatus = self.safe_integer(self.options, 'unifiedMarginStatus', 6)
3991
4013
  category = None
3992
4014
  category, params = self.get_bybit_type('createOrders', market, params)
3993
- if category == 'inverse':
3994
- raise NotSupported(self.id + ' createOrders does not allow inverse orders')
4015
+ if (category == 'inverse') and (unifiedMarginStatus < 5):
4016
+ raise NotSupported(self.id + ' createOrders does not allow inverse orders for non UTA2.0 account')
3995
4017
  request: dict = {
3996
4018
  'category': category,
3997
4019
  'request': ordersRequests,
@@ -4158,6 +4180,91 @@ class bybit(Exchange, ImplicitAPI):
4158
4180
  'id': self.safe_string(result, 'orderId'),
4159
4181
  })
4160
4182
 
4183
+ async def edit_orders(self, orders: List[OrderRequest], params={}):
4184
+ """
4185
+ edit a list of trade orders
4186
+
4187
+ https://bybit-exchange.github.io/docs/v5/order/batch-amend
4188
+
4189
+ :param Array orders: list of orders to create, each object should contain the parameters required by createOrder, namely symbol, type, side, amount, price and params
4190
+ :param dict [params]: extra parameters specific to the exchange API endpoint
4191
+ :returns dict: an `order structure <https://docs.ccxt.com/#/?id=order-structure>`
4192
+ """
4193
+ await self.load_markets()
4194
+ ordersRequests = []
4195
+ orderSymbols = []
4196
+ for i in range(0, len(orders)):
4197
+ rawOrder = orders[i]
4198
+ symbol = self.safe_string(rawOrder, 'symbol')
4199
+ orderSymbols.append(symbol)
4200
+ id = self.safe_string(rawOrder, 'id')
4201
+ type = self.safe_string(rawOrder, 'type')
4202
+ side = self.safe_string(rawOrder, 'side')
4203
+ amount = self.safe_value(rawOrder, 'amount')
4204
+ price = self.safe_value(rawOrder, 'price')
4205
+ orderParams = self.safe_dict(rawOrder, 'params', {})
4206
+ orderRequest = self.edit_order_request(id, symbol, type, side, amount, price, orderParams)
4207
+ del orderRequest['category']
4208
+ ordersRequests.append(orderRequest)
4209
+ orderSymbols = self.market_symbols(orderSymbols, None, False, True, True)
4210
+ market = self.market(orderSymbols[0])
4211
+ unifiedMarginStatus = self.safe_integer(self.options, 'unifiedMarginStatus', 6)
4212
+ category = None
4213
+ category, params = self.get_bybit_type('editOrders', market, params)
4214
+ if (category == 'inverse') and (unifiedMarginStatus < 5):
4215
+ raise NotSupported(self.id + ' editOrders does not allow inverse orders for non UTA2.0 account')
4216
+ request: dict = {
4217
+ 'category': category,
4218
+ 'request': ordersRequests,
4219
+ }
4220
+ response = await self.privatePostV5OrderAmendBatch(self.extend(request, params))
4221
+ result = self.safe_dict(response, 'result', {})
4222
+ data = self.safe_list(result, 'list', [])
4223
+ retInfo = self.safe_dict(response, 'retExtInfo', {})
4224
+ codes = self.safe_list(retInfo, 'list', [])
4225
+ # self.extend the error with the unsuccessful orders
4226
+ for i in range(0, len(codes)):
4227
+ code = codes[i]
4228
+ retCode = self.safe_integer(code, 'code')
4229
+ if retCode != 0:
4230
+ data[i] = self.extend(data[i], code)
4231
+ #
4232
+ # {
4233
+ # "retCode": 0,
4234
+ # "retMsg": "OK",
4235
+ # "result": {
4236
+ # "list": [
4237
+ # {
4238
+ # "category": "option",
4239
+ # "symbol": "ETH-30DEC22-500-C",
4240
+ # "orderId": "b551f227-7059-4fb5-a6a6-699c04dbd2f2",
4241
+ # "orderLinkId": ""
4242
+ # },
4243
+ # {
4244
+ # "category": "option",
4245
+ # "symbol": "ETH-30DEC22-700-C",
4246
+ # "orderId": "fa6a595f-1a57-483f-b9d3-30e9c8235a52",
4247
+ # "orderLinkId": ""
4248
+ # }
4249
+ # ]
4250
+ # },
4251
+ # "retExtInfo": {
4252
+ # "list": [
4253
+ # {
4254
+ # "code": 0,
4255
+ # "msg": "OK"
4256
+ # },
4257
+ # {
4258
+ # "code": 0,
4259
+ # "msg": "OK"
4260
+ # }
4261
+ # ]
4262
+ # },
4263
+ # "time": 1672222808060
4264
+ # }
4265
+ #
4266
+ return self.parse_orders(data)
4267
+
4161
4268
  def cancel_order_request(self, id: str, symbol: Str = None, params={}):
4162
4269
  market = self.market(symbol)
4163
4270
  request: dict = {
@@ -24,7 +24,6 @@ from ccxt.base.errors import OrderImmediatelyFillable
24
24
  from ccxt.base.errors import NotSupported
25
25
  from ccxt.base.errors import RateLimitExceeded
26
26
  from ccxt.base.errors import ExchangeNotAvailable
27
- from ccxt.base.errors import BadResponse
28
27
  from ccxt.base.decimal_to_precision import TICK_SIZE
29
28
  from ccxt.base.precise import Precise
30
29
 
@@ -1798,100 +1797,80 @@ class gate(Exchange, ImplicitAPI):
1798
1797
  await self.load_unified_status()
1799
1798
  response = await self.publicSpotGetCurrencies(params)
1800
1799
  #
1801
- # {
1802
- # "currency": "BCN",
1803
- # "delisted": False,
1804
- # "withdraw_disabled": True,
1805
- # "withdraw_delayed": False,
1806
- # "deposit_disabled": True,
1807
- # "trade_disabled": False
1808
- # }
1809
- #
1810
- # {
1811
- # "currency":"USDT_ETH",
1812
- # "delisted":false,
1813
- # "withdraw_disabled":false,
1814
- # "withdraw_delayed":false,
1815
- # "deposit_disabled":false,
1816
- # "trade_disabled":false,
1817
- # "chain":"ETH"
1818
- # }
1819
- #
1800
+ # [
1801
+ # {
1802
+ # "currency": "USDT_ETH",
1803
+ # "name": "Tether",
1804
+ # "delisted": False,
1805
+ # "withdraw_disabled": False,
1806
+ # "withdraw_delayed": False,
1807
+ # "deposit_disabled": False,
1808
+ # "trade_disabled": True,
1809
+ # "chain": "ETH"
1810
+ # },
1811
+ # ]
1812
+ #
1813
+ indexedCurrencies = self.index_by(response, 'currency')
1820
1814
  result: dict = {}
1821
1815
  for i in range(0, len(response)):
1822
1816
  entry = response[i]
1823
1817
  currencyId = self.safe_string(entry, 'currency')
1824
- currencyIdLower = self.safe_string_lower(entry, 'currency')
1825
1818
  parts = currencyId.split('_')
1826
- currency = parts[0]
1827
- code = self.safe_currency_code(currency)
1828
- networkId = self.safe_string(entry, 'chain')
1829
- networkCode = None
1830
- if networkId is not None:
1831
- networkCode = self.network_id_to_code(networkId, code)
1832
- delisted = self.safe_value(entry, 'delisted')
1833
- withdrawDisabled = self.safe_bool(entry, 'withdraw_disabled', False)
1834
- depositDisabled = self.safe_bool(entry, 'deposit_disabled', False)
1835
- tradeDisabled = self.safe_bool(entry, 'trade_disabled', False)
1836
- withdrawEnabled = not withdrawDisabled
1837
- depositEnabled = not depositDisabled
1838
- tradeEnabled = not tradeDisabled
1839
- listed = not delisted
1840
- active = listed and tradeEnabled and withdrawEnabled and depositEnabled
1841
- if self.safe_value(result, code) is None:
1819
+ partFirst = self.safe_string(parts, 0)
1820
+ # if there's an underscore then the second part is always the chain name(except the _OLD suffix)
1821
+ currencyName = currencyId if currencyId.endswith('_OLD') else partFirst
1822
+ withdrawEnabled = not self.safe_bool(entry, 'withdraw_disabled')
1823
+ depositEnabled = not self.safe_bool(entry, 'deposit_disabled')
1824
+ tradeDisabled = not self.safe_bool(entry, 'trade_disabled')
1825
+ precision = self.parse_number('0.0001') # temporary safe default, because no value provided from API
1826
+ code = self.safe_currency_code(currencyName)
1827
+ # check leveraged tokens(e.g. BTC3S, ETH5L)
1828
+ isLeveragedToken = False
1829
+ if currencyId.endswith('3S') or currencyId.endswith('3L') or currencyId.endswith('5S') or currencyId.endswith('5L'):
1830
+ realCurrencyId = currencyId[0:-2]
1831
+ if realCurrencyId in indexedCurrencies:
1832
+ isLeveragedToken = True
1833
+ type = 'leveraged' if isLeveragedToken else 'crypto'
1834
+ # some networks are null, they are mostly obsolete & unsupported dead tokens, so we can default their networkId to their tokenname
1835
+ networkId = self.safe_string(entry, 'chain', currencyId)
1836
+ networkCode = self.network_id_to_code(networkId, code)
1837
+ networkEntry = {
1838
+ 'info': entry,
1839
+ 'id': networkId,
1840
+ 'network': networkCode,
1841
+ 'limits': {
1842
+ 'deposit': {
1843
+ 'min': None,
1844
+ 'max': None,
1845
+ },
1846
+ 'withdraw': {
1847
+ 'min': None,
1848
+ 'max': None,
1849
+ },
1850
+ },
1851
+ 'active': not tradeDisabled,
1852
+ 'deposit': depositEnabled,
1853
+ 'withdraw': withdrawEnabled,
1854
+ 'fee': None,
1855
+ 'precision': precision,
1856
+ }
1857
+ # check if first entry for the specific currency
1858
+ if not (code in result):
1842
1859
  result[code] = {
1843
- 'id': currency,
1860
+ 'id': currencyName,
1861
+ 'lowerCaseId': currencyName.lower(),
1844
1862
  'code': code,
1845
- 'info': None,
1846
- 'name': None,
1847
- 'active': active,
1848
- 'deposit': depositEnabled,
1849
- 'withdraw': withdrawEnabled,
1850
- 'fee': None,
1851
- 'fees': [],
1852
- 'precision': self.parse_number('1e-4'),
1863
+ 'type': type,
1864
+ 'precision': precision,
1853
1865
  'limits': self.limits,
1854
1866
  'networks': {},
1867
+ 'info': [], # will be filled below
1855
1868
  }
1856
- depositAvailable = self.safe_value(result[code], 'deposit')
1857
- depositAvailable = depositEnabled if (depositEnabled) else depositAvailable
1858
- withdrawAvailable = self.safe_value(result[code], 'withdraw')
1859
- withdrawAvailable = withdrawEnabled if (withdrawEnabled) else withdrawAvailable
1860
- networks = self.safe_value(result[code], 'networks', {})
1861
- if networkCode is not None:
1862
- networks[networkCode] = {
1863
- 'info': entry,
1864
- 'id': networkId,
1865
- 'network': networkCode,
1866
- 'currencyId': currencyId,
1867
- 'lowerCaseCurrencyId': currencyIdLower,
1868
- 'deposit': depositEnabled,
1869
- 'withdraw': withdrawEnabled,
1870
- 'active': active,
1871
- 'fee': None,
1872
- 'precision': self.parse_number('1e-4'),
1873
- 'limits': {
1874
- 'amount': {
1875
- 'min': None,
1876
- 'max': None,
1877
- },
1878
- 'withdraw': {
1879
- 'min': None,
1880
- 'max': None,
1881
- },
1882
- 'deposit': {
1883
- 'min': None,
1884
- 'max': None,
1885
- },
1886
- },
1887
- }
1888
- result[code]['networks'] = networks
1889
- info = self.safe_value(result[code], 'info', [])
1869
+ result[code]['networks'][networkCode] = networkEntry
1870
+ info = self.safe_list(result[code], 'info', [])
1890
1871
  info.append(entry)
1891
1872
  result[code]['info'] = info
1892
- result[code]['active'] = depositAvailable and withdrawAvailable
1893
- result[code]['deposit'] = depositAvailable
1894
- result[code]['withdraw'] = withdrawAvailable
1873
+ result[code] = self.safe_currency_structure(result[code]) # self is needed after adding network entry
1895
1874
  return result
1896
1875
 
1897
1876
  async def fetch_funding_rate(self, symbol: str, params={}) -> FundingRate:
@@ -2136,6 +2115,27 @@ class gate(Exchange, ImplicitAPI):
2136
2115
  }
2137
2116
  return result
2138
2117
 
2118
+ async def fetch_deposit_addresses_by_network(self, code: str, params={}) -> List[DepositAddress]:
2119
+ """
2120
+ fetch a dictionary of addresses for a currency, indexed by network
2121
+ :param str code: unified currency code of the currency for the deposit address
2122
+ :param dict [params]: extra parameters specific to the api endpoint
2123
+ :returns dict: a dictionary of `address structures <https://docs.ccxt.com/#/?id=address-structure>` indexed by the network
2124
+ """
2125
+ await self.load_markets()
2126
+ currency = self.currency(code)
2127
+ request = {
2128
+ 'currency': currency['id'],
2129
+ }
2130
+ response = await self.privateWalletGetDepositAddress(self.extend(request, params))
2131
+ chains = self.safe_value(response, 'multichain_addresses', [])
2132
+ currencyId = self.safe_string(response, 'currency')
2133
+ currency = self.safe_currency(currencyId, currency)
2134
+ parsed = self.parse_deposit_addresses(chains, [currency['code']], False, {
2135
+ 'currency': currency['id'],
2136
+ })
2137
+ return self.index_by(parsed, 'network')
2138
+
2139
2139
  async def fetch_deposit_address(self, code: str, params={}) -> DepositAddress:
2140
2140
  """
2141
2141
  fetch the deposit address for a currency associated with self account
@@ -2148,65 +2148,30 @@ class gate(Exchange, ImplicitAPI):
2148
2148
  :returns dict: an `address structure <https://docs.ccxt.com/#/?id=address-structure>`
2149
2149
  """
2150
2150
  await self.load_markets()
2151
- currency = self.currency(code)
2152
- rawNetwork = self.safe_string_upper(params, 'network')
2153
- params = self.omit(params, 'network')
2154
- request: dict = {
2155
- 'currency': currency['id'], # todo: currencies have network-junctions
2156
- }
2157
- response = await self.privateWalletGetDepositAddress(self.extend(request, params))
2151
+ networkCode = None
2152
+ networkCode, params = self.handle_network_code_and_params(params)
2153
+ chainsIndexedById = await self.fetch_deposit_addresses_by_network(code, params)
2154
+ selectedNetworkId = self.select_network_code_from_unified_networks(code, networkCode, chainsIndexedById)
2155
+ return chainsIndexedById[selectedNetworkId]
2156
+
2157
+ def parse_deposit_address(self, depositAddress, currency=None):
2158
2158
  #
2159
- # {
2160
- # "currency": "XRP",
2161
- # "address": "rHcFoo6a9qT5NHiVn1THQRhsEGcxtYCV4d 391331007",
2162
- # "multichain_addresses": [
2163
- # {
2164
- # "chain": "XRP",
2165
- # "address": "rHcFoo6a9qT5NHiVn1THQRhsEGcxtYCV4d",
2166
- # "payment_id": "391331007",
2167
- # "payment_name": "Tag",
2168
- # "obtain_failed": 0
2169
- # }
2170
- # ]
2171
- # }
2159
+ # {
2160
+ # chain: "BTC",
2161
+ # address: "1Nxu.......Ys",
2162
+ # payment_id: "",
2163
+ # payment_name: "",
2164
+ # obtain_failed: "0",
2165
+ # }
2172
2166
  #
2173
- currencyId = self.safe_string(response, 'currency')
2174
- code = self.safe_currency_code(currencyId)
2175
- networkId = self.network_code_to_id(rawNetwork, code)
2176
- network = None
2177
- tag = None
2178
- address = None
2179
- if networkId is not None:
2180
- addresses = self.safe_value(response, 'multichain_addresses')
2181
- for i in range(0, len(addresses)):
2182
- entry = addresses[i]
2183
- entryNetwork = self.safe_string(entry, 'chain')
2184
- if networkId == entryNetwork:
2185
- obtainFailed = self.safe_integer(entry, 'obtain_failed')
2186
- if obtainFailed:
2187
- break
2188
- address = self.safe_string(entry, 'address')
2189
- tag = self.safe_string(entry, 'payment_id')
2190
- network = self.network_id_to_code(networkId, code)
2191
- break
2192
- else:
2193
- addressField = self.safe_string(response, 'address')
2194
- if addressField is not None:
2195
- if addressField.find('New address is being generated for you, please wait') >= 0:
2196
- raise BadResponse(self.id + ' ' + 'New address is being generated for you, please wait a few seconds and try again to get the address.')
2197
- if addressField.find(' ') >= 0:
2198
- splitted = addressField.split(' ')
2199
- address = splitted[0]
2200
- tag = splitted[1]
2201
- else:
2202
- address = addressField
2167
+ address = self.safe_string(depositAddress, 'address')
2203
2168
  self.check_address(address)
2204
2169
  return {
2205
- 'info': response,
2206
- 'currency': code,
2207
- 'network': network,
2170
+ 'info': depositAddress,
2171
+ 'currency': self.safe_string(currency, 'code'),
2208
2172
  'address': address,
2209
- 'tag': tag,
2173
+ 'tag': self.safe_string(depositAddress, 'payment_id'),
2174
+ 'network': self.network_id_to_code(self.safe_string(depositAddress, 'chain')),
2210
2175
  }
2211
2176
 
2212
2177
  async def fetch_trading_fee(self, symbol: str, params={}) -> TradingFeeInterface:
@@ -3819,14 +3784,10 @@ class gate(Exchange, ImplicitAPI):
3819
3784
  }
3820
3785
  if tag is not None:
3821
3786
  request['memo'] = tag
3822
- networks = self.safe_value(self.options, 'networks', {})
3823
- network = self.safe_string_upper(params, 'network') # self line allows the user to specify either ERC20 or ETH
3824
- network = self.safe_string_lower(networks, network, network) # handle ETH>ERC20 alias
3825
- if network is not None:
3826
- request['chain'] = network
3827
- params = self.omit(params, 'network')
3828
- else:
3829
- request['chain'] = currency['id'] # todo: currencies have network-junctions
3787
+ networkCode = None
3788
+ networkCode, params = self.handle_network_code_and_params(params)
3789
+ if networkCode is not None:
3790
+ request['chain'] = self.network_code_to_id(networkCode)
3830
3791
  response = await self.privateWithdrawalsPostWithdrawals(self.extend(request, params))
3831
3792
  #
3832
3793
  # {
@@ -981,9 +981,9 @@ class kraken(Exchange, ImplicitAPI):
981
981
  'high': self.safe_string(high, 1),
982
982
  'low': self.safe_string(low, 1),
983
983
  'bid': self.safe_string(bid, 0),
984
- 'bidVolume': None,
984
+ 'bidVolume': self.safe_string(bid, 2),
985
985
  'ask': self.safe_string(ask, 0),
986
- 'askVolume': None,
986
+ 'askVolume': self.safe_string(ask, 2),
987
987
  'vwap': vwap,
988
988
  'open': self.safe_string(ticker, 'o'),
989
989
  'close': last,