ccxt 4.2.68__py2.py3-none-any.whl → 4.2.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.

ccxt/async_support/htx.py CHANGED
@@ -486,6 +486,7 @@ class htx(Exchange, ImplicitAPI):
486
486
  'v2/sub-user/api-key-modification': 1,
487
487
  'v2/sub-user/api-key-deletion': 1,
488
488
  'v1/subuser/transfer': 10,
489
+ 'v1/trust/user/active/credit': 10,
489
490
  # Trading
490
491
  'v1/order/orders/place': 0.2,
491
492
  'v1/order/batch-orders': 0.4,
@@ -429,6 +429,7 @@ class kucoin(Exchange, ImplicitAPI):
429
429
  '12h': '12hour',
430
430
  '1d': '1day',
431
431
  '1w': '1week',
432
+ '1M': '1month',
432
433
  },
433
434
  'precisionMode': TICK_SIZE,
434
435
  'exceptions': {
@@ -4183,7 +4184,7 @@ class kucoin(Exchange, ImplicitAPI):
4183
4184
  url = url + endpoint
4184
4185
  isFuturePrivate = (api == 'futuresPrivate')
4185
4186
  isPrivate = (api == 'private')
4186
- isBroker = (api == 'private')
4187
+ isBroker = (api == 'broker')
4187
4188
  if isPrivate or isFuturePrivate or isBroker:
4188
4189
  self.check_required_credentials()
4189
4190
  timestamp = str(self.nonce())
@@ -4212,7 +4213,8 @@ class kucoin(Exchange, ImplicitAPI):
4212
4213
  headers['KC-API-PARTNER'] = partnerId
4213
4214
  if isBroker:
4214
4215
  brokerName = self.safe_string(partner, 'name')
4215
- headers['KC-BROKER-NAME'] = brokerName
4216
+ if brokerName is not None:
4217
+ headers['KC-BROKER-NAME'] = brokerName
4216
4218
  return {'url': url, 'method': method, 'body': body, 'headers': headers}
4217
4219
 
4218
4220
  def handle_errors(self, code, reason, url, method, headers, body, response, requestHeaders, requestBody):
ccxt/base/exchange.py CHANGED
@@ -4,7 +4,7 @@
4
4
 
5
5
  # -----------------------------------------------------------------------------
6
6
 
7
- __version__ = '4.2.68'
7
+ __version__ = '4.2.70'
8
8
 
9
9
  # -----------------------------------------------------------------------------
10
10
 
ccxt/bitstamp.py CHANGED
@@ -6,7 +6,7 @@
6
6
  from ccxt.base.exchange import Exchange
7
7
  from ccxt.abstract.bitstamp import ImplicitAPI
8
8
  import hashlib
9
- from ccxt.base.types import Balances, Currency, Int, Market, Order, OrderBook, OrderSide, OrderType, Str, Strings, Ticker, Tickers, Trade, Transaction
9
+ from ccxt.base.types import Balances, Currency, Int, Market, Order, TransferEntry, OrderBook, OrderSide, OrderType, Str, Strings, Ticker, Tickers, Trade, Transaction
10
10
  from typing import List
11
11
  from ccxt.base.errors import ExchangeError
12
12
  from ccxt.base.errors import PermissionDenied
@@ -98,6 +98,7 @@ class bitstamp(Exchange, ImplicitAPI):
98
98
  'setLeverage': False,
99
99
  'setMarginMode': False,
100
100
  'setPositionMode': False,
101
+ 'transfer': True,
101
102
  'withdraw': True,
102
103
  },
103
104
  'urls': {
@@ -1983,6 +1984,68 @@ class bitstamp(Exchange, ImplicitAPI):
1983
1984
  response = getattr(self, method)(self.extend(request, params))
1984
1985
  return self.parse_transaction(response, currency)
1985
1986
 
1987
+ def transfer(self, code: str, amount: float, fromAccount: str, toAccount: str, params={}) -> TransferEntry:
1988
+ """
1989
+ transfer currency internally between wallets on the same account
1990
+ :see: https://www.bitstamp.net/api/#tag/Sub-account/operation/TransferFromMainToSub
1991
+ :see: https://www.bitstamp.net/api/#tag/Sub-account/operation/TransferFromSubToMain
1992
+ :param str code: unified currency code
1993
+ :param float amount: amount to transfer
1994
+ :param str fromAccount: account to transfer from
1995
+ :param str toAccount: account to transfer to
1996
+ :param dict [params]: extra parameters specific to the exchange API endpoint
1997
+ :returns dict: a `transfer structure <https://docs.ccxt.com/#/?id=transfer-structure>`
1998
+ """
1999
+ self.load_markets()
2000
+ currency = self.currency(code)
2001
+ amount = self.currency_to_precision(code, amount)
2002
+ amount = self.parse_to_numeric(amount)
2003
+ request = {
2004
+ 'amount': amount,
2005
+ 'currency': currency['id'].upper(),
2006
+ }
2007
+ response = None
2008
+ if fromAccount == 'main':
2009
+ request['subAccount'] = toAccount
2010
+ response = self.privatePostTransferFromMain(self.extend(request, params))
2011
+ elif toAccount == 'main':
2012
+ request['subAccount'] = fromAccount
2013
+ response = self.privatePostTransferToMain(self.extend(request, params))
2014
+ else:
2015
+ raise BadRequest(self.id + ' transfer() only supports from or to main')
2016
+ #
2017
+ # {status: 'ok'}
2018
+ #
2019
+ transfer = self.parse_transfer(response, currency)
2020
+ transfer['amount'] = amount
2021
+ transfer['fromAccount'] = fromAccount
2022
+ transfer['toAccount'] = toAccount
2023
+ return transfer
2024
+
2025
+ def parse_transfer(self, transfer, currency=None):
2026
+ #
2027
+ # {status: 'ok'}
2028
+ #
2029
+ status = self.safe_string(transfer, 'status')
2030
+ return {
2031
+ 'info': transfer,
2032
+ 'id': None,
2033
+ 'timestamp': None,
2034
+ 'datetime': None,
2035
+ 'currency': currency['code'],
2036
+ 'amount': None,
2037
+ 'fromAccount': None,
2038
+ 'toAccount': None,
2039
+ 'status': self.parse_transfer_status(status),
2040
+ }
2041
+
2042
+ def parse_transfer_status(self, status):
2043
+ statuses = {
2044
+ 'ok': 'ok',
2045
+ 'error': 'failed',
2046
+ }
2047
+ return self.safe_string(statuses, status, status)
2048
+
1986
2049
  def nonce(self):
1987
2050
  return self.milliseconds()
1988
2051
 
ccxt/gate.py CHANGED
@@ -6,7 +6,7 @@
6
6
  from ccxt.base.exchange import Exchange
7
7
  from ccxt.abstract.gate import ImplicitAPI
8
8
  import hashlib
9
- from ccxt.base.types import Balances, Currency, Greeks, Int, Market, Order, TransferEntry, OrderBook, OrderRequest, OrderSide, OrderType, FundingHistory, Str, Strings, Ticker, Tickers, Trade, Transaction
9
+ from ccxt.base.types import Balances, Currency, Greeks, Int, Leverage, Leverages, Market, Order, TransferEntry, OrderBook, OrderRequest, OrderSide, OrderType, FundingHistory, Str, Strings, Ticker, Tickers, Trade, Transaction
10
10
  from typing import List
11
11
  from ccxt.base.errors import ExchangeError
12
12
  from ccxt.base.errors import PermissionDenied
@@ -133,7 +133,8 @@ class gate(Exchange, ImplicitAPI):
133
133
  'fetchIsolatedBorrowRate': False,
134
134
  'fetchIsolatedBorrowRates': False,
135
135
  'fetchLedger': True,
136
- 'fetchLeverage': False,
136
+ 'fetchLeverage': True,
137
+ 'fetchLeverages': True,
137
138
  'fetchLeverageTiers': True,
138
139
  'fetchLiquidations': True,
139
140
  'fetchMarginMode': False,
@@ -4198,7 +4199,9 @@ class gate(Exchange, ImplicitAPI):
4198
4199
  lastTradeTimestamp = self.safe_integer(order, 'update_time_ms')
4199
4200
  if lastTradeTimestamp is None:
4200
4201
  lastTradeTimestamp = self.safe_timestamp_2(order, 'update_time', 'finish_time')
4201
- marketType = 'spot' if ('currency_pair' in order) else 'contract'
4202
+ marketType = 'contract'
4203
+ if ('currency_pair' in order) or ('market' in order):
4204
+ marketType = 'spot'
4202
4205
  exchangeSymbol = self.safe_string_2(order, 'currency_pair', 'market', contract)
4203
4206
  symbol = self.safe_symbol(exchangeSymbol, market, '_', marketType)
4204
4207
  # Everything below self(above return) is related to fees
@@ -6514,6 +6517,166 @@ class gate(Exchange, ImplicitAPI):
6514
6517
  side = '' # side is not used but needs to be present, otherwise crashes in php
6515
6518
  return self.create_order(symbol, 'market', side, 0, None, params)
6516
6519
 
6520
+ def fetch_leverage(self, symbol: str, params={}) -> Leverage:
6521
+ """
6522
+ fetch the set leverage for a market
6523
+ :see: https://www.gate.io/docs/developers/apiv4/en/#get-unified-account-information
6524
+ :see: https://www.gate.io/docs/developers/apiv4/en/#get-detail-of-lending-market
6525
+ :see: https://www.gate.io/docs/developers/apiv4/en/#query-one-single-margin-currency-pair-deprecated
6526
+ :param str symbol: unified market symbol
6527
+ :param dict [params]: extra parameters specific to the exchange API endpoint
6528
+ :param boolean [params.unified]: default False, set to True for fetching the unified accounts leverage
6529
+ :returns dict: a `leverage structure <https://docs.ccxt.com/#/?id=leverage-structure>`
6530
+ """
6531
+ self.load_markets()
6532
+ market = None
6533
+ if symbol is not None:
6534
+ # unified account does not require a symbol
6535
+ market = self.market(symbol)
6536
+ request = {}
6537
+ response = None
6538
+ isUnified = self.safe_bool(params, 'unified')
6539
+ params = self.omit(params, 'unified')
6540
+ if market['spot']:
6541
+ request['currency_pair'] = market['id']
6542
+ if isUnified:
6543
+ response = self.publicMarginGetUniCurrencyPairsCurrencyPair(self.extend(request, params))
6544
+ #
6545
+ # {
6546
+ # "currency_pair": "BTC_USDT",
6547
+ # "base_min_borrow_amount": "0.0001",
6548
+ # "quote_min_borrow_amount": "1",
6549
+ # "leverage": "10"
6550
+ # }
6551
+ #
6552
+ else:
6553
+ response = self.publicMarginGetCurrencyPairsCurrencyPair(self.extend(request, params))
6554
+ #
6555
+ # {
6556
+ # "id": "BTC_USDT",
6557
+ # "base": "BTC",
6558
+ # "quote": "USDT",
6559
+ # "leverage": 10,
6560
+ # "min_base_amount": "0.0001",
6561
+ # "min_quote_amount": "1",
6562
+ # "max_quote_amount": "40000000",
6563
+ # "status": 1
6564
+ # }
6565
+ #
6566
+ elif isUnified:
6567
+ response = self.privateUnifiedGetAccounts(self.extend(request, params))
6568
+ #
6569
+ # {
6570
+ # "user_id": 10001,
6571
+ # "locked": False,
6572
+ # "balances": {
6573
+ # "ETH": {
6574
+ # "available": "0",
6575
+ # "freeze": "0",
6576
+ # "borrowed": "0.075393666654",
6577
+ # "negative_liab": "0",
6578
+ # "futures_pos_liab": "0",
6579
+ # "equity": "1016.1",
6580
+ # "total_freeze": "0",
6581
+ # "total_liab": "0"
6582
+ # },
6583
+ # "POINT": {
6584
+ # "available": "9999999999.017023138734",
6585
+ # "freeze": "0",
6586
+ # "borrowed": "0",
6587
+ # "negative_liab": "0",
6588
+ # "futures_pos_liab": "0",
6589
+ # "equity": "12016.1",
6590
+ # "total_freeze": "0",
6591
+ # "total_liab": "0"
6592
+ # },
6593
+ # "USDT": {
6594
+ # "available": "0.00000062023",
6595
+ # "freeze": "0",
6596
+ # "borrowed": "0",
6597
+ # "negative_liab": "0",
6598
+ # "futures_pos_liab": "0",
6599
+ # "equity": "16.1",
6600
+ # "total_freeze": "0",
6601
+ # "total_liab": "0"
6602
+ # }
6603
+ # },
6604
+ # "total": "230.94621713",
6605
+ # "borrowed": "161.66395521",
6606
+ # "total_initial_margin": "1025.0524665088",
6607
+ # "total_margin_balance": "3382495.944473949183",
6608
+ # "total_maintenance_margin": "205.01049330176",
6609
+ # "total_initial_margin_rate": "3299.827135672679",
6610
+ # "total_maintenance_margin_rate": "16499.135678363399",
6611
+ # "total_available_margin": "3381470.892007440383",
6612
+ # "unified_account_total": "3381470.892007440383",
6613
+ # "unified_account_total_liab": "0",
6614
+ # "unified_account_total_equity": "100016.1",
6615
+ # "leverage": "2"
6616
+ # }
6617
+ #
6618
+ else:
6619
+ raise NotSupported(self.id + ' fetchLeverage() does not support ' + market['type'] + ' markets')
6620
+ return self.parse_leverage(response, market)
6621
+
6622
+ def fetch_leverages(self, symbols: List[str] = None, params={}) -> Leverages:
6623
+ """
6624
+ fetch the set leverage for all leverage markets, only spot margin is supported on gate
6625
+ :see: https://www.gate.io/docs/developers/apiv4/en/#list-lending-markets
6626
+ :see: https://www.gate.io/docs/developers/apiv4/en/#list-all-supported-currency-pairs-supported-in-margin-trading-deprecated
6627
+ :param str[] symbols: a list of unified market symbols
6628
+ :param dict [params]: extra parameters specific to the exchange API endpoint
6629
+ :param boolean [params.unified]: default False, set to True for fetching unified account leverages
6630
+ :returns dict: a list of `leverage structures <https://docs.ccxt.com/#/?id=leverage-structure>`
6631
+ """
6632
+ self.load_markets()
6633
+ symbols = self.market_symbols(symbols)
6634
+ response = None
6635
+ isUnified = self.safe_bool(params, 'unified')
6636
+ params = self.omit(params, 'unified')
6637
+ marketIdRequest = 'id'
6638
+ if isUnified:
6639
+ marketIdRequest = 'currency_pair'
6640
+ response = self.publicMarginGetUniCurrencyPairs(params)
6641
+ #
6642
+ # [
6643
+ # {
6644
+ # "currency_pair": "1INCH_USDT",
6645
+ # "base_min_borrow_amount": "8",
6646
+ # "quote_min_borrow_amount": "1",
6647
+ # "leverage": "3"
6648
+ # },
6649
+ # ]
6650
+ #
6651
+ else:
6652
+ response = self.publicMarginGetCurrencyPairs(params)
6653
+ #
6654
+ # [
6655
+ # {
6656
+ # "id": "1CAT_USDT",
6657
+ # "base": "1CAT",
6658
+ # "quote": "USDT",
6659
+ # "leverage": 3,
6660
+ # "min_base_amount": "71",
6661
+ # "min_quote_amount": "1",
6662
+ # "max_quote_amount": "10000",
6663
+ # "status": 1
6664
+ # },
6665
+ # ]
6666
+ #
6667
+ return self.parse_leverages(response, symbols, marketIdRequest, 'spot')
6668
+
6669
+ def parse_leverage(self, leverage, market=None) -> Leverage:
6670
+ marketId = self.safe_string_2(leverage, 'currency_pair', 'id')
6671
+ leverageValue = self.safe_integer(leverage, 'leverage')
6672
+ return {
6673
+ 'info': leverage,
6674
+ 'symbol': self.safe_symbol(marketId, market, '_', 'spot'),
6675
+ 'marginMode': None,
6676
+ 'longLeverage': leverageValue,
6677
+ 'shortLeverage': leverageValue,
6678
+ }
6679
+
6517
6680
  def handle_errors(self, code, reason, url, method, headers, body, response, requestHeaders, requestBody):
6518
6681
  if response is None:
6519
6682
  return None
ccxt/gemini.py CHANGED
@@ -41,7 +41,7 @@ class gemini(Exchange, ImplicitAPI):
41
41
  'CORS': None,
42
42
  'spot': True,
43
43
  'margin': False,
44
- 'swap': False,
44
+ 'swap': True,
45
45
  'future': False,
46
46
  'option': False,
47
47
  'addMargin': False,
@@ -266,11 +266,11 @@ class gemini(Exchange, ImplicitAPI):
266
266
  },
267
267
  },
268
268
  'options': {
269
- 'fetchMarketsMethod': 'fetch_markets_from_web', # fetch_markets_from_api, fetch_markets_from_web
269
+ 'fetchMarketsMethod': 'fetch_markets_from_api', # fetch_markets_from_api, fetch_markets_from_web
270
270
  'fetchMarketFromWebRetries': 10,
271
271
  'fetchMarketsFromAPI': {
272
272
  'fetchDetailsForAllSymbols': False,
273
- 'fetchDetailsForMarketIds': [],
273
+ 'quoteCurrencies': ['USDT', 'GUSD', 'USD', 'DAI', 'EUR', 'GBP', 'SGD', 'BTC', 'ETH', 'LTC', 'BCH'],
274
274
  },
275
275
  'fetchMarkets': {
276
276
  'webApiEnable': True, # fetches from WEB
@@ -321,10 +321,7 @@ class gemini(Exchange, ImplicitAPI):
321
321
  return None
322
322
  #
323
323
  # {
324
- # "tradingPairs": [
325
- # ["BTCAUD", 2, 8, "0.00001", 10, True],
326
- # ...
327
- # ],
324
+ # "tradingPairs": [['BTCUSD', 2, 8, '0.00001', 10, True], ...],
328
325
  # "currencies": [
329
326
  # ["ORCA", "Orca", 204, 6, 0, 6, 8, False, null, "solana"], #, precisions seem to be the 5th index
330
327
  # ["ATOM", "Cosmos", 44, 6, 0, 6, 8, False, null, "cosmos"],
@@ -343,6 +340,7 @@ class gemini(Exchange, ImplicitAPI):
343
340
  # }
344
341
  #
345
342
  result = {}
343
+ self.options['tradingPairs'] = self.safe_list(data, 'tradingPairs')
346
344
  currenciesArray = self.safe_value(data, 'currencies', [])
347
345
  for i in range(0, len(currenciesArray)):
348
346
  currency = currenciesArray[i]
@@ -539,7 +537,7 @@ class gemini(Exchange, ImplicitAPI):
539
537
  return result
540
538
 
541
539
  def fetch_markets_from_api(self, params={}):
542
- response = self.publicGetV1Symbols(params)
540
+ marketIdsRaw = self.publicGetV1Symbols(params)
543
541
  #
544
542
  # [
545
543
  # "btcusd",
@@ -547,87 +545,166 @@ class gemini(Exchange, ImplicitAPI):
547
545
  # ...
548
546
  # ]
549
547
  #
550
- result = {}
551
- for i in range(0, len(response)):
552
- marketId = response[i]
553
- market = {
554
- 'symbol': marketId,
555
- }
556
- result[marketId] = self.parse_market(market)
557
- options = self.safe_value(self.options, 'fetchMarketsFromAPI', {})
558
- fetchDetailsForAllSymbols = self.safe_bool(options, 'fetchDetailsForAllSymbols', False)
559
- fetchDetailsForMarketIds = self.safe_value(options, 'fetchDetailsForMarketIds', [])
560
- promises = []
548
+ result = []
549
+ options = self.safe_dict(self.options, 'fetchMarketsFromAPI', {})
550
+ bugSymbol = 'efilfil' # we skip self inexistent test symbol, which bugs other functions
561
551
  marketIds = []
562
- if fetchDetailsForAllSymbols:
563
- marketIds = response
552
+ for i in range(0, len(marketIdsRaw)):
553
+ if marketIdsRaw[i] != bugSymbol:
554
+ marketIds.append(marketIdsRaw[i])
555
+ if self.safe_bool(options, 'fetchDetailsForAllSymbols', False):
556
+ promises = []
557
+ for i in range(0, len(marketIds)):
558
+ marketId = marketIds[i]
559
+ request = {
560
+ 'symbol': marketId,
561
+ }
562
+ promises.append(self.publicGetV1SymbolsDetailsSymbol(self.extend(request, params)))
563
+ #
564
+ # {
565
+ # "symbol": "BTCUSD",
566
+ # "base_currency": "BTC",
567
+ # "quote_currency": "USD",
568
+ # "tick_size": 1E-8,
569
+ # "quote_increment": 0.01,
570
+ # "min_order_size": "0.00001",
571
+ # "status": "open",
572
+ # "wrap_enabled": False
573
+ # }
574
+ #
575
+ responses = promises
576
+ for i in range(0, len(responses)):
577
+ result.append(self.parse_market(responses[i]))
564
578
  else:
565
- marketIds = fetchDetailsForMarketIds
566
- for i in range(0, len(marketIds)):
567
- marketId = marketIds[i]
568
- request = {
569
- 'symbol': marketId,
570
- }
571
- promises.append(self.publicGetV1SymbolsDetailsSymbol(self.extend(request, params)))
572
- #
573
- # {
574
- # "symbol": "BTCUSD",
575
- # "base_currency": "BTC",
576
- # "quote_currency": "USD",
577
- # "tick_size": 1E-8,
578
- # "quote_increment": 0.01,
579
- # "min_order_size": "0.00001",
580
- # "status": "open",
581
- # "wrap_enabled": False
582
- # }
583
- #
584
- promises = promises
585
- for i in range(0, len(promises)):
586
- responseInner = promises[i]
587
- marketId = self.safe_string_lower(responseInner, 'symbol')
588
- result[marketId] = self.parse_market(responseInner)
589
- return self.to_array(result)
579
+ # use trading-pairs info, if it was fetched
580
+ tradingPairs = self.safe_list(self.options, 'tradingPairs')
581
+ if tradingPairs is not None:
582
+ indexedTradingPairs = self.index_by(tradingPairs, 0)
583
+ for i in range(0, len(marketIds)):
584
+ marketId = marketIds[i]
585
+ tradingPair = self.safe_list(indexedTradingPairs, marketId.upper())
586
+ if tradingPair is not None:
587
+ result.append(self.parse_market(tradingPair))
588
+ else:
589
+ for i in range(0, len(marketIds)):
590
+ result.append(self.parse_market(marketIds[i]))
591
+ return result
590
592
 
591
593
  def parse_market(self, response) -> Market:
592
- marketId = self.safe_string_lower(response, 'symbol')
593
- baseId = self.safe_string(response, 'base_currency')
594
- quoteId = self.safe_string(response, 'quote_currency')
595
- if baseId is None:
596
- idLength = len(marketId) - 0
597
- isUSDT = marketId.find('usdt') >= 0
598
- quoteSize = 4 if isUSDT else 3
599
- baseId = marketId[0:idLength - quoteSize] # Not True for all markets
600
- quoteId = marketId[idLength - quoteSize:idLength]
594
+ #
595
+ # response might be:
596
+ #
597
+ # btcusd
598
+ #
599
+ # or
600
+ #
601
+ # [
602
+ # 'BTCUSD', # symbol
603
+ # 2, # priceTickDecimalPlaces
604
+ # 8, # quantityTickDecimalPlaces
605
+ # '0.00001', # quantityMinimum
606
+ # 10, # quantityRoundDecimalPlaces
607
+ # True # minimumsAreInclusive
608
+ # ],
609
+ #
610
+ # or
611
+ #
612
+ # {
613
+ # "symbol": "BTCUSD", # perpetuals have 'PERP' suffix, i.e. DOGEUSDPERP
614
+ # "base_currency": "BTC",
615
+ # "quote_currency": "USD",
616
+ # "tick_size": 1E-8,
617
+ # "quote_increment": 0.01,
618
+ # "min_order_size": "0.00001",
619
+ # "status": "open",
620
+ # "wrap_enabled": False
621
+ # "product_type": "swap", # only in perps
622
+ # "contract_type": "linear", # only in perps
623
+ # "contract_price_currency": "GUSD" # only in perps
624
+ # }
625
+ #
626
+ marketId = None
627
+ baseId = None
628
+ quoteId = None
629
+ settleId = None
630
+ tickSize = None
631
+ increment = None
632
+ minSize = None
633
+ status = None
634
+ swap = False
635
+ contractSize = None
636
+ linear = None
637
+ inverse = None
638
+ isString = (isinstance(response, str))
639
+ isArray = (isinstance(response, list))
640
+ if not isString and not isArray:
641
+ marketId = self.safe_string_lower(response, 'symbol')
642
+ minSize = self.safe_number(response, 'min_order_size')
643
+ tickSize = self.safe_number(response, 'tick_size')
644
+ increment = self.safe_number(response, 'quote_increment')
645
+ status = self.parse_market_active(self.safe_string(response, 'status'))
646
+ baseId = self.safe_string(response, 'base_currency')
647
+ quoteId = self.safe_string(response, 'quote_currency')
648
+ settleId = self.safe_string(response, 'contract_price_currency')
649
+ else:
650
+ # if no detailed API was called, then parse either string or array
651
+ if isString:
652
+ marketId = response
653
+ else:
654
+ marketId = self.safe_string_lower(response, 0)
655
+ minSize = self.safe_number(response, 3)
656
+ tickSize = self.parse_number(self.parse_precision(self.safe_string(response, 1)))
657
+ increment = self.parse_number(self.parse_precision(self.safe_string(response, 2)))
658
+ marketIdUpper = marketId.upper()
659
+ isPerp = (marketIdUpper.find('PERP') >= 0)
660
+ marketIdWithoutPerp = marketIdUpper.replace('PERP', '')
661
+ quoteQurrencies = self.handle_option('fetchMarketsFromAPI', 'quoteCurrencies', [])
662
+ for i in range(0, len(quoteQurrencies)):
663
+ quoteCurrency = quoteQurrencies[i]
664
+ if marketIdWithoutPerp.endswith(quoteCurrency):
665
+ baseId = marketIdWithoutPerp.replace(quoteCurrency, '')
666
+ quoteId = quoteCurrency
667
+ if isPerp:
668
+ settleId = quoteCurrency # always same
669
+ break
601
670
  base = self.safe_currency_code(baseId)
602
671
  quote = self.safe_currency_code(quoteId)
603
- status = self.safe_string(response, 'status')
672
+ settle = self.safe_currency_code(settleId)
673
+ symbol = base + '/' + quote
674
+ if settleId is not None:
675
+ symbol = symbol + ':' + settle
676
+ swap = True
677
+ contractSize = tickSize # always same
678
+ linear = True # always linear
679
+ inverse = False
680
+ type = 'swap' if swap else 'spot'
604
681
  return {
605
682
  'id': marketId,
606
- 'symbol': base + '/' + quote,
683
+ 'symbol': symbol,
607
684
  'base': base,
608
685
  'quote': quote,
609
- 'settle': None,
686
+ 'settle': settle,
610
687
  'baseId': baseId,
611
688
  'quoteId': quoteId,
612
- 'settleId': None,
613
- 'type': 'spot',
614
- 'spot': True,
689
+ 'settleId': settleId,
690
+ 'type': type,
691
+ 'spot': not swap,
615
692
  'margin': False,
616
- 'swap': False,
693
+ 'swap': swap,
617
694
  'future': False,
618
695
  'option': False,
619
- 'active': self.parse_market_active(status),
620
- 'contract': False,
621
- 'linear': None,
622
- 'inverse': None,
623
- 'contractSize': None,
696
+ 'active': status,
697
+ 'contract': swap,
698
+ 'linear': linear,
699
+ 'inverse': inverse,
700
+ 'contractSize': contractSize,
624
701
  'expiry': None,
625
702
  'expiryDatetime': None,
626
703
  'strike': None,
627
704
  'optionType': None,
628
705
  'precision': {
629
- 'price': self.safe_number(response, 'quote_increment'),
630
- 'amount': self.safe_number(response, 'tick_size'),
706
+ 'price': increment,
707
+ 'amount': tickSize,
631
708
  },
632
709
  'limits': {
633
710
  'leverage': {
@@ -635,7 +712,7 @@ class gemini(Exchange, ImplicitAPI):
635
712
  'max': None,
636
713
  },
637
714
  'amount': {
638
- 'min': self.safe_number(response, 'min_order_size'),
715
+ 'min': minSize,
639
716
  'max': None,
640
717
  },
641
718
  'price': {
ccxt/hitbtc.py CHANGED
@@ -332,6 +332,7 @@ class hitbtc(Exchange, ImplicitAPI):
332
332
  '2012': BadRequest,
333
333
  '2020': BadRequest,
334
334
  '2022': BadRequest,
335
+ '2024': InvalidOrder, # Invalid margin mode.
335
336
  '10001': BadRequest,
336
337
  '10021': AccountSuspended,
337
338
  '10022': BadRequest,
@@ -349,6 +350,7 @@ class hitbtc(Exchange, ImplicitAPI):
349
350
  '20012': ExchangeError,
350
351
  '20014': ExchangeError,
351
352
  '20016': ExchangeError,
353
+ '20018': ExchangeError, # Withdrawals are unavailable due to the current configuration. Any of: - internal withdrawals are disabled; - in-chain withdrawals are disabled.
352
354
  '20031': ExchangeError,
353
355
  '20032': ExchangeError,
354
356
  '20033': ExchangeError,
@@ -359,10 +361,15 @@ class hitbtc(Exchange, ImplicitAPI):
359
361
  '20043': ExchangeError,
360
362
  '20044': PermissionDenied,
361
363
  '20045': InvalidOrder,
364
+ '20047': InvalidOrder, # Order placing exceeds the central counterparty balance limit.
365
+ '20048': InvalidOrder, # Provided Time-In-Force instruction is invalid or the combination of the instruction and the order type is not allowed.
366
+ '20049': InvalidOrder, # Provided order type is invalid.
362
367
  '20080': ExchangeError,
363
368
  '21001': ExchangeError,
364
369
  '21003': AccountSuspended,
365
370
  '21004': AccountSuspended,
371
+ '22004': ExchangeError, # User is not found.
372
+ '22008': ExchangeError, # Gateway timeout exceeded.
366
373
  },
367
374
  'broad': {},
368
375
  },
ccxt/htx.py CHANGED
@@ -485,6 +485,7 @@ class htx(Exchange, ImplicitAPI):
485
485
  'v2/sub-user/api-key-modification': 1,
486
486
  'v2/sub-user/api-key-deletion': 1,
487
487
  'v1/subuser/transfer': 10,
488
+ 'v1/trust/user/active/credit': 10,
488
489
  # Trading
489
490
  'v1/order/orders/place': 0.2,
490
491
  'v1/order/batch-orders': 0.4,