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
@@ -1242,7 +1242,7 @@ class gate(Exchange, ImplicitAPI):
1242
1242
  self.fetch_option_markets(params),
1243
1243
  ]
1244
1244
  if not sandboxMode:
1245
- # gate does not have a sandbox for spot markets
1245
+ # gate doesn't have a sandbox for spot markets
1246
1246
  mainnetOnly = [self.fetch_spot_markets(params)]
1247
1247
  rawPromises = self.array_concat(rawPromises, mainnetOnly)
1248
1248
  promises = await asyncio.gather(*rawPromises)
@@ -1264,17 +1264,21 @@ class gate(Exchange, ImplicitAPI):
1264
1264
  # {
1265
1265
  # "id": "QTUM_ETH",
1266
1266
  # "base": "QTUM",
1267
+ # "base_name": "Quantum",
1267
1268
  # "quote": "ETH",
1269
+ # "quote_name": "Ethereum",
1268
1270
  # "fee": "0.2",
1269
1271
  # "min_base_amount": "0.01",
1270
1272
  # "min_quote_amount": "0.001",
1273
+ # "max_quote_amount": "50000",
1271
1274
  # "amount_precision": 3,
1272
1275
  # "precision": 6,
1273
1276
  # "trade_status": "tradable",
1274
- # "sell_start": 0,
1275
- # "buy_start": 0
1277
+ # "sell_start": 1607313600,
1278
+ # "buy_start": 1700492400,
1279
+ # "type": "normal",
1280
+ # "trade_url": "https://www.gate.io/trade/QTUM_ETH",
1276
1281
  # }
1277
- # ]
1278
1282
  #
1279
1283
  # Margin
1280
1284
  #
@@ -1305,6 +1309,8 @@ class gate(Exchange, ImplicitAPI):
1305
1309
  tradeStatus = self.safe_string(market, 'trade_status')
1306
1310
  leverage = self.safe_number(market, 'leverage')
1307
1311
  margin = leverage is not None
1312
+ buyStart = self.safe_integer_product(spotMarket, 'buy_start', 1000) # buy_start is the trading start time, while sell_start is offline orders start time
1313
+ createdTs = buyStart if (buyStart != 0) else None
1308
1314
  result.append({
1309
1315
  'id': id,
1310
1316
  'symbol': base + '/' + quote,
@@ -1354,7 +1360,7 @@ class gate(Exchange, ImplicitAPI):
1354
1360
  'max': self.safe_number(market, 'max_quote_amount') if margin else None,
1355
1361
  },
1356
1362
  },
1357
- 'created': None,
1363
+ 'created': createdTs,
1358
1364
  'info': market,
1359
1365
  })
1360
1366
  return result
@@ -1419,6 +1425,7 @@ class gate(Exchange, ImplicitAPI):
1419
1425
  # "funding_next_apply": 1610035200,
1420
1426
  # "short_users": 977,
1421
1427
  # "config_change_time": 1609899548,
1428
+ # "create_time": 1609800048,
1422
1429
  # "trade_size": 28530850594,
1423
1430
  # "position_size": 5223816,
1424
1431
  # "long_users": 455,
@@ -1549,7 +1556,7 @@ class gate(Exchange, ImplicitAPI):
1549
1556
  'max': None,
1550
1557
  },
1551
1558
  },
1552
- 'created': None,
1559
+ 'created': self.safe_integer_product(market, 'create_time', 1000),
1553
1560
  'info': market,
1554
1561
  }
1555
1562
 
@@ -1646,7 +1653,7 @@ class gate(Exchange, ImplicitAPI):
1646
1653
  'contractSize': self.parse_number('1'),
1647
1654
  'expiry': expiry,
1648
1655
  'expiryDatetime': self.iso8601(expiry),
1649
- 'strike': strike,
1656
+ 'strike': self.parse_number(strike),
1650
1657
  'optionType': optionType,
1651
1658
  'precision': {
1652
1659
  'amount': self.parse_number('1'), # all options have self step size
@@ -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
  async 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
@@ -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 await 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 = await self.publicGetChart(self.extend(request, params))
925
937
  #
@@ -1277,11 +1289,10 @@ class hollaex(Exchange, ImplicitAPI):
1277
1289
  """
1278
1290
  await 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)
@@ -57,6 +57,7 @@ class hyperliquid(Exchange, ImplicitAPI):
57
57
  'createMarketSellOrderWithCost': False,
58
58
  'createOrder': True,
59
59
  'createOrders': True,
60
+ 'createOrderWithTakeProfitAndStopLoss': True,
60
61
  'createReduceOnlyOrder': True,
61
62
  'createStopOrder': True,
62
63
  'createTriggerOrder': True,
@@ -240,7 +241,16 @@ class hyperliquid(Exchange, ImplicitAPI):
240
241
  'triggerDirection': False,
241
242
  'stopLossPrice': False,
242
243
  'takeProfitPrice': False,
243
- 'attachedStopLossTakeProfit': None,
244
+ 'attachedStopLossTakeProfit': {
245
+ 'triggerPriceType': {
246
+ 'last': False,
247
+ 'mark': False,
248
+ 'index': False,
249
+ },
250
+ 'triggerPrice': True,
251
+ 'type': True,
252
+ 'price': True,
253
+ },
244
254
  'timeInForce': {
245
255
  'IOC': True,
246
256
  'FOK': False,
@@ -1404,6 +1414,65 @@ class hyperliquid(Exchange, ImplicitAPI):
1404
1414
  statuses = self.safe_list(data, 'statuses', [])
1405
1415
  return self.parse_orders(statuses, None)
1406
1416
 
1417
+ def create_order_request(self, symbol: str, type: OrderType, side: OrderSide, amount: str, price: Str = None, params={}):
1418
+ market = self.market(symbol)
1419
+ type = type.upper()
1420
+ side = side.upper()
1421
+ isMarket = (type == 'MARKET')
1422
+ isBuy = (side == 'BUY')
1423
+ clientOrderId = self.safe_string_2(params, 'clientOrderId', 'client_id')
1424
+ slippage = self.safe_string(params, 'slippage')
1425
+ defaultTimeInForce = 'ioc' if (isMarket) else 'gtc'
1426
+ postOnly = self.safe_bool(params, 'postOnly', False)
1427
+ if postOnly:
1428
+ defaultTimeInForce = 'alo'
1429
+ timeInForce = self.safe_string_lower(params, 'timeInForce', defaultTimeInForce)
1430
+ timeInForce = self.capitalize(timeInForce)
1431
+ triggerPrice = self.safe_string_2(params, 'triggerPrice', 'stopPrice')
1432
+ stopLossPrice = self.safe_string(params, 'stopLossPrice', triggerPrice)
1433
+ takeProfitPrice = self.safe_string(params, 'takeProfitPrice')
1434
+ isTrigger = (stopLossPrice or takeProfitPrice)
1435
+ px = None
1436
+ if isMarket:
1437
+ if price is None:
1438
+ raise ArgumentsRequired(self.id + ' market orders require price to calculate the max slippage price. Default slippage can be set in options(default is 5%).')
1439
+ px = Precise.string_mul(price, Precise.string_add('1', slippage)) if (isBuy) else Precise.string_mul(price, Precise.string_sub('1', slippage))
1440
+ px = self.price_to_precision(symbol, px) # round after adding slippage
1441
+ else:
1442
+ px = self.price_to_precision(symbol, price)
1443
+ sz = self.amount_to_precision(symbol, amount)
1444
+ reduceOnly = self.safe_bool(params, 'reduceOnly', False)
1445
+ orderType: dict = {}
1446
+ if isTrigger:
1447
+ isTp = False
1448
+ if takeProfitPrice is not None:
1449
+ triggerPrice = self.price_to_precision(symbol, takeProfitPrice)
1450
+ isTp = True
1451
+ else:
1452
+ triggerPrice = self.price_to_precision(symbol, stopLossPrice)
1453
+ orderType['trigger'] = {
1454
+ 'isMarket': isMarket,
1455
+ 'triggerPx': triggerPrice,
1456
+ 'tpsl': 'tp' if (isTp) else 'sl',
1457
+ }
1458
+ else:
1459
+ orderType['limit'] = {
1460
+ 'tif': timeInForce,
1461
+ }
1462
+ params = self.omit(params, ['clientOrderId', 'slippage', 'triggerPrice', 'stopPrice', 'stopLossPrice', 'takeProfitPrice', 'timeInForce', 'client_id', 'reduceOnly', 'postOnly'])
1463
+ orderObj: dict = {
1464
+ 'a': self.parse_to_int(market['baseId']),
1465
+ 'b': isBuy,
1466
+ 'p': px,
1467
+ 's': sz,
1468
+ 'r': reduceOnly,
1469
+ 't': orderType,
1470
+ # 'c': clientOrderId,
1471
+ }
1472
+ if clientOrderId is not None:
1473
+ orderObj['c'] = clientOrderId
1474
+ return orderObj
1475
+
1407
1476
  def create_orders_request(self, orders, params={}) -> dict:
1408
1477
  """
1409
1478
  create a list of trade orders
@@ -1431,77 +1500,59 @@ class hyperliquid(Exchange, ImplicitAPI):
1431
1500
  params = self.omit(params, ['slippage', 'clientOrderId', 'client_id', 'slippage', 'triggerPrice', 'stopPrice', 'stopLossPrice', 'takeProfitPrice', 'timeInForce'])
1432
1501
  nonce = self.milliseconds()
1433
1502
  orderReq = []
1503
+ grouping = 'na'
1434
1504
  for i in range(0, len(orders)):
1435
1505
  rawOrder = orders[i]
1436
1506
  marketId = self.safe_string(rawOrder, 'symbol')
1437
1507
  market = self.market(marketId)
1438
1508
  symbol = market['symbol']
1439
1509
  type = self.safe_string_upper(rawOrder, 'type')
1440
- isMarket = (type == 'MARKET')
1441
1510
  side = self.safe_string_upper(rawOrder, 'side')
1442
- isBuy = (side == 'BUY')
1443
1511
  amount = self.safe_string(rawOrder, 'amount')
1444
1512
  price = self.safe_string(rawOrder, 'price')
1445
1513
  orderParams = self.safe_dict(rawOrder, 'params', {})
1446
- clientOrderId = self.safe_string_2(orderParams, 'clientOrderId', 'client_id')
1447
1514
  slippage = self.safe_string(orderParams, 'slippage', defaultSlippage)
1448
- defaultTimeInForce = 'ioc' if (isMarket) else 'gtc'
1449
- postOnly = self.safe_bool(orderParams, 'postOnly', False)
1450
- if postOnly:
1451
- defaultTimeInForce = 'alo'
1452
- timeInForce = self.safe_string_lower(orderParams, 'timeInForce', defaultTimeInForce)
1453
- timeInForce = self.capitalize(timeInForce)
1454
- triggerPrice = self.safe_string_2(orderParams, 'triggerPrice', 'stopPrice')
1455
- stopLossPrice = self.safe_string(orderParams, 'stopLossPrice', triggerPrice)
1456
- takeProfitPrice = self.safe_string(orderParams, 'takeProfitPrice')
1457
- isTrigger = (stopLossPrice or takeProfitPrice)
1458
- px = None
1459
- if isMarket:
1460
- if price is None:
1461
- raise ArgumentsRequired(self.id + ' market orders require price to calculate the max slippage price. Default slippage can be set in options(default is 5%).')
1462
- px = Precise.string_mul(price, Precise.string_add('1', slippage)) if (isBuy) else Precise.string_mul(price, Precise.string_sub('1', slippage))
1463
- px = self.price_to_precision(symbol, px) # round after adding slippage
1464
- else:
1465
- px = self.price_to_precision(symbol, price)
1466
- sz = self.amount_to_precision(symbol, amount)
1467
- reduceOnly = self.safe_bool(orderParams, 'reduceOnly', False)
1468
- orderType: dict = {}
1515
+ orderParams['slippage'] = slippage
1516
+ stopLoss = self.safe_value(orderParams, 'stopLoss')
1517
+ takeProfit = self.safe_value(orderParams, 'takeProfit')
1518
+ isTrigger = (stopLoss or takeProfit)
1519
+ orderParams = self.omit(orderParams, ['stopLoss', 'takeProfit'])
1520
+ mainOrderObj: dict = self.create_order_request(symbol, type, side, amount, price, orderParams)
1521
+ orderReq.append(mainOrderObj)
1469
1522
  if isTrigger:
1470
- isTp = False
1471
- if takeProfitPrice is not None:
1472
- triggerPrice = self.price_to_precision(symbol, takeProfitPrice)
1473
- isTp = True
1523
+ # grouping opposed orders for sl/tp
1524
+ stopLossOrderTriggerPrice = self.safe_string_n(stopLoss, ['triggerPrice', 'stopPrice'])
1525
+ stopLossOrderType = self.safe_string(stopLoss, 'type')
1526
+ stopLossOrderLimitPrice = self.safe_string_n(stopLoss, ['price', 'stopLossPrice'], stopLossOrderTriggerPrice)
1527
+ takeProfitOrderTriggerPrice = self.safe_string_n(takeProfit, ['triggerPrice', 'stopPrice'])
1528
+ takeProfitOrderType = self.safe_string(takeProfit, 'type')
1529
+ takeProfitOrderLimitPrice = self.safe_string_n(takeProfit, ['price', 'takeProfitPrice'], takeProfitOrderTriggerPrice)
1530
+ grouping = 'normalTpsl'
1531
+ orderParams = self.omit(orderParams, ['stopLoss', 'takeProfit'])
1532
+ triggerOrderSide = ''
1533
+ if side == 'BUY':
1534
+ triggerOrderSide = 'sell'
1474
1535
  else:
1475
- triggerPrice = self.price_to_precision(symbol, stopLossPrice)
1476
- orderType['trigger'] = {
1477
- 'isMarket': isMarket,
1478
- 'triggerPx': triggerPrice,
1479
- 'tpsl': 'tp' if (isTp) else 'sl',
1480
- }
1481
- else:
1482
- orderType['limit'] = {
1483
- 'tif': timeInForce,
1484
- }
1485
- orderParams = self.omit(orderParams, ['clientOrderId', 'slippage', 'triggerPrice', 'stopPrice', 'stopLossPrice', 'takeProfitPrice', 'timeInForce', 'client_id', 'reduceOnly', 'postOnly'])
1486
- orderObj: dict = {
1487
- 'a': self.parse_to_int(market['baseId']),
1488
- 'b': isBuy,
1489
- 'p': px,
1490
- 's': sz,
1491
- 'r': reduceOnly,
1492
- 't': orderType,
1493
- # 'c': clientOrderId,
1494
- }
1495
- if clientOrderId is not None:
1496
- orderObj['c'] = clientOrderId
1497
- orderReq.append(orderObj)
1536
+ triggerOrderSide = 'buy'
1537
+ if takeProfit is not None:
1538
+ orderObj: dict = self.create_order_request(symbol, takeProfitOrderType, triggerOrderSide, amount, takeProfitOrderLimitPrice, self.extend(orderParams, {
1539
+ 'takeProfitPrice': takeProfitOrderTriggerPrice,
1540
+ 'reduceOnly': True,
1541
+ }))
1542
+ orderReq.append(orderObj)
1543
+ if stopLoss is not None:
1544
+ orderObj: dict = self.create_order_request(symbol, stopLossOrderType, triggerOrderSide, amount, stopLossOrderLimitPrice, self.extend(orderParams, {
1545
+ 'stopLossPrice': stopLossOrderTriggerPrice,
1546
+ 'reduceOnly': True,
1547
+ }))
1548
+ orderReq.append(orderObj)
1498
1549
  vaultAddress = None
1499
1550
  vaultAddress, params = self.handle_option_and_params(params, 'createOrder', 'vaultAddress')
1500
1551
  vaultAddress = self.format_vault_address(vaultAddress)
1501
1552
  orderAction: dict = {
1502
1553
  'type': 'order',
1503
1554
  'orders': orderReq,
1504
- 'grouping': 'na',
1555
+ 'grouping': grouping,
1505
1556
  # 'brokerCode': 1, # cant
1506
1557
  }
1507
1558
  if vaultAddress is None: