ccxt 4.4.93__py2.py3-none-any.whl → 4.4.94__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.
ccxt/base/exchange.py CHANGED
@@ -4,7 +4,7 @@
4
4
 
5
5
  # -----------------------------------------------------------------------------
6
6
 
7
- __version__ = '4.4.93'
7
+ __version__ = '4.4.94'
8
8
 
9
9
  # -----------------------------------------------------------------------------
10
10
 
@@ -3808,8 +3808,7 @@ class Exchange(object):
3808
3808
 
3809
3809
  def safe_ticker(self, ticker: dict, market: Market = None):
3810
3810
  open = self.omit_zero(self.safe_string(ticker, 'open'))
3811
- close = self.omit_zero(self.safe_string(ticker, 'close'))
3812
- last = self.omit_zero(self.safe_string(ticker, 'last'))
3811
+ close = self.omit_zero(self.safe_string_2(ticker, 'close', 'last'))
3813
3812
  change = self.omit_zero(self.safe_string(ticker, 'change'))
3814
3813
  percentage = self.omit_zero(self.safe_string(ticker, 'percentage'))
3815
3814
  average = self.omit_zero(self.safe_string(ticker, 'average'))
@@ -3818,29 +3817,52 @@ class Exchange(object):
3818
3817
  quoteVolume = self.safe_string(ticker, 'quoteVolume')
3819
3818
  if vwap is None:
3820
3819
  vwap = Precise.string_div(self.omit_zero(quoteVolume), baseVolume)
3821
- if (last is not None) and (close is None):
3822
- close = last
3823
- elif (last is None) and (close is not None):
3824
- last = close
3825
- if (last is not None) and (open is not None):
3826
- if change is None:
3827
- change = Precise.string_sub(last, open)
3828
- if average is None:
3820
+ # calculate open
3821
+ if change is not None:
3822
+ if close is None and average is not None:
3823
+ close = Precise.string_add(average, Precise.string_div(change, '2'))
3824
+ if open is None and close is not None:
3825
+ open = Precise.string_sub(close, change)
3826
+ elif percentage is not None:
3827
+ if close is None and average is not None:
3828
+ openAddClose = Precise.string_mul(average, '2')
3829
+ # openAddClose = open * (1 + (100 + percentage)/100)
3830
+ denominator = Precise.string_add('2', Precise.string_div(percentage, '100'))
3831
+ calcOpen = open if (open is not None) else Precise.string_div(openAddClose, denominator)
3832
+ close = Precise.string_mul(calcOpen, Precise.string_add('1', Precise.string_div(percentage, '100')))
3833
+ if open is None and close is not None:
3834
+ open = Precise.string_div(close, Precise.string_add('1', Precise.string_div(percentage, '100')))
3835
+ # change
3836
+ if change is None:
3837
+ if close is not None and open is not None:
3838
+ change = Precise.string_sub(close, open)
3839
+ elif close is not None and percentage is not None:
3840
+ change = Precise.string_mul(Precise.string_div(percentage, '100'), Precise.string_div(close, '100'))
3841
+ elif open is not None and percentage is not None:
3842
+ change = Precise.string_mul(open, Precise.string_div(percentage, '100'))
3843
+ # calculate things according to "open"(similar can be done with "close")
3844
+ if open is not None:
3845
+ # percentage(using change)
3846
+ if percentage is None and change is not None:
3847
+ percentage = Precise.string_mul(Precise.string_div(change, open), '100')
3848
+ # close(using change)
3849
+ if close is None and change is not None:
3850
+ close = Precise.string_add(open, change)
3851
+ # close(using average)
3852
+ if close is None and average is not None:
3853
+ close = Precise.string_mul(average, '2')
3854
+ # average
3855
+ if average is None and close is not None:
3829
3856
  precision = 18
3830
3857
  if market is not None and self.is_tick_precision():
3831
3858
  marketPrecision = self.safe_dict(market, 'precision')
3832
3859
  precisionPrice = self.safe_string(marketPrecision, 'price')
3833
3860
  if precisionPrice is not None:
3834
3861
  precision = self.precision_from_string(precisionPrice)
3835
- average = Precise.string_div(Precise.string_add(last, open), '2', precision)
3836
- if (percentage is None) and (change is not None) and (open is not None) and Precise.string_gt(open, '0'):
3837
- percentage = Precise.string_mul(Precise.string_div(change, open), '100')
3838
- if (change is None) and (percentage is not None) and (open is not None):
3839
- change = Precise.string_div(Precise.string_mul(percentage, open), '100')
3840
- if (open is None) and (last is not None) and (change is not None):
3841
- open = Precise.string_sub(last, change)
3862
+ average = Precise.string_div(Precise.string_add(open, close), '2', precision)
3842
3863
  # timestamp and symbol operations don't belong in safeTicker
3843
3864
  # they should be done in the derived classes
3865
+ closeParsed = self.parse_number(self.omit_zero(close))
3844
3866
  return self.extend(ticker, {
3845
3867
  'bid': self.parse_number(self.omit_zero(self.safe_string(ticker, 'bid'))),
3846
3868
  'bidVolume': self.safe_number(ticker, 'bidVolume'),
@@ -3849,8 +3871,8 @@ class Exchange(object):
3849
3871
  'high': self.parse_number(self.omit_zero(self.safe_string(ticker, 'high'))),
3850
3872
  'low': self.parse_number(self.omit_zero(self.safe_string(ticker, 'low'))),
3851
3873
  'open': self.parse_number(self.omit_zero(open)),
3852
- 'close': self.parse_number(self.omit_zero(close)),
3853
- 'last': self.parse_number(self.omit_zero(last)),
3874
+ 'close': closeParsed,
3875
+ 'last': closeParsed,
3854
3876
  'change': self.parse_number(change),
3855
3877
  'percentage': self.parse_number(percentage),
3856
3878
  'average': self.parse_number(average),
ccxt/bybit.py CHANGED
@@ -3751,7 +3751,7 @@ class bybit(Exchange, ImplicitAPI):
3751
3751
  isTakeProfit = takeProfitPrice is not None
3752
3752
  orderRequest = self.create_order_request(symbol, type, side, amount, price, params, enableUnifiedAccount)
3753
3753
  defaultMethod = None
3754
- if isTrailingAmountOrder or isStopLoss or isTakeProfit:
3754
+ if (isTrailingAmountOrder or isStopLoss or isTakeProfit) and not market['spot']:
3755
3755
  defaultMethod = 'privatePostV5PositionTradingStop'
3756
3756
  else:
3757
3757
  defaultMethod = 'privatePostV5OrderCreate'
@@ -3827,7 +3827,7 @@ class bybit(Exchange, ImplicitAPI):
3827
3827
  isLimit = lowerCaseType == 'limit'
3828
3828
  isBuy = side == 'buy'
3829
3829
  defaultMethod = None
3830
- if isTrailingAmountOrder or isStopLossTriggerOrder or isTakeProfitTriggerOrder:
3830
+ if (isTrailingAmountOrder or isStopLossTriggerOrder or isTakeProfitTriggerOrder) and not market['spot']:
3831
3831
  defaultMethod = 'privatePostV5PositionTradingStop'
3832
3832
  else:
3833
3833
  defaultMethod = 'privatePostV5OrderCreate'
ccxt/coinbase.py CHANGED
@@ -13,6 +13,7 @@ from ccxt.base.errors import AuthenticationError
13
13
  from ccxt.base.errors import PermissionDenied
14
14
  from ccxt.base.errors import ArgumentsRequired
15
15
  from ccxt.base.errors import BadRequest
16
+ from ccxt.base.errors import InsufficientFunds
16
17
  from ccxt.base.errors import InvalidOrder
17
18
  from ccxt.base.errors import OrderNotFound
18
19
  from ccxt.base.errors import NotSupported
@@ -332,12 +333,14 @@ class coinbase(Exchange, ImplicitAPI):
332
333
  'rate_limit_exceeded': RateLimitExceeded, # 429 Rate limit exceeded
333
334
  'internal_server_error': ExchangeError, # 500 Internal server error
334
335
  'UNSUPPORTED_ORDER_CONFIGURATION': BadRequest,
335
- 'INSUFFICIENT_FUND': BadRequest,
336
+ 'INSUFFICIENT_FUND': InsufficientFunds,
336
337
  'PERMISSION_DENIED': PermissionDenied,
337
338
  'INVALID_ARGUMENT': BadRequest,
338
339
  'PREVIEW_STOP_PRICE_ABOVE_LAST_TRADE_PRICE': InvalidOrder,
340
+ 'PREVIEW_INSUFFICIENT_FUND': InsufficientFunds,
339
341
  },
340
342
  'broad': {
343
+ 'Insufficient balance in source account': InsufficientFunds,
341
344
  'request timestamp expired': InvalidNonce, # {"errors":[{"id":"authentication_error","message":"request timestamp expired"}]}
342
345
  'order with self orderID was not found': OrderNotFound, # {"error":"unknown","error_details":"order with self orderID was not found","message":"order with self orderID was not found"}
343
346
  },
ccxt/coinmetro.py CHANGED
@@ -220,6 +220,7 @@ class coinmetro(Exchange, ImplicitAPI):
220
220
  'options': {
221
221
  'currenciesByIdForParseMarket': None,
222
222
  'currencyIdsListForParseMarket': ['QRDO'],
223
+ 'skippedMarkets': ['VXVUSDT'], # broken markets which do not have enough info in API
223
224
  },
224
225
  'features': {
225
226
  'spot': {
@@ -439,9 +440,12 @@ class coinmetro(Exchange, ImplicitAPI):
439
440
  :param dict [params]: extra parameters specific to the exchange API endpoint
440
441
  :returns dict[]: an array of objects representing market data
441
442
  """
442
- response = self.publicGetMarkets(params)
443
+ promises = []
444
+ promises.append(self.publicGetMarkets(params))
443
445
  if self.safe_value(self.options, 'currenciesByIdForParseMarket') is None:
444
- self.fetch_currencies()
446
+ promises.append(self.fetch_currencies())
447
+ responses = promises
448
+ response = responses[0]
445
449
  #
446
450
  # [
447
451
  # {
@@ -457,7 +461,14 @@ class coinmetro(Exchange, ImplicitAPI):
457
461
  # ...
458
462
  # ]
459
463
  #
460
- return self.parse_markets(response)
464
+ skippedMarkets = self.safe_list(self.options, 'skippedMarkets', [])
465
+ result = []
466
+ for i in range(0, len(response)):
467
+ market = self.parse_market(response[i])
468
+ if self.in_array(market['id'], skippedMarkets):
469
+ continue
470
+ result.append(market)
471
+ return result
461
472
 
462
473
  def parse_market(self, market: dict) -> Market:
463
474
  id = self.safe_string(market, 'pair')
ccxt/htx.py CHANGED
@@ -6576,12 +6576,16 @@ class htx(Exchange, ImplicitAPI):
6576
6576
  paginate = False
6577
6577
  paginate, params = self.handle_option_and_params(params, 'fetchFundingRateHistory', 'paginate')
6578
6578
  if paginate:
6579
- return self.fetch_paginated_call_cursor('fetchFundingRateHistory', symbol, since, limit, params, 'page_index', 'current_page', 1, 50)
6579
+ return self.fetch_paginated_call_cursor('fetchFundingRateHistory', symbol, since, limit, params, 'current_page', 'page_index', 1, 50)
6580
6580
  self.load_markets()
6581
6581
  market = self.market(symbol)
6582
6582
  request: dict = {
6583
6583
  'contract_code': market['id'],
6584
6584
  }
6585
+ if limit is not None:
6586
+ request['page_size'] = limit
6587
+ else:
6588
+ request['page_size'] = 50 # max
6585
6589
  response = None
6586
6590
  if market['inverse']:
6587
6591
  response = self.contractPublicGetSwapApiV1SwapHistoricalFundingRate(self.extend(request, params))
ccxt/hyperliquid.py CHANGED
@@ -353,6 +353,8 @@ class hyperliquid(Exchange, ImplicitAPI):
353
353
  :param dict [params]: extra parameters specific to the exchange API endpoint
354
354
  :returns dict: an associative dictionary of currencies
355
355
  """
356
+ if self.check_required_credentials(False):
357
+ self.handle_builder_fee_approval()
356
358
  request: dict = {
357
359
  'type': 'meta',
358
360
  }
@@ -626,6 +628,7 @@ class hyperliquid(Exchange, ImplicitAPI):
626
628
  'quote': quote,
627
629
  'settle': None,
628
630
  'baseId': baseId,
631
+ 'baseName': baseName,
629
632
  'quoteId': quoteId,
630
633
  'settleId': None,
631
634
  'type': 'spot',
@@ -728,6 +731,7 @@ class hyperliquid(Exchange, ImplicitAPI):
728
731
  'quote': quote,
729
732
  'settle': settle,
730
733
  'baseId': baseId,
734
+ 'baseName': baseName,
731
735
  'quoteId': quoteId,
732
736
  'settleId': settleId,
733
737
  'type': 'swap',
@@ -784,6 +788,7 @@ class hyperliquid(Exchange, ImplicitAPI):
784
788
  :param str [params.user]: user address, will default to self.walletAddress if not provided
785
789
  :param str [params.type]: wallet type, ['spot', 'swap'], defaults to swap
786
790
  :param str [params.marginMode]: 'cross' or 'isolated', for margin trading, uses self.options.defaultMarginMode if not passed, defaults to None/None/None
791
+ :param str [params.subAccountAddress]: sub account user address
787
792
  :returns dict: a `balance structure <https://docs.ccxt.com/#/?id=balance-structure>`
788
793
  """
789
794
  userAddress = None
@@ -793,9 +798,8 @@ class hyperliquid(Exchange, ImplicitAPI):
793
798
  marginMode = None
794
799
  marginMode, params = self.handle_margin_mode_and_params('fetchBalance', params)
795
800
  isSpot = (type == 'spot')
796
- reqType = 'spotClearinghouseState' if (isSpot) else 'clearinghouseState'
797
801
  request: dict = {
798
- 'type': reqType,
802
+ 'type': 'spotClearinghouseState' if (isSpot) else 'clearinghouseState',
799
803
  'user': userAddress,
800
804
  }
801
805
  response = self.publicPostInfo(self.extend(request, params))
@@ -879,7 +883,7 @@ class hyperliquid(Exchange, ImplicitAPI):
879
883
  market = self.market(symbol)
880
884
  request: dict = {
881
885
  'type': 'l2Book',
882
- 'coin': market['base'] if market['swap'] else market['id'],
886
+ 'coin': market['baseName'] if market['swap'] else market['id'],
883
887
  }
884
888
  response = self.publicPostInfo(self.extend(request, params))
885
889
  #
@@ -1112,7 +1116,7 @@ class hyperliquid(Exchange, ImplicitAPI):
1112
1116
  request: dict = {
1113
1117
  'type': 'candleSnapshot',
1114
1118
  'req': {
1115
- 'coin': market['base'] if market['swap'] else market['id'],
1119
+ 'coin': market['baseName'] if market['swap'] else market['id'],
1116
1120
  'interval': self.safe_string(self.timeframes, timeframe, timeframe),
1117
1121
  'startTime': since,
1118
1122
  'endTime': until,
@@ -1175,6 +1179,7 @@ class hyperliquid(Exchange, ImplicitAPI):
1175
1179
  :param int [params.until]: timestamp in ms of the latest trade
1176
1180
  :param str [params.address]: wallet address that made trades
1177
1181
  :param str [params.user]: wallet address that made trades
1182
+ :param str [params.subAccountAddress]: sub account user address
1178
1183
  :returns Trade[]: a list of `trade structures <https://docs.ccxt.com/#/?id=trade-structure>`
1179
1184
  """
1180
1185
  userAddress = None
@@ -1353,6 +1358,67 @@ class hyperliquid(Exchange, ImplicitAPI):
1353
1358
  }
1354
1359
  return self.sign_user_signed_action(messageTypes, message)
1355
1360
 
1361
+ def build_approve_builder_fee_sig(self, message):
1362
+ messageTypes: dict = {
1363
+ 'HyperliquidTransaction:ApproveBuilderFee': [
1364
+ {'name': 'hyperliquidChain', 'type': 'string'},
1365
+ {'name': 'maxFeeRate', 'type': 'string'},
1366
+ {'name': 'builder', 'type': 'address'},
1367
+ {'name': 'nonce', 'type': 'uint64'},
1368
+ ],
1369
+ }
1370
+ return self.sign_user_signed_action(messageTypes, message)
1371
+
1372
+ def approve_builder_fee(self, builder: str, maxFeeRate: str):
1373
+ nonce = self.milliseconds()
1374
+ isSandboxMode = self.safe_bool(self.options, 'sandboxMode', False)
1375
+ payload: dict = {
1376
+ 'hyperliquidChain': 'Testnet' if isSandboxMode else 'Mainnet',
1377
+ 'maxFeeRate': maxFeeRate,
1378
+ 'builder': builder,
1379
+ 'nonce': nonce,
1380
+ }
1381
+ sig = self.build_approve_builder_fee_sig(payload)
1382
+ action = {
1383
+ 'hyperliquidChain': payload['hyperliquidChain'],
1384
+ 'signatureChainId': '0x66eee',
1385
+ 'maxFeeRate': payload['maxFeeRate'],
1386
+ 'builder': payload['builder'],
1387
+ 'nonce': nonce,
1388
+ 'type': 'approveBuilderFee',
1389
+ }
1390
+ request: dict = {
1391
+ 'action': action,
1392
+ 'nonce': nonce,
1393
+ 'signature': sig,
1394
+ 'vaultAddress': None,
1395
+ }
1396
+ #
1397
+ # {
1398
+ # "status": "ok",
1399
+ # "response": {
1400
+ # "type": "default"
1401
+ # }
1402
+ # }
1403
+ #
1404
+ return self.privatePostExchange(request)
1405
+
1406
+ def handle_builder_fee_approval(self):
1407
+ buildFee = self.safe_bool(self.options, 'builderFee', True)
1408
+ if not buildFee:
1409
+ return False # skip if builder fee is not enabled
1410
+ approvedBuilderFee = self.safe_bool(self.options, 'approvedBuilderFee', False)
1411
+ if approvedBuilderFee:
1412
+ return True # skip if builder fee is already approved
1413
+ try:
1414
+ builder = self.safe_string(self.options, 'builder', '0x6530512A6c89C7cfCEbC3BA7fcD9aDa5f30827a6')
1415
+ maxFeeRate = self.safe_string(self.options, 'feeRate', '0.01%')
1416
+ self.approve_builder_fee(builder, maxFeeRate)
1417
+ self.options['approvedBuilderFee'] = True
1418
+ except Exception as e:
1419
+ self.options['builderFee'] = False # disable builder fee if an error occurs
1420
+ return True
1421
+
1356
1422
  def create_order(self, symbol: str, type: OrderType, side: OrderSide, amount: float, price: Num = None, params={}):
1357
1423
  """
1358
1424
  create a trade order
@@ -1372,6 +1438,7 @@ class hyperliquid(Exchange, ImplicitAPI):
1372
1438
  :param str [params.clientOrderId]: client order id,(optional 128 bit hex string e.g. 0x1234567890abcdef1234567890abcdef)
1373
1439
  :param str [params.slippage]: the slippage for market order
1374
1440
  :param str [params.vaultAddress]: the vault address for order
1441
+ :param str [params.subAccountAddress]: sub account user address
1375
1442
  :returns dict: an `order structure <https://docs.ccxt.com/#/?id=order-structure>`
1376
1443
  """
1377
1444
  self.load_markets()
@@ -1390,6 +1457,7 @@ class hyperliquid(Exchange, ImplicitAPI):
1390
1457
  :returns dict: an `order structure <https://docs.ccxt.com/#/?id=order-structure>`
1391
1458
  """
1392
1459
  self.load_markets()
1460
+ self.handle_builder_fee_approval()
1393
1461
  request = self.create_orders_request(orders, params)
1394
1462
  response = self.privatePostExchange(request)
1395
1463
  #
@@ -1553,10 +1621,10 @@ class hyperliquid(Exchange, ImplicitAPI):
1553
1621
  'type': 'order',
1554
1622
  'orders': orderReq,
1555
1623
  'grouping': grouping,
1556
- # 'brokerCode': 1, # cant
1557
1624
  }
1558
- if vaultAddress is None:
1559
- orderAction['brokerCode'] = 1
1625
+ if self.safe_bool(self.options, 'approvedBuilderFee', False):
1626
+ wallet = self.safe_string_lower(self.options, 'builder', '0x6530512A6c89C7cfCEbC3BA7fcD9aDa5f30827a6')
1627
+ orderAction['builder'] = {'b': wallet, 'f': self.safe_integer(self.options, 'feeInt', 10)}
1560
1628
  signature = self.sign_l1_action(orderAction, nonce, vaultAddress)
1561
1629
  request: dict = {
1562
1630
  'action': orderAction,
@@ -1581,6 +1649,7 @@ class hyperliquid(Exchange, ImplicitAPI):
1581
1649
  :param dict [params]: extra parameters specific to the exchange API endpoint
1582
1650
  :param str [params.clientOrderId]: client order id,(optional 128 bit hex string e.g. 0x1234567890abcdef1234567890abcdef)
1583
1651
  :param str [params.vaultAddress]: the vault address for order
1652
+ :param str [params.subAccountAddress]: sub account user address
1584
1653
  :returns dict: An `order structure <https://docs.ccxt.com/#/?id=order-structure>`
1585
1654
  """
1586
1655
  orders = self.cancel_orders([id], symbol, params)
@@ -1598,6 +1667,7 @@ class hyperliquid(Exchange, ImplicitAPI):
1598
1667
  :param dict [params]: extra parameters specific to the exchange API endpoint
1599
1668
  :param string|str[] [params.clientOrderId]: client order ids,(optional 128 bit hex string e.g. 0x1234567890abcdef1234567890abcdef)
1600
1669
  :param str [params.vaultAddress]: the vault address
1670
+ :param str [params.subAccountAddress]: sub account user address
1601
1671
  :returns dict: an list of `order structures <https://docs.ccxt.com/#/?id=order-structure>`
1602
1672
  """
1603
1673
  self.check_required_credentials()
@@ -1636,7 +1706,7 @@ class hyperliquid(Exchange, ImplicitAPI):
1636
1706
  })
1637
1707
  cancelAction['cancels'] = cancelReq
1638
1708
  vaultAddress = None
1639
- vaultAddress, params = self.handle_option_and_params(params, 'cancelOrders', 'vaultAddress')
1709
+ vaultAddress, params = self.handle_option_and_params_2(params, 'cancelOrders', 'vaultAddress', 'subAccountAddress')
1640
1710
  vaultAddress = self.format_vault_address(vaultAddress)
1641
1711
  signature = self.sign_l1_action(cancelAction, nonce, vaultAddress)
1642
1712
  request['action'] = cancelAction
@@ -1680,6 +1750,7 @@ class hyperliquid(Exchange, ImplicitAPI):
1680
1750
  :param CancellationRequest[] orders: each order should contain the parameters required by cancelOrder namely id and symbol, example [{"id": "a", "symbol": "BTC/USDT"}, {"id": "b", "symbol": "ETH/USDT"}]
1681
1751
  :param dict [params]: extra parameters specific to the exchange API endpoint
1682
1752
  :param str [params.vaultAddress]: the vault address
1753
+ :param str [params.subAccountAddress]: sub account user address
1683
1754
  :returns dict: an list of `order structures <https://docs.ccxt.com/#/?id=order-structure>`
1684
1755
  """
1685
1756
  self.check_required_credentials()
@@ -1716,7 +1787,7 @@ class hyperliquid(Exchange, ImplicitAPI):
1716
1787
  cancelAction['type'] = 'cancelByCloid' if cancelByCloid else 'cancel'
1717
1788
  cancelAction['cancels'] = cancelReq
1718
1789
  vaultAddress = None
1719
- vaultAddress, params = self.handle_option_and_params(params, 'cancelOrdersForSymbols', 'vaultAddress')
1790
+ vaultAddress, params = self.handle_option_and_params_2(params, 'cancelOrdersForSymbols', 'vaultAddress', 'subAccountAddress')
1720
1791
  vaultAddress = self.format_vault_address(vaultAddress)
1721
1792
  signature = self.sign_l1_action(cancelAction, nonce, vaultAddress)
1722
1793
  request['action'] = cancelAction
@@ -1746,6 +1817,7 @@ class hyperliquid(Exchange, ImplicitAPI):
1746
1817
  :param number timeout: time in milliseconds, 0 represents cancel the timer
1747
1818
  :param dict [params]: extra parameters specific to the exchange API endpoint
1748
1819
  :param str [params.vaultAddress]: the vault address
1820
+ :param str [params.subAccountAddress]: sub account user address
1749
1821
  :returns dict: the api result
1750
1822
  """
1751
1823
  self.check_required_credentials()
@@ -1761,7 +1833,7 @@ class hyperliquid(Exchange, ImplicitAPI):
1761
1833
  'time': nonce + timeout,
1762
1834
  }
1763
1835
  vaultAddress = None
1764
- vaultAddress, params = self.handle_option_and_params(params, 'cancelAllOrdersAfter', 'vaultAddress')
1836
+ vaultAddress, params = self.handle_option_and_params_2(params, 'cancelAllOrdersAfter', 'vaultAddress', 'subAccountAddress')
1765
1837
  vaultAddress = self.format_vault_address(vaultAddress)
1766
1838
  signature = self.sign_l1_action(cancelAction, nonce, vaultAddress)
1767
1839
  request['action'] = cancelAction
@@ -1904,6 +1976,7 @@ class hyperliquid(Exchange, ImplicitAPI):
1904
1976
  :param float [params.triggerPrice]: The price at which a trigger order is triggered at
1905
1977
  :param str [params.clientOrderId]: client order id,(optional 128 bit hex string e.g. 0x1234567890abcdef1234567890abcdef)
1906
1978
  :param str [params.vaultAddress]: the vault address for order
1979
+ :param str [params.subAccountAddress]: sub account user address
1907
1980
  :returns dict: an `order structure <https://docs.ccxt.com/#/?id=order-structure>`
1908
1981
  """
1909
1982
  self.load_markets()
@@ -2023,7 +2096,7 @@ class hyperliquid(Exchange, ImplicitAPI):
2023
2096
  market = self.market(symbol)
2024
2097
  request: dict = {
2025
2098
  'type': 'fundingHistory',
2026
- 'coin': market['base'],
2099
+ 'coin': market['baseName'],
2027
2100
  }
2028
2101
  if since is not None:
2029
2102
  request['startTime'] = since
@@ -2071,6 +2144,7 @@ class hyperliquid(Exchange, ImplicitAPI):
2071
2144
  :param dict [params]: extra parameters specific to the exchange API endpoint
2072
2145
  :param str [params.user]: user address, will default to self.walletAddress if not provided
2073
2146
  :param str [params.method]: 'openOrders' or 'frontendOpenOrders' default is 'frontendOpenOrders'
2147
+ :param str [params.subAccountAddress]: sub account user address
2074
2148
  :returns Order[]: a list of `order structures <https://docs.ccxt.com/#/?id=order-structure>`
2075
2149
  """
2076
2150
  userAddress = None
@@ -2159,6 +2233,7 @@ class hyperliquid(Exchange, ImplicitAPI):
2159
2233
  :param int [limit]: the maximum number of open orders structures to retrieve
2160
2234
  :param dict [params]: extra parameters specific to the exchange API endpoint
2161
2235
  :param str [params.user]: user address, will default to self.walletAddress if not provided
2236
+ :param str [params.subAccountAddress]: sub account user address
2162
2237
  :returns Order[]: a list of `order structures <https://docs.ccxt.com/#/?id=order-structure>`
2163
2238
  """
2164
2239
  userAddress = None
@@ -2195,6 +2270,7 @@ class hyperliquid(Exchange, ImplicitAPI):
2195
2270
  :param str symbol: unified symbol of the market the order was made in
2196
2271
  :param dict [params]: extra parameters specific to the exchange API endpoint
2197
2272
  :param str [params.user]: user address, will default to self.walletAddress if not provided
2273
+ :param str [params.subAccountAddress]: sub account user address
2198
2274
  :returns dict: An `order structure <https://docs.ccxt.com/#/?id=order-structure>`
2199
2275
  """
2200
2276
  userAddress = None
@@ -2420,6 +2496,7 @@ class hyperliquid(Exchange, ImplicitAPI):
2420
2496
  :param int [limit]: the maximum number of trades structures to retrieve
2421
2497
  :param dict [params]: extra parameters specific to the exchange API endpoint
2422
2498
  :param int [params.until]: timestamp in ms of the latest trade
2499
+ :param str [params.subAccountAddress]: sub account user address
2423
2500
  :returns Trade[]: a list of `trade structures <https://docs.ccxt.com/#/?id=trade-structure>`
2424
2501
  """
2425
2502
  userAddress = None
@@ -2540,6 +2617,7 @@ class hyperliquid(Exchange, ImplicitAPI):
2540
2617
  :param str[] [symbols]: list of unified market symbols
2541
2618
  :param dict [params]: extra parameters specific to the exchange API endpoint
2542
2619
  :param str [params.user]: user address, will default to self.walletAddress if not provided
2620
+ :param str [params.subAccountAddress]: sub account user address
2543
2621
  :returns dict[]: a list of `position structure <https://docs.ccxt.com/#/?id=position-structure>`
2544
2622
  """
2545
2623
  self.load_markets()
@@ -2680,6 +2758,8 @@ class hyperliquid(Exchange, ImplicitAPI):
2680
2758
  :param str symbol: unified market symbol of the market the position is held in, default is None
2681
2759
  :param dict [params]: extra parameters specific to the exchange API endpoint
2682
2760
  :param str [params.leverage]: the rate of leverage, is required if setting trade mode(symbol)
2761
+ :param str [params.vaultAddress]: the vault address
2762
+ :param str [params.subAccountAddress]: sub account user address
2683
2763
  :returns dict: response from the exchange
2684
2764
  """
2685
2765
  if symbol is None:
@@ -2700,7 +2780,7 @@ class hyperliquid(Exchange, ImplicitAPI):
2700
2780
  'leverage': leverage,
2701
2781
  }
2702
2782
  vaultAddress = None
2703
- vaultAddress, params = self.handle_option_and_params(params, 'setMarginMode', 'vaultAddress')
2783
+ vaultAddress, params = self.handle_option_and_params_2(params, 'setMarginMode', 'vaultAddress', 'subAccountAddress')
2704
2784
  if vaultAddress is not None:
2705
2785
  if vaultAddress.startswith('0x'):
2706
2786
  vaultAddress = vaultAddress.replace('0x', '')
@@ -2749,7 +2829,7 @@ class hyperliquid(Exchange, ImplicitAPI):
2749
2829
  'leverage': leverage,
2750
2830
  }
2751
2831
  vaultAddress = None
2752
- vaultAddress, params = self.handle_option_and_params(params, 'setLeverage', 'vaultAddress')
2832
+ vaultAddress, params = self.handle_option_and_params_2(params, 'setLeverage', 'vaultAddress', 'subAccountAddress')
2753
2833
  vaultAddress = self.format_vault_address(vaultAddress)
2754
2834
  signature = self.sign_l1_action(updateAction, nonce, vaultAddress)
2755
2835
  request: dict = {
@@ -2781,6 +2861,8 @@ class hyperliquid(Exchange, ImplicitAPI):
2781
2861
  :param str symbol: unified market symbol
2782
2862
  :param float amount: amount of margin to add
2783
2863
  :param dict [params]: extra parameters specific to the exchange API endpoint
2864
+ :param str [params.vaultAddress]: the vault address
2865
+ :param str [params.subAccountAddress]: sub account user address
2784
2866
  :returns dict: a `margin structure <https://docs.ccxt.com/#/?id=add-margin-structure>`
2785
2867
  """
2786
2868
  return self.modify_margin_helper(symbol, amount, 'add', params)
@@ -2794,6 +2876,8 @@ class hyperliquid(Exchange, ImplicitAPI):
2794
2876
  :param str symbol: unified market symbol
2795
2877
  :param float amount: the amount of margin to remove
2796
2878
  :param dict [params]: extra parameters specific to the exchange API endpoint
2879
+ :param str [params.vaultAddress]: the vault address
2880
+ :param str [params.subAccountAddress]: sub account user address
2797
2881
  :returns dict: a `margin structure <https://docs.ccxt.com/#/?id=reduce-margin-structure>`
2798
2882
  """
2799
2883
  return self.modify_margin_helper(symbol, amount, 'reduce', params)
@@ -2813,7 +2897,7 @@ class hyperliquid(Exchange, ImplicitAPI):
2813
2897
  'ntli': sz,
2814
2898
  }
2815
2899
  vaultAddress = None
2816
- vaultAddress, params = self.handle_option_and_params(params, 'modifyMargin', 'vaultAddress')
2900
+ vaultAddress, params = self.handle_option_and_params_2(params, 'modifyMargin', 'vaultAddress', 'subAccountAddress')
2817
2901
  vaultAddress = self.format_vault_address(vaultAddress)
2818
2902
  signature = self.sign_l1_action(updateAction, nonce, vaultAddress)
2819
2903
  request: dict = {
@@ -2908,28 +2992,31 @@ class hyperliquid(Exchange, ImplicitAPI):
2908
2992
  transferRequest['vaultAddress'] = vaultAddress
2909
2993
  transferResponse = self.privatePostExchange(transferRequest)
2910
2994
  return transferResponse
2911
- # handle sub-account/different account transfer
2912
- self.check_address(toAccount)
2995
+ # transfer between main account and subaccount
2913
2996
  if code is not None:
2914
2997
  code = code.upper()
2915
2998
  if code != 'USDC':
2916
2999
  raise NotSupported(self.id + ' transfer() only support USDC')
2917
- payload: dict = {
2918
- 'hyperliquidChain': 'Testnet' if isSandboxMode else 'Mainnet',
2919
- 'destination': toAccount,
2920
- 'amount': self.number_to_string(amount),
2921
- 'time': nonce,
3000
+ isDeposit = False
3001
+ subAccountAddress = None
3002
+ if fromAccount == 'main':
3003
+ subAccountAddress = toAccount
3004
+ isDeposit = True
3005
+ elif toAccount == 'main':
3006
+ subAccountAddress = fromAccount
3007
+ else:
3008
+ raise NotSupported(self.id + ' transfer() only support main <> subaccount transfer')
3009
+ self.check_address(subAccountAddress)
3010
+ usd = self.parse_to_int(Precise.string_mul(self.number_to_string(amount), '1000000'))
3011
+ action = {
3012
+ 'type': 'subAccountTransfer',
3013
+ 'subAccountUser': subAccountAddress,
3014
+ 'isDeposit': isDeposit,
3015
+ 'usd': usd,
2922
3016
  }
2923
- sig = self.build_usd_send_sig(payload)
3017
+ sig = self.sign_l1_action(action, nonce)
2924
3018
  request: dict = {
2925
- 'action': {
2926
- 'hyperliquidChain': payload['hyperliquidChain'],
2927
- 'signatureChainId': '0x66eee', # check self out
2928
- 'destination': toAccount,
2929
- 'amount': str(amount),
2930
- 'time': nonce,
2931
- 'type': 'usdSend',
2932
- },
3019
+ 'action': action,
2933
3020
  'nonce': nonce,
2934
3021
  'signature': sig,
2935
3022
  }
@@ -3074,6 +3161,7 @@ class hyperliquid(Exchange, ImplicitAPI):
3074
3161
  :param str symbol: unified market symbol
3075
3162
  :param dict [params]: extra parameters specific to the exchange API endpoint
3076
3163
  :param str [params.user]: user address, will default to self.walletAddress if not provided
3164
+ :param str [params.subAccountAddress]: sub account user address
3077
3165
  :returns dict: a `fee structure <https://docs.ccxt.com/#/?id=fee-structure>`
3078
3166
  """
3079
3167
  self.load_markets()
@@ -3180,6 +3268,7 @@ class hyperliquid(Exchange, ImplicitAPI):
3180
3268
  :param int [limit]: max number of ledger entries to return
3181
3269
  :param dict [params]: extra parameters specific to the exchange API endpoint
3182
3270
  :param int [params.until]: timestamp in ms of the latest ledger entry
3271
+ :param str [params.subAccountAddress]: sub account user address
3183
3272
  :returns dict: a `ledger structure <https://docs.ccxt.com/#/?id=ledger>`
3184
3273
  """
3185
3274
  self.load_markets()
@@ -3267,6 +3356,7 @@ class hyperliquid(Exchange, ImplicitAPI):
3267
3356
  :param int [limit]: the maximum number of deposits structures to retrieve
3268
3357
  :param dict [params]: extra parameters specific to the exchange API endpoint
3269
3358
  :param int [params.until]: the latest time in ms to fetch withdrawals for
3359
+ :param str [params.subAccountAddress]: sub account user address
3270
3360
  :returns dict[]: a list of `transaction structures <https://docs.ccxt.com/#/?id=transaction-structure>`
3271
3361
  """
3272
3362
  self.load_markets()
@@ -3308,6 +3398,7 @@ class hyperliquid(Exchange, ImplicitAPI):
3308
3398
  :param int [limit]: the maximum number of withdrawals structures to retrieve
3309
3399
  :param dict [params]: extra parameters specific to the exchange API endpoint
3310
3400
  :param int [params.until]: the latest time in ms to fetch withdrawals for
3401
+ :param str [params.subAccountAddress]: sub account user address
3311
3402
  :returns dict[]: a list of `transaction structures <https://docs.ccxt.com/#/?id=transaction-structure>`
3312
3403
  """
3313
3404
  self.load_markets()
@@ -3405,6 +3496,7 @@ class hyperliquid(Exchange, ImplicitAPI):
3405
3496
  :param int [since]: the earliest time in ms to fetch funding history for
3406
3497
  :param int [limit]: the maximum number of funding history structures to retrieve
3407
3498
  :param dict [params]: extra parameters specific to the exchange API endpoint
3499
+ :param str [params.subAccountAddress]: sub account user address
3408
3500
  :returns dict: a `funding history structure <https://docs.ccxt.com/#/?id=funding-history-structure>`
3409
3501
  """
3410
3502
  self.load_markets()
@@ -3495,7 +3587,7 @@ class hyperliquid(Exchange, ImplicitAPI):
3495
3587
 
3496
3588
  def handle_public_address(self, methodName: str, params: dict):
3497
3589
  userAux = None
3498
- userAux, params = self.handle_option_and_params(params, methodName, 'user')
3590
+ userAux, params = self.handle_option_and_params_2(params, methodName, 'user', 'subAccountAddress')
3499
3591
  user = userAux
3500
3592
  user, params = self.handle_option_and_params(params, methodName, 'address', userAux)
3501
3593
  if (user is not None) and (user != ''):
@@ -3562,7 +3654,7 @@ class hyperliquid(Exchange, ImplicitAPI):
3562
3654
  def parse_create_edit_order_args(self, id: Str, symbol: str, type: OrderType, side: OrderSide, amount: float, price: Num = None, params={}):
3563
3655
  market = self.market(symbol)
3564
3656
  vaultAddress = None
3565
- vaultAddress, params = self.handle_option_and_params(params, 'createOrder', 'vaultAddress')
3657
+ vaultAddress, params = self.handle_option_and_params_2(params, 'createOrder', 'vaultAddress', 'subAccountAddress')
3566
3658
  vaultAddress = self.format_vault_address(vaultAddress)
3567
3659
  symbol = market['symbol']
3568
3660
  order = {