ccxt 4.4.34__py2.py3-none-any.whl → 4.4.35__py2.py3-none-any.whl

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (46) hide show
  1. ccxt/__init__.py +1 -1
  2. ccxt/abstract/bingx.py +1 -0
  3. ccxt/abstract/bitpanda.py +0 -12
  4. ccxt/abstract/bitrue.py +3 -3
  5. ccxt/abstract/okx.py +1 -0
  6. ccxt/abstract/onetrading.py +0 -12
  7. ccxt/abstract/xt.py +5 -5
  8. ccxt/async_support/__init__.py +1 -1
  9. ccxt/async_support/base/exchange.py +1 -1
  10. ccxt/async_support/bingx.py +324 -138
  11. ccxt/async_support/bitmex.py +1 -1
  12. ccxt/async_support/bitrue.py +2 -2
  13. ccxt/async_support/btcmarkets.py +3 -3
  14. ccxt/async_support/btcturk.py +19 -19
  15. ccxt/async_support/gate.py +142 -39
  16. ccxt/async_support/hyperliquid.py +68 -11
  17. ccxt/async_support/idex.py +3 -4
  18. ccxt/async_support/kraken.py +58 -49
  19. ccxt/async_support/kucoin.py +1 -1
  20. ccxt/async_support/okx.py +1 -0
  21. ccxt/async_support/onetrading.py +47 -369
  22. ccxt/async_support/xt.py +10 -10
  23. ccxt/base/exchange.py +2 -1
  24. ccxt/bingx.py +324 -138
  25. ccxt/bitmex.py +1 -1
  26. ccxt/bitrue.py +2 -2
  27. ccxt/btcmarkets.py +3 -3
  28. ccxt/btcturk.py +19 -19
  29. ccxt/gate.py +142 -39
  30. ccxt/hyperliquid.py +68 -11
  31. ccxt/idex.py +3 -4
  32. ccxt/kraken.py +58 -49
  33. ccxt/kucoin.py +1 -1
  34. ccxt/okx.py +1 -0
  35. ccxt/onetrading.py +47 -369
  36. ccxt/pro/__init__.py +1 -1
  37. ccxt/pro/bitrue.py +13 -11
  38. ccxt/pro/probit.py +54 -66
  39. ccxt/test/tests_async.py +29 -2
  40. ccxt/test/tests_sync.py +29 -2
  41. ccxt/xt.py +10 -10
  42. {ccxt-4.4.34.dist-info → ccxt-4.4.35.dist-info}/METADATA +4 -4
  43. {ccxt-4.4.34.dist-info → ccxt-4.4.35.dist-info}/RECORD +46 -46
  44. {ccxt-4.4.34.dist-info → ccxt-4.4.35.dist-info}/LICENSE.txt +0 -0
  45. {ccxt-4.4.34.dist-info → ccxt-4.4.35.dist-info}/WHEEL +0 -0
  46. {ccxt-4.4.34.dist-info → ccxt-4.4.35.dist-info}/top_level.txt +0 -0
ccxt/bitmex.py CHANGED
@@ -1471,7 +1471,7 @@ class bitmex(Exchange, ImplicitAPI):
1471
1471
  }
1472
1472
  if limit is not None:
1473
1473
  request['count'] = limit # default 100, max 500
1474
- until = self.safe_integer_2(params, 'until', 'endTime')
1474
+ until = self.safe_integer(params, 'until')
1475
1475
  if until is not None:
1476
1476
  params = self.omit(params, ['until'])
1477
1477
  request['endTime'] = self.iso8601(until)
ccxt/bitrue.py CHANGED
@@ -2939,7 +2939,7 @@ class bitrue(Exchange, ImplicitAPI):
2939
2939
  version = self.safe_string(api, 1)
2940
2940
  access = self.safe_string(api, 2)
2941
2941
  url = None
2942
- if type == 'api' and version == 'kline':
2942
+ if (type == 'api' and version == 'kline') or (type == 'open' and path.find('listenKey') >= 0):
2943
2943
  url = self.urls['api'][type]
2944
2944
  else:
2945
2945
  url = self.urls['api'][type] + '/' + version
@@ -2948,7 +2948,7 @@ class bitrue(Exchange, ImplicitAPI):
2948
2948
  if access == 'private':
2949
2949
  self.check_required_credentials()
2950
2950
  recvWindow = self.safe_integer(self.options, 'recvWindow', 5000)
2951
- if type == 'spot':
2951
+ if type == 'spot' or type == 'open':
2952
2952
  query = self.urlencode(self.extend({
2953
2953
  'timestamp': self.nonce(),
2954
2954
  'recvWindow': recvWindow,
ccxt/btcmarkets.py CHANGED
@@ -313,7 +313,7 @@ class btcmarkets(Exchange, ImplicitAPI):
313
313
  type = self.parse_transaction_type(self.safe_string_lower(transaction, 'type'))
314
314
  if type == 'withdraw':
315
315
  type = 'withdrawal'
316
- cryptoPaymentDetail = self.safe_value(transaction, 'paymentDetail', {})
316
+ cryptoPaymentDetail = self.safe_dict(transaction, 'paymentDetail', {})
317
317
  txid = self.safe_string(cryptoPaymentDetail, 'txId')
318
318
  address = self.safe_string(cryptoPaymentDetail, 'address')
319
319
  tag = None
@@ -394,7 +394,7 @@ class btcmarkets(Exchange, ImplicitAPI):
394
394
  base = self.safe_currency_code(baseId)
395
395
  quote = self.safe_currency_code(quoteId)
396
396
  symbol = base + '/' + quote
397
- fees = self.safe_value(self.safe_value(self.options, 'fees', {}), quote, self.fees)
397
+ fees = self.safe_value(self.safe_dict(self.options, 'fees', {}), quote, self.fees)
398
398
  pricePrecision = self.parse_number(self.parse_precision(self.safe_string(market, 'priceDecimals')))
399
399
  minAmount = self.safe_number(market, 'minOrderAmount')
400
400
  maxAmount = self.safe_number(market, 'maxOrderAmount')
@@ -1016,7 +1016,7 @@ class btcmarkets(Exchange, ImplicitAPI):
1016
1016
  clientOrderId = self.safe_string(order, 'clientOrderId')
1017
1017
  timeInForce = self.safe_string(order, 'timeInForce')
1018
1018
  stopPrice = self.safe_number(order, 'triggerPrice')
1019
- postOnly = self.safe_value(order, 'postOnly')
1019
+ postOnly = self.safe_bool(order, 'postOnly')
1020
1020
  return self.safe_order({
1021
1021
  'info': order,
1022
1022
  'id': id,
ccxt/btcturk.py CHANGED
@@ -205,8 +205,8 @@ class btcturk(Exchange, ImplicitAPI):
205
205
  # ],
206
206
  # }
207
207
  #
208
- data = self.safe_value(response, 'data')
209
- markets = self.safe_value(data, 'symbols', [])
208
+ data = self.safe_dict(response, 'data', {})
209
+ markets = self.safe_list(data, 'symbols', [])
210
210
  return self.parse_markets(markets)
211
211
 
212
212
  def parse_market(self, entry) -> Market:
@@ -215,7 +215,7 @@ class btcturk(Exchange, ImplicitAPI):
215
215
  quoteId = self.safe_string(entry, 'denominator')
216
216
  base = self.safe_currency_code(baseId)
217
217
  quote = self.safe_currency_code(quoteId)
218
- filters = self.safe_value(entry, 'filters', [])
218
+ filters = self.safe_list(entry, 'filters', [])
219
219
  minPrice = None
220
220
  maxPrice = None
221
221
  minAmount = None
@@ -282,7 +282,7 @@ class btcturk(Exchange, ImplicitAPI):
282
282
  }
283
283
 
284
284
  def parse_balance(self, response) -> Balances:
285
- data = self.safe_value(response, 'data', [])
285
+ data = self.safe_list(response, 'data', [])
286
286
  result: dict = {
287
287
  'info': response,
288
288
  'timestamp': None,
@@ -356,7 +356,7 @@ class btcturk(Exchange, ImplicitAPI):
356
356
  # ]
357
357
  # }
358
358
  # }
359
- data = self.safe_value(response, 'data')
359
+ data = self.safe_dict(response, 'data', {})
360
360
  timestamp = self.safe_integer(data, 'timestamp')
361
361
  return self.parse_order_book(data, market['symbol'], timestamp, 'bids', 'asks', 0, 1)
362
362
 
@@ -637,20 +637,20 @@ class btcturk(Exchange, ImplicitAPI):
637
637
 
638
638
  def parse_ohlcvs(self, ohlcvs, market=None, timeframe='1m', since: Int = None, limit: Int = None, tail: Bool = False):
639
639
  results = []
640
- timestamp = self.safe_value(ohlcvs, 't')
641
- high = self.safe_value(ohlcvs, 'h')
642
- open = self.safe_value(ohlcvs, 'o')
643
- low = self.safe_value(ohlcvs, 'l')
644
- close = self.safe_value(ohlcvs, 'c')
645
- volume = self.safe_value(ohlcvs, 'v')
640
+ timestamp = self.safe_list(ohlcvs, 't', [])
641
+ high = self.safe_list(ohlcvs, 'h', [])
642
+ open = self.safe_list(ohlcvs, 'o', [])
643
+ low = self.safe_list(ohlcvs, 'l', [])
644
+ close = self.safe_list(ohlcvs, 'c', [])
645
+ volume = self.safe_list(ohlcvs, 'v', [])
646
646
  for i in range(0, len(timestamp)):
647
647
  ohlcv: dict = {
648
- 'timestamp': self.safe_value(timestamp, i),
649
- 'high': self.safe_value(high, i),
650
- 'open': self.safe_value(open, i),
651
- 'low': self.safe_value(low, i),
652
- 'close': self.safe_value(close, i),
653
- 'volume': self.safe_value(volume, i),
648
+ 'timestamp': self.safe_integer(timestamp, i),
649
+ 'high': self.safe_number(high, i),
650
+ 'open': self.safe_number(open, i),
651
+ 'low': self.safe_number(low, i),
652
+ 'close': self.safe_number(close, i),
653
+ 'volume': self.safe_number(volume, i),
654
654
  }
655
655
  results.append(self.parse_ohlcv(ohlcv, market))
656
656
  sorted = self.sort_by(results, 0)
@@ -733,8 +733,8 @@ class btcturk(Exchange, ImplicitAPI):
733
733
  market = self.market(symbol)
734
734
  request['pairSymbol'] = market['id']
735
735
  response = self.privateGetOpenOrders(self.extend(request, params))
736
- data = self.safe_value(response, 'data')
737
- bids = self.safe_value(data, 'bids', [])
736
+ data = self.safe_dict(response, 'data', {})
737
+ bids = self.safe_list(data, 'bids', [])
738
738
  asks = self.safe_list(data, 'asks', [])
739
739
  return self.parse_orders(self.array_concat(bids, asks), market, since, limit)
740
740
 
ccxt/gate.py CHANGED
@@ -708,6 +708,110 @@ class gate(Exchange, ImplicitAPI):
708
708
  },
709
709
  },
710
710
  },
711
+ 'features': {
712
+ 'spot': {
713
+ 'sandbox': True,
714
+ 'createOrder': {
715
+ 'marginMode': True,
716
+ 'triggerPrice': True,
717
+ 'triggerDirection': True, # todo: implementation edit needed
718
+ 'triggerPriceType': None,
719
+ 'stopLossPrice': True,
720
+ 'takeProfitPrice': True,
721
+ 'attachedStopLossTakeProfit': None,
722
+ 'timeInForce': {
723
+ 'GTC': True,
724
+ 'IOC': True,
725
+ 'FOK': True,
726
+ 'PO': True,
727
+ 'GTD': False,
728
+ },
729
+ 'hedged': False,
730
+ 'trailing': False,
731
+ # exchange-specific features
732
+ 'iceberg': True,
733
+ 'selfTradePrevention': True,
734
+ },
735
+ 'createOrders': {
736
+ 'max': 40, # NOTE! max 10 per symbol
737
+ },
738
+ 'fetchMyTrades': {
739
+ 'marginMode': True,
740
+ 'limit': 1000,
741
+ 'daysBack': None,
742
+ 'untilDays': 30,
743
+ },
744
+ 'fetchOrder': {
745
+ 'marginMode': False,
746
+ 'trigger': True,
747
+ 'trailing': False,
748
+ },
749
+ 'fetchOpenOrders': {
750
+ 'marginMode': True,
751
+ 'trigger': True,
752
+ 'trailing': False,
753
+ 'limit': 100,
754
+ },
755
+ 'fetchOrders': None,
756
+ 'fetchClosedOrders': {
757
+ 'marginMode': True,
758
+ 'trigger': True,
759
+ 'trailing': False,
760
+ 'limit': 100,
761
+ 'untilDays': 30,
762
+ 'daysBackClosed': None,
763
+ 'daysBackCanceled': None,
764
+ },
765
+ 'fetchOHLCV': {
766
+ 'limit': 1000,
767
+ },
768
+ },
769
+ 'forDerivatives': {
770
+ 'extends': 'spot',
771
+ 'createOrder': {
772
+ 'marginMode': False,
773
+ 'triggerPriceType': {
774
+ 'last': True,
775
+ 'mark': True,
776
+ 'index': True,
777
+ },
778
+ },
779
+ 'createOrders': {
780
+ 'max': 10,
781
+ },
782
+ 'fetchMyTrades': {
783
+ 'marginMode': False,
784
+ 'untilDays': None,
785
+ },
786
+ 'fetchOpenOrders': {
787
+ 'marginMode': False,
788
+ },
789
+ 'fetchClosedOrders': {
790
+ 'marginMode': False,
791
+ 'untilDays': None,
792
+ 'limit': 1000,
793
+ },
794
+ 'fetchOHLCV': {
795
+ 'limit': 1999,
796
+ },
797
+ },
798
+ 'swap': {
799
+ 'linear': {
800
+ 'extends': 'forDerivatives',
801
+ },
802
+ 'inverse': {
803
+ 'extends': 'forDerivatives',
804
+ },
805
+ },
806
+ 'future': {
807
+ 'linear': {
808
+ 'extends': 'forDerivatives',
809
+ },
810
+ 'inverse': {
811
+ 'extends': 'forDerivatives',
812
+ },
813
+ },
814
+ },
711
815
  'precisionMode': TICK_SIZE,
712
816
  'fees': {
713
817
  'trading': {
@@ -1532,22 +1636,22 @@ class gate(Exchange, ImplicitAPI):
1532
1636
  request['settle'] = settle
1533
1637
  return [request, params]
1534
1638
 
1535
- def spot_order_prepare_request(self, market=None, stop=False, params={}):
1639
+ def spot_order_prepare_request(self, market=None, trigger=False, params={}):
1536
1640
  """
1537
1641
  @ignore
1538
1642
  Fills request params currency_pair, market and account where applicable for spot order methods like fetchOpenOrders, cancelAllOrders
1539
1643
  :param dict market: CCXT market
1540
- :param bool stop: True if for a stop order
1644
+ :param bool trigger: True if for a trigger order
1541
1645
  :param dict [params]: request parameters
1542
1646
  :returns: the api request object, and the new params object with non-needed parameters removed
1543
1647
  """
1544
- marginMode, query = self.get_margin_mode(stop, params)
1648
+ marginMode, query = self.get_margin_mode(trigger, params)
1545
1649
  request: dict = {}
1546
- if not stop:
1650
+ if not trigger:
1547
1651
  if market is None:
1548
- raise ArgumentsRequired(self.id + ' spotOrderPrepareRequest() requires a market argument for non-stop orders')
1652
+ raise ArgumentsRequired(self.id + ' spotOrderPrepareRequest() requires a market argument for non-trigger orders')
1549
1653
  request['account'] = marginMode
1550
- request['currency_pair'] = market['id'] # Should always be set for non-stop
1654
+ request['currency_pair'] = market['id'] # Should always be set for non-trigger
1551
1655
  return [request, query]
1552
1656
 
1553
1657
  def multi_order_spot_prepare_request(self, market=None, trigger=False, params={}):
@@ -1555,7 +1659,7 @@ class gate(Exchange, ImplicitAPI):
1555
1659
  @ignore
1556
1660
  Fills request params currency_pair, market and account where applicable for spot order methods like fetchOpenOrders, cancelAllOrders
1557
1661
  :param dict market: CCXT market
1558
- :param bool stop: True if for a stop order
1662
+ :param bool trigger: True if for a trigger order
1559
1663
  :param dict [params]: request parameters
1560
1664
  :returns: the api request object, and the new params object with non-needed parameters removed
1561
1665
  """
@@ -1565,17 +1669,17 @@ class gate(Exchange, ImplicitAPI):
1565
1669
  }
1566
1670
  if market is not None:
1567
1671
  if trigger:
1568
- # gate spot and margin stop orders use the term market instead of currency_pair, and normal instead of spot. Neither parameter is used when fetching/cancelling a single order. They are used for creating a single stop order, but createOrder does not call self method
1672
+ # gate spot and margin trigger orders use the term market instead of currency_pair, and normal instead of spot. Neither parameter is used when fetching/cancelling a single order. They are used for creating a single trigger order, but createOrder does not call self method
1569
1673
  request['market'] = market['id']
1570
1674
  else:
1571
1675
  request['currency_pair'] = market['id']
1572
1676
  return [request, query]
1573
1677
 
1574
- def get_margin_mode(self, stop, params):
1678
+ def get_margin_mode(self, trigger, params):
1575
1679
  """
1576
1680
  @ignore
1577
1681
  Gets the margin type for self api call
1578
- :param bool stop: True if for a stop order
1682
+ :param bool trigger: True if for a trigger order
1579
1683
  :param dict [params]: Request params
1580
1684
  :returns: The marginMode and the updated request params with marginMode removed, marginMode value is the value that can be read by the "account" property specified in gates api docs
1581
1685
  """
@@ -1588,12 +1692,12 @@ class gate(Exchange, ImplicitAPI):
1588
1692
  marginMode = 'margin'
1589
1693
  elif marginMode == '':
1590
1694
  marginMode = 'spot'
1591
- if stop:
1695
+ if trigger:
1592
1696
  if marginMode == 'spot':
1593
- # gate spot stop orders use the term normal instead of spot
1697
+ # gate spot trigger orders use the term normal instead of spot
1594
1698
  marginMode = 'normal'
1595
1699
  if marginMode == 'cross_margin':
1596
- raise BadRequest(self.id + ' getMarginMode() does not support stop orders for cross margin')
1700
+ raise BadRequest(self.id + ' getMarginMode() does not support trigger orders for cross margin')
1597
1701
  isUnifiedAccount = False
1598
1702
  isUnifiedAccount, params = self.handle_option_and_params(params, 'getMarginMode', 'unifiedAccount')
1599
1703
  if isUnifiedAccount:
@@ -2989,7 +3093,6 @@ class gate(Exchange, ImplicitAPI):
2989
3093
  request['limit'] = limit
2990
3094
  response = None
2991
3095
  if market['contract']:
2992
- maxLimit = 1999
2993
3096
  isMark = (price == 'mark')
2994
3097
  isIndex = (price == 'index')
2995
3098
  if isMark or isIndex:
@@ -3306,7 +3409,7 @@ class gate(Exchange, ImplicitAPI):
3306
3409
  params = self.omit(params, 'order_id')
3307
3410
  else:
3308
3411
  if market is not None:
3309
- request['currency_pair'] = market['id'] # Should always be set for non-stop
3412
+ request['currency_pair'] = market['id'] # Should always be set for non-trigger
3310
3413
  marginMode, params = self.get_margin_mode(False, params)
3311
3414
  request['account'] = marginMode
3312
3415
  if limit is not None:
@@ -3825,8 +3928,8 @@ class gate(Exchange, ImplicitAPI):
3825
3928
  takeProfitPrice = self.safe_value(params, 'takeProfitPrice')
3826
3929
  isStopLossOrder = stopLossPrice is not None
3827
3930
  isTakeProfitOrder = takeProfitPrice is not None
3828
- isStopOrder = isStopLossOrder or isTakeProfitOrder
3829
- nonTriggerOrder = not isStopOrder and (trigger is None)
3931
+ isTpsl = isStopLossOrder or isTakeProfitOrder
3932
+ nonTriggerOrder = not isTpsl and (trigger is None)
3830
3933
  orderRequest = self.create_order_request(symbol, type, side, amount, price, params)
3831
3934
  response = None
3832
3935
  if market['spot'] or market['margin']:
@@ -3975,7 +4078,7 @@ class gate(Exchange, ImplicitAPI):
3975
4078
  takeProfitPrice = self.safe_value(params, 'takeProfitPrice')
3976
4079
  isStopLossOrder = stopLossPrice is not None
3977
4080
  isTakeProfitOrder = takeProfitPrice is not None
3978
- isStopOrder = isStopLossOrder or isTakeProfitOrder
4081
+ isTpsl = isStopLossOrder or isTakeProfitOrder
3979
4082
  if isStopLossOrder and isTakeProfitOrder:
3980
4083
  raise ExchangeError(self.id + ' createOrder() stopLossPrice and takeProfitPrice cannot both be defined')
3981
4084
  reduceOnly = self.safe_value(params, 'reduceOnly')
@@ -4011,7 +4114,7 @@ class gate(Exchange, ImplicitAPI):
4011
4114
  signedAmount = Precise.string_neg(amountToPrecision) if (side == 'sell') else amountToPrecision
4012
4115
  amount = int(signedAmount)
4013
4116
  request = None
4014
- nonTriggerOrder = not isStopOrder and (trigger is None)
4117
+ nonTriggerOrder = not isTpsl and (trigger is None)
4015
4118
  if nonTriggerOrder:
4016
4119
  if contract:
4017
4120
  # contract order
@@ -4561,7 +4664,7 @@ class gate(Exchange, ImplicitAPI):
4561
4664
 
4562
4665
  def fetch_order_request(self, id: str, symbol: Str = None, params={}):
4563
4666
  market = None if (symbol is None) else self.market(symbol)
4564
- stop = self.safe_bool_n(params, ['trigger', 'is_stop_order', 'stop'], False)
4667
+ trigger = self.safe_bool_n(params, ['trigger', 'is_stop_order', 'stop'], False)
4565
4668
  params = self.omit(params, ['is_stop_order', 'stop', 'trigger'])
4566
4669
  clientOrderId = self.safe_string_2(params, 'text', 'clientOrderId')
4567
4670
  orderId = id
@@ -4572,7 +4675,7 @@ class gate(Exchange, ImplicitAPI):
4572
4675
  orderId = clientOrderId
4573
4676
  type, query = self.handle_market_type_and_params('fetchOrder', market, params)
4574
4677
  contract = (type == 'swap') or (type == 'future') or (type == 'option')
4575
- request, requestParams = self.prepare_request(market, type, query) if contract else self.spot_order_prepare_request(market, stop, query)
4678
+ request, requestParams = self.prepare_request(market, type, query) if contract else self.spot_order_prepare_request(market, trigger, query)
4576
4679
  request['order_id'] = str(orderId)
4577
4680
  return [request, requestParams]
4578
4681
 
@@ -4600,21 +4703,21 @@ class gate(Exchange, ImplicitAPI):
4600
4703
  market = None if (symbol is None) else self.market(symbol)
4601
4704
  result = self.handle_market_type_and_params('fetchOrder', market, params)
4602
4705
  type = self.safe_string(result, 0)
4603
- stop = self.safe_bool_n(params, ['trigger', 'is_stop_order', 'stop'], False)
4706
+ trigger = self.safe_bool_n(params, ['trigger', 'is_stop_order', 'stop'], False)
4604
4707
  request, requestParams = self.fetch_order_request(id, symbol, params)
4605
4708
  response = None
4606
4709
  if type == 'spot' or type == 'margin':
4607
- if stop:
4710
+ if trigger:
4608
4711
  response = self.privateSpotGetPriceOrdersOrderId(self.extend(request, requestParams))
4609
4712
  else:
4610
4713
  response = self.privateSpotGetOrdersOrderId(self.extend(request, requestParams))
4611
4714
  elif type == 'swap':
4612
- if stop:
4715
+ if trigger:
4613
4716
  response = self.privateFuturesGetSettlePriceOrdersOrderId(self.extend(request, requestParams))
4614
4717
  else:
4615
4718
  response = self.privateFuturesGetSettleOrdersOrderId(self.extend(request, requestParams))
4616
4719
  elif type == 'future':
4617
- if stop:
4720
+ if trigger:
4618
4721
  response = self.privateDeliveryGetSettlePriceOrdersOrderId(self.extend(request, requestParams))
4619
4722
  else:
4620
4723
  response = self.privateDeliveryGetSettleOrdersOrderId(self.extend(request, requestParams))
@@ -4635,7 +4738,7 @@ class gate(Exchange, ImplicitAPI):
4635
4738
  :param int [since]: the earliest time in ms to fetch open orders for
4636
4739
  :param int [limit]: the maximum number of open orders structures to retrieve
4637
4740
  :param dict [params]: extra parameters specific to the exchange API endpoint
4638
- :param bool [params.stop]: True for fetching stop orders
4741
+ :param bool [params.trigger]: True for fetching trigger orders
4639
4742
  :param str [params.type]: spot, margin, swap or future, if not provided self.options['defaultType'] is used
4640
4743
  :param str [params.marginMode]: 'cross' or 'isolated' - marginMode for type='margin', if not provided self.options['defaultMarginMode'] is used
4641
4744
  :param bool [params.unifiedAccount]: set to True for fetching unified account orders
@@ -4660,7 +4763,7 @@ class gate(Exchange, ImplicitAPI):
4660
4763
  :param int [since]: the earliest time in ms to fetch orders for
4661
4764
  :param int [limit]: the maximum number of order structures to retrieve
4662
4765
  :param dict [params]: extra parameters specific to the exchange API endpoint
4663
- :param bool [params.stop]: True for fetching stop orders
4766
+ :param bool [params.trigger]: True for fetching trigger orders
4664
4767
  :param str [params.type]: spot, swap or future, if not provided self.options['defaultType'] is used
4665
4768
  :param str [params.marginMode]: 'cross' or 'isolated' - marginMode for margin trading if not provided self.options['defaultMarginMode'] is used
4666
4769
  :param boolean [params.historical]: *swap only* True for using historical endpoint
@@ -4834,7 +4937,7 @@ class gate(Exchange, ImplicitAPI):
4834
4937
  # }
4835
4938
  # ]
4836
4939
  #
4837
- # spot stop
4940
+ # spot trigger
4838
4941
  #
4839
4942
  # [
4840
4943
  # {
@@ -4929,31 +5032,31 @@ class gate(Exchange, ImplicitAPI):
4929
5032
  :param str id: Order id
4930
5033
  :param str symbol: Unified market symbol
4931
5034
  :param dict [params]: Parameters specified by the exchange api
4932
- :param bool [params.stop]: True if the order to be cancelled is a trigger order
5035
+ :param bool [params.trigger]: True if the order to be cancelled is a trigger order
4933
5036
  :param bool [params.unifiedAccount]: set to True for canceling unified account orders
4934
5037
  :returns: An `order structure <https://docs.ccxt.com/#/?id=order-structure>`
4935
5038
  """
4936
5039
  self.load_markets()
4937
5040
  self.load_unified_status()
4938
5041
  market = None if (symbol is None) else self.market(symbol)
4939
- stop = self.safe_bool_n(params, ['is_stop_order', 'stop', 'trigger'], False)
5042
+ trigger = self.safe_bool_n(params, ['is_stop_order', 'stop', 'trigger'], False)
4940
5043
  params = self.omit(params, ['is_stop_order', 'stop', 'trigger'])
4941
5044
  type, query = self.handle_market_type_and_params('cancelOrder', market, params)
4942
- request, requestParams = self.spot_order_prepare_request(market, stop, query) if (type == 'spot' or type == 'margin') else self.prepare_request(market, type, query)
5045
+ request, requestParams = self.spot_order_prepare_request(market, trigger, query) if (type == 'spot' or type == 'margin') else self.prepare_request(market, type, query)
4943
5046
  request['order_id'] = id
4944
5047
  response = None
4945
5048
  if type == 'spot' or type == 'margin':
4946
- if stop:
5049
+ if trigger:
4947
5050
  response = self.privateSpotDeletePriceOrdersOrderId(self.extend(request, requestParams))
4948
5051
  else:
4949
5052
  response = self.privateSpotDeleteOrdersOrderId(self.extend(request, requestParams))
4950
5053
  elif type == 'swap':
4951
- if stop:
5054
+ if trigger:
4952
5055
  response = self.privateFuturesDeleteSettlePriceOrdersOrderId(self.extend(request, requestParams))
4953
5056
  else:
4954
5057
  response = self.privateFuturesDeleteSettleOrdersOrderId(self.extend(request, requestParams))
4955
5058
  elif type == 'future':
4956
- if stop:
5059
+ if trigger:
4957
5060
  response = self.privateDeliveryDeleteSettlePriceOrdersOrderId(self.extend(request, requestParams))
4958
5061
  else:
4959
5062
  response = self.privateDeliveryDeleteSettleOrdersOrderId(self.extend(request, requestParams))
@@ -5143,23 +5246,23 @@ class gate(Exchange, ImplicitAPI):
5143
5246
  self.load_markets()
5144
5247
  self.load_unified_status()
5145
5248
  market = None if (symbol is None) else self.market(symbol)
5146
- stop = self.safe_bool_2(params, 'stop', 'trigger')
5249
+ trigger = self.safe_bool_2(params, 'stop', 'trigger')
5147
5250
  params = self.omit(params, ['stop', 'trigger'])
5148
5251
  type, query = self.handle_market_type_and_params('cancelAllOrders', market, params)
5149
- request, requestParams = self.multi_order_spot_prepare_request(market, stop, query) if (type == 'spot') else self.prepare_request(market, type, query)
5252
+ request, requestParams = self.multi_order_spot_prepare_request(market, trigger, query) if (type == 'spot') else self.prepare_request(market, type, query)
5150
5253
  response = None
5151
5254
  if type == 'spot' or type == 'margin':
5152
- if stop:
5255
+ if trigger:
5153
5256
  response = self.privateSpotDeletePriceOrders(self.extend(request, requestParams))
5154
5257
  else:
5155
5258
  response = self.privateSpotDeleteOrders(self.extend(request, requestParams))
5156
5259
  elif type == 'swap':
5157
- if stop:
5260
+ if trigger:
5158
5261
  response = self.privateFuturesDeleteSettlePriceOrders(self.extend(request, requestParams))
5159
5262
  else:
5160
5263
  response = self.privateFuturesDeleteSettleOrders(self.extend(request, requestParams))
5161
5264
  elif type == 'future':
5162
- if stop:
5265
+ if trigger:
5163
5266
  response = self.privateDeliveryDeleteSettlePriceOrders(self.extend(request, requestParams))
5164
5267
  else:
5165
5268
  response = self.privateDeliveryDeleteSettleOrders(self.extend(request, requestParams))
ccxt/hyperliquid.py CHANGED
@@ -11,12 +11,14 @@ from typing import List
11
11
  from ccxt.base.errors import ExchangeError
12
12
  from ccxt.base.errors import ArgumentsRequired
13
13
  from ccxt.base.errors import BadRequest
14
+ from ccxt.base.errors import InsufficientFunds
14
15
  from ccxt.base.errors import InvalidOrder
15
16
  from ccxt.base.errors import OrderNotFound
16
17
  from ccxt.base.errors import NotSupported
17
18
  from ccxt.base.decimal_to_precision import ROUND
18
19
  from ccxt.base.decimal_to_precision import DECIMAL_PLACES
19
20
  from ccxt.base.decimal_to_precision import SIGNIFICANT_DIGITS
21
+ from ccxt.base.decimal_to_precision import TICK_SIZE
20
22
  from ccxt.base.precise import Precise
21
23
 
22
24
 
@@ -210,9 +212,11 @@ class hyperliquid(Exchange, ImplicitAPI):
210
212
  'User or API Wallet ': InvalidOrder,
211
213
  'Order has invalid size': InvalidOrder,
212
214
  'Order price cannot be more than 80% away from the reference price': InvalidOrder,
215
+ 'Order has zero size.': InvalidOrder,
216
+ 'Insufficient spot balance asset': InsufficientFunds,
213
217
  },
214
218
  },
215
- 'precisionMode': DECIMAL_PLACES,
219
+ 'precisionMode': TICK_SIZE,
216
220
  'commonCurrencies': {
217
221
  },
218
222
  'options': {
@@ -361,6 +365,48 @@ class hyperliquid(Exchange, ImplicitAPI):
361
365
  result.append(data)
362
366
  return self.parse_markets(result)
363
367
 
368
+ def calculate_price_precision(self, price: float, amountPrecision: float, maxDecimals: float):
369
+ """
370
+ Helper function to calculate the Hyperliquid DECIMAL_PLACES price precision
371
+ :param float price: the price to use in the calculation
372
+ :param int amountPrecision: the amountPrecision to use in the calculation
373
+ :param int maxDecimals: the maxDecimals to use in the calculation
374
+ :returns int: The calculated price precision
375
+ """
376
+ pricePrecision = 0
377
+ priceStr = self.number_to_string(price)
378
+ if priceStr is None:
379
+ return 0
380
+ priceSplitted = priceStr.split('.')
381
+ if Precise.string_eq(priceStr, '0'):
382
+ # Significant digits is always hasattr(self, 5) case
383
+ significantDigits = 5
384
+ # Integer digits is always hasattr(self, 0) case(0 doesn't count)
385
+ integerDigits = 0
386
+ # Calculate the price precision
387
+ pricePrecision = min(maxDecimals - amountPrecision, significantDigits - integerDigits)
388
+ elif Precise.string_gt(priceStr, '0') and Precise.string_lt(priceStr, '1'):
389
+ # Significant digits, always hasattr(self, 5) case
390
+ significantDigits = 5
391
+ # Get the part after the decimal separator
392
+ decimalPart = self.safe_string(priceSplitted, 1, '')
393
+ # Count the number of leading zeros in the decimal part
394
+ leadingZeros = 0
395
+ while((leadingZeros <= len(decimalPart)) and (decimalPart[leadingZeros] == '0')):
396
+ leadingZeros = leadingZeros + 1
397
+ # Calculate price precision based on leading zeros and significant digits
398
+ pricePrecision = leadingZeros + significantDigits
399
+ # Calculate the price precision based on maxDecimals - szDecimals and the calculated price precision from the previous step
400
+ pricePrecision = min(maxDecimals - amountPrecision, pricePrecision)
401
+ else:
402
+ # Count the numbers before the decimal separator
403
+ integerPart = self.safe_string(priceSplitted, 0, '')
404
+ # Get significant digits, take the max() of 5 and the integer digits count
405
+ significantDigits = max(5, len(integerPart))
406
+ # Calculate price precision based on maxDecimals - szDecimals and significantDigits - len(integerPart)
407
+ pricePrecision = min(maxDecimals - amountPrecision, significantDigits - len(integerPart))
408
+ return self.parse_to_int(pricePrecision)
409
+
364
410
  def fetch_spot_markets(self, params={}) -> List[Market]:
365
411
  """
366
412
  retrieves data on all spot markets for hyperliquid
@@ -450,7 +496,11 @@ class hyperliquid(Exchange, ImplicitAPI):
450
496
  symbol = base + '/' + quote
451
497
  innerBaseTokenInfo = self.safe_dict(baseTokenInfo, 'spec', baseTokenInfo)
452
498
  # innerQuoteTokenInfo = self.safe_dict(quoteTokenInfo, 'spec', quoteTokenInfo)
453
- amountPrecision = self.safe_integer(innerBaseTokenInfo, 'szDecimals')
499
+ amountPrecisionStr = self.safe_string(innerBaseTokenInfo, 'szDecimals')
500
+ amountPrecision = int(amountPrecisionStr)
501
+ price = self.safe_number(extraData, 'midPx')
502
+ pricePrecision = self.calculate_price_precision(price, amountPrecision, 8)
503
+ pricePrecisionStr = self.number_to_string(pricePrecision)
454
504
  # quotePrecision = self.parse_number(self.parse_precision(self.safe_string(innerQuoteTokenInfo, 'szDecimals')))
455
505
  baseId = self.number_to_string(i + 10000)
456
506
  markets.append(self.safe_market_structure({
@@ -481,8 +531,8 @@ class hyperliquid(Exchange, ImplicitAPI):
481
531
  'strike': None,
482
532
  'optionType': None,
483
533
  'precision': {
484
- 'amount': amountPrecision, # decimal places
485
- 'price': 8 - amountPrecision, # MAX_DECIMALS is 8
534
+ 'amount': self.parse_number(self.parse_precision(amountPrecisionStr)),
535
+ 'price': self.parse_number(self.parse_precision(pricePrecisionStr)),
486
536
  },
487
537
  'limits': {
488
538
  'leverage': {
@@ -543,7 +593,11 @@ class hyperliquid(Exchange, ImplicitAPI):
543
593
  fees = self.safe_dict(self.fees, 'swap', {})
544
594
  taker = self.safe_number(fees, 'taker')
545
595
  maker = self.safe_number(fees, 'maker')
546
- amountPrecision = self.safe_integer(market, 'szDecimals')
596
+ amountPrecisionStr = self.safe_string(market, 'szDecimals')
597
+ amountPrecision = int(amountPrecisionStr)
598
+ price = self.safe_number(market, 'markPx', 0)
599
+ pricePrecision = self.calculate_price_precision(price, amountPrecision, 6)
600
+ pricePrecisionStr = self.number_to_string(pricePrecision)
547
601
  return self.safe_market_structure({
548
602
  'id': baseId,
549
603
  'symbol': symbol,
@@ -571,8 +625,8 @@ class hyperliquid(Exchange, ImplicitAPI):
571
625
  'strike': None,
572
626
  'optionType': None,
573
627
  'precision': {
574
- 'amount': amountPrecision, # decimal places
575
- 'price': 6 - amountPrecision, # MAX_DECIMALS is 6
628
+ 'amount': self.parse_number(self.parse_precision(amountPrecisionStr)),
629
+ 'price': self.parse_number(self.parse_precision(pricePrecisionStr)),
576
630
  },
577
631
  'limits': {
578
632
  'leverage': {
@@ -1039,10 +1093,13 @@ class hyperliquid(Exchange, ImplicitAPI):
1039
1093
 
1040
1094
  def price_to_precision(self, symbol: str, price) -> str:
1041
1095
  market = self.market(symbol)
1042
- # https://hyperliquid.gitbook.io/hyperliquid-docs/for-developers/api/tick-and-lot-size
1043
- result = self.decimal_to_precision(price, ROUND, 5, SIGNIFICANT_DIGITS, self.paddingMode)
1044
- decimalParsedResult = self.decimal_to_precision(result, ROUND, market['precision']['price'], self.precisionMode, self.paddingMode)
1045
- return decimalParsedResult
1096
+ priceStr = self.number_to_string(price)
1097
+ integerPart = priceStr.split('.')[0]
1098
+ significantDigits = max(5, len(integerPart))
1099
+ result = self.decimal_to_precision(price, ROUND, significantDigits, SIGNIFICANT_DIGITS, self.paddingMode)
1100
+ maxDecimals = 8 if market['spot'] else 6
1101
+ subtractedValue = maxDecimals - self.precision_from_string(self.safe_string(market['precision'], 'amount'))
1102
+ return self.decimal_to_precision(result, ROUND, subtractedValue, DECIMAL_PLACES, self.paddingMode)
1046
1103
 
1047
1104
  def hash_message(self, message):
1048
1105
  return '0x' + self.hash(message, 'keccak', 'hex')