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
@@ -729,6 +729,7 @@ class timex(Exchange, ImplicitAPI):
729
729
  :param int [since]: timestamp in ms of the earliest candle to fetch
730
730
  :param int [limit]: the maximum amount of candles to fetch
731
731
  :param dict [params]: extra parameters specific to the exchange API endpoint
732
+ :param int [params.until]: timestamp in ms of the latest candle to fetch
732
733
  :returns int[][]: A list of candles ordered, open, high, low, close, volume
733
734
  """
734
735
  await self.load_markets()
@@ -739,15 +740,24 @@ class timex(Exchange, ImplicitAPI):
739
740
  }
740
741
  # if since and limit are not specified
741
742
  duration = self.parse_timeframe(timeframe)
743
+ until = self.safe_integer(params, 'until')
742
744
  if limit is None:
743
745
  limit = 1000 # exchange provides tens of thousands of data, but we set generous default value
744
746
  if since is not None:
745
747
  request['from'] = self.iso8601(since)
746
- request['till'] = self.iso8601(self.sum(since, self.sum(limit, 1) * duration * 1000))
748
+ if until is None:
749
+ request['till'] = self.iso8601(self.sum(since, self.sum(limit, 1) * duration * 1000))
750
+ else:
751
+ request['till'] = self.iso8601(until)
752
+ elif until is not None:
753
+ request['till'] = self.iso8601(until)
754
+ fromTimestamp = until - self.sum(limit, 1) * duration * 1000
755
+ request['from'] = self.iso8601(fromTimestamp)
747
756
  else:
748
757
  now = self.milliseconds()
749
758
  request['till'] = self.iso8601(now)
750
- request['from'] = self.iso8601(now - limit * duration * 1000 - 1)
759
+ request['from'] = self.iso8601(now - self.sum(limit, 1) * duration * 1000 - 1)
760
+ params = self.omit(params, 'until')
751
761
  response = await self.publicGetCandles(self.extend(request, params))
752
762
  #
753
763
  # [
@@ -1523,7 +1523,7 @@ class vertex(Exchange, ImplicitAPI):
1523
1523
  marketId = base + '/' + quote
1524
1524
  if base.find('PERP') > 0:
1525
1525
  marketId = marketId.replace('-PERP', '') + ':USDC'
1526
- market = self.market(marketId)
1526
+ market = self.safe_market(marketId, market)
1527
1527
  last = self.safe_string(ticker, 'last_price')
1528
1528
  return self.safe_ticker({
1529
1529
  'symbol': market['symbol'],
@@ -2626,12 +2626,13 @@ class whitebit(Exchange, ImplicitAPI):
2626
2626
  # For cases where we have a meaningful status
2627
2627
  # {"response":null,"status":422,"errors":{"orderId":["Finished order id 435453454535 not found on your account"]},"notification":null,"warning":"Finished order id 435453454535 not found on your account","_token":null}
2628
2628
  status = self.safe_string(response, 'status')
2629
+ errors = self.safe_value(response, 'errors')
2629
2630
  # {"code":10,"message":"Unauthorized request."}
2630
2631
  message = self.safe_string(response, 'message')
2631
2632
  # For these cases where we have a generic code variable error key
2632
2633
  # {"code":0,"message":"Validation failed","errors":{"amount":["Amount must be greater than 0"]}}
2633
2634
  codeNew = self.safe_integer(response, 'code')
2634
- hasErrorStatus = status is not None and status != '200'
2635
+ hasErrorStatus = status is not None and status != '200' and errors is not None
2635
2636
  if hasErrorStatus or codeNew is not None:
2636
2637
  feedback = self.id + ' ' + body
2637
2638
  errorInfo = message
@@ -1507,7 +1507,7 @@ class woofipro(Exchange, ImplicitAPI):
1507
1507
  takeProfit = self.safe_value(orderParams, 'takeProfit')
1508
1508
  isConditional = triggerPrice is not None or stopLoss is not None or takeProfit is not None or (self.safe_value(orderParams, 'childOrders') is not None)
1509
1509
  if isConditional:
1510
- raise NotSupported(self.id + 'createOrders() only support non-stop order')
1510
+ raise NotSupported(self.id + ' createOrders() only support non-stop order')
1511
1511
  orderRequest = self.create_order_request(marketId, type, side, amount, price, orderParams)
1512
1512
  ordersRequests.append(orderRequest)
1513
1513
  request: dict = {
@@ -2355,7 +2355,7 @@ class woofipro(Exchange, ImplicitAPI):
2355
2355
  if code is not None:
2356
2356
  code = code.upper()
2357
2357
  if code != 'USDC':
2358
- raise NotSupported(self.id + 'withdraw() only support USDC')
2358
+ raise NotSupported(self.id + ' withdraw() only support USDC')
2359
2359
  currency = self.currency(code)
2360
2360
  verifyingContractAddress = self.safe_string(self.options, 'verifyingContractAddress')
2361
2361
  chainId = self.safe_string(params, 'chainId')
ccxt/async_support/xt.py CHANGED
@@ -129,7 +129,7 @@ class xt(Exchange, ImplicitAPI):
129
129
  'repayMargin': False,
130
130
  'setLeverage': True,
131
131
  'setMargin': False,
132
- 'setMarginMode': False,
132
+ 'setMarginMode': True,
133
133
  'setPositionMode': False,
134
134
  'signIn': False,
135
135
  'transfer': True,
@@ -287,6 +287,7 @@ class xt(Exchange, ImplicitAPI):
287
287
  'future/user/v1/position/margin': 1,
288
288
  'future/user/v1/user/collection/add': 1,
289
289
  'future/user/v1/user/collection/cancel': 1,
290
+ 'future/user/v1/position/change-type': 1,
290
291
  },
291
292
  },
292
293
  'inverse': {
@@ -532,10 +533,12 @@ class xt(Exchange, ImplicitAPI):
532
533
  'TRANSFER_012': PermissionDenied, # Currency transfer prohibited
533
534
  'symbol_not_support_trading_via_api': BadSymbol, # {"returnCode":1,"msgInfo":"failure","error":{"code":"symbol_not_support_trading_via_api","msg":"The symbol does not support trading via API"},"result":null}
534
535
  'open_order_min_nominal_value_limit': InvalidOrder, # {"returnCode":1,"msgInfo":"failure","error":{"code":"open_order_min_nominal_value_limit","msg":"Exceeds the minimum notional value of a single order"},"result":null}
536
+ 'insufficient_balance': InsufficientFunds,
535
537
  },
536
538
  'broad': {
537
539
  'The symbol does not support trading via API': BadSymbol, # {"returnCode":1,"msgInfo":"failure","error":{"code":"symbol_not_support_trading_via_api","msg":"The symbol does not support trading via API"},"result":null}
538
540
  'Exceeds the minimum notional value of a single order': InvalidOrder, # {"returnCode":1,"msgInfo":"failure","error":{"code":"open_order_min_nominal_value_limit","msg":"Exceeds the minimum notional value of a single order"},"result":null}
541
+ 'insufficient balance': InsufficientFunds,
539
542
  },
540
543
  },
541
544
  'timeframes': {
@@ -4617,6 +4620,53 @@ class xt(Exchange, ImplicitAPI):
4617
4620
  'status': None,
4618
4621
  }
4619
4622
 
4623
+ async def set_margin_mode(self, marginMode: str, symbol: Str = None, params={}):
4624
+ """
4625
+ set margin mode to 'cross' or 'isolated'
4626
+
4627
+ https://doc.xt.com/#futures_userchangePositionType
4628
+
4629
+ :param str marginMode: 'cross' or 'isolated'
4630
+ :param str [symbol]: required
4631
+ :param dict [params]: extra parameters specific to the exchange API endpoint
4632
+ :param str [params.positionSide]: *required* "long" or "short"
4633
+ :returns dict: response from the exchange
4634
+ """
4635
+ if symbol is None:
4636
+ raise ArgumentsRequired(self.id + ' setMarginMode() requires a symbol argument')
4637
+ await self.load_markets()
4638
+ market = self.market(symbol)
4639
+ if market['spot']:
4640
+ raise BadSymbol(self.id + ' setMarginMode() supports contract markets only')
4641
+ marginMode = marginMode.lower()
4642
+ if marginMode != 'isolated' and marginMode != 'cross':
4643
+ raise BadRequest(self.id + ' setMarginMode() marginMode argument should be isolated or cross')
4644
+ if marginMode == 'cross':
4645
+ marginMode = 'CROSSED'
4646
+ else:
4647
+ marginMode = 'ISOLATED'
4648
+ posSide = self.safe_string_upper(params, 'positionSide')
4649
+ if posSide is None:
4650
+ raise ArgumentsRequired(self.id + ' setMarginMode() requires a positionSide parameter, either "LONG" or "SHORT"')
4651
+ request: dict = {
4652
+ 'positionType': marginMode,
4653
+ 'positionSide': posSide,
4654
+ 'symbol': market['id'],
4655
+ }
4656
+ response = await self.privateLinearPostFutureUserV1PositionChangeType(self.extend(request, params))
4657
+ #
4658
+ # {
4659
+ # "error": {
4660
+ # "code": "",
4661
+ # "msg": ""
4662
+ # },
4663
+ # "msgInfo": "",
4664
+ # "result": {},
4665
+ # "returnCode": 0
4666
+ # }
4667
+ #
4668
+ return response # unify return type
4669
+
4620
4670
  def handle_errors(self, code, reason, url, method, headers, body, response, requestHeaders, requestBody):
4621
4671
  #
4622
4672
  # spot: error
@@ -4667,6 +4717,9 @@ class xt(Exchange, ImplicitAPI):
4667
4717
  # "result": {}
4668
4718
  # }
4669
4719
  #
4720
+ # {"returnCode":1,"msgInfo":"failure","error":{"code":"insufficient_balance","msg":"insufficient balance","args":[]},"result":null}
4721
+ #
4722
+ #
4670
4723
  status = self.safe_string_upper_2(response, 'msgInfo', 'mc')
4671
4724
  if status is not None and status != 'SUCCESS':
4672
4725
  feedback = self.id + ' ' + body
ccxt/base/errors.py CHANGED
@@ -1,9 +1,3 @@
1
- # ----------------------------------------------------------------------------
2
-
3
- # PLEASE DO NOT EDIT THIS FILE, IT IS GENERATED AND WILL BE OVERWRITTEN:
4
- # https://github.com/ccxt/ccxt/blob/master/CONTRIBUTING.md#how-to-contribute-code
5
- # EDIT THE CORRESPONDENT .ts FILE INSTEAD
6
-
7
1
  error_hierarchy = {
8
2
  'BaseError': {
9
3
  'ExchangeError': {
ccxt/base/exchange.py CHANGED
@@ -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
 
@@ -3657,7 +3657,7 @@ class Exchange(object):
3657
3657
  change = self.omit_zero(self.safe_string(ticker, 'change'))
3658
3658
  percentage = self.omit_zero(self.safe_string(ticker, 'percentage'))
3659
3659
  average = self.omit_zero(self.safe_string(ticker, 'average'))
3660
- vwap = self.omit_zero(self.safe_string(ticker, 'vwap'))
3660
+ vwap = self.safe_string(ticker, 'vwap')
3661
3661
  baseVolume = self.safe_string(ticker, 'baseVolume')
3662
3662
  quoteVolume = self.safe_string(ticker, 'quoteVolume')
3663
3663
  if vwap is None:
@@ -5544,25 +5544,25 @@ class Exchange(object):
5544
5544
 
5545
5545
  def create_post_only_order(self, symbol: str, type: OrderType, side: OrderSide, amount: float, price: Num = None, params={}):
5546
5546
  if not self.has['createPostOnlyOrder']:
5547
- raise NotSupported(self.id + 'createPostOnlyOrder() is not supported yet')
5547
+ raise NotSupported(self.id + ' createPostOnlyOrder() is not supported yet')
5548
5548
  query = self.extend(params, {'postOnly': True})
5549
5549
  return self.create_order(symbol, type, side, amount, price, query)
5550
5550
 
5551
5551
  def create_post_only_order_ws(self, symbol: str, type: OrderType, side: OrderSide, amount: float, price: Num = None, params={}):
5552
5552
  if not self.has['createPostOnlyOrderWs']:
5553
- raise NotSupported(self.id + 'createPostOnlyOrderWs() is not supported yet')
5553
+ raise NotSupported(self.id + ' createPostOnlyOrderWs() is not supported yet')
5554
5554
  query = self.extend(params, {'postOnly': True})
5555
5555
  return self.create_order_ws(symbol, type, side, amount, price, query)
5556
5556
 
5557
5557
  def create_reduce_only_order(self, symbol: str, type: OrderType, side: OrderSide, amount: float, price: Num = None, params={}):
5558
5558
  if not self.has['createReduceOnlyOrder']:
5559
- raise NotSupported(self.id + 'createReduceOnlyOrder() is not supported yet')
5559
+ raise NotSupported(self.id + ' createReduceOnlyOrder() is not supported yet')
5560
5560
  query = self.extend(params, {'reduceOnly': True})
5561
5561
  return self.create_order(symbol, type, side, amount, price, query)
5562
5562
 
5563
5563
  def create_reduce_only_order_ws(self, symbol: str, type: OrderType, side: OrderSide, amount: float, price: Num = None, params={}):
5564
5564
  if not self.has['createReduceOnlyOrderWs']:
5565
- raise NotSupported(self.id + 'createReduceOnlyOrderWs() is not supported yet')
5565
+ raise NotSupported(self.id + ' createReduceOnlyOrderWs() is not supported yet')
5566
5566
  query = self.extend(params, {'reduceOnly': True})
5567
5567
  return self.create_order_ws(symbol, type, side, amount, price, query)
5568
5568
 
ccxt/binance.py CHANGED
@@ -1584,6 +1584,7 @@ class binance(Exchange, ImplicitAPI):
1584
1584
  'legalMoneyCurrenciesById': {
1585
1585
  'BUSD': 'USD',
1586
1586
  },
1587
+ 'defaultWithdrawPrecision': 0.00000001,
1587
1588
  },
1588
1589
  'features': {
1589
1590
  'spot': {
@@ -1634,6 +1635,7 @@ class binance(Exchange, ImplicitAPI):
1634
1635
  'limit': None,
1635
1636
  'trigger': False,
1636
1637
  'trailing': False,
1638
+ 'symbolRequired': False,
1637
1639
  },
1638
1640
  'fetchOrders': {
1639
1641
  'marginMode': True,
@@ -3096,6 +3098,7 @@ class binance(Exchange, ImplicitAPI):
3096
3098
  id = self.safe_string(entry, 'coin')
3097
3099
  name = self.safe_string(entry, 'name')
3098
3100
  code = self.safe_currency_code(id)
3101
+ isFiat = self.safe_bool(entry, 'isLegalMoney')
3099
3102
  minPrecision = None
3100
3103
  isWithdrawEnabled = True
3101
3104
  isDepositEnabled = True
@@ -3107,6 +3110,7 @@ class binance(Exchange, ImplicitAPI):
3107
3110
  networkItem = networkList[j]
3108
3111
  network = self.safe_string(networkItem, 'network')
3109
3112
  networkCode = self.network_id_to_code(network)
3113
+ isETF = (network == 'ETF') # e.g. BTCUP, ETHDOWN
3110
3114
  # name = self.safe_string(networkItem, 'name')
3111
3115
  withdrawFee = self.safe_number(networkItem, 'withdrawFee')
3112
3116
  depositEnable = self.safe_bool(networkItem, 'depositEnable')
@@ -3117,11 +3121,22 @@ class binance(Exchange, ImplicitAPI):
3117
3121
  isDefault = self.safe_bool(networkItem, 'isDefault')
3118
3122
  if isDefault or (fee is None):
3119
3123
  fee = withdrawFee
3124
+ # todo: default networks in "setMarkets" overload
3125
+ # if isDefault:
3126
+ # self.options['defaultNetworkCodesForCurrencies'][code] = networkCode
3127
+ # }
3120
3128
  precisionTick = self.safe_string(networkItem, 'withdrawIntegerMultiple')
3121
- # avoid zero values, which are mostly from fiat or leveraged tokens : https://github.com/ccxt/ccxt/pull/14902#issuecomment-1271636731
3122
- # 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
3129
+ withdrawPrecision = precisionTick
3130
+ # avoid zero values, which are mostly from fiat or leveraged tokens or some abandoned coins : https://github.com/ccxt/ccxt/pull/14902#issuecomment-1271636731
3123
3131
  if not Precise.string_eq(precisionTick, '0'):
3124
3132
  minPrecision = precisionTick if (minPrecision is None) else Precise.string_min(minPrecision, precisionTick)
3133
+ else:
3134
+ if not isFiat and not isETF:
3135
+ # non-fiat and non-ETF currency, there are many cases when precision is set to zero(probably bug, we've reported to binance already)
3136
+ # in such cases, we can set default precision of 8(which is in UI for such coins)
3137
+ withdrawPrecision = self.omit_zero(self.safe_string(networkItem, 'withdrawInternalMin'))
3138
+ if withdrawPrecision is None:
3139
+ withdrawPrecision = self.safe_string(self.options, 'defaultWithdrawPrecision')
3125
3140
  networks[networkCode] = {
3126
3141
  'info': networkItem,
3127
3142
  'id': network,
@@ -3130,7 +3145,7 @@ class binance(Exchange, ImplicitAPI):
3130
3145
  'deposit': depositEnable,
3131
3146
  'withdraw': withdrawEnable,
3132
3147
  'fee': withdrawFee,
3133
- 'precision': self.parse_number(precisionTick),
3148
+ 'precision': self.parse_number(withdrawPrecision),
3134
3149
  'limits': {
3135
3150
  'withdraw': {
3136
3151
  'min': self.safe_number(networkItem, 'withdrawMin'),
@@ -3159,6 +3174,7 @@ class binance(Exchange, ImplicitAPI):
3159
3174
  'id': id,
3160
3175
  'name': name,
3161
3176
  'code': code,
3177
+ 'type': 'fiat' if isFiat else 'crypto',
3162
3178
  'precision': self.parse_number(minPrecision),
3163
3179
  'info': entry,
3164
3180
  'active': active,
@@ -5272,10 +5288,10 @@ class binance(Exchange, ImplicitAPI):
5272
5288
  request: dict = {
5273
5289
  'symbol': market['id'],
5274
5290
  'side': side.upper(),
5291
+ 'orderId': id,
5292
+ 'quantity': self.amount_to_precision(symbol, amount),
5275
5293
  }
5276
5294
  clientOrderId = self.safe_string_n(params, ['newClientOrderId', 'clientOrderId', 'origClientOrderId'])
5277
- request['orderId'] = id
5278
- request['quantity'] = self.amount_to_precision(symbol, amount)
5279
5295
  if price is not None:
5280
5296
  request['price'] = self.price_to_precision(symbol, price)
5281
5297
  if clientOrderId is not None:
@@ -5289,6 +5305,8 @@ class binance(Exchange, ImplicitAPI):
5289
5305
 
5290
5306
  https://developers.binance.com/docs/derivatives/usds-margined-futures/trade/rest-api/Modify-Order
5291
5307
  https://developers.binance.com/docs/derivatives/coin-margined-futures/trade/Modify-Order
5308
+ https://developers.binance.com/docs/derivatives/portfolio-margin/trade/Modify-UM-Order
5309
+ https://developers.binance.com/docs/derivatives/portfolio-margin/trade/Modify-CM-Order
5292
5310
 
5293
5311
  :param str id: cancel order id
5294
5312
  :param str symbol: unified symbol of the market to create an order in
@@ -5297,16 +5315,28 @@ class binance(Exchange, ImplicitAPI):
5297
5315
  :param float amount: how much of currency you want to trade in units of base currency
5298
5316
  :param float [price]: the price at which the order is to be fulfilled, in units of the quote currency, ignored in market orders
5299
5317
  :param dict [params]: extra parameters specific to the exchange API endpoint
5318
+ :param boolean [params.portfolioMargin]: set to True if you would like to edit an order in a portfolio margin account
5300
5319
  :returns dict: an `order structure <https://docs.ccxt.com/#/?id=order-structure>`
5301
5320
  """
5302
5321
  self.load_markets()
5303
5322
  market = self.market(symbol)
5323
+ isPortfolioMargin = None
5324
+ isPortfolioMargin, params = self.handle_option_and_params_2(params, 'editContractOrder', 'papi', 'portfolioMargin', False)
5325
+ if market['linear'] or isPortfolioMargin:
5326
+ if (price is None) and not ('priceMatch' in params):
5327
+ raise ArgumentsRequired(self.id + ' editOrder() requires a price argument for portfolio margin and linear orders')
5304
5328
  request = self.edit_contract_order_request(id, symbol, type, side, amount, price, params)
5305
5329
  response = None
5306
5330
  if market['linear']:
5307
- response = self.fapiPrivatePutOrder(self.extend(request, params))
5331
+ if isPortfolioMargin:
5332
+ response = self.papiPutUmOrder(self.extend(request, params))
5333
+ else:
5334
+ response = self.fapiPrivatePutOrder(self.extend(request, params))
5308
5335
  elif market['inverse']:
5309
- response = self.dapiPrivatePutOrder(self.extend(request, params))
5336
+ if isPortfolioMargin:
5337
+ response = self.papiPutCmOrder(self.extend(request, params))
5338
+ else:
5339
+ response = self.dapiPrivatePutOrder(self.extend(request, params))
5310
5340
  #
5311
5341
  # swap and future
5312
5342
  #
@@ -12059,7 +12089,7 @@ class binance(Exchange, ImplicitAPI):
12059
12089
  :returns dict: an array of `open interest structure <https://docs.ccxt.com/#/?id=open-interest-structure>`
12060
12090
  """
12061
12091
  if timeframe == '1m':
12062
- raise BadRequest(self.id + 'fetchOpenInterestHistory cannot use the 1m timeframe')
12092
+ raise BadRequest(self.id + ' fetchOpenInterestHistory cannot use the 1m timeframe')
12063
12093
  self.load_markets()
12064
12094
  paginate = False
12065
12095
  paginate, params = self.handle_option_and_params(params, 'fetchOpenInterestHistory', 'paginate', False)
@@ -12818,7 +12848,7 @@ class binance(Exchange, ImplicitAPI):
12818
12848
  elif market['inverse']:
12819
12849
  response = self.dapiPrivateGetPositionMarginHistory(self.extend(request, params))
12820
12850
  else:
12821
- raise BadRequest(self.id + 'fetchMarginAdjustmentHistory() is not supported for markets of type ' + market['type'])
12851
+ raise BadRequest(self.id + ' fetchMarginAdjustmentHistory() is not supported for markets of type ' + market['type'])
12822
12852
  #
12823
12853
  # [
12824
12854
  # {
ccxt/bitget.py CHANGED
@@ -4632,7 +4632,7 @@ class bitget(Exchange, ImplicitAPI):
4632
4632
  response = None
4633
4633
  if market['spot']:
4634
4634
  if triggerPrice is None:
4635
- raise NotSupported(self.id + 'editOrder() only supports plan/trigger spot orders')
4635
+ raise NotSupported(self.id + ' editOrder() only supports plan/trigger spot orders')
4636
4636
  editMarketBuyOrderRequiresPrice = self.safe_bool(self.options, 'editMarketBuyOrderRequiresPrice', True)
4637
4637
  if editMarketBuyOrderRequiresPrice and isMarketOrder and (side == 'buy'):
4638
4638
  if price is None:
@@ -8854,4 +8854,9 @@ class bitget(Exchange, ImplicitAPI):
8854
8854
  }
8855
8855
  if method == 'POST':
8856
8856
  headers['Content-Type'] = 'application/json'
8857
+ sandboxMode = self.safe_bool(self.options, 'sandboxMode', False)
8858
+ if sandboxMode:
8859
+ if headers is None:
8860
+ headers = {}
8861
+ headers['PAPTRADING'] = 1
8857
8862
  return {'url': url, 'method': method, 'body': body, 'headers': headers}
ccxt/bitmart.py CHANGED
@@ -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
ccxt/blofin.py CHANGED
@@ -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
  },
ccxt/bybit.py CHANGED
@@ -1420,8 +1420,8 @@ class bybit(Exchange, ImplicitAPI):
1420
1420
 
1421
1421
  def create_expired_option_market(self, symbol: str):
1422
1422
  # support expired option contracts
1423
- quote = 'USD'
1424
- settle = 'USDC'
1423
+ quote = None
1424
+ settle = None
1425
1425
  optionParts = symbol.split('-')
1426
1426
  symbolBase = symbol.split('/')
1427
1427
  base = None
@@ -1429,9 +1429,20 @@ class bybit(Exchange, ImplicitAPI):
1429
1429
  if symbol.find('/') > -1:
1430
1430
  base = self.safe_string(symbolBase, 0)
1431
1431
  expiry = self.safe_string(optionParts, 1)
1432
+ symbolQuoteAndSettle = self.safe_string(symbolBase, 1)
1433
+ splitQuote = symbolQuoteAndSettle.split(':')
1434
+ quoteAndSettle = self.safe_string(splitQuote, 0)
1435
+ quote = quoteAndSettle
1436
+ settle = quoteAndSettle
1432
1437
  else:
1433
1438
  base = self.safe_string(optionParts, 0)
1434
1439
  expiry = self.convert_market_id_expire_date(self.safe_string(optionParts, 1))
1440
+ if symbol.endswith('-USDT'):
1441
+ quote = 'USDT'
1442
+ settle = 'USDT'
1443
+ else:
1444
+ quote = 'USDC'
1445
+ settle = 'USDC'
1435
1446
  strike = self.safe_string(optionParts, 2)
1436
1447
  optionType = self.safe_string(optionParts, 3)
1437
1448
  datetime = self.convert_expire_date(expiry)
@@ -6488,7 +6499,7 @@ classic accounts only/ spot not supported* fetches information on an order made
6488
6499
  :returns: An array of open interest structures
6489
6500
  """
6490
6501
  if timeframe == '1m':
6491
- raise BadRequest(self.id + 'fetchOpenInterestHistory cannot use the 1m timeframe')
6502
+ raise BadRequest(self.id + ' fetchOpenInterestHistory cannot use the 1m timeframe')
6492
6503
  self.load_markets()
6493
6504
  paginate = self.safe_bool(params, 'paginate')
6494
6505
  if paginate:
@@ -8704,6 +8715,8 @@ classic accounts only/ spot not supported* fetches information on an order made
8704
8715
  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
8705
8716
  else:
8706
8717
  feedback = self.id + ' ' + body
8718
+ if body.find('Withdraw address chain or destination tag are not equal'):
8719
+ feedback = feedback + '; You might also need to ensure the address is whitelisted'
8707
8720
  self.throw_broadly_matched_exception(self.exceptions['broad'], body, feedback)
8708
8721
  self.throw_exactly_matched_exception(self.exceptions['exact'], errorCode, feedback)
8709
8722
  raise ExchangeError(feedback) # unknown message
ccxt/coinbase.py CHANGED
@@ -452,7 +452,7 @@ class coinbase(Exchange, ImplicitAPI):
452
452
  'symbolRequired': False,
453
453
  },
454
454
  'fetchOHLCV': {
455
- 'limit': 350,
455
+ 'limit': 300,
456
456
  },
457
457
  },
458
458
  'spot': {
@@ -4795,10 +4795,17 @@ class coinbase(Exchange, ImplicitAPI):
4795
4795
  # }
4796
4796
  # ]
4797
4797
  # }
4798
+ # or
4799
+ # {
4800
+ # "error": "UNKNOWN_FAILURE_REASON",
4801
+ # "message": "",
4802
+ # "error_details": "",
4803
+ # "preview_failure_reason": "PREVIEW_STOP_PRICE_BELOW_LAST_TRADE_PRICE"
4804
+ # }
4798
4805
  #
4799
4806
  errorCode = self.safe_string(response, 'error')
4800
4807
  if errorCode is not None:
4801
- errorMessage = self.safe_string(response, 'error_description')
4808
+ errorMessage = self.safe_string_2(response, 'error_description', 'preview_failure_reason')
4802
4809
  self.throw_exactly_matched_exception(self.exceptions['exact'], errorCode, feedback)
4803
4810
  self.throw_broadly_matched_exception(self.exceptions['broad'], errorMessage, feedback)
4804
4811
  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 = 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
ccxt/coinex.py CHANGED
@@ -4615,7 +4615,7 @@ class coinex(Exchange, ImplicitAPI):
4615
4615
  request: dict = {
4616
4616
  'ccy': currency['id'],
4617
4617
  'to_address': address, # must be authorized, inter-user transfer by a registered mobile phone number or an email address is supported
4618
- 'amount': self.number_to_string(amount), # the actual amount without fees, https://www.coinex.com/fees
4618
+ 'amount': self.currency_to_precision(code, amount), # the actual amount without fees, https://www.coinex.com/fees
4619
4619
  }
4620
4620
  if tag is not None:
4621
4621
  request['memo'] = tag
ccxt/deribit.py CHANGED
@@ -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
  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 = self.privateGetGetPositions(self.extend(request, params))
2693
2676
  #
2694
2677
  # {
ccxt/exmo.py CHANGED
@@ -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
  self.load_markets()
1356
1356
  market = self.market(symbol)
1357
1357
  pair = market['id']