ccxt 4.4.35__py2.py3-none-any.whl → 4.4.37__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 (55) hide show
  1. ccxt/__init__.py +5 -3
  2. ccxt/abstract/bitfinex.py +136 -65
  3. ccxt/abstract/bitfinex1.py +69 -0
  4. ccxt/abstract/bitopro.py +1 -0
  5. ccxt/abstract/bybit.py +15 -0
  6. ccxt/abstract/defx.py +69 -0
  7. ccxt/abstract/deribit.py +1 -0
  8. ccxt/abstract/gate.py +14 -0
  9. ccxt/abstract/gateio.py +14 -0
  10. ccxt/async_support/__init__.py +5 -3
  11. ccxt/async_support/base/exchange.py +1 -1
  12. ccxt/async_support/bitfinex.py +3005 -1084
  13. ccxt/async_support/bitfinex1.py +1704 -0
  14. ccxt/async_support/bitfinex2.py +18 -13
  15. ccxt/async_support/bitmex.py +103 -1
  16. ccxt/async_support/bitopro.py +21 -4
  17. ccxt/async_support/bitso.py +2 -1
  18. ccxt/async_support/bybit.py +21 -1
  19. ccxt/async_support/coinbase.py +86 -0
  20. ccxt/async_support/defx.py +1981 -0
  21. ccxt/async_support/deribit.py +27 -12
  22. ccxt/async_support/gate.py +15 -1
  23. ccxt/async_support/htx.py +11 -2
  24. ccxt/async_support/hyperliquid.py +124 -14
  25. ccxt/async_support/kraken.py +39 -41
  26. ccxt/async_support/paradex.py +2 -2
  27. ccxt/base/exchange.py +6 -2
  28. ccxt/bitfinex.py +3005 -1084
  29. ccxt/bitfinex1.py +1703 -0
  30. ccxt/bitfinex2.py +18 -13
  31. ccxt/bitmex.py +103 -1
  32. ccxt/bitopro.py +21 -4
  33. ccxt/bitso.py +2 -1
  34. ccxt/bybit.py +21 -1
  35. ccxt/coinbase.py +86 -0
  36. ccxt/defx.py +1980 -0
  37. ccxt/deribit.py +27 -12
  38. ccxt/gate.py +15 -1
  39. ccxt/htx.py +11 -2
  40. ccxt/hyperliquid.py +124 -14
  41. ccxt/kraken.py +39 -41
  42. ccxt/paradex.py +2 -2
  43. ccxt/pro/__init__.py +5 -3
  44. ccxt/pro/bitfinex.py +725 -274
  45. ccxt/pro/bitfinex1.py +635 -0
  46. ccxt/pro/defx.py +832 -0
  47. ccxt/pro/probit.py +1 -0
  48. ccxt/test/tests_async.py +15 -1
  49. ccxt/test/tests_sync.py +15 -1
  50. {ccxt-4.4.35.dist-info → ccxt-4.4.37.dist-info}/METADATA +11 -10
  51. {ccxt-4.4.35.dist-info → ccxt-4.4.37.dist-info}/RECORD +54 -47
  52. ccxt/abstract/bitfinex2.py +0 -140
  53. {ccxt-4.4.35.dist-info → ccxt-4.4.37.dist-info}/LICENSE.txt +0 -0
  54. {ccxt-4.4.35.dist-info → ccxt-4.4.37.dist-info}/WHEEL +0 -0
  55. {ccxt-4.4.35.dist-info → ccxt-4.4.37.dist-info}/top_level.txt +0 -0
@@ -220,6 +220,7 @@ class deribit(Exchange, ImplicitAPI):
220
220
  'enable_api_key': 1,
221
221
  'get_access_log': 1,
222
222
  'get_account_summary': 1,
223
+ 'get_account_summaries': 1,
223
224
  'get_affiliate_program_info': 1,
224
225
  'get_email_language': 1,
225
226
  'get_new_announcements': 1,
@@ -931,13 +932,20 @@ class deribit(Exchange, ImplicitAPI):
931
932
  result: dict = {
932
933
  'info': balance,
933
934
  }
934
- currencyId = self.safe_string(balance, 'currency')
935
- currencyCode = self.safe_currency_code(currencyId)
936
- account = self.account()
937
- account['free'] = self.safe_string(balance, 'available_funds')
938
- account['used'] = self.safe_string(balance, 'maintenance_margin')
939
- account['total'] = self.safe_string(balance, 'equity')
940
- result[currencyCode] = account
935
+ summaries = []
936
+ if 'summaries' in balance:
937
+ summaries = self.safe_list(balance, 'summaries')
938
+ else:
939
+ summaries = [balance]
940
+ for i in range(0, len(summaries)):
941
+ data = summaries[i]
942
+ currencyId = self.safe_string(data, 'currency')
943
+ currencyCode = self.safe_currency_code(currencyId)
944
+ account = self.account()
945
+ account['free'] = self.safe_string(data, 'available_funds')
946
+ account['used'] = self.safe_string(data, 'maintenance_margin')
947
+ account['total'] = self.safe_string(data, 'equity')
948
+ result[currencyCode] = account
941
949
  return self.safe_balance(result)
942
950
 
943
951
  async def fetch_balance(self, params={}) -> Balances:
@@ -945,17 +953,24 @@ class deribit(Exchange, ImplicitAPI):
945
953
  query for balance and get the amount of funds available for trading or funds locked in orders
946
954
 
947
955
  https://docs.deribit.com/#private-get_account_summary
956
+ https://docs.deribit.com/#private-get_account_summaries
948
957
 
949
958
  :param dict [params]: extra parameters specific to the exchange API endpoint
959
+ :param str [params.code]: unified currency code of the currency for the balance, if defined 'privateGetGetAccountSummary' will be used, otherwise 'privateGetGetAccountSummaries' will be used
950
960
  :returns dict: a `balance structure <https://docs.ccxt.com/#/?id=balance-structure>`
951
961
  """
952
962
  await self.load_markets()
953
- code = self.code_from_options('fetchBalance', params)
954
- currency = self.currency(code)
963
+ code = self.safe_string(params, 'code')
964
+ params = self.omit(params, 'code')
955
965
  request: dict = {
956
- 'currency': currency['id'],
957
966
  }
958
- response = await self.privateGetGetAccountSummary(self.extend(request, params))
967
+ if code is not None:
968
+ request['currency'] = self.currency_id(code)
969
+ response = None
970
+ if code is None:
971
+ response = await self.privateGetGetAccountSummaries(params)
972
+ else:
973
+ response = await self.privateGetGetAccountSummary(self.extend(request, params))
959
974
  #
960
975
  # {
961
976
  # "jsonrpc": "2.0",
@@ -998,7 +1013,7 @@ class deribit(Exchange, ImplicitAPI):
998
1013
  # "testnet": False
999
1014
  # }
1000
1015
  #
1001
- result = self.safe_value(response, 'result', {})
1016
+ result = self.safe_dict(response, 'result', {})
1002
1017
  return self.parse_balance(result)
1003
1018
 
1004
1019
  async def create_deposit_address(self, code: str, params={}):
@@ -41,7 +41,7 @@ class gate(Exchange, ImplicitAPI):
41
41
  'certified': True,
42
42
  'pro': True,
43
43
  'urls': {
44
- 'logo': 'https://user-images.githubusercontent.com/1294454/31784029-0313c702-b509-11e7-9ccc-bc0da6a0e435.jpg',
44
+ 'logo': 'https://github.com/user-attachments/assets/64f988c5-07b6-4652-b5c1-679a6bf67c85',
45
45
  'doc': 'https://www.gate.io/docs/developers/apiv4/en/',
46
46
  'www': 'https://gate.io/',
47
47
  'api': {
@@ -240,6 +240,7 @@ class gate(Exchange, ImplicitAPI):
240
240
  '{settle}/contract_stats': 1,
241
241
  '{settle}/index_constituents/{index}': 1,
242
242
  '{settle}/liq_orders': 1,
243
+ '{settle}/risk_limit_tiers': 1,
243
244
  },
244
245
  },
245
246
  'delivery': {
@@ -281,6 +282,7 @@ class gate(Exchange, ImplicitAPI):
281
282
  'withdrawals': {
282
283
  'post': {
283
284
  'withdrawals': 20, # 1r/s cost = 20 / 1 = 20
285
+ 'push': 1,
284
286
  },
285
287
  'delete': {
286
288
  'withdrawals/{withdrawal_id}': 1,
@@ -292,6 +294,7 @@ class gate(Exchange, ImplicitAPI):
292
294
  'withdrawals': 1,
293
295
  'deposits': 1,
294
296
  'sub_account_transfers': 1,
297
+ 'order_status': 1,
295
298
  'withdraw_status': 1,
296
299
  'sub_account_balances': 2.5,
297
300
  'sub_account_margin_balances': 2.5,
@@ -302,6 +305,7 @@ class gate(Exchange, ImplicitAPI):
302
305
  'total_balance': 2.5,
303
306
  'small_balance': 1,
304
307
  'small_balance_history': 1,
308
+ 'push': 1,
305
309
  },
306
310
  'post': {
307
311
  'transfers': 2.5, # 8r/s cost = 20 / 8 = 2.5
@@ -344,11 +348,14 @@ class gate(Exchange, ImplicitAPI):
344
348
  'risk_units': 20 / 15,
345
349
  'unified_mode': 20 / 15,
346
350
  'loan_margin_tiers': 20 / 15,
351
+ 'leverage/user_currency_config': 20 / 15,
352
+ 'leverage/user_currency_setting': 20 / 15,
347
353
  },
348
354
  'post': {
349
355
  'account_mode': 20 / 15,
350
356
  'loans': 200 / 15, # 15r/10s cost = 20 / 1.5 = 13.33
351
357
  'portfolio_calculator': 20 / 15,
358
+ 'leverage/user_currency_setting': 20 / 15,
352
359
  },
353
360
  'put': {
354
361
  'unified_mode': 20 / 15,
@@ -528,9 +535,13 @@ class gate(Exchange, ImplicitAPI):
528
535
  'orders': 20 / 15,
529
536
  'orders/{order_id}': 20 / 15,
530
537
  'my_trades': 20 / 15,
538
+ 'mmp': 20 / 15,
531
539
  },
532
540
  'post': {
533
541
  'orders': 20 / 15,
542
+ 'countdown_cancel_all': 20 / 15,
543
+ 'mmp': 20 / 15,
544
+ 'mmp/reset': 20 / 15,
534
545
  },
535
546
  'delete': {
536
547
  'orders': 20 / 15,
@@ -574,6 +585,7 @@ class gate(Exchange, ImplicitAPI):
574
585
  'multi_collateral/currencies': 20 / 15,
575
586
  'multi_collateral/ltv': 20 / 15,
576
587
  'multi_collateral/fixed_rate': 20 / 15,
588
+ 'multi_collateral/current_rate': 20 / 15,
577
589
  },
578
590
  'post': {
579
591
  'collateral/orders': 20 / 15,
@@ -587,8 +599,10 @@ class gate(Exchange, ImplicitAPI):
587
599
  'account': {
588
600
  'get': {
589
601
  'detail': 20 / 15,
602
+ 'rate_limit': 20 / 15,
590
603
  'stp_groups': 20 / 15,
591
604
  'stp_groups/{stp_id}/users': 20 / 15,
605
+ 'stp_groups/debit_fee': 20 / 15,
592
606
  },
593
607
  'post': {
594
608
  'stp_groups': 20 / 15,
ccxt/async_support/htx.py CHANGED
@@ -5092,17 +5092,23 @@ class htx(Exchange, ImplicitAPI):
5092
5092
  params = self.omit(params, ['clientOrderId'])
5093
5093
  if type == 'limit' or type == 'ioc' or type == 'fok' or type == 'post_only':
5094
5094
  request['price'] = self.price_to_precision(symbol, price)
5095
+ reduceOnly = self.safe_bool_2(params, 'reduceOnly', 'reduce_only', False)
5095
5096
  if not isStopLossTriggerOrder and not isTakeProfitTriggerOrder:
5096
- reduceOnly = self.safe_value_2(params, 'reduceOnly', 'reduce_only', False)
5097
5097
  if reduceOnly:
5098
5098
  request['reduce_only'] = 1
5099
5099
  request['lever_rate'] = self.safe_integer_n(params, ['leverRate', 'lever_rate', 'leverage'], 1)
5100
5100
  if not isTrailingPercentOrder:
5101
5101
  request['order_price_type'] = type
5102
+ hedged = self.safe_bool(params, 'hedged', False)
5103
+ if hedged:
5104
+ if reduceOnly:
5105
+ request['offset'] = 'close'
5106
+ else:
5107
+ request['offset'] = 'open'
5102
5108
  broker = self.safe_value(self.options, 'broker', {})
5103
5109
  brokerId = self.safe_string(broker, 'id')
5104
5110
  request['channel_code'] = brokerId
5105
- params = self.omit(params, ['reduceOnly', 'triggerPrice', 'stopPrice', 'stopLossPrice', 'takeProfitPrice', 'triggerType', 'leverRate', 'timeInForce', 'leverage', 'trailingPercent', 'trailingTriggerPrice'])
5111
+ params = self.omit(params, ['reduceOnly', 'triggerPrice', 'stopPrice', 'stopLossPrice', 'takeProfitPrice', 'triggerType', 'leverRate', 'timeInForce', 'leverage', 'trailingPercent', 'trailingTriggerPrice', 'hedged'])
5106
5112
  return self.extend(request, params)
5107
5113
 
5108
5114
  async def create_order(self, symbol: str, type: OrderType, side: OrderSide, amount: float, price: Num = None, params={}):
@@ -5116,6 +5122,8 @@ class htx(Exchange, ImplicitAPI):
5116
5122
  https://huobiapi.github.io/docs/usdt_swap/v1/en/#cross-place-trigger-order # usdt-m swap cross trigger
5117
5123
  https://huobiapi.github.io/docs/usdt_swap/v1/en/#isolated-place-an-order # usdt-m swap isolated
5118
5124
  https://huobiapi.github.io/docs/usdt_swap/v1/en/#isolated-place-trigger-order # usdt-m swap isolated trigger
5125
+ https://huobiapi.github.io/docs/usdt_swap/v1/en/#isolated-set-a-take-profit-and-stop-loss-order-for-an-existing-position
5126
+ https://huobiapi.github.io/docs/usdt_swap/v1/en/#cross-set-a-take-profit-and-stop-loss-order-for-an-existing-position
5119
5127
  https://huobiapi.github.io/docs/dm/v1/en/#place-an-order # coin-m futures
5120
5128
  https://huobiapi.github.io/docs/dm/v1/en/#place-trigger-order # coin-m futures contract trigger
5121
5129
 
@@ -5137,6 +5145,7 @@ class htx(Exchange, ImplicitAPI):
5137
5145
  :param float [params.cost]: *spot market buy only* the quote quantity that can be used alternative for the amount
5138
5146
  :param float [params.trailingPercent]: *contract only* the percent to trail away from the current market price
5139
5147
  :param float [params.trailingTriggerPrice]: *contract only* the price to trigger a trailing order, default uses the price argument
5148
+ :param bool [params.hedged]: *contract only* True for hedged mode, False for one way mode, default is False
5140
5149
  :returns dict: an `order structure <https://docs.ccxt.com/#/?id=order-structure>`
5141
5150
  """
5142
5151
  await self.load_markets()
@@ -226,6 +226,96 @@ class hyperliquid(Exchange, ImplicitAPI):
226
226
  'defaultSlippage': 0.05,
227
227
  'zeroAddress': '0x0000000000000000000000000000000000000000',
228
228
  },
229
+ 'features': {
230
+ 'default': {
231
+ 'sandbox': True,
232
+ 'createOrder': {
233
+ 'marginMode': False,
234
+ 'triggerPrice': False,
235
+ 'triggerPriceType': None,
236
+ 'triggerDirection': False,
237
+ 'stopLossPrice': False,
238
+ 'takeProfitPrice': False,
239
+ 'attachedStopLossTakeProfit': None,
240
+ 'timeInForce': {
241
+ 'GTC': True,
242
+ 'IOC': True,
243
+ 'FOK': False,
244
+ 'PO': True,
245
+ 'GTD': False,
246
+ },
247
+ 'hedged': False,
248
+ 'trailing': False,
249
+ },
250
+ 'createOrders': {
251
+ 'max': 1000,
252
+ },
253
+ 'fetchMyTrades': {
254
+ 'marginMode': False,
255
+ 'limit': 2000,
256
+ 'daysBack': None,
257
+ 'untilDays': None,
258
+ },
259
+ 'fetchOrder': {
260
+ 'marginMode': False,
261
+ 'trigger': False,
262
+ 'trailing': False,
263
+ },
264
+ 'fetchOpenOrders': {
265
+ 'marginMode': False,
266
+ 'limit': 2000,
267
+ 'trigger': False,
268
+ 'trailing': False,
269
+ },
270
+ 'fetchOrders': {
271
+ 'marginMode': False,
272
+ 'limit': 2000,
273
+ 'daysBack': None,
274
+ 'untilDays': None,
275
+ 'trigger': False,
276
+ 'trailing': False,
277
+ },
278
+ 'fetchClosedOrders': {
279
+ 'marginMode': False,
280
+ 'limit': 2000,
281
+ 'daysBackClosed': None,
282
+ 'daysBackCanceled': None,
283
+ 'untilDays': None,
284
+ 'trigger': False,
285
+ 'trailing': False,
286
+ },
287
+ 'fetchOHLCV': {
288
+ 'limit': 5000,
289
+ },
290
+ },
291
+ 'spot': {
292
+ 'extends': 'default',
293
+ },
294
+ 'forPerps': {
295
+ 'extends': 'default',
296
+ 'createOrder': {
297
+ 'stopLossPrice': True,
298
+ 'takeProfitPrice': True,
299
+ 'attachedStopLossTakeProfit': None, # todo, in two orders
300
+ },
301
+ },
302
+ 'swap': {
303
+ 'linear': {
304
+ 'extends': 'forPerps',
305
+ },
306
+ 'inverse': {
307
+ 'extends': 'forPerps',
308
+ },
309
+ },
310
+ 'future': {
311
+ 'linear': {
312
+ 'extends': 'forPerps',
313
+ },
314
+ 'inverse': {
315
+ 'extends': 'forPerps',
316
+ },
317
+ },
318
+ },
229
319
  })
230
320
 
231
321
  def set_sandbox_mode(self, enabled):
@@ -503,7 +593,7 @@ class hyperliquid(Exchange, ImplicitAPI):
503
593
  pricePrecision = self.calculate_price_precision(price, amountPrecision, 8)
504
594
  pricePrecisionStr = self.number_to_string(pricePrecision)
505
595
  # quotePrecision = self.parse_number(self.parse_precision(self.safe_string(innerQuoteTokenInfo, 'szDecimals')))
506
- baseId = self.number_to_string(i + 10000)
596
+ baseId = self.number_to_string(index + 10000)
507
597
  markets.append(self.safe_market_structure({
508
598
  'id': marketName,
509
599
  'symbol': symbol,
@@ -1192,7 +1282,7 @@ class hyperliquid(Exchange, ImplicitAPI):
1192
1282
  signature = self.sign_message(msg, self.privateKey)
1193
1283
  return signature
1194
1284
 
1195
- def build_transfer_sig(self, message):
1285
+ def build_usd_send_sig(self, message):
1196
1286
  messageTypes: dict = {
1197
1287
  'HyperliquidTransaction:UsdSend': [
1198
1288
  {'name': 'hyperliquidChain', 'type': 'string'},
@@ -1203,6 +1293,17 @@ class hyperliquid(Exchange, ImplicitAPI):
1203
1293
  }
1204
1294
  return self.sign_user_signed_action(messageTypes, message)
1205
1295
 
1296
+ def build_usd_class_send_sig(self, message):
1297
+ messageTypes: dict = {
1298
+ 'HyperliquidTransaction:UsdClassTransfer': [
1299
+ {'name': 'hyperliquidChain', 'type': 'string'},
1300
+ {'name': 'amount', 'type': 'string'},
1301
+ {'name': 'toPerp', 'type': 'bool'},
1302
+ {'name': 'nonce', 'type': 'uint64'},
1303
+ ],
1304
+ }
1305
+ return self.sign_user_signed_action(messageTypes, message)
1306
+
1206
1307
  def build_withdraw_sig(self, message):
1207
1308
  messageTypes: dict = {
1208
1309
  'HyperliquidTransaction:Withdraw': [
@@ -2590,25 +2691,34 @@ class hyperliquid(Exchange, ImplicitAPI):
2590
2691
  # handle swap <> spot account transfer
2591
2692
  if not self.in_array(toAccount, ['spot', 'swap', 'perp']):
2592
2693
  raise NotSupported(self.id + 'transfer() only support spot <> swap transfer')
2694
+ strAmount = self.number_to_string(amount)
2593
2695
  vaultAddress = self.format_vault_address(self.safe_string(params, 'vaultAddress'))
2594
2696
  params = self.omit(params, 'vaultAddress')
2697
+ if vaultAddress is not None:
2698
+ strAmount = strAmount + ' subaccount:' + vaultAddress
2595
2699
  toPerp = (toAccount == 'perp') or (toAccount == 'swap')
2596
- action: dict = {
2597
- 'type': 'spotUser',
2598
- 'classTransfer': {
2599
- 'usdc': amount,
2700
+ transferPayload: dict = {
2701
+ 'hyperliquidChain': 'Testnet' if isSandboxMode else 'Mainnet',
2702
+ 'amount': strAmount,
2703
+ 'toPerp': toPerp,
2704
+ 'nonce': nonce,
2705
+ }
2706
+ transferSig = self.build_usd_class_send_sig(transferPayload)
2707
+ transferRequest: dict = {
2708
+ 'action': {
2709
+ 'hyperliquidChain': transferPayload['hyperliquidChain'],
2710
+ 'signatureChainId': '0x66eee',
2711
+ 'type': 'usdClassTransfer',
2712
+ 'amount': strAmount,
2600
2713
  'toPerp': toPerp,
2714
+ 'nonce': nonce,
2601
2715
  },
2602
- }
2603
- signature = self.sign_l1_action(action, nonce, vaultAddress)
2604
- innerRequest: dict = {
2605
- 'action': action,
2606
2716
  'nonce': nonce,
2607
- 'signature': signature,
2717
+ 'signature': transferSig,
2608
2718
  }
2609
2719
  if vaultAddress is not None:
2610
- innerRequest['vaultAddress'] = vaultAddress
2611
- transferResponse = await self.privatePostExchange(innerRequest)
2720
+ transferRequest['vaultAddress'] = vaultAddress
2721
+ transferResponse = await self.privatePostExchange(transferRequest)
2612
2722
  return transferResponse
2613
2723
  # handle sub-account/different account transfer
2614
2724
  self.check_address(toAccount)
@@ -2622,7 +2732,7 @@ class hyperliquid(Exchange, ImplicitAPI):
2622
2732
  'amount': self.number_to_string(amount),
2623
2733
  'time': nonce,
2624
2734
  }
2625
- sig = self.build_transfer_sig(payload)
2735
+ sig = self.build_usd_send_sig(payload)
2626
2736
  request: dict = {
2627
2737
  'action': {
2628
2738
  'hyperliquidChain': payload['hyperliquidChain'],
@@ -447,31 +447,43 @@ class kraken(Exchange, ImplicitAPI):
447
447
  },
448
448
  'precisionMode': TICK_SIZE,
449
449
  'exceptions': {
450
- 'EQuery:Invalid asset pair': BadSymbol, # {"error":["EQuery:Invalid asset pair"]}
451
- 'EAPI:Invalid key': AuthenticationError,
452
- 'EFunding:Unknown withdraw key': InvalidAddress, # {"error":["EFunding:Unknown withdraw key"]}
453
- 'EFunding:Invalid amount': InsufficientFunds,
454
- 'EService:Unavailable': ExchangeNotAvailable,
455
- 'EDatabase:Internal error': ExchangeNotAvailable,
456
- 'EService:Busy': ExchangeNotAvailable,
457
- 'EQuery:Unknown asset': BadSymbol, # {"error":["EQuery:Unknown asset"]}
458
- 'EAPI:Rate limit exceeded': DDoSProtection,
459
- 'EOrder:Rate limit exceeded': DDoSProtection,
460
- 'EGeneral:Internal error': ExchangeNotAvailable,
461
- 'EGeneral:Temporary lockout': DDoSProtection,
462
- 'EGeneral:Permission denied': PermissionDenied,
463
- 'EGeneral:Invalid arguments:price': InvalidOrder,
464
- 'EOrder:Unknown order': InvalidOrder,
465
- 'EOrder:Invalid price:Invalid price argument': InvalidOrder,
466
- 'EOrder:Order minimum not met': InvalidOrder,
467
- 'EGeneral:Invalid arguments': BadRequest,
468
- 'ESession:Invalid session': AuthenticationError,
469
- 'EAPI:Invalid nonce': InvalidNonce,
470
- 'EFunding:No funding method': BadRequest, # {"error":"EFunding:No funding method"}
471
- 'EFunding:Unknown asset': BadSymbol, # {"error":["EFunding:Unknown asset"]}
472
- 'EService:Market in post_only mode': OnMaintenance, # {"error":["EService:Market in post_only mode"]}
473
- 'EGeneral:Too many requests': DDoSProtection, # {"error":["EGeneral:Too many requests"]}
474
- 'ETrade:User Locked': AccountSuspended, # {"error":["ETrade:User Locked"]}
450
+ 'exact': {
451
+ 'EQuery:Invalid asset pair': BadSymbol, # {"error":["EQuery:Invalid asset pair"]}
452
+ 'EAPI:Invalid key': AuthenticationError,
453
+ 'EFunding:Unknown withdraw key': InvalidAddress, # {"error":["EFunding:Unknown withdraw key"]}
454
+ 'EFunding:Invalid amount': InsufficientFunds,
455
+ 'EService:Unavailable': ExchangeNotAvailable,
456
+ 'EDatabase:Internal error': ExchangeNotAvailable,
457
+ 'EService:Busy': ExchangeNotAvailable,
458
+ 'EQuery:Unknown asset': BadSymbol, # {"error":["EQuery:Unknown asset"]}
459
+ 'EAPI:Rate limit exceeded': DDoSProtection,
460
+ 'EOrder:Rate limit exceeded': DDoSProtection,
461
+ 'EGeneral:Internal error': ExchangeNotAvailable,
462
+ 'EGeneral:Temporary lockout': DDoSProtection,
463
+ 'EGeneral:Permission denied': PermissionDenied,
464
+ 'EGeneral:Invalid arguments:price': InvalidOrder,
465
+ 'EOrder:Unknown order': InvalidOrder,
466
+ 'EOrder:Invalid price:Invalid price argument': InvalidOrder,
467
+ 'EOrder:Order minimum not met': InvalidOrder,
468
+ 'EOrder:Insufficient funds': InsufficientFunds,
469
+ 'EGeneral:Invalid arguments': BadRequest,
470
+ 'ESession:Invalid session': AuthenticationError,
471
+ 'EAPI:Invalid nonce': InvalidNonce,
472
+ 'EFunding:No funding method': BadRequest, # {"error":"EFunding:No funding method"}
473
+ 'EFunding:Unknown asset': BadSymbol, # {"error":["EFunding:Unknown asset"]}
474
+ 'EService:Market in post_only mode': OnMaintenance, # {"error":["EService:Market in post_only mode"]}
475
+ 'EGeneral:Too many requests': DDoSProtection, # {"error":["EGeneral:Too many requests"]}
476
+ 'ETrade:User Locked': AccountSuspended, # {"error":["ETrade:User Locked"]}
477
+ },
478
+ 'broad': {
479
+ ':Invalid order': InvalidOrder,
480
+ ':Invalid arguments:volume': InvalidOrder,
481
+ ':Invalid arguments:viqc': InvalidOrder,
482
+ ':Invalid nonce': InvalidNonce,
483
+ ':IInsufficient funds': InsufficientFunds,
484
+ ':Cancel pending': CancelPending,
485
+ ':Rate limit exceeded': RateLimitExceeded,
486
+ },
475
487
  },
476
488
  })
477
489
 
@@ -3064,21 +3076,6 @@ class kraken(Exchange, ImplicitAPI):
3064
3076
  def handle_errors(self, code: int, reason: str, url: str, method: str, headers: dict, body: str, response, requestHeaders, requestBody):
3065
3077
  if code == 520:
3066
3078
  raise ExchangeNotAvailable(self.id + ' ' + str(code) + ' ' + reason)
3067
- # todo: rewrite self for "broad" exceptions matching
3068
- if body.find('Invalid order') >= 0:
3069
- raise InvalidOrder(self.id + ' ' + body)
3070
- if body.find('Invalid nonce') >= 0:
3071
- raise InvalidNonce(self.id + ' ' + body)
3072
- if body.find('Insufficient funds') >= 0:
3073
- raise InsufficientFunds(self.id + ' ' + body)
3074
- if body.find('Cancel pending') >= 0:
3075
- raise CancelPending(self.id + ' ' + body)
3076
- if body.find('Invalid arguments:volume') >= 0:
3077
- raise InvalidOrder(self.id + ' ' + body)
3078
- if body.find('Invalid arguments:viqc') >= 0:
3079
- raise InvalidOrder(self.id + ' ' + body)
3080
- if body.find('Rate limit exceeded') >= 0:
3081
- raise RateLimitExceeded(self.id + ' ' + body)
3082
3079
  if response is None:
3083
3080
  return None
3084
3081
  if body[0] == '{':
@@ -3089,6 +3086,7 @@ class kraken(Exchange, ImplicitAPI):
3089
3086
  message = self.id + ' ' + body
3090
3087
  for i in range(0, len(response['error'])):
3091
3088
  error = response['error'][i]
3092
- self.throw_exactly_matched_exception(self.exceptions, error, message)
3089
+ self.throw_exactly_matched_exception(self.exceptions['exact'], error, message)
3090
+ self.throw_exactly_matched_exception(self.exceptions['broad'], error, message)
3093
3091
  raise ExchangeError(message)
3094
3092
  return None
@@ -276,6 +276,7 @@ class paradex(Exchange, ImplicitAPI):
276
276
  'commonCurrencies': {
277
277
  },
278
278
  'options': {
279
+ 'paradexAccount': None, # add {"privateKey": A, "publicKey": B, "address": C}
279
280
  'broker': 'CCXT',
280
281
  },
281
282
  })
@@ -964,10 +965,10 @@ class paradex(Exchange, ImplicitAPI):
964
965
  }
965
966
 
966
967
  async def retrieve_account(self):
967
- self.check_required_credentials()
968
968
  cachedAccount: dict = self.safe_dict(self.options, 'paradexAccount')
969
969
  if cachedAccount is not None:
970
970
  return cachedAccount
971
+ self.check_required_credentials()
971
972
  systemConfig = await self.get_system_config()
972
973
  domain = await self.prepare_paradex_domain(True)
973
974
  messageTypes = {
@@ -1956,7 +1957,6 @@ class paradex(Exchange, ImplicitAPI):
1956
1957
  if query:
1957
1958
  url += '?' + self.urlencode(query)
1958
1959
  elif api == 'private':
1959
- self.check_required_credentials()
1960
1960
  headers = {
1961
1961
  'Accept': 'application/json',
1962
1962
  'PARADEX-PARTNER': self.safe_string(self.options, 'broker', 'CCXT'),
ccxt/base/exchange.py CHANGED
@@ -4,7 +4,7 @@
4
4
 
5
5
  # -----------------------------------------------------------------------------
6
6
 
7
- __version__ = '4.4.35'
7
+ __version__ = '4.4.37'
8
8
 
9
9
  # -----------------------------------------------------------------------------
10
10
 
@@ -2804,9 +2804,13 @@ class Exchange(object):
2804
2804
  if value is not None:
2805
2805
  featuresObj['createOrder']['stopLoss'] = value
2806
2806
  featuresObj['createOrder']['takeProfit'] = value
2807
- # False 'hedged' for spot
2807
+ # for spot, default 'hedged' to False
2808
2808
  if marketType == 'spot':
2809
2809
  featuresObj['createOrder']['hedged'] = False
2810
+ # default 'GTC' to True
2811
+ gtcValue = self.safe_bool(featuresObj['createOrder']['timeInForce'], 'gtc')
2812
+ if gtcValue is None:
2813
+ featuresObj['createOrder']['timeInForce']['gtc'] = True
2810
2814
  return featuresObj
2811
2815
 
2812
2816
  def orderbook_checksum_message(self, symbol: Str):