ccxt 4.4.53__py2.py3-none-any.whl → 4.4.58__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 (68) hide show
  1. ccxt/__init__.py +1 -3
  2. ccxt/abstract/xt.py +1 -0
  3. ccxt/ascendex.py +2 -0
  4. ccxt/async_support/__init__.py +1 -3
  5. ccxt/async_support/ascendex.py +2 -0
  6. ccxt/async_support/base/exchange.py +5 -5
  7. ccxt/async_support/binance.py +39 -9
  8. ccxt/async_support/bitget.py +6 -1
  9. ccxt/async_support/bitmart.py +13 -3
  10. ccxt/async_support/blofin.py +1 -2
  11. ccxt/async_support/bybit.py +16 -3
  12. ccxt/async_support/coinbase.py +9 -2
  13. ccxt/async_support/coinbaseinternational.py +2 -2
  14. ccxt/async_support/coinex.py +1 -1
  15. ccxt/async_support/deribit.py +8 -25
  16. ccxt/async_support/exmo.py +1 -1
  17. ccxt/async_support/hyperliquid.py +11 -6
  18. ccxt/async_support/kraken.py +1 -1
  19. ccxt/async_support/timex.py +12 -2
  20. ccxt/async_support/vertex.py +1 -1
  21. ccxt/async_support/whitebit.py +2 -1
  22. ccxt/async_support/woofipro.py +2 -2
  23. ccxt/async_support/xt.py +54 -1
  24. ccxt/base/errors.py +0 -6
  25. ccxt/base/exchange.py +6 -6
  26. ccxt/binance.py +39 -9
  27. ccxt/bitget.py +6 -1
  28. ccxt/bitmart.py +13 -3
  29. ccxt/blofin.py +1 -2
  30. ccxt/bybit.py +16 -3
  31. ccxt/coinbase.py +9 -2
  32. ccxt/coinbaseinternational.py +2 -2
  33. ccxt/coinex.py +1 -1
  34. ccxt/deribit.py +8 -25
  35. ccxt/exmo.py +1 -1
  36. ccxt/hyperliquid.py +11 -6
  37. ccxt/kraken.py +1 -1
  38. ccxt/pro/__init__.py +1 -1
  39. ccxt/pro/binance.py +2 -2
  40. ccxt/pro/bitget.py +3 -3
  41. ccxt/pro/bybit.py +11 -3
  42. ccxt/pro/cex.py +1 -1
  43. ccxt/pro/coincatch.py +3 -3
  44. ccxt/pro/mexc.py +6 -4
  45. ccxt/pro/okx.py +1 -1
  46. ccxt/static_dependencies/ethereum/abi/py.typed +0 -0
  47. ccxt/static_dependencies/ethereum/account/py.typed +0 -0
  48. ccxt/static_dependencies/ethereum/hexbytes/py.typed +0 -0
  49. ccxt/static_dependencies/ethereum/typing/py.typed +0 -0
  50. ccxt/static_dependencies/ethereum/utils/py.typed +0 -0
  51. ccxt/static_dependencies/lark/py.typed +0 -0
  52. ccxt/static_dependencies/marshmallow/py.typed +0 -0
  53. ccxt/static_dependencies/marshmallow_dataclass/py.typed +0 -0
  54. ccxt/static_dependencies/marshmallow_oneofschema/py.typed +0 -0
  55. ccxt/test/tests_init.py +2 -2
  56. ccxt/timex.py +12 -2
  57. ccxt/vertex.py +1 -1
  58. ccxt/whitebit.py +2 -1
  59. ccxt/woofipro.py +2 -2
  60. ccxt/xt.py +54 -1
  61. {ccxt-4.4.53.dist-info → ccxt-4.4.58.dist-info}/METADATA +9 -12
  62. {ccxt-4.4.53.dist-info → ccxt-4.4.58.dist-info}/RECORD +65 -59
  63. ccxt/abstract/lykke.py +0 -29
  64. ccxt/async_support/lykke.py +0 -1374
  65. ccxt/lykke.py +0 -1374
  66. {ccxt-4.4.53.dist-info → ccxt-4.4.58.dist-info}/LICENSE.txt +0 -0
  67. {ccxt-4.4.53.dist-info → ccxt-4.4.58.dist-info}/WHEEL +0 -0
  68. {ccxt-4.4.53.dist-info → ccxt-4.4.58.dist-info}/top_level.txt +0 -0
ccxt/__init__.py CHANGED
@@ -22,7 +22,7 @@
22
22
 
23
23
  # ----------------------------------------------------------------------------
24
24
 
25
- __version__ = '4.4.53'
25
+ __version__ = '4.4.58'
26
26
 
27
27
  # ----------------------------------------------------------------------------
28
28
 
@@ -163,7 +163,6 @@ from ccxt.kuna import kuna # noqa: F4
163
163
  from ccxt.latoken import latoken # noqa: F401
164
164
  from ccxt.lbank import lbank # noqa: F401
165
165
  from ccxt.luno import luno # noqa: F401
166
- from ccxt.lykke import lykke # noqa: F401
167
166
  from ccxt.mercado import mercado # noqa: F401
168
167
  from ccxt.mexc import mexc # noqa: F401
169
168
  from ccxt.myokx import myokx # noqa: F401
@@ -277,7 +276,6 @@ exchanges = [
277
276
  'latoken',
278
277
  'lbank',
279
278
  'luno',
280
- 'lykke',
281
279
  'mercado',
282
280
  'mexc',
283
281
  'myokx',
ccxt/abstract/xt.py CHANGED
@@ -109,6 +109,7 @@ class ImplicitAPI:
109
109
  private_linear_post_future_user_v1_position_margin = privateLinearPostFutureUserV1PositionMargin = Entry('future/user/v1/position/margin', ['private', 'linear'], 'POST', {'cost': 1})
110
110
  private_linear_post_future_user_v1_user_collection_add = privateLinearPostFutureUserV1UserCollectionAdd = Entry('future/user/v1/user/collection/add', ['private', 'linear'], 'POST', {'cost': 1})
111
111
  private_linear_post_future_user_v1_user_collection_cancel = privateLinearPostFutureUserV1UserCollectionCancel = Entry('future/user/v1/user/collection/cancel', ['private', 'linear'], 'POST', {'cost': 1})
112
+ private_linear_post_future_user_v1_position_change_type = privateLinearPostFutureUserV1PositionChangeType = Entry('future/user/v1/position/change-type', ['private', 'linear'], 'POST', {'cost': 1})
112
113
  private_inverse_get_future_trade_v1_entrust_plan_detail = privateInverseGetFutureTradeV1EntrustPlanDetail = Entry('future/trade/v1/entrust/plan-detail', ['private', 'inverse'], 'GET', {'cost': 1})
113
114
  private_inverse_get_future_trade_v1_entrust_plan_list = privateInverseGetFutureTradeV1EntrustPlanList = Entry('future/trade/v1/entrust/plan-list', ['private', 'inverse'], 'GET', {'cost': 1})
114
115
  private_inverse_get_future_trade_v1_entrust_plan_list_history = privateInverseGetFutureTradeV1EntrustPlanListHistory = Entry('future/trade/v1/entrust/plan-list-history', ['private', 'inverse'], 'GET', {'cost': 1})
ccxt/ascendex.py CHANGED
@@ -365,6 +365,7 @@ class ascendex(Exchange, ImplicitAPI):
365
365
  'untilDays': 100000,
366
366
  'trigger': False,
367
367
  'trailing': False,
368
+ 'symbolRequired': False,
368
369
  },
369
370
  },
370
371
  'forDerivatives': {
@@ -388,6 +389,7 @@ class ascendex(Exchange, ImplicitAPI):
388
389
  'untilDays': None,
389
390
  'trigger': False,
390
391
  'trailing': False,
392
+ 'symbolRequired': False,
391
393
  },
392
394
  },
393
395
  'swap': {
@@ -4,7 +4,7 @@
4
4
 
5
5
  # -----------------------------------------------------------------------------
6
6
 
7
- __version__ = '4.4.53'
7
+ __version__ = '4.4.58'
8
8
 
9
9
  # -----------------------------------------------------------------------------
10
10
 
@@ -143,7 +143,6 @@ from ccxt.async_support.kuna import kuna
143
143
  from ccxt.async_support.latoken import latoken # noqa: F401
144
144
  from ccxt.async_support.lbank import lbank # noqa: F401
145
145
  from ccxt.async_support.luno import luno # noqa: F401
146
- from ccxt.async_support.lykke import lykke # noqa: F401
147
146
  from ccxt.async_support.mercado import mercado # noqa: F401
148
147
  from ccxt.async_support.mexc import mexc # noqa: F401
149
148
  from ccxt.async_support.myokx import myokx # noqa: F401
@@ -257,7 +256,6 @@ exchanges = [
257
256
  'latoken',
258
257
  'lbank',
259
258
  'luno',
260
- 'lykke',
261
259
  'mercado',
262
260
  'mexc',
263
261
  'myokx',
@@ -366,6 +366,7 @@ class ascendex(Exchange, ImplicitAPI):
366
366
  'untilDays': 100000,
367
367
  'trigger': False,
368
368
  'trailing': False,
369
+ 'symbolRequired': False,
369
370
  },
370
371
  },
371
372
  'forDerivatives': {
@@ -389,6 +390,7 @@ class ascendex(Exchange, ImplicitAPI):
389
390
  'untilDays': None,
390
391
  'trigger': False,
391
392
  'trailing': False,
393
+ 'symbolRequired': False,
392
394
  },
393
395
  },
394
396
  'swap': {
@@ -2,7 +2,7 @@
2
2
 
3
3
  # -----------------------------------------------------------------------------
4
4
 
5
- __version__ = '4.4.53'
5
+ __version__ = '4.4.58'
6
6
 
7
7
  # -----------------------------------------------------------------------------
8
8
 
@@ -1648,25 +1648,25 @@ class Exchange(BaseExchange):
1648
1648
 
1649
1649
  async def create_post_only_order(self, symbol: str, type: OrderType, side: OrderSide, amount: float, price: Num = None, params={}):
1650
1650
  if not self.has['createPostOnlyOrder']:
1651
- raise NotSupported(self.id + 'createPostOnlyOrder() is not supported yet')
1651
+ raise NotSupported(self.id + ' createPostOnlyOrder() is not supported yet')
1652
1652
  query = self.extend(params, {'postOnly': True})
1653
1653
  return await self.create_order(symbol, type, side, amount, price, query)
1654
1654
 
1655
1655
  async def create_post_only_order_ws(self, symbol: str, type: OrderType, side: OrderSide, amount: float, price: Num = None, params={}):
1656
1656
  if not self.has['createPostOnlyOrderWs']:
1657
- raise NotSupported(self.id + 'createPostOnlyOrderWs() is not supported yet')
1657
+ raise NotSupported(self.id + ' createPostOnlyOrderWs() is not supported yet')
1658
1658
  query = self.extend(params, {'postOnly': True})
1659
1659
  return await self.create_order_ws(symbol, type, side, amount, price, query)
1660
1660
 
1661
1661
  async def create_reduce_only_order(self, symbol: str, type: OrderType, side: OrderSide, amount: float, price: Num = None, params={}):
1662
1662
  if not self.has['createReduceOnlyOrder']:
1663
- raise NotSupported(self.id + 'createReduceOnlyOrder() is not supported yet')
1663
+ raise NotSupported(self.id + ' createReduceOnlyOrder() is not supported yet')
1664
1664
  query = self.extend(params, {'reduceOnly': True})
1665
1665
  return await self.create_order(symbol, type, side, amount, price, query)
1666
1666
 
1667
1667
  async def create_reduce_only_order_ws(self, symbol: str, type: OrderType, side: OrderSide, amount: float, price: Num = None, params={}):
1668
1668
  if not self.has['createReduceOnlyOrderWs']:
1669
- raise NotSupported(self.id + 'createReduceOnlyOrderWs() is not supported yet')
1669
+ raise NotSupported(self.id + ' createReduceOnlyOrderWs() is not supported yet')
1670
1670
  query = self.extend(params, {'reduceOnly': True})
1671
1671
  return await self.create_order_ws(symbol, type, side, amount, price, query)
1672
1672
 
@@ -1585,6 +1585,7 @@ class binance(Exchange, ImplicitAPI):
1585
1585
  'legalMoneyCurrenciesById': {
1586
1586
  'BUSD': 'USD',
1587
1587
  },
1588
+ 'defaultWithdrawPrecision': 0.00000001,
1588
1589
  },
1589
1590
  'features': {
1590
1591
  'spot': {
@@ -1635,6 +1636,7 @@ class binance(Exchange, ImplicitAPI):
1635
1636
  'limit': None,
1636
1637
  'trigger': False,
1637
1638
  'trailing': False,
1639
+ 'symbolRequired': False,
1638
1640
  },
1639
1641
  'fetchOrders': {
1640
1642
  'marginMode': True,
@@ -3097,6 +3099,7 @@ class binance(Exchange, ImplicitAPI):
3097
3099
  id = self.safe_string(entry, 'coin')
3098
3100
  name = self.safe_string(entry, 'name')
3099
3101
  code = self.safe_currency_code(id)
3102
+ isFiat = self.safe_bool(entry, 'isLegalMoney')
3100
3103
  minPrecision = None
3101
3104
  isWithdrawEnabled = True
3102
3105
  isDepositEnabled = True
@@ -3108,6 +3111,7 @@ class binance(Exchange, ImplicitAPI):
3108
3111
  networkItem = networkList[j]
3109
3112
  network = self.safe_string(networkItem, 'network')
3110
3113
  networkCode = self.network_id_to_code(network)
3114
+ isETF = (network == 'ETF') # e.g. BTCUP, ETHDOWN
3111
3115
  # name = self.safe_string(networkItem, 'name')
3112
3116
  withdrawFee = self.safe_number(networkItem, 'withdrawFee')
3113
3117
  depositEnable = self.safe_bool(networkItem, 'depositEnable')
@@ -3118,11 +3122,22 @@ class binance(Exchange, ImplicitAPI):
3118
3122
  isDefault = self.safe_bool(networkItem, 'isDefault')
3119
3123
  if isDefault or (fee is None):
3120
3124
  fee = withdrawFee
3125
+ # todo: default networks in "setMarkets" overload
3126
+ # if isDefault:
3127
+ # self.options['defaultNetworkCodesForCurrencies'][code] = networkCode
3128
+ # }
3121
3129
  precisionTick = self.safe_string(networkItem, 'withdrawIntegerMultiple')
3122
- # avoid zero values, which are mostly from fiat or leveraged tokens : https://github.com/ccxt/ccxt/pull/14902#issuecomment-1271636731
3123
- # so, when there is zero instead of i.e. 0.001, then we skip those cases, because we don't know the precision - it might be because of network is suspended or other reasons
3130
+ withdrawPrecision = precisionTick
3131
+ # avoid zero values, which are mostly from fiat or leveraged tokens or some abandoned coins : https://github.com/ccxt/ccxt/pull/14902#issuecomment-1271636731
3124
3132
  if not Precise.string_eq(precisionTick, '0'):
3125
3133
  minPrecision = precisionTick if (minPrecision is None) else Precise.string_min(minPrecision, precisionTick)
3134
+ else:
3135
+ if not isFiat and not isETF:
3136
+ # non-fiat and non-ETF currency, there are many cases when precision is set to zero(probably bug, we've reported to binance already)
3137
+ # in such cases, we can set default precision of 8(which is in UI for such coins)
3138
+ withdrawPrecision = self.omit_zero(self.safe_string(networkItem, 'withdrawInternalMin'))
3139
+ if withdrawPrecision is None:
3140
+ withdrawPrecision = self.safe_string(self.options, 'defaultWithdrawPrecision')
3126
3141
  networks[networkCode] = {
3127
3142
  'info': networkItem,
3128
3143
  'id': network,
@@ -3131,7 +3146,7 @@ class binance(Exchange, ImplicitAPI):
3131
3146
  'deposit': depositEnable,
3132
3147
  'withdraw': withdrawEnable,
3133
3148
  'fee': withdrawFee,
3134
- 'precision': self.parse_number(precisionTick),
3149
+ 'precision': self.parse_number(withdrawPrecision),
3135
3150
  'limits': {
3136
3151
  'withdraw': {
3137
3152
  'min': self.safe_number(networkItem, 'withdrawMin'),
@@ -3160,6 +3175,7 @@ class binance(Exchange, ImplicitAPI):
3160
3175
  'id': id,
3161
3176
  'name': name,
3162
3177
  'code': code,
3178
+ 'type': 'fiat' if isFiat else 'crypto',
3163
3179
  'precision': self.parse_number(minPrecision),
3164
3180
  'info': entry,
3165
3181
  'active': active,
@@ -5273,10 +5289,10 @@ class binance(Exchange, ImplicitAPI):
5273
5289
  request: dict = {
5274
5290
  'symbol': market['id'],
5275
5291
  'side': side.upper(),
5292
+ 'orderId': id,
5293
+ 'quantity': self.amount_to_precision(symbol, amount),
5276
5294
  }
5277
5295
  clientOrderId = self.safe_string_n(params, ['newClientOrderId', 'clientOrderId', 'origClientOrderId'])
5278
- request['orderId'] = id
5279
- request['quantity'] = self.amount_to_precision(symbol, amount)
5280
5296
  if price is not None:
5281
5297
  request['price'] = self.price_to_precision(symbol, price)
5282
5298
  if clientOrderId is not None:
@@ -5290,6 +5306,8 @@ class binance(Exchange, ImplicitAPI):
5290
5306
 
5291
5307
  https://developers.binance.com/docs/derivatives/usds-margined-futures/trade/rest-api/Modify-Order
5292
5308
  https://developers.binance.com/docs/derivatives/coin-margined-futures/trade/Modify-Order
5309
+ https://developers.binance.com/docs/derivatives/portfolio-margin/trade/Modify-UM-Order
5310
+ https://developers.binance.com/docs/derivatives/portfolio-margin/trade/Modify-CM-Order
5293
5311
 
5294
5312
  :param str id: cancel order id
5295
5313
  :param str symbol: unified symbol of the market to create an order in
@@ -5298,16 +5316,28 @@ class binance(Exchange, ImplicitAPI):
5298
5316
  :param float amount: how much of currency you want to trade in units of base currency
5299
5317
  :param float [price]: the price at which the order is to be fulfilled, in units of the quote currency, ignored in market orders
5300
5318
  :param dict [params]: extra parameters specific to the exchange API endpoint
5319
+ :param boolean [params.portfolioMargin]: set to True if you would like to edit an order in a portfolio margin account
5301
5320
  :returns dict: an `order structure <https://docs.ccxt.com/#/?id=order-structure>`
5302
5321
  """
5303
5322
  await self.load_markets()
5304
5323
  market = self.market(symbol)
5324
+ isPortfolioMargin = None
5325
+ isPortfolioMargin, params = self.handle_option_and_params_2(params, 'editContractOrder', 'papi', 'portfolioMargin', False)
5326
+ if market['linear'] or isPortfolioMargin:
5327
+ if (price is None) and not ('priceMatch' in params):
5328
+ raise ArgumentsRequired(self.id + ' editOrder() requires a price argument for portfolio margin and linear orders')
5305
5329
  request = self.edit_contract_order_request(id, symbol, type, side, amount, price, params)
5306
5330
  response = None
5307
5331
  if market['linear']:
5308
- response = await self.fapiPrivatePutOrder(self.extend(request, params))
5332
+ if isPortfolioMargin:
5333
+ response = await self.papiPutUmOrder(self.extend(request, params))
5334
+ else:
5335
+ response = await self.fapiPrivatePutOrder(self.extend(request, params))
5309
5336
  elif market['inverse']:
5310
- response = await self.dapiPrivatePutOrder(self.extend(request, params))
5337
+ if isPortfolioMargin:
5338
+ response = await self.papiPutCmOrder(self.extend(request, params))
5339
+ else:
5340
+ response = await self.dapiPrivatePutOrder(self.extend(request, params))
5311
5341
  #
5312
5342
  # swap and future
5313
5343
  #
@@ -12060,7 +12090,7 @@ class binance(Exchange, ImplicitAPI):
12060
12090
  :returns dict: an array of `open interest structure <https://docs.ccxt.com/#/?id=open-interest-structure>`
12061
12091
  """
12062
12092
  if timeframe == '1m':
12063
- raise BadRequest(self.id + 'fetchOpenInterestHistory cannot use the 1m timeframe')
12093
+ raise BadRequest(self.id + ' fetchOpenInterestHistory cannot use the 1m timeframe')
12064
12094
  await self.load_markets()
12065
12095
  paginate = False
12066
12096
  paginate, params = self.handle_option_and_params(params, 'fetchOpenInterestHistory', 'paginate', False)
@@ -12819,7 +12849,7 @@ class binance(Exchange, ImplicitAPI):
12819
12849
  elif market['inverse']:
12820
12850
  response = await self.dapiPrivateGetPositionMarginHistory(self.extend(request, params))
12821
12851
  else:
12822
- raise BadRequest(self.id + 'fetchMarginAdjustmentHistory() is not supported for markets of type ' + market['type'])
12852
+ raise BadRequest(self.id + ' fetchMarginAdjustmentHistory() is not supported for markets of type ' + market['type'])
12823
12853
  #
12824
12854
  # [
12825
12855
  # {
@@ -4633,7 +4633,7 @@ class bitget(Exchange, ImplicitAPI):
4633
4633
  response = None
4634
4634
  if market['spot']:
4635
4635
  if triggerPrice is None:
4636
- raise NotSupported(self.id + 'editOrder() only supports plan/trigger spot orders')
4636
+ raise NotSupported(self.id + ' editOrder() only supports plan/trigger spot orders')
4637
4637
  editMarketBuyOrderRequiresPrice = self.safe_bool(self.options, 'editMarketBuyOrderRequiresPrice', True)
4638
4638
  if editMarketBuyOrderRequiresPrice and isMarketOrder and (side == 'buy'):
4639
4639
  if price is None:
@@ -8855,4 +8855,9 @@ class bitget(Exchange, ImplicitAPI):
8855
8855
  }
8856
8856
  if method == 'POST':
8857
8857
  headers['Content-Type'] = 'application/json'
8858
+ sandboxMode = self.safe_bool(self.options, 'sandboxMode', False)
8859
+ if sandboxMode:
8860
+ if headers is None:
8861
+ headers = {}
8862
+ headers['PAPTRADING'] = 1
8858
8863
  return {'url': url, 'method': method, 'body': body, 'headers': headers}
@@ -528,7 +528,10 @@ class bitmart(Exchange, ImplicitAPI):
528
528
  '40049': InvalidOrder, # 403, The maximum length of clientOrderId cannot exceed 32
529
529
  '40050': InvalidOrder, # 403, Client OrderId duplicated with existing orders
530
530
  },
531
- 'broad': {},
531
+ 'broad': {
532
+ 'You contract account available balance not enough': InsufficientFunds,
533
+ 'you contract account available balance not enough': InsufficientFunds,
534
+ },
532
535
  },
533
536
  'commonCurrencies': {
534
537
  '$GM': 'GOLDMINER',
@@ -968,6 +971,7 @@ class bitmart(Exchange, ImplicitAPI):
968
971
  data = self.safe_dict(response, 'data', {})
969
972
  symbols = self.safe_list(data, 'symbols', [])
970
973
  result = []
974
+ fees = self.fees['trading']
971
975
  for i in range(0, len(symbols)):
972
976
  market = symbols[i]
973
977
  id = self.safe_string(market, 'symbol')
@@ -1006,6 +1010,8 @@ class bitmart(Exchange, ImplicitAPI):
1006
1010
  'expiryDatetime': None,
1007
1011
  'strike': None,
1008
1012
  'optionType': None,
1013
+ 'maker': fees['maker'],
1014
+ 'taker': fees['taker'],
1009
1015
  'precision': {
1010
1016
  'amount': baseMinSize,
1011
1017
  'price': self.parse_number(self.parse_precision(self.safe_string(market, 'price_max_precision'))),
@@ -1077,6 +1083,7 @@ class bitmart(Exchange, ImplicitAPI):
1077
1083
  data = self.safe_dict(response, 'data', {})
1078
1084
  symbols = self.safe_list(data, 'symbols', [])
1079
1085
  result = []
1086
+ fees = self.fees['trading']
1080
1087
  for i in range(0, len(symbols)):
1081
1088
  market = symbols[i]
1082
1089
  id = self.safe_string(market, 'symbol')
@@ -1118,6 +1125,8 @@ class bitmart(Exchange, ImplicitAPI):
1118
1125
  'expiryDatetime': self.iso8601(expiry),
1119
1126
  'strike': None,
1120
1127
  'optionType': None,
1128
+ 'maker': fees['maker'],
1129
+ 'taker': fees['taker'],
1121
1130
  'precision': {
1122
1131
  'amount': self.safe_number(market, 'vol_precision'),
1123
1132
  'price': self.safe_number(market, 'price_precision'),
@@ -5085,6 +5094,7 @@ class bitmart(Exchange, ImplicitAPI):
5085
5094
  # {"message":"Bad Request [from is empty]","code":50000,"trace":"579986f7-c93a-4559-926b-06ba9fa79d76","data":{}}
5086
5095
  # {"message":"Kline size over 500","code":50004,"trace":"d625caa8-e8ca-4bd2-b77c-958776965819","data":{}}
5087
5096
  # {"message":"Balance not enough","code":50020,"trace":"7c709d6a-3292-462c-98c5-32362540aeef","data":{}}
5097
+ # {"code":40012,"message":"You contract account available balance not enough.","trace":"..."}
5088
5098
  #
5089
5099
  # contract
5090
5100
  #
@@ -5096,9 +5106,9 @@ class bitmart(Exchange, ImplicitAPI):
5096
5106
  isErrorCode = (errorCode is not None) and (errorCode != '1000')
5097
5107
  if isErrorCode or isErrorMessage:
5098
5108
  feedback = self.id + ' ' + body
5099
- self.throw_exactly_matched_exception(self.exceptions['exact'], errorCode, feedback)
5100
- self.throw_broadly_matched_exception(self.exceptions['broad'], errorCode, feedback)
5101
5109
  self.throw_exactly_matched_exception(self.exceptions['exact'], message, feedback)
5102
5110
  self.throw_broadly_matched_exception(self.exceptions['broad'], message, feedback)
5111
+ self.throw_exactly_matched_exception(self.exceptions['exact'], errorCode, feedback)
5112
+ self.throw_broadly_matched_exception(self.exceptions['broad'], errorCode, feedback)
5103
5113
  raise ExchangeError(feedback) # unknown message
5104
5114
  return None
@@ -328,8 +328,7 @@ class blofin(Exchange, ImplicitAPI):
328
328
  'takeProfitPrice': True,
329
329
  'attachedStopLossTakeProfit': {
330
330
  'triggerPriceType': None,
331
- 'limit': True,
332
- 'price': None,
331
+ 'price': True,
333
332
  },
334
333
  'hedged': True,
335
334
  },
@@ -1421,8 +1421,8 @@ class bybit(Exchange, ImplicitAPI):
1421
1421
 
1422
1422
  def create_expired_option_market(self, symbol: str):
1423
1423
  # support expired option contracts
1424
- quote = 'USD'
1425
- settle = 'USDC'
1424
+ quote = None
1425
+ settle = None
1426
1426
  optionParts = symbol.split('-')
1427
1427
  symbolBase = symbol.split('/')
1428
1428
  base = None
@@ -1430,9 +1430,20 @@ class bybit(Exchange, ImplicitAPI):
1430
1430
  if symbol.find('/') > -1:
1431
1431
  base = self.safe_string(symbolBase, 0)
1432
1432
  expiry = self.safe_string(optionParts, 1)
1433
+ symbolQuoteAndSettle = self.safe_string(symbolBase, 1)
1434
+ splitQuote = symbolQuoteAndSettle.split(':')
1435
+ quoteAndSettle = self.safe_string(splitQuote, 0)
1436
+ quote = quoteAndSettle
1437
+ settle = quoteAndSettle
1433
1438
  else:
1434
1439
  base = self.safe_string(optionParts, 0)
1435
1440
  expiry = self.convert_market_id_expire_date(self.safe_string(optionParts, 1))
1441
+ if symbol.endswith('-USDT'):
1442
+ quote = 'USDT'
1443
+ settle = 'USDT'
1444
+ else:
1445
+ quote = 'USDC'
1446
+ settle = 'USDC'
1436
1447
  strike = self.safe_string(optionParts, 2)
1437
1448
  optionType = self.safe_string(optionParts, 3)
1438
1449
  datetime = self.convert_expire_date(expiry)
@@ -6489,7 +6500,7 @@ classic accounts only/ spot not supported* fetches information on an order made
6489
6500
  :returns: An array of open interest structures
6490
6501
  """
6491
6502
  if timeframe == '1m':
6492
- raise BadRequest(self.id + 'fetchOpenInterestHistory cannot use the 1m timeframe')
6503
+ raise BadRequest(self.id + ' fetchOpenInterestHistory cannot use the 1m timeframe')
6493
6504
  await self.load_markets()
6494
6505
  paginate = self.safe_bool(params, 'paginate')
6495
6506
  if paginate:
@@ -8705,6 +8716,8 @@ classic accounts only/ spot not supported* fetches information on an order made
8705
8716
  feedback = self.id + ' private api uses /user/v3/private/query-api to check if you have a unified account. The API key of user id must own one of permissions: "Account Transfer", "Subaccount Transfer", "Withdrawal" ' + body
8706
8717
  else:
8707
8718
  feedback = self.id + ' ' + body
8719
+ if body.find('Withdraw address chain or destination tag are not equal'):
8720
+ feedback = feedback + '; You might also need to ensure the address is whitelisted'
8708
8721
  self.throw_broadly_matched_exception(self.exceptions['broad'], body, feedback)
8709
8722
  self.throw_exactly_matched_exception(self.exceptions['exact'], errorCode, feedback)
8710
8723
  raise ExchangeError(feedback) # unknown message
@@ -453,7 +453,7 @@ class coinbase(Exchange, ImplicitAPI):
453
453
  'symbolRequired': False,
454
454
  },
455
455
  'fetchOHLCV': {
456
- 'limit': 350,
456
+ 'limit': 300,
457
457
  },
458
458
  },
459
459
  'spot': {
@@ -4796,10 +4796,17 @@ class coinbase(Exchange, ImplicitAPI):
4796
4796
  # }
4797
4797
  # ]
4798
4798
  # }
4799
+ # or
4800
+ # {
4801
+ # "error": "UNKNOWN_FAILURE_REASON",
4802
+ # "message": "",
4803
+ # "error_details": "",
4804
+ # "preview_failure_reason": "PREVIEW_STOP_PRICE_BELOW_LAST_TRADE_PRICE"
4805
+ # }
4799
4806
  #
4800
4807
  errorCode = self.safe_string(response, 'error')
4801
4808
  if errorCode is not None:
4802
- errorMessage = self.safe_string(response, 'error_description')
4809
+ errorMessage = self.safe_string_2(response, 'error_description', 'preview_failure_reason')
4803
4810
  self.throw_exactly_matched_exception(self.exceptions['exact'], errorCode, feedback)
4804
4811
  self.throw_broadly_matched_exception(self.exceptions['broad'], errorMessage, feedback)
4805
4812
  raise ExchangeError(feedback)
@@ -1684,7 +1684,7 @@ class coinbaseinternational(Exchange, ImplicitAPI):
1684
1684
  request['type'] = typeId
1685
1685
  if type == 'limit':
1686
1686
  if price is None:
1687
- raise InvalidOrder(self.id + 'createOrder() requires a price parameter for a limit order types')
1687
+ raise InvalidOrder(self.id + ' createOrder() requires a price parameter for a limit order types')
1688
1688
  request['price'] = price
1689
1689
  portfolio = None
1690
1690
  portfolio, params = await self.handle_portfolio_and_params('createOrder', params)
@@ -1695,7 +1695,7 @@ class coinbaseinternational(Exchange, ImplicitAPI):
1695
1695
  # market orders must be IOC
1696
1696
  if typeId == 'MARKET':
1697
1697
  if tif is not None and tif != 'IOC':
1698
- raise InvalidOrder(self.id + 'createOrder() market orders must have tif set to "IOC"')
1698
+ raise InvalidOrder(self.id + ' createOrder() market orders must have tif set to "IOC"')
1699
1699
  tif = 'IOC'
1700
1700
  else:
1701
1701
  tif = 'GTC' if (tif is None) else tif
@@ -4616,7 +4616,7 @@ class coinex(Exchange, ImplicitAPI):
4616
4616
  request: dict = {
4617
4617
  'ccy': currency['id'],
4618
4618
  'to_address': address, # must be authorized, inter-user transfer by a registered mobile phone number or an email address is supported
4619
- 'amount': self.number_to_string(amount), # the actual amount without fees, https://www.coinex.com/fees
4619
+ 'amount': self.currency_to_precision(code, amount), # the actual amount without fees, https://www.coinex.com/fees
4620
4620
  }
4621
4621
  if tag is not None:
4622
4622
  request['memo'] = tag
@@ -498,9 +498,6 @@ class deribit(Exchange, ImplicitAPI):
498
498
  'fetchBalance': {
499
499
  'code': 'BTC',
500
500
  },
501
- 'fetchPositions': {
502
- 'code': 'BTC',
503
- },
504
501
  'transfer': {
505
502
  'method': 'privateGetSubmitTransferToSubaccount', # or 'privateGetSubmitTransferToUser'
506
503
  },
@@ -2663,32 +2660,18 @@ class deribit(Exchange, ImplicitAPI):
2663
2660
 
2664
2661
  :param str[]|None symbols: list of unified market symbols
2665
2662
  :param dict [params]: extra parameters specific to the exchange API endpoint
2663
+ :param str [params.currency]: currency code filter for positions
2666
2664
  :param str [params.kind]: market type filter for positions 'future', 'option', 'spot', 'future_combo' or 'option_combo'
2665
+ :param int [params.subaccount_id]: the user id for the subaccount
2667
2666
  :returns dict[]: a list of `position structure <https://docs.ccxt.com/#/?id=position-structure>`
2668
2667
  """
2669
2668
  await self.load_markets()
2670
- kind = self.safe_string(params, 'kind')
2671
- code = None
2672
- if symbols is None:
2673
- code = self.code_from_options('fetchPositions', params)
2674
- elif isinstance(symbols, str):
2675
- code = symbols
2676
- symbols = None # fix https://github.com/ccxt/ccxt/issues/13961
2677
- else:
2678
- if isinstance(symbols, list):
2679
- length = len(symbols)
2680
- if length != 1:
2681
- raise BadRequest(self.id + ' fetchPositions() symbols argument cannot contain more than 1 symbol')
2682
- market = self.market(symbols[0])
2683
- settle = market['settle']
2684
- code = settle if (settle is not None) else market['base']
2685
- kind = market['info']['kind']
2686
- currency = self.currency(code)
2687
- request: dict = {
2688
- 'currency': currency['id'],
2689
- }
2690
- if kind is not None:
2691
- request['kind'] = kind
2669
+ code = self.safe_string(params, 'currency')
2670
+ request: dict = {}
2671
+ if code is not None:
2672
+ params = self.omit(params, 'currency')
2673
+ currency = self.currency(code)
2674
+ request['currency'] = currency['id']
2692
2675
  response = await self.privateGetGetPositions(self.extend(request, params))
2693
2676
  #
2694
2677
  # {
@@ -1351,7 +1351,7 @@ class exmo(Exchange, ImplicitAPI):
1351
1351
  marginMode = None
1352
1352
  marginMode, params = self.handle_margin_mode_and_params('fetchMyTrades', params)
1353
1353
  if marginMode == 'cross':
1354
- raise BadRequest(self.id + 'only isolated margin is supported')
1354
+ raise BadRequest(self.id + ' only isolated margin is supported')
1355
1355
  await self.load_markets()
1356
1356
  market = self.market(symbol)
1357
1357
  pair = market['id']
@@ -204,7 +204,7 @@ class hyperliquid(Exchange, ImplicitAPI):
204
204
  'broad': {
205
205
  'Price must be divisible by tick size.': InvalidOrder,
206
206
  'Order must have minimum value of $10': InvalidOrder,
207
- 'Insufficient margin to place order.': InvalidOrder,
207
+ 'Insufficient margin to place order.': InsufficientFunds,
208
208
  'Reduce only order would increase position.': InvalidOrder,
209
209
  'Post only order would have immediately matched,': InvalidOrder,
210
210
  'Order could not immediately match against any resting orders.': InvalidOrder,
@@ -602,7 +602,9 @@ class hyperliquid(Exchange, ImplicitAPI):
602
602
  amountPrecisionStr = self.safe_string(innerBaseTokenInfo, 'szDecimals')
603
603
  amountPrecision = int(amountPrecisionStr)
604
604
  price = self.safe_number(extraData, 'midPx')
605
- pricePrecision = self.calculate_price_precision(price, amountPrecision, 8)
605
+ pricePrecision = 0
606
+ if price is not None:
607
+ pricePrecision = self.calculate_price_precision(price, amountPrecision, 8)
606
608
  pricePrecisionStr = self.number_to_string(pricePrecision)
607
609
  # quotePrecision = self.parse_number(self.parse_precision(self.safe_string(innerQuoteTokenInfo, 'szDecimals')))
608
610
  baseId = self.number_to_string(index + 10000)
@@ -699,7 +701,9 @@ class hyperliquid(Exchange, ImplicitAPI):
699
701
  amountPrecisionStr = self.safe_string(market, 'szDecimals')
700
702
  amountPrecision = int(amountPrecisionStr)
701
703
  price = self.safe_number(market, 'markPx', 0)
702
- pricePrecision = self.calculate_price_precision(price, amountPrecision, 6)
704
+ pricePrecision = 0
705
+ if price is not None:
706
+ pricePrecision = self.calculate_price_precision(price, amountPrecision, 6)
703
707
  pricePrecisionStr = self.number_to_string(pricePrecision)
704
708
  isDelisted = self.safe_bool(market, 'isDelisted')
705
709
  active = True
@@ -2707,7 +2711,7 @@ class hyperliquid(Exchange, ImplicitAPI):
2707
2711
  if self.in_array(fromAccount, ['spot', 'swap', 'perp']):
2708
2712
  # handle swap <> spot account transfer
2709
2713
  if not self.in_array(toAccount, ['spot', 'swap', 'perp']):
2710
- raise NotSupported(self.id + 'transfer() only support spot <> swap transfer')
2714
+ raise NotSupported(self.id + ' transfer() only support spot <> swap transfer')
2711
2715
  strAmount = self.number_to_string(amount)
2712
2716
  vaultAddress = self.format_vault_address(self.safe_string(params, 'vaultAddress'))
2713
2717
  params = self.omit(params, 'vaultAddress')
@@ -2742,7 +2746,7 @@ class hyperliquid(Exchange, ImplicitAPI):
2742
2746
  if code is not None:
2743
2747
  code = code.upper()
2744
2748
  if code != 'USDC':
2745
- raise NotSupported(self.id + 'transfer() only support USDC')
2749
+ raise NotSupported(self.id + ' transfer() only support USDC')
2746
2750
  payload: dict = {
2747
2751
  'hyperliquidChain': 'Testnet' if isSandboxMode else 'Mainnet',
2748
2752
  'destination': toAccount,
@@ -2805,7 +2809,7 @@ class hyperliquid(Exchange, ImplicitAPI):
2805
2809
  if code is not None:
2806
2810
  code = code.upper()
2807
2811
  if code != 'USDC':
2808
- raise NotSupported(self.id + 'withdraw() only support USDC')
2812
+ raise NotSupported(self.id + ' withdraw() only support USDC')
2809
2813
  vaultAddress = self.format_vault_address(self.safe_string(params, 'vaultAddress'))
2810
2814
  params = self.omit(params, 'vaultAddress')
2811
2815
  nonce = self.milliseconds()
@@ -3265,6 +3269,7 @@ class hyperliquid(Exchange, ImplicitAPI):
3265
3269
  # status: 'ok',
3266
3270
  # response: {type: 'order', data: {statuses: [{error: 'Insufficient margin to place order. asset=4'}]}}
3267
3271
  # }
3272
+ # {"status":"ok","response":{"type":"order","data":{"statuses":[{"error":"Insufficient margin to place order. asset=84"}]}}}
3268
3273
  #
3269
3274
  status = self.safe_string(response, 'status', '')
3270
3275
  message = None
@@ -2365,7 +2365,7 @@ class kraken(Exchange, ImplicitAPI):
2365
2365
  :returns dict: the api result
2366
2366
  """
2367
2367
  if timeout > 86400000:
2368
- raise BadRequest(self.id + 'cancelAllOrdersAfter timeout should be less than 86400000 milliseconds')
2368
+ raise BadRequest(self.id + ' cancelAllOrdersAfter timeout should be less than 86400000 milliseconds')
2369
2369
  await self.load_markets()
2370
2370
  request: dict = {
2371
2371
  'timeout': (self.parse_to_int(timeout / 1000)) if (timeout > 0) else 0,