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/__init__.py CHANGED
@@ -22,7 +22,7 @@
22
22
 
23
23
  # ----------------------------------------------------------------------------
24
24
 
25
- __version__ = '4.4.93'
25
+ __version__ = '4.4.94'
26
26
 
27
27
  # ----------------------------------------------------------------------------
28
28
 
@@ -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
 
@@ -2,7 +2,7 @@
2
2
 
3
3
  # -----------------------------------------------------------------------------
4
4
 
5
- __version__ = '4.4.93'
5
+ __version__ = '4.4.94'
6
6
 
7
7
  # -----------------------------------------------------------------------------
8
8
 
@@ -3752,7 +3752,7 @@ class bybit(Exchange, ImplicitAPI):
3752
3752
  isTakeProfit = takeProfitPrice is not None
3753
3753
  orderRequest = self.create_order_request(symbol, type, side, amount, price, params, enableUnifiedAccount)
3754
3754
  defaultMethod = None
3755
- if isTrailingAmountOrder or isStopLoss or isTakeProfit:
3755
+ if (isTrailingAmountOrder or isStopLoss or isTakeProfit) and not market['spot']:
3756
3756
  defaultMethod = 'privatePostV5PositionTradingStop'
3757
3757
  else:
3758
3758
  defaultMethod = 'privatePostV5OrderCreate'
@@ -3828,7 +3828,7 @@ class bybit(Exchange, ImplicitAPI):
3828
3828
  isLimit = lowerCaseType == 'limit'
3829
3829
  isBuy = side == 'buy'
3830
3830
  defaultMethod = None
3831
- if isTrailingAmountOrder or isStopLossTriggerOrder or isTakeProfitTriggerOrder:
3831
+ if (isTrailingAmountOrder or isStopLossTriggerOrder or isTakeProfitTriggerOrder) and not market['spot']:
3832
3832
  defaultMethod = 'privatePostV5PositionTradingStop'
3833
3833
  else:
3834
3834
  defaultMethod = 'privatePostV5OrderCreate'
@@ -14,6 +14,7 @@ from ccxt.base.errors import AuthenticationError
14
14
  from ccxt.base.errors import PermissionDenied
15
15
  from ccxt.base.errors import ArgumentsRequired
16
16
  from ccxt.base.errors import BadRequest
17
+ from ccxt.base.errors import InsufficientFunds
17
18
  from ccxt.base.errors import InvalidOrder
18
19
  from ccxt.base.errors import OrderNotFound
19
20
  from ccxt.base.errors import NotSupported
@@ -333,12 +334,14 @@ class coinbase(Exchange, ImplicitAPI):
333
334
  'rate_limit_exceeded': RateLimitExceeded, # 429 Rate limit exceeded
334
335
  'internal_server_error': ExchangeError, # 500 Internal server error
335
336
  'UNSUPPORTED_ORDER_CONFIGURATION': BadRequest,
336
- 'INSUFFICIENT_FUND': BadRequest,
337
+ 'INSUFFICIENT_FUND': InsufficientFunds,
337
338
  'PERMISSION_DENIED': PermissionDenied,
338
339
  'INVALID_ARGUMENT': BadRequest,
339
340
  'PREVIEW_STOP_PRICE_ABOVE_LAST_TRADE_PRICE': InvalidOrder,
341
+ 'PREVIEW_INSUFFICIENT_FUND': InsufficientFunds,
340
342
  },
341
343
  'broad': {
344
+ 'Insufficient balance in source account': InsufficientFunds,
342
345
  'request timestamp expired': InvalidNonce, # {"errors":[{"id":"authentication_error","message":"request timestamp expired"}]}
343
346
  '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"}
344
347
  },
@@ -5,6 +5,7 @@
5
5
 
6
6
  from ccxt.async_support.base.exchange import Exchange
7
7
  from ccxt.abstract.coinmetro import ImplicitAPI
8
+ import asyncio
8
9
  from ccxt.base.types import Any, Balances, Currencies, Currency, IndexType, Int, LedgerEntry, Market, Num, Order, OrderBook, OrderSide, OrderType, Str, Strings, Ticker, Tickers, Trade
9
10
  from typing import List
10
11
  from ccxt.base.errors import ExchangeError
@@ -220,6 +221,7 @@ class coinmetro(Exchange, ImplicitAPI):
220
221
  'options': {
221
222
  'currenciesByIdForParseMarket': None,
222
223
  'currencyIdsListForParseMarket': ['QRDO'],
224
+ 'skippedMarkets': ['VXVUSDT'], # broken markets which do not have enough info in API
223
225
  },
224
226
  'features': {
225
227
  'spot': {
@@ -439,9 +441,12 @@ class coinmetro(Exchange, ImplicitAPI):
439
441
  :param dict [params]: extra parameters specific to the exchange API endpoint
440
442
  :returns dict[]: an array of objects representing market data
441
443
  """
442
- response = await self.publicGetMarkets(params)
444
+ promises = []
445
+ promises.append(self.publicGetMarkets(params))
443
446
  if self.safe_value(self.options, 'currenciesByIdForParseMarket') is None:
444
- await self.fetch_currencies()
447
+ promises.append(self.fetch_currencies())
448
+ responses = await asyncio.gather(*promises)
449
+ response = responses[0]
445
450
  #
446
451
  # [
447
452
  # {
@@ -457,7 +462,14 @@ class coinmetro(Exchange, ImplicitAPI):
457
462
  # ...
458
463
  # ]
459
464
  #
460
- return self.parse_markets(response)
465
+ skippedMarkets = self.safe_list(self.options, 'skippedMarkets', [])
466
+ result = []
467
+ for i in range(0, len(response)):
468
+ market = self.parse_market(response[i])
469
+ if self.in_array(market['id'], skippedMarkets):
470
+ continue
471
+ result.append(market)
472
+ return result
461
473
 
462
474
  def parse_market(self, market: dict) -> Market:
463
475
  id = self.safe_string(market, 'pair')
ccxt/async_support/htx.py CHANGED
@@ -6577,12 +6577,16 @@ class htx(Exchange, ImplicitAPI):
6577
6577
  paginate = False
6578
6578
  paginate, params = self.handle_option_and_params(params, 'fetchFundingRateHistory', 'paginate')
6579
6579
  if paginate:
6580
- return await self.fetch_paginated_call_cursor('fetchFundingRateHistory', symbol, since, limit, params, 'page_index', 'current_page', 1, 50)
6580
+ return await self.fetch_paginated_call_cursor('fetchFundingRateHistory', symbol, since, limit, params, 'current_page', 'page_index', 1, 50)
6581
6581
  await self.load_markets()
6582
6582
  market = self.market(symbol)
6583
6583
  request: dict = {
6584
6584
  'contract_code': market['id'],
6585
6585
  }
6586
+ if limit is not None:
6587
+ request['page_size'] = limit
6588
+ else:
6589
+ request['page_size'] = 50 # max
6586
6590
  response = None
6587
6591
  if market['inverse']:
6588
6592
  response = await self.contractPublicGetSwapApiV1SwapHistoricalFundingRate(self.extend(request, params))
@@ -354,6 +354,8 @@ class hyperliquid(Exchange, ImplicitAPI):
354
354
  :param dict [params]: extra parameters specific to the exchange API endpoint
355
355
  :returns dict: an associative dictionary of currencies
356
356
  """
357
+ if self.check_required_credentials(False):
358
+ await self.handle_builder_fee_approval()
357
359
  request: dict = {
358
360
  'type': 'meta',
359
361
  }
@@ -627,6 +629,7 @@ class hyperliquid(Exchange, ImplicitAPI):
627
629
  'quote': quote,
628
630
  'settle': None,
629
631
  'baseId': baseId,
632
+ 'baseName': baseName,
630
633
  'quoteId': quoteId,
631
634
  'settleId': None,
632
635
  'type': 'spot',
@@ -729,6 +732,7 @@ class hyperliquid(Exchange, ImplicitAPI):
729
732
  'quote': quote,
730
733
  'settle': settle,
731
734
  'baseId': baseId,
735
+ 'baseName': baseName,
732
736
  'quoteId': quoteId,
733
737
  'settleId': settleId,
734
738
  'type': 'swap',
@@ -785,6 +789,7 @@ class hyperliquid(Exchange, ImplicitAPI):
785
789
  :param str [params.user]: user address, will default to self.walletAddress if not provided
786
790
  :param str [params.type]: wallet type, ['spot', 'swap'], defaults to swap
787
791
  :param str [params.marginMode]: 'cross' or 'isolated', for margin trading, uses self.options.defaultMarginMode if not passed, defaults to None/None/None
792
+ :param str [params.subAccountAddress]: sub account user address
788
793
  :returns dict: a `balance structure <https://docs.ccxt.com/#/?id=balance-structure>`
789
794
  """
790
795
  userAddress = None
@@ -794,9 +799,8 @@ class hyperliquid(Exchange, ImplicitAPI):
794
799
  marginMode = None
795
800
  marginMode, params = self.handle_margin_mode_and_params('fetchBalance', params)
796
801
  isSpot = (type == 'spot')
797
- reqType = 'spotClearinghouseState' if (isSpot) else 'clearinghouseState'
798
802
  request: dict = {
799
- 'type': reqType,
803
+ 'type': 'spotClearinghouseState' if (isSpot) else 'clearinghouseState',
800
804
  'user': userAddress,
801
805
  }
802
806
  response = await self.publicPostInfo(self.extend(request, params))
@@ -880,7 +884,7 @@ class hyperliquid(Exchange, ImplicitAPI):
880
884
  market = self.market(symbol)
881
885
  request: dict = {
882
886
  'type': 'l2Book',
883
- 'coin': market['base'] if market['swap'] else market['id'],
887
+ 'coin': market['baseName'] if market['swap'] else market['id'],
884
888
  }
885
889
  response = await self.publicPostInfo(self.extend(request, params))
886
890
  #
@@ -1113,7 +1117,7 @@ class hyperliquid(Exchange, ImplicitAPI):
1113
1117
  request: dict = {
1114
1118
  'type': 'candleSnapshot',
1115
1119
  'req': {
1116
- 'coin': market['base'] if market['swap'] else market['id'],
1120
+ 'coin': market['baseName'] if market['swap'] else market['id'],
1117
1121
  'interval': self.safe_string(self.timeframes, timeframe, timeframe),
1118
1122
  'startTime': since,
1119
1123
  'endTime': until,
@@ -1176,6 +1180,7 @@ class hyperliquid(Exchange, ImplicitAPI):
1176
1180
  :param int [params.until]: timestamp in ms of the latest trade
1177
1181
  :param str [params.address]: wallet address that made trades
1178
1182
  :param str [params.user]: wallet address that made trades
1183
+ :param str [params.subAccountAddress]: sub account user address
1179
1184
  :returns Trade[]: a list of `trade structures <https://docs.ccxt.com/#/?id=trade-structure>`
1180
1185
  """
1181
1186
  userAddress = None
@@ -1354,6 +1359,67 @@ class hyperliquid(Exchange, ImplicitAPI):
1354
1359
  }
1355
1360
  return self.sign_user_signed_action(messageTypes, message)
1356
1361
 
1362
+ def build_approve_builder_fee_sig(self, message):
1363
+ messageTypes: dict = {
1364
+ 'HyperliquidTransaction:ApproveBuilderFee': [
1365
+ {'name': 'hyperliquidChain', 'type': 'string'},
1366
+ {'name': 'maxFeeRate', 'type': 'string'},
1367
+ {'name': 'builder', 'type': 'address'},
1368
+ {'name': 'nonce', 'type': 'uint64'},
1369
+ ],
1370
+ }
1371
+ return self.sign_user_signed_action(messageTypes, message)
1372
+
1373
+ async def approve_builder_fee(self, builder: str, maxFeeRate: str):
1374
+ nonce = self.milliseconds()
1375
+ isSandboxMode = self.safe_bool(self.options, 'sandboxMode', False)
1376
+ payload: dict = {
1377
+ 'hyperliquidChain': 'Testnet' if isSandboxMode else 'Mainnet',
1378
+ 'maxFeeRate': maxFeeRate,
1379
+ 'builder': builder,
1380
+ 'nonce': nonce,
1381
+ }
1382
+ sig = self.build_approve_builder_fee_sig(payload)
1383
+ action = {
1384
+ 'hyperliquidChain': payload['hyperliquidChain'],
1385
+ 'signatureChainId': '0x66eee',
1386
+ 'maxFeeRate': payload['maxFeeRate'],
1387
+ 'builder': payload['builder'],
1388
+ 'nonce': nonce,
1389
+ 'type': 'approveBuilderFee',
1390
+ }
1391
+ request: dict = {
1392
+ 'action': action,
1393
+ 'nonce': nonce,
1394
+ 'signature': sig,
1395
+ 'vaultAddress': None,
1396
+ }
1397
+ #
1398
+ # {
1399
+ # "status": "ok",
1400
+ # "response": {
1401
+ # "type": "default"
1402
+ # }
1403
+ # }
1404
+ #
1405
+ return await self.privatePostExchange(request)
1406
+
1407
+ async def handle_builder_fee_approval(self):
1408
+ buildFee = self.safe_bool(self.options, 'builderFee', True)
1409
+ if not buildFee:
1410
+ return False # skip if builder fee is not enabled
1411
+ approvedBuilderFee = self.safe_bool(self.options, 'approvedBuilderFee', False)
1412
+ if approvedBuilderFee:
1413
+ return True # skip if builder fee is already approved
1414
+ try:
1415
+ builder = self.safe_string(self.options, 'builder', '0x6530512A6c89C7cfCEbC3BA7fcD9aDa5f30827a6')
1416
+ maxFeeRate = self.safe_string(self.options, 'feeRate', '0.01%')
1417
+ await self.approve_builder_fee(builder, maxFeeRate)
1418
+ self.options['approvedBuilderFee'] = True
1419
+ except Exception as e:
1420
+ self.options['builderFee'] = False # disable builder fee if an error occurs
1421
+ return True
1422
+
1357
1423
  async def create_order(self, symbol: str, type: OrderType, side: OrderSide, amount: float, price: Num = None, params={}):
1358
1424
  """
1359
1425
  create a trade order
@@ -1373,6 +1439,7 @@ class hyperliquid(Exchange, ImplicitAPI):
1373
1439
  :param str [params.clientOrderId]: client order id,(optional 128 bit hex string e.g. 0x1234567890abcdef1234567890abcdef)
1374
1440
  :param str [params.slippage]: the slippage for market order
1375
1441
  :param str [params.vaultAddress]: the vault address for order
1442
+ :param str [params.subAccountAddress]: sub account user address
1376
1443
  :returns dict: an `order structure <https://docs.ccxt.com/#/?id=order-structure>`
1377
1444
  """
1378
1445
  await self.load_markets()
@@ -1391,6 +1458,7 @@ class hyperliquid(Exchange, ImplicitAPI):
1391
1458
  :returns dict: an `order structure <https://docs.ccxt.com/#/?id=order-structure>`
1392
1459
  """
1393
1460
  await self.load_markets()
1461
+ await self.handle_builder_fee_approval()
1394
1462
  request = self.create_orders_request(orders, params)
1395
1463
  response = await self.privatePostExchange(request)
1396
1464
  #
@@ -1554,10 +1622,10 @@ class hyperliquid(Exchange, ImplicitAPI):
1554
1622
  'type': 'order',
1555
1623
  'orders': orderReq,
1556
1624
  'grouping': grouping,
1557
- # 'brokerCode': 1, # cant
1558
1625
  }
1559
- if vaultAddress is None:
1560
- orderAction['brokerCode'] = 1
1626
+ if self.safe_bool(self.options, 'approvedBuilderFee', False):
1627
+ wallet = self.safe_string_lower(self.options, 'builder', '0x6530512A6c89C7cfCEbC3BA7fcD9aDa5f30827a6')
1628
+ orderAction['builder'] = {'b': wallet, 'f': self.safe_integer(self.options, 'feeInt', 10)}
1561
1629
  signature = self.sign_l1_action(orderAction, nonce, vaultAddress)
1562
1630
  request: dict = {
1563
1631
  'action': orderAction,
@@ -1582,6 +1650,7 @@ class hyperliquid(Exchange, ImplicitAPI):
1582
1650
  :param dict [params]: extra parameters specific to the exchange API endpoint
1583
1651
  :param str [params.clientOrderId]: client order id,(optional 128 bit hex string e.g. 0x1234567890abcdef1234567890abcdef)
1584
1652
  :param str [params.vaultAddress]: the vault address for order
1653
+ :param str [params.subAccountAddress]: sub account user address
1585
1654
  :returns dict: An `order structure <https://docs.ccxt.com/#/?id=order-structure>`
1586
1655
  """
1587
1656
  orders = await self.cancel_orders([id], symbol, params)
@@ -1599,6 +1668,7 @@ class hyperliquid(Exchange, ImplicitAPI):
1599
1668
  :param dict [params]: extra parameters specific to the exchange API endpoint
1600
1669
  :param string|str[] [params.clientOrderId]: client order ids,(optional 128 bit hex string e.g. 0x1234567890abcdef1234567890abcdef)
1601
1670
  :param str [params.vaultAddress]: the vault address
1671
+ :param str [params.subAccountAddress]: sub account user address
1602
1672
  :returns dict: an list of `order structures <https://docs.ccxt.com/#/?id=order-structure>`
1603
1673
  """
1604
1674
  self.check_required_credentials()
@@ -1637,7 +1707,7 @@ class hyperliquid(Exchange, ImplicitAPI):
1637
1707
  })
1638
1708
  cancelAction['cancels'] = cancelReq
1639
1709
  vaultAddress = None
1640
- vaultAddress, params = self.handle_option_and_params(params, 'cancelOrders', 'vaultAddress')
1710
+ vaultAddress, params = self.handle_option_and_params_2(params, 'cancelOrders', 'vaultAddress', 'subAccountAddress')
1641
1711
  vaultAddress = self.format_vault_address(vaultAddress)
1642
1712
  signature = self.sign_l1_action(cancelAction, nonce, vaultAddress)
1643
1713
  request['action'] = cancelAction
@@ -1681,6 +1751,7 @@ class hyperliquid(Exchange, ImplicitAPI):
1681
1751
  :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"}]
1682
1752
  :param dict [params]: extra parameters specific to the exchange API endpoint
1683
1753
  :param str [params.vaultAddress]: the vault address
1754
+ :param str [params.subAccountAddress]: sub account user address
1684
1755
  :returns dict: an list of `order structures <https://docs.ccxt.com/#/?id=order-structure>`
1685
1756
  """
1686
1757
  self.check_required_credentials()
@@ -1717,7 +1788,7 @@ class hyperliquid(Exchange, ImplicitAPI):
1717
1788
  cancelAction['type'] = 'cancelByCloid' if cancelByCloid else 'cancel'
1718
1789
  cancelAction['cancels'] = cancelReq
1719
1790
  vaultAddress = None
1720
- vaultAddress, params = self.handle_option_and_params(params, 'cancelOrdersForSymbols', 'vaultAddress')
1791
+ vaultAddress, params = self.handle_option_and_params_2(params, 'cancelOrdersForSymbols', 'vaultAddress', 'subAccountAddress')
1721
1792
  vaultAddress = self.format_vault_address(vaultAddress)
1722
1793
  signature = self.sign_l1_action(cancelAction, nonce, vaultAddress)
1723
1794
  request['action'] = cancelAction
@@ -1747,6 +1818,7 @@ class hyperliquid(Exchange, ImplicitAPI):
1747
1818
  :param number timeout: time in milliseconds, 0 represents cancel the timer
1748
1819
  :param dict [params]: extra parameters specific to the exchange API endpoint
1749
1820
  :param str [params.vaultAddress]: the vault address
1821
+ :param str [params.subAccountAddress]: sub account user address
1750
1822
  :returns dict: the api result
1751
1823
  """
1752
1824
  self.check_required_credentials()
@@ -1762,7 +1834,7 @@ class hyperliquid(Exchange, ImplicitAPI):
1762
1834
  'time': nonce + timeout,
1763
1835
  }
1764
1836
  vaultAddress = None
1765
- vaultAddress, params = self.handle_option_and_params(params, 'cancelAllOrdersAfter', 'vaultAddress')
1837
+ vaultAddress, params = self.handle_option_and_params_2(params, 'cancelAllOrdersAfter', 'vaultAddress', 'subAccountAddress')
1766
1838
  vaultAddress = self.format_vault_address(vaultAddress)
1767
1839
  signature = self.sign_l1_action(cancelAction, nonce, vaultAddress)
1768
1840
  request['action'] = cancelAction
@@ -1905,6 +1977,7 @@ class hyperliquid(Exchange, ImplicitAPI):
1905
1977
  :param float [params.triggerPrice]: The price at which a trigger order is triggered at
1906
1978
  :param str [params.clientOrderId]: client order id,(optional 128 bit hex string e.g. 0x1234567890abcdef1234567890abcdef)
1907
1979
  :param str [params.vaultAddress]: the vault address for order
1980
+ :param str [params.subAccountAddress]: sub account user address
1908
1981
  :returns dict: an `order structure <https://docs.ccxt.com/#/?id=order-structure>`
1909
1982
  """
1910
1983
  await self.load_markets()
@@ -2024,7 +2097,7 @@ class hyperliquid(Exchange, ImplicitAPI):
2024
2097
  market = self.market(symbol)
2025
2098
  request: dict = {
2026
2099
  'type': 'fundingHistory',
2027
- 'coin': market['base'],
2100
+ 'coin': market['baseName'],
2028
2101
  }
2029
2102
  if since is not None:
2030
2103
  request['startTime'] = since
@@ -2072,6 +2145,7 @@ class hyperliquid(Exchange, ImplicitAPI):
2072
2145
  :param dict [params]: extra parameters specific to the exchange API endpoint
2073
2146
  :param str [params.user]: user address, will default to self.walletAddress if not provided
2074
2147
  :param str [params.method]: 'openOrders' or 'frontendOpenOrders' default is 'frontendOpenOrders'
2148
+ :param str [params.subAccountAddress]: sub account user address
2075
2149
  :returns Order[]: a list of `order structures <https://docs.ccxt.com/#/?id=order-structure>`
2076
2150
  """
2077
2151
  userAddress = None
@@ -2160,6 +2234,7 @@ class hyperliquid(Exchange, ImplicitAPI):
2160
2234
  :param int [limit]: the maximum number of open orders structures to retrieve
2161
2235
  :param dict [params]: extra parameters specific to the exchange API endpoint
2162
2236
  :param str [params.user]: user address, will default to self.walletAddress if not provided
2237
+ :param str [params.subAccountAddress]: sub account user address
2163
2238
  :returns Order[]: a list of `order structures <https://docs.ccxt.com/#/?id=order-structure>`
2164
2239
  """
2165
2240
  userAddress = None
@@ -2196,6 +2271,7 @@ class hyperliquid(Exchange, ImplicitAPI):
2196
2271
  :param str symbol: unified symbol of the market the order was made in
2197
2272
  :param dict [params]: extra parameters specific to the exchange API endpoint
2198
2273
  :param str [params.user]: user address, will default to self.walletAddress if not provided
2274
+ :param str [params.subAccountAddress]: sub account user address
2199
2275
  :returns dict: An `order structure <https://docs.ccxt.com/#/?id=order-structure>`
2200
2276
  """
2201
2277
  userAddress = None
@@ -2421,6 +2497,7 @@ class hyperliquid(Exchange, ImplicitAPI):
2421
2497
  :param int [limit]: the maximum number of trades structures to retrieve
2422
2498
  :param dict [params]: extra parameters specific to the exchange API endpoint
2423
2499
  :param int [params.until]: timestamp in ms of the latest trade
2500
+ :param str [params.subAccountAddress]: sub account user address
2424
2501
  :returns Trade[]: a list of `trade structures <https://docs.ccxt.com/#/?id=trade-structure>`
2425
2502
  """
2426
2503
  userAddress = None
@@ -2541,6 +2618,7 @@ class hyperliquid(Exchange, ImplicitAPI):
2541
2618
  :param str[] [symbols]: list of unified market symbols
2542
2619
  :param dict [params]: extra parameters specific to the exchange API endpoint
2543
2620
  :param str [params.user]: user address, will default to self.walletAddress if not provided
2621
+ :param str [params.subAccountAddress]: sub account user address
2544
2622
  :returns dict[]: a list of `position structure <https://docs.ccxt.com/#/?id=position-structure>`
2545
2623
  """
2546
2624
  await self.load_markets()
@@ -2681,6 +2759,8 @@ class hyperliquid(Exchange, ImplicitAPI):
2681
2759
  :param str symbol: unified market symbol of the market the position is held in, default is None
2682
2760
  :param dict [params]: extra parameters specific to the exchange API endpoint
2683
2761
  :param str [params.leverage]: the rate of leverage, is required if setting trade mode(symbol)
2762
+ :param str [params.vaultAddress]: the vault address
2763
+ :param str [params.subAccountAddress]: sub account user address
2684
2764
  :returns dict: response from the exchange
2685
2765
  """
2686
2766
  if symbol is None:
@@ -2701,7 +2781,7 @@ class hyperliquid(Exchange, ImplicitAPI):
2701
2781
  'leverage': leverage,
2702
2782
  }
2703
2783
  vaultAddress = None
2704
- vaultAddress, params = self.handle_option_and_params(params, 'setMarginMode', 'vaultAddress')
2784
+ vaultAddress, params = self.handle_option_and_params_2(params, 'setMarginMode', 'vaultAddress', 'subAccountAddress')
2705
2785
  if vaultAddress is not None:
2706
2786
  if vaultAddress.startswith('0x'):
2707
2787
  vaultAddress = vaultAddress.replace('0x', '')
@@ -2750,7 +2830,7 @@ class hyperliquid(Exchange, ImplicitAPI):
2750
2830
  'leverage': leverage,
2751
2831
  }
2752
2832
  vaultAddress = None
2753
- vaultAddress, params = self.handle_option_and_params(params, 'setLeverage', 'vaultAddress')
2833
+ vaultAddress, params = self.handle_option_and_params_2(params, 'setLeverage', 'vaultAddress', 'subAccountAddress')
2754
2834
  vaultAddress = self.format_vault_address(vaultAddress)
2755
2835
  signature = self.sign_l1_action(updateAction, nonce, vaultAddress)
2756
2836
  request: dict = {
@@ -2782,6 +2862,8 @@ class hyperliquid(Exchange, ImplicitAPI):
2782
2862
  :param str symbol: unified market symbol
2783
2863
  :param float amount: amount of margin to add
2784
2864
  :param dict [params]: extra parameters specific to the exchange API endpoint
2865
+ :param str [params.vaultAddress]: the vault address
2866
+ :param str [params.subAccountAddress]: sub account user address
2785
2867
  :returns dict: a `margin structure <https://docs.ccxt.com/#/?id=add-margin-structure>`
2786
2868
  """
2787
2869
  return await self.modify_margin_helper(symbol, amount, 'add', params)
@@ -2795,6 +2877,8 @@ class hyperliquid(Exchange, ImplicitAPI):
2795
2877
  :param str symbol: unified market symbol
2796
2878
  :param float amount: the amount of margin to remove
2797
2879
  :param dict [params]: extra parameters specific to the exchange API endpoint
2880
+ :param str [params.vaultAddress]: the vault address
2881
+ :param str [params.subAccountAddress]: sub account user address
2798
2882
  :returns dict: a `margin structure <https://docs.ccxt.com/#/?id=reduce-margin-structure>`
2799
2883
  """
2800
2884
  return await self.modify_margin_helper(symbol, amount, 'reduce', params)
@@ -2814,7 +2898,7 @@ class hyperliquid(Exchange, ImplicitAPI):
2814
2898
  'ntli': sz,
2815
2899
  }
2816
2900
  vaultAddress = None
2817
- vaultAddress, params = self.handle_option_and_params(params, 'modifyMargin', 'vaultAddress')
2901
+ vaultAddress, params = self.handle_option_and_params_2(params, 'modifyMargin', 'vaultAddress', 'subAccountAddress')
2818
2902
  vaultAddress = self.format_vault_address(vaultAddress)
2819
2903
  signature = self.sign_l1_action(updateAction, nonce, vaultAddress)
2820
2904
  request: dict = {
@@ -2909,28 +2993,31 @@ class hyperliquid(Exchange, ImplicitAPI):
2909
2993
  transferRequest['vaultAddress'] = vaultAddress
2910
2994
  transferResponse = await self.privatePostExchange(transferRequest)
2911
2995
  return transferResponse
2912
- # handle sub-account/different account transfer
2913
- self.check_address(toAccount)
2996
+ # transfer between main account and subaccount
2914
2997
  if code is not None:
2915
2998
  code = code.upper()
2916
2999
  if code != 'USDC':
2917
3000
  raise NotSupported(self.id + ' transfer() only support USDC')
2918
- payload: dict = {
2919
- 'hyperliquidChain': 'Testnet' if isSandboxMode else 'Mainnet',
2920
- 'destination': toAccount,
2921
- 'amount': self.number_to_string(amount),
2922
- 'time': nonce,
3001
+ isDeposit = False
3002
+ subAccountAddress = None
3003
+ if fromAccount == 'main':
3004
+ subAccountAddress = toAccount
3005
+ isDeposit = True
3006
+ elif toAccount == 'main':
3007
+ subAccountAddress = fromAccount
3008
+ else:
3009
+ raise NotSupported(self.id + ' transfer() only support main <> subaccount transfer')
3010
+ self.check_address(subAccountAddress)
3011
+ usd = self.parse_to_int(Precise.string_mul(self.number_to_string(amount), '1000000'))
3012
+ action = {
3013
+ 'type': 'subAccountTransfer',
3014
+ 'subAccountUser': subAccountAddress,
3015
+ 'isDeposit': isDeposit,
3016
+ 'usd': usd,
2923
3017
  }
2924
- sig = self.build_usd_send_sig(payload)
3018
+ sig = self.sign_l1_action(action, nonce)
2925
3019
  request: dict = {
2926
- 'action': {
2927
- 'hyperliquidChain': payload['hyperliquidChain'],
2928
- 'signatureChainId': '0x66eee', # check self out
2929
- 'destination': toAccount,
2930
- 'amount': str(amount),
2931
- 'time': nonce,
2932
- 'type': 'usdSend',
2933
- },
3020
+ 'action': action,
2934
3021
  'nonce': nonce,
2935
3022
  'signature': sig,
2936
3023
  }
@@ -3075,6 +3162,7 @@ class hyperliquid(Exchange, ImplicitAPI):
3075
3162
  :param str symbol: unified market symbol
3076
3163
  :param dict [params]: extra parameters specific to the exchange API endpoint
3077
3164
  :param str [params.user]: user address, will default to self.walletAddress if not provided
3165
+ :param str [params.subAccountAddress]: sub account user address
3078
3166
  :returns dict: a `fee structure <https://docs.ccxt.com/#/?id=fee-structure>`
3079
3167
  """
3080
3168
  await self.load_markets()
@@ -3181,6 +3269,7 @@ class hyperliquid(Exchange, ImplicitAPI):
3181
3269
  :param int [limit]: max number of ledger entries to return
3182
3270
  :param dict [params]: extra parameters specific to the exchange API endpoint
3183
3271
  :param int [params.until]: timestamp in ms of the latest ledger entry
3272
+ :param str [params.subAccountAddress]: sub account user address
3184
3273
  :returns dict: a `ledger structure <https://docs.ccxt.com/#/?id=ledger>`
3185
3274
  """
3186
3275
  await self.load_markets()
@@ -3268,6 +3357,7 @@ class hyperliquid(Exchange, ImplicitAPI):
3268
3357
  :param int [limit]: the maximum number of deposits structures to retrieve
3269
3358
  :param dict [params]: extra parameters specific to the exchange API endpoint
3270
3359
  :param int [params.until]: the latest time in ms to fetch withdrawals for
3360
+ :param str [params.subAccountAddress]: sub account user address
3271
3361
  :returns dict[]: a list of `transaction structures <https://docs.ccxt.com/#/?id=transaction-structure>`
3272
3362
  """
3273
3363
  await self.load_markets()
@@ -3309,6 +3399,7 @@ class hyperliquid(Exchange, ImplicitAPI):
3309
3399
  :param int [limit]: the maximum number of withdrawals structures to retrieve
3310
3400
  :param dict [params]: extra parameters specific to the exchange API endpoint
3311
3401
  :param int [params.until]: the latest time in ms to fetch withdrawals for
3402
+ :param str [params.subAccountAddress]: sub account user address
3312
3403
  :returns dict[]: a list of `transaction structures <https://docs.ccxt.com/#/?id=transaction-structure>`
3313
3404
  """
3314
3405
  await self.load_markets()
@@ -3406,6 +3497,7 @@ class hyperliquid(Exchange, ImplicitAPI):
3406
3497
  :param int [since]: the earliest time in ms to fetch funding history for
3407
3498
  :param int [limit]: the maximum number of funding history structures to retrieve
3408
3499
  :param dict [params]: extra parameters specific to the exchange API endpoint
3500
+ :param str [params.subAccountAddress]: sub account user address
3409
3501
  :returns dict: a `funding history structure <https://docs.ccxt.com/#/?id=funding-history-structure>`
3410
3502
  """
3411
3503
  await self.load_markets()
@@ -3496,7 +3588,7 @@ class hyperliquid(Exchange, ImplicitAPI):
3496
3588
 
3497
3589
  def handle_public_address(self, methodName: str, params: dict):
3498
3590
  userAux = None
3499
- userAux, params = self.handle_option_and_params(params, methodName, 'user')
3591
+ userAux, params = self.handle_option_and_params_2(params, methodName, 'user', 'subAccountAddress')
3500
3592
  user = userAux
3501
3593
  user, params = self.handle_option_and_params(params, methodName, 'address', userAux)
3502
3594
  if (user is not None) and (user != ''):
@@ -3563,7 +3655,7 @@ class hyperliquid(Exchange, ImplicitAPI):
3563
3655
  def parse_create_edit_order_args(self, id: Str, symbol: str, type: OrderType, side: OrderSide, amount: float, price: Num = None, params={}):
3564
3656
  market = self.market(symbol)
3565
3657
  vaultAddress = None
3566
- vaultAddress, params = self.handle_option_and_params(params, 'createOrder', 'vaultAddress')
3658
+ vaultAddress, params = self.handle_option_and_params_2(params, 'createOrder', 'vaultAddress', 'subAccountAddress')
3567
3659
  vaultAddress = self.format_vault_address(vaultAddress)
3568
3660
  symbol = market['symbol']
3569
3661
  order = {
ccxt/async_support/okx.py CHANGED
@@ -2557,11 +2557,11 @@ class okx(Exchange, ImplicitAPI):
2557
2557
  # it may be incorrect to use total, free and used for swap accounts
2558
2558
  eq = self.safe_string(balance, 'eq')
2559
2559
  availEq = self.safe_string(balance, 'availEq')
2560
- if (eq is None) or (availEq is None):
2560
+ account['total'] = eq
2561
+ if availEq is None:
2561
2562
  account['free'] = self.safe_string(balance, 'availBal')
2562
2563
  account['used'] = self.safe_string(balance, 'frozenBal')
2563
2564
  else:
2564
- account['total'] = eq
2565
2565
  account['free'] = availEq
2566
2566
  result[code] = account
2567
2567
  result['timestamp'] = timestamp
@@ -2959,7 +2959,8 @@ class okx(Exchange, ImplicitAPI):
2959
2959
  stopLossTriggerPrice = self.safe_value_n(stopLoss, ['triggerPrice', 'stopPrice', 'slTriggerPx'])
2960
2960
  if stopLossTriggerPrice is None:
2961
2961
  raise InvalidOrder(self.id + ' createOrder() requires a trigger price in params["stopLoss"]["triggerPrice"], or params["stopLoss"]["stopPrice"], or params["stopLoss"]["slTriggerPx"] for a stop loss order')
2962
- request['slTriggerPx'] = self.price_to_precision(symbol, stopLossTriggerPrice)
2962
+ slTriggerPx = self.price_to_precision(symbol, stopLossTriggerPrice)
2963
+ request['slTriggerPx'] = slTriggerPx
2963
2964
  stopLossLimitPrice = self.safe_value_n(stopLoss, ['price', 'stopLossPrice', 'slOrdPx'])
2964
2965
  stopLossOrderType = self.safe_string(stopLoss, 'type')
2965
2966
  if stopLossOrderType is not None:
@@ -3025,6 +3026,12 @@ class okx(Exchange, ImplicitAPI):
3025
3026
  # tpOrdKind is 'condition' which is the default
3026
3027
  if twoWayCondition:
3027
3028
  request['ordType'] = 'oco'
3029
+ if side == 'sell':
3030
+ request = self.omit(request, 'tgtCcy')
3031
+ if self.safe_string(request, 'tdMode') == 'cash':
3032
+ # for some reason tdMode = cash throws
3033
+ # {"code":"1","data":[{"algoClOrdId":"","algoId":"","clOrdId":"","sCode":"51000","sMsg":"Parameter tdMode error ","tag":""}],"msg":""}
3034
+ request['tdMode'] = marginMode
3028
3035
  if takeProfitPrice is not None:
3029
3036
  request['tpTriggerPx'] = self.price_to_precision(symbol, takeProfitPrice)
3030
3037
  tpOrdPxReq = '-1'
@@ -2215,11 +2215,21 @@ class wavesexchange(Exchange, ImplicitAPI):
2215
2215
  order1 = self.safe_value(data, 'order1')
2216
2216
  order2 = self.safe_value(data, 'order2')
2217
2217
  order = None
2218
- # order2 arrived after order1
2218
+ # at first, detect if response is from `fetch_my_trades`
2219
2219
  if self.safe_string(order1, 'senderPublicKey') == self.apiKey:
2220
2220
  order = order1
2221
- else:
2221
+ elif self.safe_string(order2, 'senderPublicKey') == self.apiKey:
2222
2222
  order = order2
2223
+ else:
2224
+ # response is from `fetch_trades`, so find only taker order
2225
+ date1 = self.safe_string(order1, 'timestamp')
2226
+ date2 = self.safe_string(order2, 'timestamp')
2227
+ ts1 = self.parse8601(date1)
2228
+ ts2 = self.parse8601(date2)
2229
+ if ts1 > ts2:
2230
+ order = order1
2231
+ else:
2232
+ order = order2
2223
2233
  symbol = None
2224
2234
  assetPair = self.safe_value(order, 'assetPair')
2225
2235
  if assetPair is not None: