ccxt 4.4.88__py2.py3-none-any.whl → 4.4.91__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 (101) hide show
  1. ccxt/__init__.py +1 -3
  2. ccxt/abstract/bitget.py +58 -0
  3. ccxt/abstract/bitrue.py +65 -65
  4. ccxt/abstract/cryptocom.py +2 -0
  5. ccxt/abstract/luno.py +1 -0
  6. ccxt/async_support/__init__.py +1 -3
  7. ccxt/async_support/base/exchange.py +6 -3
  8. ccxt/async_support/base/ws/client.py +173 -64
  9. ccxt/async_support/base/ws/future.py +23 -50
  10. ccxt/async_support/binance.py +2 -2
  11. ccxt/async_support/bingx.py +55 -29
  12. ccxt/async_support/bitget.py +469 -147
  13. ccxt/async_support/bitmex.py +2 -1
  14. ccxt/async_support/bitrue.py +72 -66
  15. ccxt/async_support/bitvavo.py +34 -0
  16. ccxt/async_support/btcalpha.py +35 -0
  17. ccxt/async_support/btcbox.py +35 -0
  18. ccxt/async_support/btcmarkets.py +35 -0
  19. ccxt/async_support/btcturk.py +35 -0
  20. ccxt/async_support/bybit.py +9 -3
  21. ccxt/async_support/cex.py +61 -0
  22. ccxt/async_support/coinbase.py +1 -3
  23. ccxt/async_support/cryptocom.py +66 -2
  24. ccxt/async_support/cryptomus.py +1 -1
  25. ccxt/async_support/delta.py +2 -2
  26. ccxt/async_support/digifinex.py +39 -99
  27. ccxt/async_support/exmo.py +14 -7
  28. ccxt/async_support/gate.py +14 -7
  29. ccxt/async_support/hashkey.py +15 -28
  30. ccxt/async_support/hollaex.py +27 -22
  31. ccxt/async_support/hyperliquid.py +104 -53
  32. ccxt/async_support/kraken.py +54 -50
  33. ccxt/async_support/luno.py +87 -1
  34. ccxt/async_support/mexc.py +1 -0
  35. ccxt/async_support/modetrade.py +2 -2
  36. ccxt/async_support/okx.py +2 -1
  37. ccxt/async_support/paradex.py +1 -1
  38. ccxt/async_support/phemex.py +16 -8
  39. ccxt/async_support/tradeogre.py +3 -3
  40. ccxt/async_support/xt.py +1 -1
  41. ccxt/base/exchange.py +20 -8
  42. ccxt/binance.py +2 -2
  43. ccxt/bingx.py +55 -29
  44. ccxt/bitget.py +469 -147
  45. ccxt/bitmex.py +2 -1
  46. ccxt/bitrue.py +72 -66
  47. ccxt/bitvavo.py +34 -0
  48. ccxt/btcalpha.py +35 -0
  49. ccxt/btcbox.py +35 -0
  50. ccxt/btcmarkets.py +35 -0
  51. ccxt/btcturk.py +35 -0
  52. ccxt/bybit.py +9 -3
  53. ccxt/cex.py +61 -0
  54. ccxt/coinbase.py +1 -3
  55. ccxt/cryptocom.py +66 -2
  56. ccxt/cryptomus.py +1 -1
  57. ccxt/delta.py +2 -2
  58. ccxt/digifinex.py +39 -99
  59. ccxt/exmo.py +13 -7
  60. ccxt/gate.py +14 -7
  61. ccxt/hashkey.py +15 -28
  62. ccxt/hollaex.py +27 -22
  63. ccxt/hyperliquid.py +104 -53
  64. ccxt/kraken.py +53 -50
  65. ccxt/luno.py +87 -1
  66. ccxt/mexc.py +1 -0
  67. ccxt/modetrade.py +2 -2
  68. ccxt/okx.py +2 -1
  69. ccxt/paradex.py +1 -1
  70. ccxt/phemex.py +16 -8
  71. ccxt/pro/__init__.py +1 -127
  72. ccxt/pro/bitstamp.py +1 -1
  73. ccxt/pro/bybit.py +6 -136
  74. ccxt/pro/coinbase.py +2 -0
  75. ccxt/pro/cryptocom.py +27 -0
  76. ccxt/pro/kraken.py +249 -267
  77. ccxt/pro/mexc.py +0 -1
  78. ccxt/tradeogre.py +3 -3
  79. ccxt/xt.py +1 -1
  80. {ccxt-4.4.88.dist-info → ccxt-4.4.91.dist-info}/METADATA +64 -23
  81. {ccxt-4.4.88.dist-info → ccxt-4.4.91.dist-info}/RECORD +84 -101
  82. ccxt/abstract/coinlist.py +0 -57
  83. ccxt/async_support/base/ws/aiohttp_client.py +0 -147
  84. ccxt/async_support/bitcoincom.py +0 -18
  85. ccxt/async_support/bitfinex1.py +0 -1711
  86. ccxt/async_support/bitpanda.py +0 -17
  87. ccxt/async_support/coinlist.py +0 -2542
  88. ccxt/async_support/poloniexfutures.py +0 -1875
  89. ccxt/bitcoincom.py +0 -18
  90. ccxt/bitfinex1.py +0 -1710
  91. ccxt/bitpanda.py +0 -17
  92. ccxt/coinlist.py +0 -2542
  93. ccxt/poloniexfutures.py +0 -1875
  94. ccxt/pro/bitcoincom.py +0 -35
  95. ccxt/pro/bitfinex1.py +0 -635
  96. ccxt/pro/bitpanda.py +0 -16
  97. ccxt/pro/poloniexfutures.py +0 -1004
  98. ccxt/pro/wazirx.py +0 -766
  99. {ccxt-4.4.88.dist-info → ccxt-4.4.91.dist-info}/LICENSE.txt +0 -0
  100. {ccxt-4.4.88.dist-info → ccxt-4.4.91.dist-info}/WHEEL +0 -0
  101. {ccxt-4.4.88.dist-info → ccxt-4.4.91.dist-info}/top_level.txt +0 -0
ccxt/gate.py CHANGED
@@ -1241,7 +1241,7 @@ class gate(Exchange, ImplicitAPI):
1241
1241
  self.fetch_option_markets(params),
1242
1242
  ]
1243
1243
  if not sandboxMode:
1244
- # gate does not have a sandbox for spot markets
1244
+ # gate doesn't have a sandbox for spot markets
1245
1245
  mainnetOnly = [self.fetch_spot_markets(params)]
1246
1246
  rawPromises = self.array_concat(rawPromises, mainnetOnly)
1247
1247
  promises = rawPromises
@@ -1263,17 +1263,21 @@ class gate(Exchange, ImplicitAPI):
1263
1263
  # {
1264
1264
  # "id": "QTUM_ETH",
1265
1265
  # "base": "QTUM",
1266
+ # "base_name": "Quantum",
1266
1267
  # "quote": "ETH",
1268
+ # "quote_name": "Ethereum",
1267
1269
  # "fee": "0.2",
1268
1270
  # "min_base_amount": "0.01",
1269
1271
  # "min_quote_amount": "0.001",
1272
+ # "max_quote_amount": "50000",
1270
1273
  # "amount_precision": 3,
1271
1274
  # "precision": 6,
1272
1275
  # "trade_status": "tradable",
1273
- # "sell_start": 0,
1274
- # "buy_start": 0
1276
+ # "sell_start": 1607313600,
1277
+ # "buy_start": 1700492400,
1278
+ # "type": "normal",
1279
+ # "trade_url": "https://www.gate.io/trade/QTUM_ETH",
1275
1280
  # }
1276
- # ]
1277
1281
  #
1278
1282
  # Margin
1279
1283
  #
@@ -1304,6 +1308,8 @@ class gate(Exchange, ImplicitAPI):
1304
1308
  tradeStatus = self.safe_string(market, 'trade_status')
1305
1309
  leverage = self.safe_number(market, 'leverage')
1306
1310
  margin = leverage is not None
1311
+ buyStart = self.safe_integer_product(spotMarket, 'buy_start', 1000) # buy_start is the trading start time, while sell_start is offline orders start time
1312
+ createdTs = buyStart if (buyStart != 0) else None
1307
1313
  result.append({
1308
1314
  'id': id,
1309
1315
  'symbol': base + '/' + quote,
@@ -1353,7 +1359,7 @@ class gate(Exchange, ImplicitAPI):
1353
1359
  'max': self.safe_number(market, 'max_quote_amount') if margin else None,
1354
1360
  },
1355
1361
  },
1356
- 'created': None,
1362
+ 'created': createdTs,
1357
1363
  'info': market,
1358
1364
  })
1359
1365
  return result
@@ -1418,6 +1424,7 @@ class gate(Exchange, ImplicitAPI):
1418
1424
  # "funding_next_apply": 1610035200,
1419
1425
  # "short_users": 977,
1420
1426
  # "config_change_time": 1609899548,
1427
+ # "create_time": 1609800048,
1421
1428
  # "trade_size": 28530850594,
1422
1429
  # "position_size": 5223816,
1423
1430
  # "long_users": 455,
@@ -1548,7 +1555,7 @@ class gate(Exchange, ImplicitAPI):
1548
1555
  'max': None,
1549
1556
  },
1550
1557
  },
1551
- 'created': None,
1558
+ 'created': self.safe_integer_product(market, 'create_time', 1000),
1552
1559
  'info': market,
1553
1560
  }
1554
1561
 
@@ -1645,7 +1652,7 @@ class gate(Exchange, ImplicitAPI):
1645
1652
  'contractSize': self.parse_number('1'),
1646
1653
  'expiry': expiry,
1647
1654
  'expiryDatetime': self.iso8601(expiry),
1648
- 'strike': strike,
1655
+ 'strike': self.parse_number(strike),
1649
1656
  'optionType': optionType,
1650
1657
  'precision': {
1651
1658
  'amount': self.parse_number('1'), # all options have self step size
ccxt/hashkey.py CHANGED
@@ -1151,47 +1151,43 @@ class hashkey(Exchange, ImplicitAPI):
1151
1151
  currecy = coins[i]
1152
1152
  currencyId = self.safe_string(currecy, 'coinId')
1153
1153
  code = self.safe_currency_code(currencyId)
1154
- allowWithdraw = self.safe_bool(currecy, 'allowWithdraw')
1155
- allowDeposit = self.safe_bool(currecy, 'allowDeposit')
1156
1154
  networks = self.safe_list(currecy, 'chainTypes')
1157
- networksById = self.safe_dict(self.options, 'networksById')
1158
1155
  parsedNetworks: dict = {}
1159
1156
  for j in range(0, len(networks)):
1160
1157
  network = networks[j]
1161
1158
  networkId = self.safe_string(network, 'chainType')
1162
- networkName = self.safe_string(networksById, networkId, networkId)
1163
- maxWithdrawQuantity = self.omit_zero(self.safe_string(network, 'maxWithdrawQuantity'))
1164
- networkDeposit = self.safe_bool(network, 'allowDeposit')
1165
- networkWithdraw = self.safe_bool(network, 'allowWithdraw')
1166
- parsedNetworks[networkName] = {
1159
+ networkCode = self.network_code_to_id(networkId)
1160
+ parsedNetworks[networkCode] = {
1167
1161
  'id': networkId,
1168
- 'network': networkName,
1162
+ 'network': networkCode,
1169
1163
  'limits': {
1170
1164
  'withdraw': {
1171
1165
  'min': self.safe_number(network, 'minWithdrawQuantity'),
1172
- 'max': self.parse_number(maxWithdrawQuantity),
1166
+ 'max': self.parse_number(self.omit_zero(self.safe_string(network, 'maxWithdrawQuantity'))),
1173
1167
  },
1174
1168
  'deposit': {
1175
1169
  'min': self.safe_number(network, 'minDepositQuantity'),
1176
1170
  'max': None,
1177
1171
  },
1178
1172
  },
1179
- 'active': networkDeposit and networkWithdraw,
1180
- 'deposit': networkDeposit,
1181
- 'withdraw': networkWithdraw,
1173
+ 'active': None,
1174
+ 'deposit': self.safe_bool(network, 'allowDeposit'),
1175
+ 'withdraw': self.safe_bool(network, 'allowWithdraw'),
1182
1176
  'fee': self.safe_number(network, 'withdrawFee'),
1183
1177
  'precision': None,
1184
1178
  'info': network,
1185
1179
  }
1186
- result[code] = {
1180
+ rawType = self.safe_string(currecy, 'tokenType')
1181
+ type = 'fiat' if (rawType == 'REAL_MONEY') else 'crypto'
1182
+ result[code] = self.safe_currency_structure({
1187
1183
  'id': currencyId,
1188
1184
  'code': code,
1189
1185
  'precision': None,
1190
- 'type': self.parse_currency_type(self.safe_string(currecy, 'tokenType')),
1186
+ 'type': type,
1191
1187
  'name': self.safe_string(currecy, 'coinFullName'),
1192
- 'active': allowWithdraw and allowDeposit,
1193
- 'deposit': allowDeposit,
1194
- 'withdraw': allowWithdraw,
1188
+ 'active': None,
1189
+ 'deposit': self.safe_bool(currecy, 'allowDeposit'),
1190
+ 'withdraw': self.safe_bool(currecy, 'allowWithdraw'),
1195
1191
  'fee': None,
1196
1192
  'limits': {
1197
1193
  'deposit': {
@@ -1205,18 +1201,9 @@ class hashkey(Exchange, ImplicitAPI):
1205
1201
  },
1206
1202
  'networks': parsedNetworks,
1207
1203
  'info': currecy,
1208
- }
1204
+ })
1209
1205
  return result
1210
1206
 
1211
- def parse_currency_type(self, type):
1212
- types = {
1213
- 'CHAIN_TOKEN': 'crypto',
1214
- 'ERC20_TOKEN': 'crypto',
1215
- 'BSC_TOKEN': 'crypto',
1216
- 'REAL_MONEY': 'fiat',
1217
- }
1218
- return self.safe_string(types, type)
1219
-
1220
1207
  def fetch_order_book(self, symbol: str, limit: Int = None, params={}) -> OrderBook:
1221
1208
  """
1222
1209
  fetches information on open orders with bid(buy) and ask(sell) prices, volumes and other data
ccxt/hollaex.py CHANGED
@@ -15,6 +15,7 @@ from ccxt.base.errors import InsufficientFunds
15
15
  from ccxt.base.errors import OrderNotFound
16
16
  from ccxt.base.errors import OrderImmediatelyFillable
17
17
  from ccxt.base.errors import NetworkError
18
+ from ccxt.base.errors import InvalidNonce
18
19
  from ccxt.base.decimal_to_precision import TICK_SIZE
19
20
  from ccxt.base.precise import Precise
20
21
 
@@ -262,6 +263,7 @@ class hollaex(Exchange, ImplicitAPI):
262
263
  },
263
264
  'exceptions': {
264
265
  'broad': {
266
+ 'API request is expired': InvalidNonce,
265
267
  'Invalid token': AuthenticationError,
266
268
  'Order not found': OrderNotFound,
267
269
  'Insufficient balance': InsufficientFunds,
@@ -796,7 +798,8 @@ class hollaex(Exchange, ImplicitAPI):
796
798
  # "price":0.147411,
797
799
  # "timestamp":"2022-01-26T17:53:34.650Z",
798
800
  # "order_id":"cba78ecb-4187-4da2-9d2f-c259aa693b5a",
799
- # "fee":0.01031877,"fee_coin":"usdt"
801
+ # "fee":0.01031877,
802
+ # "fee_coin":"usdt"
800
803
  # }
801
804
  #
802
805
  marketId = self.safe_string(trade, 'symbol')
@@ -809,11 +812,12 @@ class hollaex(Exchange, ImplicitAPI):
809
812
  priceString = self.safe_string(trade, 'price')
810
813
  amountString = self.safe_string(trade, 'size')
811
814
  feeCostString = self.safe_string(trade, 'fee')
815
+ feeCoin = self.safe_string(trade, 'fee_coin')
812
816
  fee = None
813
817
  if feeCostString is not None:
814
818
  fee = {
815
819
  'cost': feeCostString,
816
- 'currency': market['quote'],
820
+ 'currency': self.safe_currency_code(feeCoin),
817
821
  }
818
822
  return self.safe_trade({
819
823
  'info': trade,
@@ -899,7 +903,7 @@ class hollaex(Exchange, ImplicitAPI):
899
903
  :param str symbol: unified symbol of the market to fetch OHLCV data for
900
904
  :param str timeframe: the length of time each candle represents
901
905
  :param int [since]: timestamp in ms of the earliest candle to fetch
902
- :param int [limit]: the maximum amount of candles to fetch
906
+ :param int [limit]: the maximum amount of candles to fetch(max 500)
903
907
  :param dict [params]: extra parameters specific to the exchange API endpoint
904
908
  :param int [params.until]: timestamp in ms of the latest candle to fetch
905
909
  :returns int[][]: A list of candles ordered, open, high, low, close, volume
@@ -910,16 +914,24 @@ class hollaex(Exchange, ImplicitAPI):
910
914
  'symbol': market['id'],
911
915
  'resolution': self.safe_string(self.timeframes, timeframe, timeframe),
912
916
  }
917
+ paginate = False
918
+ maxLimit = 500
919
+ paginate, params = self.handle_option_and_params(params, 'fetchOHLCV', 'paginate', paginate)
920
+ if paginate:
921
+ return self.fetch_paginated_call_deterministic('fetchOHLCV', symbol, since, limit, timeframe, params, maxLimit)
913
922
  until = self.safe_integer(params, 'until')
914
- end = self.seconds()
915
- if until is not None:
916
- end = self.parse_to_int(until / 1000)
917
- defaultSpan = 2592000 # 30 days
918
- if since is not None:
919
- request['from'] = self.parse_to_int(since / 1000)
920
- else:
921
- request['from'] = end - defaultSpan
922
- request['to'] = end
923
+ timeDelta = self.parse_timeframe(timeframe) * maxLimit * 1000
924
+ start = since
925
+ now = self.milliseconds()
926
+ if until is None and start is None:
927
+ until = now
928
+ start = until - timeDelta
929
+ elif until is None:
930
+ until = now # the exchange has not a lot of trades, so if we count until by limit and limit is small, it may return empty result
931
+ elif start is None:
932
+ start = until - timeDelta
933
+ request['from'] = self.parse_to_int(start / 1000) # convert to seconds
934
+ request['to'] = self.parse_to_int(until / 1000) # convert to seconds
923
935
  params = self.omit(params, 'until')
924
936
  response = self.publicGetChart(self.extend(request, params))
925
937
  #
@@ -1277,11 +1289,10 @@ class hollaex(Exchange, ImplicitAPI):
1277
1289
  """
1278
1290
  self.load_markets()
1279
1291
  market = self.market(symbol)
1280
- convertedAmount = float(self.amount_to_precision(symbol, amount))
1281
1292
  request: dict = {
1282
1293
  'symbol': market['id'],
1283
1294
  'side': side,
1284
- 'size': self.normalize_number_if_needed(convertedAmount),
1295
+ 'size': self.amount_to_precision(symbol, amount),
1285
1296
  'type': type,
1286
1297
  # 'stop': float(self.price_to_precision(symbol, stopPrice)),
1287
1298
  # 'meta': {}, # other options such
@@ -1292,10 +1303,9 @@ class hollaex(Exchange, ImplicitAPI):
1292
1303
  isMarketOrder = type == 'market'
1293
1304
  postOnly = self.is_post_only(isMarketOrder, exchangeSpecificParam, params)
1294
1305
  if not isMarketOrder:
1295
- convertedPrice = float(self.price_to_precision(symbol, price))
1296
- request['price'] = self.normalize_number_if_needed(convertedPrice)
1306
+ request['price'] = self.price_to_precision(symbol, price)
1297
1307
  if triggerPrice is not None:
1298
- request['stop'] = self.normalize_number_if_needed(float(self.price_to_precision(symbol, triggerPrice)))
1308
+ request['stop'] = self.price_to_precision(symbol, triggerPrice)
1299
1309
  if postOnly:
1300
1310
  request['meta'] = {'post_only': True}
1301
1311
  params = self.omit(params, ['postOnly', 'timeInForce', 'stopPrice', 'triggerPrice', 'stop'])
@@ -1943,11 +1953,6 @@ class hollaex(Exchange, ImplicitAPI):
1943
1953
  coins = self.safe_dict(response, 'coins', {})
1944
1954
  return self.parse_deposit_withdraw_fees(coins, codes, 'symbol')
1945
1955
 
1946
- def normalize_number_if_needed(self, number):
1947
- if self.is_round_number(number):
1948
- number = int(number)
1949
- return number
1950
-
1951
1956
  def sign(self, path, api='public', method='GET', params={}, headers=None, body=None):
1952
1957
  query = self.omit(params, self.extract_params(path))
1953
1958
  path = '/' + self.version + '/' + self.implode_params(path, params)
ccxt/hyperliquid.py CHANGED
@@ -56,6 +56,7 @@ class hyperliquid(Exchange, ImplicitAPI):
56
56
  'createMarketSellOrderWithCost': False,
57
57
  'createOrder': True,
58
58
  'createOrders': True,
59
+ 'createOrderWithTakeProfitAndStopLoss': True,
59
60
  'createReduceOnlyOrder': True,
60
61
  'createStopOrder': True,
61
62
  'createTriggerOrder': True,
@@ -239,7 +240,16 @@ class hyperliquid(Exchange, ImplicitAPI):
239
240
  'triggerDirection': False,
240
241
  'stopLossPrice': False,
241
242
  'takeProfitPrice': False,
242
- 'attachedStopLossTakeProfit': None,
243
+ 'attachedStopLossTakeProfit': {
244
+ 'triggerPriceType': {
245
+ 'last': False,
246
+ 'mark': False,
247
+ 'index': False,
248
+ },
249
+ 'triggerPrice': True,
250
+ 'type': True,
251
+ 'price': True,
252
+ },
243
253
  'timeInForce': {
244
254
  'IOC': True,
245
255
  'FOK': False,
@@ -1403,6 +1413,65 @@ class hyperliquid(Exchange, ImplicitAPI):
1403
1413
  statuses = self.safe_list(data, 'statuses', [])
1404
1414
  return self.parse_orders(statuses, None)
1405
1415
 
1416
+ def create_order_request(self, symbol: str, type: OrderType, side: OrderSide, amount: str, price: Str = None, params={}):
1417
+ market = self.market(symbol)
1418
+ type = type.upper()
1419
+ side = side.upper()
1420
+ isMarket = (type == 'MARKET')
1421
+ isBuy = (side == 'BUY')
1422
+ clientOrderId = self.safe_string_2(params, 'clientOrderId', 'client_id')
1423
+ slippage = self.safe_string(params, 'slippage')
1424
+ defaultTimeInForce = 'ioc' if (isMarket) else 'gtc'
1425
+ postOnly = self.safe_bool(params, 'postOnly', False)
1426
+ if postOnly:
1427
+ defaultTimeInForce = 'alo'
1428
+ timeInForce = self.safe_string_lower(params, 'timeInForce', defaultTimeInForce)
1429
+ timeInForce = self.capitalize(timeInForce)
1430
+ triggerPrice = self.safe_string_2(params, 'triggerPrice', 'stopPrice')
1431
+ stopLossPrice = self.safe_string(params, 'stopLossPrice', triggerPrice)
1432
+ takeProfitPrice = self.safe_string(params, 'takeProfitPrice')
1433
+ isTrigger = (stopLossPrice or takeProfitPrice)
1434
+ px = None
1435
+ if isMarket:
1436
+ if price is None:
1437
+ raise ArgumentsRequired(self.id + ' market orders require price to calculate the max slippage price. Default slippage can be set in options(default is 5%).')
1438
+ px = Precise.string_mul(price, Precise.string_add('1', slippage)) if (isBuy) else Precise.string_mul(price, Precise.string_sub('1', slippage))
1439
+ px = self.price_to_precision(symbol, px) # round after adding slippage
1440
+ else:
1441
+ px = self.price_to_precision(symbol, price)
1442
+ sz = self.amount_to_precision(symbol, amount)
1443
+ reduceOnly = self.safe_bool(params, 'reduceOnly', False)
1444
+ orderType: dict = {}
1445
+ if isTrigger:
1446
+ isTp = False
1447
+ if takeProfitPrice is not None:
1448
+ triggerPrice = self.price_to_precision(symbol, takeProfitPrice)
1449
+ isTp = True
1450
+ else:
1451
+ triggerPrice = self.price_to_precision(symbol, stopLossPrice)
1452
+ orderType['trigger'] = {
1453
+ 'isMarket': isMarket,
1454
+ 'triggerPx': triggerPrice,
1455
+ 'tpsl': 'tp' if (isTp) else 'sl',
1456
+ }
1457
+ else:
1458
+ orderType['limit'] = {
1459
+ 'tif': timeInForce,
1460
+ }
1461
+ params = self.omit(params, ['clientOrderId', 'slippage', 'triggerPrice', 'stopPrice', 'stopLossPrice', 'takeProfitPrice', 'timeInForce', 'client_id', 'reduceOnly', 'postOnly'])
1462
+ orderObj: dict = {
1463
+ 'a': self.parse_to_int(market['baseId']),
1464
+ 'b': isBuy,
1465
+ 'p': px,
1466
+ 's': sz,
1467
+ 'r': reduceOnly,
1468
+ 't': orderType,
1469
+ # 'c': clientOrderId,
1470
+ }
1471
+ if clientOrderId is not None:
1472
+ orderObj['c'] = clientOrderId
1473
+ return orderObj
1474
+
1406
1475
  def create_orders_request(self, orders, params={}) -> dict:
1407
1476
  """
1408
1477
  create a list of trade orders
@@ -1430,77 +1499,59 @@ class hyperliquid(Exchange, ImplicitAPI):
1430
1499
  params = self.omit(params, ['slippage', 'clientOrderId', 'client_id', 'slippage', 'triggerPrice', 'stopPrice', 'stopLossPrice', 'takeProfitPrice', 'timeInForce'])
1431
1500
  nonce = self.milliseconds()
1432
1501
  orderReq = []
1502
+ grouping = 'na'
1433
1503
  for i in range(0, len(orders)):
1434
1504
  rawOrder = orders[i]
1435
1505
  marketId = self.safe_string(rawOrder, 'symbol')
1436
1506
  market = self.market(marketId)
1437
1507
  symbol = market['symbol']
1438
1508
  type = self.safe_string_upper(rawOrder, 'type')
1439
- isMarket = (type == 'MARKET')
1440
1509
  side = self.safe_string_upper(rawOrder, 'side')
1441
- isBuy = (side == 'BUY')
1442
1510
  amount = self.safe_string(rawOrder, 'amount')
1443
1511
  price = self.safe_string(rawOrder, 'price')
1444
1512
  orderParams = self.safe_dict(rawOrder, 'params', {})
1445
- clientOrderId = self.safe_string_2(orderParams, 'clientOrderId', 'client_id')
1446
1513
  slippage = self.safe_string(orderParams, 'slippage', defaultSlippage)
1447
- defaultTimeInForce = 'ioc' if (isMarket) else 'gtc'
1448
- postOnly = self.safe_bool(orderParams, 'postOnly', False)
1449
- if postOnly:
1450
- defaultTimeInForce = 'alo'
1451
- timeInForce = self.safe_string_lower(orderParams, 'timeInForce', defaultTimeInForce)
1452
- timeInForce = self.capitalize(timeInForce)
1453
- triggerPrice = self.safe_string_2(orderParams, 'triggerPrice', 'stopPrice')
1454
- stopLossPrice = self.safe_string(orderParams, 'stopLossPrice', triggerPrice)
1455
- takeProfitPrice = self.safe_string(orderParams, 'takeProfitPrice')
1456
- isTrigger = (stopLossPrice or takeProfitPrice)
1457
- px = None
1458
- if isMarket:
1459
- if price is None:
1460
- raise ArgumentsRequired(self.id + ' market orders require price to calculate the max slippage price. Default slippage can be set in options(default is 5%).')
1461
- px = Precise.string_mul(price, Precise.string_add('1', slippage)) if (isBuy) else Precise.string_mul(price, Precise.string_sub('1', slippage))
1462
- px = self.price_to_precision(symbol, px) # round after adding slippage
1463
- else:
1464
- px = self.price_to_precision(symbol, price)
1465
- sz = self.amount_to_precision(symbol, amount)
1466
- reduceOnly = self.safe_bool(orderParams, 'reduceOnly', False)
1467
- orderType: dict = {}
1514
+ orderParams['slippage'] = slippage
1515
+ stopLoss = self.safe_value(orderParams, 'stopLoss')
1516
+ takeProfit = self.safe_value(orderParams, 'takeProfit')
1517
+ isTrigger = (stopLoss or takeProfit)
1518
+ orderParams = self.omit(orderParams, ['stopLoss', 'takeProfit'])
1519
+ mainOrderObj: dict = self.create_order_request(symbol, type, side, amount, price, orderParams)
1520
+ orderReq.append(mainOrderObj)
1468
1521
  if isTrigger:
1469
- isTp = False
1470
- if takeProfitPrice is not None:
1471
- triggerPrice = self.price_to_precision(symbol, takeProfitPrice)
1472
- isTp = True
1522
+ # grouping opposed orders for sl/tp
1523
+ stopLossOrderTriggerPrice = self.safe_string_n(stopLoss, ['triggerPrice', 'stopPrice'])
1524
+ stopLossOrderType = self.safe_string(stopLoss, 'type')
1525
+ stopLossOrderLimitPrice = self.safe_string_n(stopLoss, ['price', 'stopLossPrice'], stopLossOrderTriggerPrice)
1526
+ takeProfitOrderTriggerPrice = self.safe_string_n(takeProfit, ['triggerPrice', 'stopPrice'])
1527
+ takeProfitOrderType = self.safe_string(takeProfit, 'type')
1528
+ takeProfitOrderLimitPrice = self.safe_string_n(takeProfit, ['price', 'takeProfitPrice'], takeProfitOrderTriggerPrice)
1529
+ grouping = 'normalTpsl'
1530
+ orderParams = self.omit(orderParams, ['stopLoss', 'takeProfit'])
1531
+ triggerOrderSide = ''
1532
+ if side == 'BUY':
1533
+ triggerOrderSide = 'sell'
1473
1534
  else:
1474
- triggerPrice = self.price_to_precision(symbol, stopLossPrice)
1475
- orderType['trigger'] = {
1476
- 'isMarket': isMarket,
1477
- 'triggerPx': triggerPrice,
1478
- 'tpsl': 'tp' if (isTp) else 'sl',
1479
- }
1480
- else:
1481
- orderType['limit'] = {
1482
- 'tif': timeInForce,
1483
- }
1484
- orderParams = self.omit(orderParams, ['clientOrderId', 'slippage', 'triggerPrice', 'stopPrice', 'stopLossPrice', 'takeProfitPrice', 'timeInForce', 'client_id', 'reduceOnly', 'postOnly'])
1485
- orderObj: dict = {
1486
- 'a': self.parse_to_int(market['baseId']),
1487
- 'b': isBuy,
1488
- 'p': px,
1489
- 's': sz,
1490
- 'r': reduceOnly,
1491
- 't': orderType,
1492
- # 'c': clientOrderId,
1493
- }
1494
- if clientOrderId is not None:
1495
- orderObj['c'] = clientOrderId
1496
- orderReq.append(orderObj)
1535
+ triggerOrderSide = 'buy'
1536
+ if takeProfit is not None:
1537
+ orderObj: dict = self.create_order_request(symbol, takeProfitOrderType, triggerOrderSide, amount, takeProfitOrderLimitPrice, self.extend(orderParams, {
1538
+ 'takeProfitPrice': takeProfitOrderTriggerPrice,
1539
+ 'reduceOnly': True,
1540
+ }))
1541
+ orderReq.append(orderObj)
1542
+ if stopLoss is not None:
1543
+ orderObj: dict = self.create_order_request(symbol, stopLossOrderType, triggerOrderSide, amount, stopLossOrderLimitPrice, self.extend(orderParams, {
1544
+ 'stopLossPrice': stopLossOrderTriggerPrice,
1545
+ 'reduceOnly': True,
1546
+ }))
1547
+ orderReq.append(orderObj)
1497
1548
  vaultAddress = None
1498
1549
  vaultAddress, params = self.handle_option_and_params(params, 'createOrder', 'vaultAddress')
1499
1550
  vaultAddress = self.format_vault_address(vaultAddress)
1500
1551
  orderAction: dict = {
1501
1552
  'type': 'order',
1502
1553
  'orders': orderReq,
1503
- 'grouping': 'na',
1554
+ 'grouping': grouping,
1504
1555
  # 'brokerCode': 1, # cant
1505
1556
  }
1506
1557
  if vaultAddress is None: