ccxt 4.2.38__py2.py3-none-any.whl → 4.2.40__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.

Potentially problematic release.


This version of ccxt might be problematic. Click here for more details.

Files changed (141) hide show
  1. ccxt/__init__.py +1 -1
  2. ccxt/abstract/bingx.py +4 -0
  3. ccxt/abstract/coinbase.py +1 -0
  4. ccxt/abstract/coinbasepro.py +1 -0
  5. ccxt/abstract/okx.py +1 -0
  6. ccxt/ascendex.py +31 -27
  7. ccxt/async_support/__init__.py +1 -1
  8. ccxt/async_support/ascendex.py +31 -27
  9. ccxt/async_support/base/exchange.py +19 -7
  10. ccxt/async_support/bigone.py +2 -2
  11. ccxt/async_support/binance.py +478 -188
  12. ccxt/async_support/bingx.py +250 -28
  13. ccxt/async_support/bitfinex.py +3 -3
  14. ccxt/async_support/bitfinex2.py +2 -2
  15. ccxt/async_support/bitget.py +16 -7
  16. ccxt/async_support/bitmart.py +2 -2
  17. ccxt/async_support/bitmex.py +2 -2
  18. ccxt/async_support/bitrue.py +2 -2
  19. ccxt/async_support/bitso.py +19 -3
  20. ccxt/async_support/bitstamp.py +25 -3
  21. ccxt/async_support/bitvavo.py +1 -1
  22. ccxt/async_support/bl3p.py +6 -0
  23. ccxt/async_support/blockchaincom.py +21 -0
  24. ccxt/async_support/blofin.py +2 -2
  25. ccxt/async_support/btcalpha.py +9 -0
  26. ccxt/async_support/btcbox.py +9 -0
  27. ccxt/async_support/btcmarkets.py +19 -0
  28. ccxt/async_support/bybit.py +9 -7
  29. ccxt/async_support/cex.py +1 -1
  30. ccxt/async_support/coinbase.py +20 -9
  31. ccxt/async_support/coinbasepro.py +1 -0
  32. ccxt/async_support/coinex.py +4 -4
  33. ccxt/async_support/coinlist.py +11 -9
  34. ccxt/async_support/coinmetro.py +2 -1
  35. ccxt/async_support/coinone.py +1 -1
  36. ccxt/async_support/delta.py +2 -2
  37. ccxt/async_support/deribit.py +3 -3
  38. ccxt/async_support/digifinex.py +3 -3
  39. ccxt/async_support/exmo.py +2 -2
  40. ccxt/async_support/gate.py +6 -6
  41. ccxt/async_support/hitbtc.py +2 -2
  42. ccxt/async_support/hollaex.py +1 -1
  43. ccxt/async_support/htx.py +3 -3
  44. ccxt/async_support/huobijp.py +1 -1
  45. ccxt/async_support/kraken.py +2 -2
  46. ccxt/async_support/krakenfutures.py +117 -16
  47. ccxt/async_support/kucoin.py +5 -5
  48. ccxt/async_support/kucoinfutures.py +2 -2
  49. ccxt/async_support/latoken.py +1 -1
  50. ccxt/async_support/lbank.py +2 -2
  51. ccxt/async_support/luno.py +2 -2
  52. ccxt/async_support/mexc.py +5 -5
  53. ccxt/async_support/ndax.py +1 -1
  54. ccxt/async_support/novadax.py +1 -1
  55. ccxt/async_support/okcoin.py +2 -2
  56. ccxt/async_support/okx.py +18 -21
  57. ccxt/async_support/paymium.py +2 -2
  58. ccxt/async_support/phemex.py +5 -4
  59. ccxt/async_support/poloniex.py +2 -2
  60. ccxt/async_support/poloniexfutures.py +10 -6
  61. ccxt/async_support/probit.py +1 -1
  62. ccxt/async_support/timex.py +1 -1
  63. ccxt/async_support/upbit.py +1 -1
  64. ccxt/async_support/wavesexchange.py +1 -1
  65. ccxt/async_support/whitebit.py +2 -2
  66. ccxt/async_support/woo.py +4 -4
  67. ccxt/async_support/zonda.py +3 -3
  68. ccxt/base/exchange.py +37 -25
  69. ccxt/bigone.py +2 -2
  70. ccxt/binance.py +478 -188
  71. ccxt/bingx.py +250 -28
  72. ccxt/bitfinex.py +3 -3
  73. ccxt/bitfinex2.py +2 -2
  74. ccxt/bitget.py +16 -7
  75. ccxt/bitmart.py +2 -2
  76. ccxt/bitmex.py +2 -2
  77. ccxt/bitrue.py +2 -2
  78. ccxt/bitso.py +19 -3
  79. ccxt/bitstamp.py +25 -3
  80. ccxt/bitvavo.py +1 -1
  81. ccxt/bl3p.py +6 -0
  82. ccxt/blockchaincom.py +21 -0
  83. ccxt/blofin.py +2 -2
  84. ccxt/btcalpha.py +9 -0
  85. ccxt/btcbox.py +9 -0
  86. ccxt/btcmarkets.py +19 -0
  87. ccxt/bybit.py +9 -7
  88. ccxt/cex.py +1 -1
  89. ccxt/coinbase.py +20 -9
  90. ccxt/coinbasepro.py +1 -0
  91. ccxt/coinex.py +4 -4
  92. ccxt/coinlist.py +11 -9
  93. ccxt/coinmetro.py +2 -1
  94. ccxt/coinone.py +1 -1
  95. ccxt/delta.py +2 -2
  96. ccxt/deribit.py +3 -3
  97. ccxt/digifinex.py +3 -3
  98. ccxt/exmo.py +2 -2
  99. ccxt/gate.py +6 -6
  100. ccxt/hitbtc.py +2 -2
  101. ccxt/hollaex.py +1 -1
  102. ccxt/htx.py +3 -3
  103. ccxt/huobijp.py +1 -1
  104. ccxt/kraken.py +2 -2
  105. ccxt/krakenfutures.py +117 -16
  106. ccxt/kucoin.py +5 -5
  107. ccxt/kucoinfutures.py +2 -2
  108. ccxt/latoken.py +1 -1
  109. ccxt/lbank.py +2 -2
  110. ccxt/luno.py +2 -2
  111. ccxt/mexc.py +5 -5
  112. ccxt/ndax.py +1 -1
  113. ccxt/novadax.py +1 -1
  114. ccxt/okcoin.py +2 -2
  115. ccxt/okx.py +18 -21
  116. ccxt/paymium.py +2 -2
  117. ccxt/phemex.py +5 -4
  118. ccxt/poloniex.py +2 -2
  119. ccxt/poloniexfutures.py +10 -6
  120. ccxt/pro/__init__.py +1 -1
  121. ccxt/pro/bitmart.py +129 -46
  122. ccxt/pro/bitvavo.py +1 -1
  123. ccxt/pro/bybit.py +6 -6
  124. ccxt/pro/cex.py +2 -2
  125. ccxt/pro/coinbase.py +2 -2
  126. ccxt/pro/coinex.py +1 -1
  127. ccxt/pro/lbank.py +1 -1
  128. ccxt/pro/mexc.py +1 -1
  129. ccxt/probit.py +1 -1
  130. ccxt/test/test_async.py +3 -1
  131. ccxt/test/test_sync.py +3 -1
  132. ccxt/timex.py +1 -1
  133. ccxt/upbit.py +1 -1
  134. ccxt/wavesexchange.py +1 -1
  135. ccxt/whitebit.py +2 -2
  136. ccxt/woo.py +4 -4
  137. ccxt/zonda.py +3 -3
  138. {ccxt-4.2.38.dist-info → ccxt-4.2.40.dist-info}/METADATA +4 -4
  139. {ccxt-4.2.38.dist-info → ccxt-4.2.40.dist-info}/RECORD +141 -141
  140. {ccxt-4.2.38.dist-info → ccxt-4.2.40.dist-info}/WHEEL +0 -0
  141. {ccxt-4.2.38.dist-info → ccxt-4.2.40.dist-info}/top_level.txt +0 -0
ccxt/binance.py CHANGED
@@ -2361,7 +2361,7 @@ class binance(Exchange, ImplicitAPI):
2361
2361
  reconstructedDate = '20' + year + '-' + month + '-' + day + 'T00:00:00Z'
2362
2362
  return reconstructedDate
2363
2363
 
2364
- def create_expired_option_market(self, symbol):
2364
+ def create_expired_option_market(self, symbol: str):
2365
2365
  # support expired option contracts
2366
2366
  settle = 'USDT'
2367
2367
  optionParts = symbol.split('-')
@@ -2637,7 +2637,7 @@ class binance(Exchange, ImplicitAPI):
2637
2637
  minPrecision = None
2638
2638
  isWithdrawEnabled = True
2639
2639
  isDepositEnabled = True
2640
- networkList = self.safe_value(entry, 'networkList', [])
2640
+ networkList = self.safe_list(entry, 'networkList', [])
2641
2641
  fees = {}
2642
2642
  fee = None
2643
2643
  for j in range(0, len(networkList)):
@@ -2645,12 +2645,12 @@ class binance(Exchange, ImplicitAPI):
2645
2645
  network = self.safe_string(networkItem, 'network')
2646
2646
  # name = self.safe_string(networkItem, 'name')
2647
2647
  withdrawFee = self.safe_number(networkItem, 'withdrawFee')
2648
- depositEnable = self.safe_value(networkItem, 'depositEnable')
2649
- withdrawEnable = self.safe_value(networkItem, 'withdrawEnable')
2648
+ depositEnable = self.safe_bool(networkItem, 'depositEnable')
2649
+ withdrawEnable = self.safe_bool(networkItem, 'withdrawEnable')
2650
2650
  isDepositEnabled = isDepositEnabled or depositEnable
2651
2651
  isWithdrawEnabled = isWithdrawEnabled or withdrawEnable
2652
2652
  fees[network] = withdrawFee
2653
- isDefault = self.safe_value(networkItem, 'isDefault')
2653
+ isDefault = self.safe_bool(networkItem, 'isDefault')
2654
2654
  if isDefault or (fee is None):
2655
2655
  fee = withdrawFee
2656
2656
  precisionTick = self.safe_string(networkItem, 'withdrawIntegerMultiple')
@@ -2658,7 +2658,7 @@ class binance(Exchange, ImplicitAPI):
2658
2658
  # 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
2659
2659
  if not Precise.string_eq(precisionTick, '0'):
2660
2660
  minPrecision = precisionTick if (minPrecision is None) else Precise.string_min(minPrecision, precisionTick)
2661
- trading = self.safe_value(entry, 'trading')
2661
+ trading = self.safe_bool(entry, 'trading')
2662
2662
  active = (isWithdrawEnabled and isDepositEnabled and trading)
2663
2663
  maxDecimalPlaces = None
2664
2664
  if minPrecision is not None:
@@ -2690,8 +2690,8 @@ class binance(Exchange, ImplicitAPI):
2690
2690
  :returns dict[]: an array of objects representing market data
2691
2691
  """
2692
2692
  promisesRaw = []
2693
- rawFetchMarkets = self.safe_value(self.options, 'fetchMarkets', ['spot', 'linear', 'inverse'])
2694
- sandboxMode = self.safe_value(self.options, 'sandboxMode', False)
2693
+ rawFetchMarkets = self.safe_list(self.options, 'fetchMarkets', ['spot', 'linear', 'inverse'])
2694
+ sandboxMode = self.safe_bool(self.options, 'sandboxMode', False)
2695
2695
  fetchMarkets = []
2696
2696
  for i in range(0, len(rawFetchMarkets)):
2697
2697
  type = rawFetchMarkets[i]
@@ -2956,7 +2956,7 @@ class binance(Exchange, ImplicitAPI):
2956
2956
  future = True
2957
2957
  settle = self.safe_currency_code(settleId)
2958
2958
  spot = not contract
2959
- filters = self.safe_value(market, 'filters', [])
2959
+ filters = self.safe_list(market, 'filters', [])
2960
2960
  filtersByType = self.index_by(filters, 'filterType')
2961
2961
  status = self.safe_string_2(market, 'status', 'contractStatus')
2962
2962
  contractSize = None
@@ -2976,10 +2976,10 @@ class binance(Exchange, ImplicitAPI):
2976
2976
  linear = settle == quote
2977
2977
  inverse = settle == base
2978
2978
  feesType = 'linear' if linear else 'inverse'
2979
- fees = self.safe_value(self.fees, feesType, {})
2979
+ fees = self.safe_dict(self.fees, feesType, {})
2980
2980
  active = (status == 'TRADING')
2981
2981
  if spot:
2982
- permissions = self.safe_value(market, 'permissions', [])
2982
+ permissions = self.safe_list(market, 'permissions', [])
2983
2983
  for j in range(0, len(permissions)):
2984
2984
  if permissions[j] == 'TRD_GRP_003':
2985
2985
  active = False
@@ -3050,7 +3050,7 @@ class binance(Exchange, ImplicitAPI):
3050
3050
  'created': self.safe_integer(market, 'onboardDate'), # present in inverse & linear apis
3051
3051
  }
3052
3052
  if 'PRICE_FILTER' in filtersByType:
3053
- filter = self.safe_value(filtersByType, 'PRICE_FILTER', {})
3053
+ filter = self.safe_dict(filtersByType, 'PRICE_FILTER', {})
3054
3054
  # PRICE_FILTER reports zero values for maxPrice
3055
3055
  # since they updated filter types in November 2018
3056
3056
  # https://github.com/ccxt/ccxt/issues/4286
@@ -3061,7 +3061,7 @@ class binance(Exchange, ImplicitAPI):
3061
3061
  }
3062
3062
  entry['precision']['price'] = self.precision_from_string(filter['tickSize'])
3063
3063
  if 'LOT_SIZE' in filtersByType:
3064
- filter = self.safe_value(filtersByType, 'LOT_SIZE', {})
3064
+ filter = self.safe_dict(filtersByType, 'LOT_SIZE', {})
3065
3065
  stepSize = self.safe_string(filter, 'stepSize')
3066
3066
  entry['precision']['amount'] = self.precision_from_string(stepSize)
3067
3067
  entry['limits']['amount'] = {
@@ -3069,13 +3069,13 @@ class binance(Exchange, ImplicitAPI):
3069
3069
  'max': self.safe_number(filter, 'maxQty'),
3070
3070
  }
3071
3071
  if 'MARKET_LOT_SIZE' in filtersByType:
3072
- filter = self.safe_value(filtersByType, 'MARKET_LOT_SIZE', {})
3072
+ filter = self.safe_dict(filtersByType, 'MARKET_LOT_SIZE', {})
3073
3073
  entry['limits']['market'] = {
3074
3074
  'min': self.safe_number(filter, 'minQty'),
3075
3075
  'max': self.safe_number(filter, 'maxQty'),
3076
3076
  }
3077
3077
  if ('MIN_NOTIONAL' in filtersByType) or ('NOTIONAL' in filtersByType): # notional added in 12/04/23 to spot testnet
3078
- filter = self.safe_value_2(filtersByType, 'MIN_NOTIONAL', 'NOTIONAL', {})
3078
+ filter = self.safe_dict_2(filtersByType, 'MIN_NOTIONAL', 'NOTIONAL', {})
3079
3079
  entry['limits']['cost']['min'] = self.safe_number_2(filter, 'minNotional', 'notional')
3080
3080
  entry['limits']['cost']['max'] = self.safe_number(filter, 'maxNotional')
3081
3081
  return entry
@@ -3719,7 +3719,7 @@ class binance(Exchange, ImplicitAPI):
3719
3719
  else:
3720
3720
  response = self.publicGetTicker24hr(self.extend(request, params))
3721
3721
  if isinstance(response, list):
3722
- firstTicker = self.safe_value(response, 0, {})
3722
+ firstTicker = self.safe_dict(response, 0, {})
3723
3723
  return self.parse_ticker(firstTicker, market)
3724
3724
  return self.parse_ticker(response, market)
3725
3725
 
@@ -4626,7 +4626,7 @@ class binance(Exchange, ImplicitAPI):
4626
4626
  #
4627
4627
  return self.parse_order(response, market)
4628
4628
 
4629
- def edit_order(self, id: str, symbol, type, side, amount=None, price=None, params={}):
4629
+ def edit_order(self, id: str, symbol: str, type: OrderType, side: OrderSide, amount: float = None, price: float = None, params={}):
4630
4630
  """
4631
4631
  edit a trade order
4632
4632
  :see: https://binance-docs.github.io/apidocs/spot/en/#cancel-an-existing-order-and-send-a-new-order-trade
@@ -4840,7 +4840,7 @@ class binance(Exchange, ImplicitAPI):
4840
4840
  # "msg": "Quantity greater than max quantity."
4841
4841
  # }
4842
4842
  #
4843
- # createOrder, fetchOpenOrders: portfolio margin linear swap and future
4843
+ # createOrder, fetchOpenOrders, fetchOrder, cancelOrder: portfolio margin linear swap and future
4844
4844
  #
4845
4845
  # {
4846
4846
  # "symbol": "BTCUSDT",
@@ -4863,7 +4863,7 @@ class binance(Exchange, ImplicitAPI):
4863
4863
  # "status": "NEW"
4864
4864
  # }
4865
4865
  #
4866
- # createOrder, fetchOpenOrders: portfolio margin inverse swap and future
4866
+ # createOrder, fetchOpenOrders, fetchOrder, cancelOrder: portfolio margin inverse swap and future
4867
4867
  #
4868
4868
  # {
4869
4869
  # "symbol": "ETHUSD_PERP",
@@ -4929,7 +4929,7 @@ class binance(Exchange, ImplicitAPI):
4929
4929
  # "priceProtect": False
4930
4930
  # }
4931
4931
  #
4932
- # createOrder, cancelAllOrders: portfolio margin spot margin
4932
+ # createOrder, cancelAllOrders, cancelOrder: portfolio margin spot margin
4933
4933
  #
4934
4934
  # {
4935
4935
  # "clientOrderId": "x-R4BD3S82e9ef29d8346440f0b28b86",
@@ -4948,7 +4948,7 @@ class binance(Exchange, ImplicitAPI):
4948
4948
  # "type": "LIMIT"
4949
4949
  # }
4950
4950
  #
4951
- # fetchOpenOrders: portfolio margin spot margin
4951
+ # fetchOpenOrders, fetchOrder: portfolio margin spot margin
4952
4952
  #
4953
4953
  # {
4954
4954
  # "symbol": "BTCUSDT",
@@ -4973,6 +4973,31 @@ class binance(Exchange, ImplicitAPI):
4973
4973
  # "preventedQuantity": null
4974
4974
  # }
4975
4975
  #
4976
+ # cancelOrder: portfolio margin linear and inverse swap conditional
4977
+ #
4978
+ # {
4979
+ # "strategyId": 3733211,
4980
+ # "newClientStrategyId": "x-xcKtGhcuaf166172ed504cd1bc0396",
4981
+ # "strategyType": "STOP",
4982
+ # "strategyStatus": "CANCELED",
4983
+ # "origQty": "0.010",
4984
+ # "price": "35000.00",
4985
+ # "reduceOnly": False,
4986
+ # "side": "BUY",
4987
+ # "positionSide": "BOTH",
4988
+ # "stopPrice": "50000.00", # ignored with trailing orders
4989
+ # "symbol": "BTCUSDT",
4990
+ # "timeInForce": "GTC",
4991
+ # "activatePrice": null, # only return with trailing orders
4992
+ # "priceRate": null, # only return with trailing orders
4993
+ # "bookTime": 1707270098774,
4994
+ # "updateTime": 1707270119261,
4995
+ # "workingType": "CONTRACT_PRICE",
4996
+ # "priceProtect": False,
4997
+ # "goodTillDate": 0,
4998
+ # "selfTradePreventionMode": "NONE"
4999
+ # }
5000
+ #
4976
5001
  code = self.safe_string(order, 'code')
4977
5002
  if code is not None:
4978
5003
  # cancelOrders/createOrders might have a partial success
@@ -5003,7 +5028,7 @@ class binance(Exchange, ImplicitAPI):
5003
5028
  cost = self.safe_string(order, 'cumBase', cost)
5004
5029
  type = self.safe_string_lower(order, 'type')
5005
5030
  side = self.safe_string_lower(order, 'side')
5006
- fills = self.safe_value(order, 'fills', [])
5031
+ fills = self.safe_list(order, 'fills', [])
5007
5032
  timeInForce = self.safe_string(order, 'timeInForce')
5008
5033
  if timeInForce == 'GTX':
5009
5034
  # GTX means "Good Till Crossing" and is an equivalent way of saying Post Only
@@ -5033,7 +5058,7 @@ class binance(Exchange, ImplicitAPI):
5033
5058
  'type': type,
5034
5059
  'timeInForce': timeInForce,
5035
5060
  'postOnly': postOnly,
5036
- 'reduceOnly': self.safe_value(order, 'reduceOnly'),
5061
+ 'reduceOnly': self.safe_bool(order, 'reduceOnly'),
5037
5062
  'side': side,
5038
5063
  'price': price,
5039
5064
  'triggerPrice': stopPrice,
@@ -5065,7 +5090,7 @@ class binance(Exchange, ImplicitAPI):
5065
5090
  side = self.safe_string(rawOrder, 'side')
5066
5091
  amount = self.safe_value(rawOrder, 'amount')
5067
5092
  price = self.safe_value(rawOrder, 'price')
5068
- orderParams = self.safe_value(rawOrder, 'params', {})
5093
+ orderParams = self.safe_dict(rawOrder, 'params', {})
5069
5094
  orderRequest = self.create_order_request(marketId, type, side, amount, price, orderParams)
5070
5095
  ordersRequests.append(orderRequest)
5071
5096
  orderSymbols = self.market_symbols(orderSymbols, None, False, True, True)
@@ -5416,7 +5441,7 @@ class binance(Exchange, ImplicitAPI):
5416
5441
  requestParams = self.omit(params, ['type', 'newClientOrderId', 'clientOrderId', 'postOnly', 'stopLossPrice', 'takeProfitPrice', 'stopPrice', 'triggerPrice', 'trailingTriggerPrice', 'trailingPercent', 'quoteOrderQty', 'cost', 'test'])
5417
5442
  return self.extend(request, requestParams)
5418
5443
 
5419
- def create_market_order_with_cost(self, symbol: str, side: OrderSide, cost, params={}):
5444
+ def create_market_order_with_cost(self, symbol: str, side: OrderSide, cost: float, params={}):
5420
5445
  """
5421
5446
  create a market order by providing the symbol, side and cost
5422
5447
  :see: https://binance-docs.github.io/apidocs/spot/en/#new-order-trade
@@ -5433,7 +5458,7 @@ class binance(Exchange, ImplicitAPI):
5433
5458
  params['quoteOrderQty'] = cost
5434
5459
  return self.create_order(symbol, 'market', side, cost, None, params)
5435
5460
 
5436
- def create_market_buy_order_with_cost(self, symbol: str, cost, params={}):
5461
+ def create_market_buy_order_with_cost(self, symbol: str, cost: float, params={}):
5437
5462
  """
5438
5463
  create a market buy order by providing the symbol and cost
5439
5464
  :see: https://binance-docs.github.io/apidocs/spot/en/#new-order-trade
@@ -5449,7 +5474,7 @@ class binance(Exchange, ImplicitAPI):
5449
5474
  params['quoteOrderQty'] = cost
5450
5475
  return self.create_order(symbol, 'market', 'buy', cost, None, params)
5451
5476
 
5452
- def create_market_sell_order_with_cost(self, symbol: str, cost, params={}):
5477
+ def create_market_sell_order_with_cost(self, symbol: str, cost: float, params={}):
5453
5478
  """
5454
5479
  create a market sell order by providing the symbol and cost
5455
5480
  :see: https://binance-docs.github.io/apidocs/spot/en/#new-order-trade
@@ -5473,9 +5498,14 @@ class binance(Exchange, ImplicitAPI):
5473
5498
  :see: https://binance-docs.github.io/apidocs/delivery/en/#query-order-user_data
5474
5499
  :see: https://binance-docs.github.io/apidocs/voptions/en/#query-single-order-trade
5475
5500
  :see: https://binance-docs.github.io/apidocs/spot/en/#query-margin-account-39-s-order-user_data
5501
+ :see: https://binance-docs.github.io/apidocs/pm/en/#query-um-order-user_data
5502
+ :see: https://binance-docs.github.io/apidocs/pm/en/#query-cm-order-user_data
5503
+ :see: https://binance-docs.github.io/apidocs/pm/en/#query-margin-account-order-user_data
5504
+ :param str id: the order id
5476
5505
  :param str symbol: unified symbol of the market the order was made in
5477
5506
  :param dict [params]: extra parameters specific to the exchange API endpoint
5478
5507
  :param str [params.marginMode]: 'cross' or 'isolated', for spot margin trading
5508
+ :param boolean [params.portfolioMargin]: set to True if you would like to fetch an order in a portfolio margin account
5479
5509
  :returns dict: An `order structure <https://docs.ccxt.com/#/?id=order-structure>`
5480
5510
  """
5481
5511
  if symbol is None:
@@ -5484,11 +5514,14 @@ class binance(Exchange, ImplicitAPI):
5484
5514
  market = self.market(symbol)
5485
5515
  defaultType = self.safe_string_2(self.options, 'fetchOrder', 'defaultType', 'spot')
5486
5516
  type = self.safe_string(params, 'type', defaultType)
5487
- marginMode, query = self.handle_margin_mode_and_params('fetchOrder', params)
5517
+ marginMode = None
5518
+ marginMode, params = self.handle_margin_mode_and_params('fetchOrder', params)
5519
+ isPortfolioMargin = None
5520
+ isPortfolioMargin, params = self.handle_option_and_params_2(params, 'fetchOrder', 'papi', 'portfolioMargin', False)
5488
5521
  request = {
5489
5522
  'symbol': market['id'],
5490
5523
  }
5491
- clientOrderId = self.safe_value_2(params, 'origClientOrderId', 'clientOrderId')
5524
+ clientOrderId = self.safe_string_2(params, 'origClientOrderId', 'clientOrderId')
5492
5525
  if clientOrderId is not None:
5493
5526
  if market['option']:
5494
5527
  request['clientOrderId'] = clientOrderId
@@ -5496,20 +5529,29 @@ class binance(Exchange, ImplicitAPI):
5496
5529
  request['origClientOrderId'] = clientOrderId
5497
5530
  else:
5498
5531
  request['orderId'] = id
5499
- requestParams = self.omit(query, ['type', 'clientOrderId', 'origClientOrderId'])
5532
+ params = self.omit(params, ['type', 'clientOrderId', 'origClientOrderId'])
5500
5533
  response = None
5501
5534
  if market['option']:
5502
- response = self.eapiPrivateGetOrder(self.extend(request, requestParams))
5535
+ response = self.eapiPrivateGetOrder(self.extend(request, params))
5503
5536
  elif market['linear']:
5504
- response = self.fapiPrivateGetOrder(self.extend(request, requestParams))
5537
+ if isPortfolioMargin:
5538
+ response = self.papiGetUmOrder(self.extend(request, params))
5539
+ else:
5540
+ response = self.fapiPrivateGetOrder(self.extend(request, params))
5505
5541
  elif market['inverse']:
5506
- response = self.dapiPrivateGetOrder(self.extend(request, requestParams))
5507
- elif type == 'margin' or marginMode is not None:
5508
- if marginMode == 'isolated':
5509
- request['isIsolated'] = True
5510
- response = self.sapiGetMarginOrder(self.extend(request, requestParams))
5542
+ if isPortfolioMargin:
5543
+ response = self.papiGetCmOrder(self.extend(request, params))
5544
+ else:
5545
+ response = self.dapiPrivateGetOrder(self.extend(request, params))
5546
+ elif (type == 'margin') or (marginMode is not None) or isPortfolioMargin:
5547
+ if isPortfolioMargin:
5548
+ response = self.papiGetMarginOrder(self.extend(request, params))
5549
+ else:
5550
+ if marginMode == 'isolated':
5551
+ request['isIsolated'] = True
5552
+ response = self.sapiGetMarginOrder(self.extend(request, params))
5511
5553
  else:
5512
- response = self.privateGetOrder(self.extend(request, requestParams))
5554
+ response = self.privateGetOrder(self.extend(request, params))
5513
5555
  return self.parse_order(response, market)
5514
5556
 
5515
5557
  def fetch_orders(self, symbol: Str = None, since: Int = None, limit: Int = None, params={}) -> List[Order]:
@@ -5778,9 +5820,16 @@ class binance(Exchange, ImplicitAPI):
5778
5820
  :see: https://binance-docs.github.io/apidocs/delivery/en/#cancel-order-trade
5779
5821
  :see: https://binance-docs.github.io/apidocs/voptions/en/#cancel-option-order-trade
5780
5822
  :see: https://binance-docs.github.io/apidocs/spot/en/#margin-account-cancel-order-trade
5823
+ :see: https://binance-docs.github.io/apidocs/pm/en/#cancel-um-order-trade
5824
+ :see: https://binance-docs.github.io/apidocs/pm/en/#cancel-cm-order-trade
5825
+ :see: https://binance-docs.github.io/apidocs/pm/en/#cancel-um-conditional-order-trade
5826
+ :see: https://binance-docs.github.io/apidocs/pm/en/#cancel-cm-conditional-order-trade
5827
+ :see: https://binance-docs.github.io/apidocs/pm/en/#cancel-margin-account-order-trade
5781
5828
  :param str id: order id
5782
5829
  :param str symbol: unified symbol of the market the order was made in
5783
5830
  :param dict [params]: extra parameters specific to the exchange API endpoint
5831
+ :param boolean [params.portfolioMargin]: set to True if you would like to cancel an order in a portfolio margin account
5832
+ :param boolean [params.stop]: set to True if you would like to cancel a portfolio margin account conditional order
5784
5833
  :returns dict: An `order structure <https://docs.ccxt.com/#/?id=order-structure>`
5785
5834
  """
5786
5835
  if symbol is None:
@@ -5789,34 +5838,57 @@ class binance(Exchange, ImplicitAPI):
5789
5838
  market = self.market(symbol)
5790
5839
  defaultType = self.safe_string_2(self.options, 'cancelOrder', 'defaultType', 'spot')
5791
5840
  type = self.safe_string(params, 'type', defaultType)
5792
- marginMode, query = self.handle_margin_mode_and_params('cancelOrder', params)
5841
+ marginMode = None
5842
+ marginMode, params = self.handle_margin_mode_and_params('cancelOrder', params)
5843
+ isPortfolioMargin = None
5844
+ isPortfolioMargin, params = self.handle_option_and_params_2(params, 'cancelOrder', 'papi', 'portfolioMargin', False)
5845
+ isConditional = self.safe_bool_2(params, 'stop', 'conditional')
5793
5846
  request = {
5794
5847
  'symbol': market['id'],
5795
- # 'orderId': id,
5796
- # 'origClientOrderId': id,
5797
5848
  }
5798
- clientOrderId = self.safe_value_2(params, 'origClientOrderId', 'clientOrderId')
5849
+ clientOrderId = self.safe_string_n(params, ['origClientOrderId', 'clientOrderId', 'newClientStrategyId'])
5799
5850
  if clientOrderId is not None:
5800
5851
  if market['option']:
5801
5852
  request['clientOrderId'] = clientOrderId
5802
5853
  else:
5803
- request['origClientOrderId'] = clientOrderId
5854
+ if isPortfolioMargin and isConditional:
5855
+ request['newClientStrategyId'] = clientOrderId
5856
+ else:
5857
+ request['origClientOrderId'] = clientOrderId
5804
5858
  else:
5805
- request['orderId'] = id
5806
- requestParams = self.omit(query, ['type', 'origClientOrderId', 'clientOrderId'])
5859
+ if isPortfolioMargin and isConditional:
5860
+ request['strategyId'] = id
5861
+ else:
5862
+ request['orderId'] = id
5863
+ params = self.omit(params, ['type', 'origClientOrderId', 'clientOrderId', 'newClientStrategyId', 'stop', 'conditional'])
5807
5864
  response = None
5808
5865
  if market['option']:
5809
- response = self.eapiPrivateDeleteOrder(self.extend(request, requestParams))
5866
+ response = self.eapiPrivateDeleteOrder(self.extend(request, params))
5810
5867
  elif market['linear']:
5811
- response = self.fapiPrivateDeleteOrder(self.extend(request, requestParams))
5868
+ if isPortfolioMargin:
5869
+ if isConditional:
5870
+ response = self.papiDeleteUmConditionalOrder(self.extend(request, params))
5871
+ else:
5872
+ response = self.papiDeleteUmOrder(self.extend(request, params))
5873
+ else:
5874
+ response = self.fapiPrivateDeleteOrder(self.extend(request, params))
5812
5875
  elif market['inverse']:
5813
- response = self.dapiPrivateDeleteOrder(self.extend(request, requestParams))
5814
- elif type == 'margin' or marginMode is not None:
5815
- if marginMode == 'isolated':
5816
- request['isIsolated'] = True
5817
- response = self.sapiDeleteMarginOrder(self.extend(request, requestParams))
5876
+ if isPortfolioMargin:
5877
+ if isConditional:
5878
+ response = self.papiDeleteCmConditionalOrder(self.extend(request, params))
5879
+ else:
5880
+ response = self.papiDeleteCmOrder(self.extend(request, params))
5881
+ else:
5882
+ response = self.dapiPrivateDeleteOrder(self.extend(request, params))
5883
+ elif (type == 'margin') or (marginMode is not None) or isPortfolioMargin:
5884
+ if isPortfolioMargin:
5885
+ response = self.papiDeleteMarginOrder(self.extend(request, params))
5886
+ else:
5887
+ if marginMode == 'isolated':
5888
+ request['isIsolated'] = True
5889
+ response = self.sapiDeleteMarginOrder(self.extend(request, params))
5818
5890
  else:
5819
- response = self.privateDeleteOrder(self.extend(request, requestParams))
5891
+ response = self.privateDeleteOrder(self.extend(request, params))
5820
5892
  return self.parse_order(response, market)
5821
5893
 
5822
5894
  def cancel_all_orders(self, symbol: Str = None, params={}):
@@ -6169,11 +6241,11 @@ class binance(Exchange, ImplicitAPI):
6169
6241
  # },
6170
6242
  # ]
6171
6243
  # }
6172
- results = self.safe_value(response, 'userAssetDribblets', [])
6244
+ results = self.safe_list(response, 'userAssetDribblets', [])
6173
6245
  rows = self.safe_integer(response, 'total', 0)
6174
6246
  data = []
6175
6247
  for i in range(0, rows):
6176
- logs = self.safe_value(results[i], 'userAssetDribbletDetails', [])
6248
+ logs = self.safe_list(results[i], 'userAssetDribbletDetails', [])
6177
6249
  for j in range(0, len(logs)):
6178
6250
  logs[j]['isDustTrade'] = True
6179
6251
  data.append(logs[j])
@@ -6270,7 +6342,7 @@ class binance(Exchange, ImplicitAPI):
6270
6342
  currency = None
6271
6343
  response = None
6272
6344
  request = {}
6273
- legalMoney = self.safe_value(self.options, 'legalMoney', {})
6345
+ legalMoney = self.safe_dict(self.options, 'legalMoney', {})
6274
6346
  fiatOnly = self.safe_bool(params, 'fiat', False)
6275
6347
  params = self.omit(params, 'fiatOnly')
6276
6348
  until = self.safe_integer(params, 'until')
@@ -6369,7 +6441,7 @@ class binance(Exchange, ImplicitAPI):
6369
6441
  paginate, params = self.handle_option_and_params(params, 'fetchWithdrawals', 'paginate')
6370
6442
  if paginate:
6371
6443
  return self.fetch_paginated_call_dynamic('fetchWithdrawals', code, since, limit, params)
6372
- legalMoney = self.safe_value(self.options, 'legalMoney', {})
6444
+ legalMoney = self.safe_dict(self.options, 'legalMoney', {})
6373
6445
  fiatOnly = self.safe_bool(params, 'fiat', False)
6374
6446
  params = self.omit(params, 'fiatOnly')
6375
6447
  request = {}
@@ -6504,7 +6576,7 @@ class binance(Exchange, ImplicitAPI):
6504
6576
  'Refund Failed': 'failed',
6505
6577
  },
6506
6578
  }
6507
- statuses = self.safe_value(statusesByType, type, {})
6579
+ statuses = self.safe_dict(statusesByType, type, {})
6508
6580
  return self.safe_string(statuses, status, status)
6509
6581
 
6510
6582
  def parse_transaction(self, transaction, currency: Currency = None) -> Transaction:
@@ -6661,7 +6733,7 @@ class binance(Exchange, ImplicitAPI):
6661
6733
  type = self.safe_string(transfer, 'type')
6662
6734
  fromAccount = None
6663
6735
  toAccount = None
6664
- accountsById = self.safe_value(self.options, 'accountsById', {})
6736
+ accountsById = self.safe_dict(self.options, 'accountsById', {})
6665
6737
  if type is not None:
6666
6738
  parts = type.split('_')
6667
6739
  fromAccount = self.safe_value(parts, 0)
@@ -6696,23 +6768,19 @@ class binance(Exchange, ImplicitAPI):
6696
6768
  # }
6697
6769
  #
6698
6770
  marketId = self.safe_string(income, 'symbol')
6699
- symbol = self.safe_symbol(marketId, market, None, 'swap')
6700
- amount = self.safe_number(income, 'income')
6701
6771
  currencyId = self.safe_string(income, 'asset')
6702
- code = self.safe_currency_code(currencyId)
6703
- id = self.safe_string(income, 'tranId')
6704
6772
  timestamp = self.safe_integer(income, 'time')
6705
6773
  return {
6706
6774
  'info': income,
6707
- 'symbol': symbol,
6708
- 'code': code,
6775
+ 'symbol': self.safe_symbol(marketId, market, None, 'swap'),
6776
+ 'code': self.safe_currency_code(currencyId),
6709
6777
  'timestamp': timestamp,
6710
6778
  'datetime': self.iso8601(timestamp),
6711
- 'id': id,
6712
- 'amount': amount,
6779
+ 'id': self.safe_string(income, 'tranId'),
6780
+ 'amount': self.safe_number(income, 'income'),
6713
6781
  }
6714
6782
 
6715
- def transfer(self, code: str, amount: float, fromAccount, toAccount, params={}) -> TransferEntry:
6783
+ def transfer(self, code: str, amount: float, fromAccount: str, toAccount: str, params={}) -> TransferEntry:
6716
6784
  """
6717
6785
  transfer currency internally between wallets on the same account
6718
6786
  :see: https://binance-docs.github.io/apidocs/spot/en/#user-universal-transfer-user_data
@@ -6750,7 +6818,7 @@ class binance(Exchange, ImplicitAPI):
6750
6818
  if toId == 'ISOLATED':
6751
6819
  if symbol is None:
6752
6820
  raise ArgumentsRequired(self.id + ' transfer() requires params["symbol"] when toAccount is ' + toAccount)
6753
- accountsById = self.safe_value(self.options, 'accountsById', {})
6821
+ accountsById = self.safe_dict(self.options, 'accountsById', {})
6754
6822
  fromIsolated = not (fromId in accountsById)
6755
6823
  toIsolated = not (toId in accountsById)
6756
6824
  if fromIsolated and (market is None):
@@ -6823,7 +6891,7 @@ class binance(Exchange, ImplicitAPI):
6823
6891
  defaultTo = 'spot' if (fromAccount == 'future') else 'future'
6824
6892
  toAccount = self.safe_string(params, 'toAccount', defaultTo)
6825
6893
  type = self.safe_string(params, 'type')
6826
- accountsByType = self.safe_value(self.options, 'accountsByType', {})
6894
+ accountsByType = self.safe_dict(self.options, 'accountsByType', {})
6827
6895
  fromId = self.safe_string(accountsByType, fromAccount)
6828
6896
  toId = self.safe_string(accountsByType, toAccount)
6829
6897
  if type is None:
@@ -6861,7 +6929,7 @@ class binance(Exchange, ImplicitAPI):
6861
6929
  # ]
6862
6930
  # }
6863
6931
  #
6864
- rows = self.safe_value(response, 'rows', [])
6932
+ rows = self.safe_list(response, 'rows', [])
6865
6933
  return self.parse_transfers(rows, currency, since, limit)
6866
6934
 
6867
6935
  def fetch_deposit_address(self, code: str, params={}):
@@ -6878,7 +6946,7 @@ class binance(Exchange, ImplicitAPI):
6878
6946
  'coin': currency['id'],
6879
6947
  # 'network': 'ETH', # 'BSC', 'XMR', you can get network and isDefault in networkList in the response of sapiGetCapitalConfigDetail
6880
6948
  }
6881
- networks = self.safe_value(self.options, 'networks', {})
6949
+ networks = self.safe_dict(self.options, 'networks', {})
6882
6950
  network = self.safe_string_upper(params, 'network') # self line allows the user to specify either ERC20 or ETH
6883
6951
  network = self.safe_string(networks, network, network) # handle ERC20>ETH alias
6884
6952
  if network is not None:
@@ -6904,7 +6972,7 @@ class binance(Exchange, ImplicitAPI):
6904
6972
  url = self.safe_string(response, 'url')
6905
6973
  impliedNetwork = None
6906
6974
  if url is not None:
6907
- reverseNetworks = self.safe_value(self.options, 'reverseNetworks', {})
6975
+ reverseNetworks = self.safe_dict(self.options, 'reverseNetworks', {})
6908
6976
  parts = url.split('/')
6909
6977
  topLevel = self.safe_string(parts, 2)
6910
6978
  if (topLevel == 'blockchair.com') or (topLevel == 'viewblock.io'):
@@ -6917,7 +6985,7 @@ class binance(Exchange, ImplicitAPI):
6917
6985
  'TRX': {'TRC20': 'TRX'},
6918
6986
  })
6919
6987
  if code in impliedNetworks:
6920
- conversion = self.safe_value(impliedNetworks, code, {})
6988
+ conversion = self.safe_dict(impliedNetworks, code, {})
6921
6989
  impliedNetwork = self.safe_string(conversion, impliedNetwork, impliedNetwork)
6922
6990
  tag = self.safe_string(response, 'tag', '')
6923
6991
  if len(tag) == 0:
@@ -6931,7 +6999,7 @@ class binance(Exchange, ImplicitAPI):
6931
6999
  'info': response,
6932
7000
  }
6933
7001
 
6934
- def fetch_transaction_fees(self, codes=None, params={}):
7002
+ def fetch_transaction_fees(self, codes: List[str] = None, params={}):
6935
7003
  """
6936
7004
  * @deprecated
6937
7005
  please use fetchDepositWithdrawFees instead
@@ -7028,7 +7096,7 @@ class binance(Exchange, ImplicitAPI):
7028
7096
  entry = response[i]
7029
7097
  currencyId = self.safe_string(entry, 'coin')
7030
7098
  code = self.safe_currency_code(currencyId)
7031
- networkList = self.safe_value(entry, 'networkList', [])
7099
+ networkList = self.safe_list(entry, 'networkList', [])
7032
7100
  withdrawFees[code] = {}
7033
7101
  for j in range(0, len(networkList)):
7034
7102
  networkEntry = networkList[j]
@@ -7137,14 +7205,14 @@ class binance(Exchange, ImplicitAPI):
7137
7205
  # ]
7138
7206
  # }
7139
7207
  #
7140
- networkList = self.safe_value(fee, 'networkList', [])
7208
+ networkList = self.safe_list(fee, 'networkList', [])
7141
7209
  result = self.deposit_withdraw_fee(fee)
7142
7210
  for j in range(0, len(networkList)):
7143
7211
  networkEntry = networkList[j]
7144
7212
  networkId = self.safe_string(networkEntry, 'network')
7145
7213
  networkCode = self.network_id_to_code(networkId)
7146
7214
  withdrawFee = self.safe_number(networkEntry, 'withdrawFee')
7147
- isDefault = self.safe_value(networkEntry, 'isDefault')
7215
+ isDefault = self.safe_bool(networkEntry, 'isDefault')
7148
7216
  if isDefault is True:
7149
7217
  result['withdraw'] = {
7150
7218
  'fee': withdrawFee,
@@ -7187,7 +7255,7 @@ class binance(Exchange, ImplicitAPI):
7187
7255
  }
7188
7256
  if tag is not None:
7189
7257
  request['addressTag'] = tag
7190
- networks = self.safe_value(self.options, 'networks', {})
7258
+ networks = self.safe_dict(self.options, 'networks', {})
7191
7259
  network = self.safe_string_upper(params, 'network') # self line allows the user to specify either ERC20 or ETH
7192
7260
  network = self.safe_string(networks, network, network) # handle ERC20>ETH alias
7193
7261
  if network is not None:
@@ -7273,7 +7341,7 @@ class binance(Exchange, ImplicitAPI):
7273
7341
  #
7274
7342
  data = response
7275
7343
  if isinstance(data, list):
7276
- data = self.safe_value(data, 0, {})
7344
+ data = self.safe_dict(data, 0, {})
7277
7345
  return self.parse_trading_fee(data)
7278
7346
 
7279
7347
  def fetch_trading_fees(self, params={}):
@@ -7647,8 +7715,8 @@ class binance(Exchange, ImplicitAPI):
7647
7715
  }
7648
7716
 
7649
7717
  def parse_account_positions(self, account):
7650
- positions = self.safe_value(account, 'positions')
7651
- assets = self.safe_value(account, 'assets', [])
7718
+ positions = self.safe_list(account, 'positions')
7719
+ assets = self.safe_list(account, 'assets', [])
7652
7720
  balances = {}
7653
7721
  for i in range(0, len(assets)):
7654
7722
  entry = assets[i]
@@ -7666,18 +7734,22 @@ class binance(Exchange, ImplicitAPI):
7666
7734
  marketId = self.safe_string(position, 'symbol')
7667
7735
  market = self.safe_market(marketId, None, None, 'contract')
7668
7736
  code = market['quote'] if market['linear'] else market['base']
7669
- # sometimes not all the codes are correctly returned...
7670
- if code in balances:
7671
- parsed = self.parse_account_position(self.extend(position, {
7672
- 'crossMargin': balances[code]['crossMargin'],
7673
- 'crossWalletBalance': balances[code]['crossWalletBalance'],
7674
- }), market)
7675
- result.append(parsed)
7737
+ maintenanceMargin = self.safe_string(position, 'maintMargin')
7738
+ # check for maintenance margin so empty positions are not returned
7739
+ if (maintenanceMargin != '0') and (maintenanceMargin != '0.00000000'):
7740
+ # sometimes not all the codes are correctly returned...
7741
+ if code in balances:
7742
+ parsed = self.parse_account_position(self.extend(position, {
7743
+ 'crossMargin': balances[code]['crossMargin'],
7744
+ 'crossWalletBalance': balances[code]['crossWalletBalance'],
7745
+ }), market)
7746
+ result.append(parsed)
7676
7747
  return result
7677
7748
 
7678
7749
  def parse_account_position(self, position, market: Market = None):
7679
7750
  #
7680
7751
  # usdm
7752
+ #
7681
7753
  # {
7682
7754
  # "symbol": "BTCBUSD",
7683
7755
  # "initialMargin": "0",
@@ -7698,6 +7770,7 @@ class binance(Exchange, ImplicitAPI):
7698
7770
  # }
7699
7771
  #
7700
7772
  # coinm
7773
+ #
7701
7774
  # {
7702
7775
  # "symbol": "BTCUSD_210625",
7703
7776
  # "initialMargin": "0.00024393",
@@ -7716,6 +7789,46 @@ class binance(Exchange, ImplicitAPI):
7716
7789
  # "crossWalletBalance": "34",
7717
7790
  # }
7718
7791
  #
7792
+ # linear portfolio margin
7793
+ #
7794
+ # {
7795
+ # "symbol": "CTSIUSDT",
7796
+ # "initialMargin": "0",
7797
+ # "maintMargin": "0",
7798
+ # "unrealizedProfit": "0.00000000",
7799
+ # "positionInitialMargin": "0",
7800
+ # "openOrderInitialMargin": "0",
7801
+ # "leverage": "20",
7802
+ # "entryPrice": "0.0",
7803
+ # "maxNotional": "25000",
7804
+ # "bidNotional": "0",
7805
+ # "askNotional": "0",
7806
+ # "positionSide": "SHORT",
7807
+ # "positionAmt": "0",
7808
+ # "updateTime": 0,
7809
+ # "notional": "0",
7810
+ # "breakEvenPrice": "0.0"
7811
+ # }
7812
+ #
7813
+ # inverse portoflio margin
7814
+ #
7815
+ # {
7816
+ # "symbol": "TRXUSD_PERP",
7817
+ # "initialMargin": "0",
7818
+ # "maintMargin": "0",
7819
+ # "unrealizedProfit": "0.00000000",
7820
+ # "positionInitialMargin": "0",
7821
+ # "openOrderInitialMargin": "0",
7822
+ # "leverage": "20",
7823
+ # "entryPrice": "0.00000000",
7824
+ # "positionSide": "SHORT",
7825
+ # "positionAmt": "0",
7826
+ # "maxQty": "5000000",
7827
+ # "updateTime": 0,
7828
+ # "notionalValue": "0",
7829
+ # "breakEvenPrice": "0.00000000"
7830
+ # }
7831
+ #
7719
7832
  marketId = self.safe_string(position, 'symbol')
7720
7833
  market = self.safe_market(marketId, market, None, 'contract')
7721
7834
  symbol = self.safe_string(market, 'symbol')
@@ -7744,8 +7857,8 @@ class binance(Exchange, ImplicitAPI):
7744
7857
  contractsString = Precise.string_div(entryNotional, contractSizeNew)
7745
7858
  contractsStringAbs = Precise.string_div(Precise.string_add(contractsString, '0.5'), '1', 0)
7746
7859
  contracts = self.parse_number(contractsStringAbs)
7747
- leverageBrackets = self.safe_value(self.options, 'leverageBrackets', {})
7748
- leverageBracket = self.safe_value(leverageBrackets, symbol, [])
7860
+ leverageBrackets = self.safe_dict(self.options, 'leverageBrackets', {})
7861
+ leverageBracket = self.safe_list(leverageBrackets, symbol, [])
7749
7862
  maintenanceMarginPercentageString = None
7750
7863
  for i in range(0, len(leverageBracket)):
7751
7864
  bracket = leverageBracket[i]
@@ -7758,7 +7871,7 @@ class binance(Exchange, ImplicitAPI):
7758
7871
  timestamp = self.safe_integer(position, 'updateTime')
7759
7872
  if timestamp == 0:
7760
7873
  timestamp = None
7761
- isolated = self.safe_value(position, 'isolated')
7874
+ isolated = self.safe_bool(position, 'isolated')
7762
7875
  marginMode = None
7763
7876
  collateralString = None
7764
7877
  walletBalance = None
@@ -7900,11 +8013,45 @@ class binance(Exchange, ImplicitAPI):
7900
8013
  # "isolatedWallet": "0.00268058"
7901
8014
  # }
7902
8015
  #
8016
+ # inverse portfolio margin
8017
+ #
8018
+ # {
8019
+ # "symbol": "ETHUSD_PERP",
8020
+ # "positionAmt": "1",
8021
+ # "entryPrice": "2422.400000007",
8022
+ # "markPrice": "2424.51267823",
8023
+ # "unRealizedProfit": "0.0000036",
8024
+ # "liquidationPrice": "293.57678898",
8025
+ # "leverage": "100",
8026
+ # "positionSide": "LONG",
8027
+ # "updateTime": 1707371941861,
8028
+ # "maxQty": "15",
8029
+ # "notionalValue": "0.00412454",
8030
+ # "breakEvenPrice": "2423.368960034"
8031
+ # }
8032
+ #
8033
+ # linear portfolio margin
8034
+ #
8035
+ # {
8036
+ # "symbol": "BTCUSDT",
8037
+ # "positionAmt": "0.01",
8038
+ # "entryPrice": "44525.0",
8039
+ # "markPrice": "45464.1735922",
8040
+ # "unRealizedProfit": "9.39173592",
8041
+ # "liquidationPrice": "38007.16308568",
8042
+ # "leverage": "100",
8043
+ # "positionSide": "LONG",
8044
+ # "updateTime": 1707371879042,
8045
+ # "maxNotionalValue": "500000.0",
8046
+ # "notional": "454.64173592",
8047
+ # "breakEvenPrice": "44542.81"
8048
+ # }
8049
+ #
7903
8050
  marketId = self.safe_string(position, 'symbol')
7904
8051
  market = self.safe_market(marketId, market, None, 'contract')
7905
8052
  symbol = self.safe_string(market, 'symbol')
7906
- leverageBrackets = self.safe_value(self.options, 'leverageBrackets', {})
7907
- leverageBracket = self.safe_value(leverageBrackets, symbol, [])
8053
+ leverageBrackets = self.safe_dict(self.options, 'leverageBrackets', {})
8054
+ leverageBracket = self.safe_list(leverageBrackets, symbol, [])
7908
8055
  notionalString = self.safe_string_2(position, 'notional', 'notionalValue')
7909
8056
  notionalStringAbs = Precise.string_abs(notionalString)
7910
8057
  maintenanceMarginPercentageString = None
@@ -7937,7 +8084,7 @@ class binance(Exchange, ImplicitAPI):
7937
8084
  linear = ('notional' in position)
7938
8085
  if marginMode == 'cross':
7939
8086
  # calculate collateral
7940
- precision = self.safe_value(market, 'precision', {})
8087
+ precision = self.safe_dict(market, 'precision', {})
7941
8088
  if linear:
7942
8089
  # walletBalance = (liquidationPrice * (±1 + mmp) ± entryPrice) * contracts
7943
8090
  onePlusMaintenanceMarginPercentageString = None
@@ -8031,11 +8178,19 @@ class binance(Exchange, ImplicitAPI):
8031
8178
  query = self.omit(params, 'type')
8032
8179
  subType = None
8033
8180
  subType, params = self.handle_sub_type_and_params('loadLeverageBrackets', None, params, 'linear')
8181
+ isPortfolioMargin = None
8182
+ isPortfolioMargin, params = self.handle_option_and_params_2(params, 'loadLeverageBrackets', 'papi', 'portfolioMargin', False)
8034
8183
  response = None
8035
8184
  if self.is_linear(type, subType):
8036
- response = self.fapiPrivateGetLeverageBracket(query)
8185
+ if isPortfolioMargin:
8186
+ response = self.papiGetUmLeverageBracket(query)
8187
+ else:
8188
+ response = self.fapiPrivateGetLeverageBracket(query)
8037
8189
  elif self.is_inverse(type, subType):
8038
- response = self.dapiPrivateV2GetLeverageBracket(query)
8190
+ if isPortfolioMargin:
8191
+ response = self.papiGetCmLeverageBracket(query)
8192
+ else:
8193
+ response = self.dapiPrivateV2GetLeverageBracket(query)
8039
8194
  else:
8040
8195
  raise NotSupported(self.id + ' loadLeverageBrackets() supports linear and inverse contracts only')
8041
8196
  self.options['leverageBrackets'] = {}
@@ -8043,7 +8198,7 @@ class binance(Exchange, ImplicitAPI):
8043
8198
  entry = response[i]
8044
8199
  marketId = self.safe_string(entry, 'symbol')
8045
8200
  symbol = self.safe_symbol(marketId, None, None, 'contract')
8046
- brackets = self.safe_value(entry, 'brackets', [])
8201
+ brackets = self.safe_list(entry, 'brackets', [])
8047
8202
  result = []
8048
8203
  for j in range(0, len(brackets)):
8049
8204
  bracket = brackets[j]
@@ -8058,8 +8213,11 @@ class binance(Exchange, ImplicitAPI):
8058
8213
  retrieve information on the maximum leverage, and maintenance margin for trades of varying trade sizes
8059
8214
  :see: https://binance-docs.github.io/apidocs/futures/en/#notional-and-leverage-brackets-user_data
8060
8215
  :see: https://binance-docs.github.io/apidocs/delivery/en/#notional-bracket-for-symbol-user_data
8216
+ :see: https://binance-docs.github.io/apidocs/pm/en/#um-notional-and-leverage-brackets-user_data
8217
+ :see: https://binance-docs.github.io/apidocs/pm/en/#cm-notional-and-leverage-brackets-user_data
8061
8218
  :param str[]|None symbols: list of unified market symbols
8062
8219
  :param dict [params]: extra parameters specific to the exchange API endpoint
8220
+ :param boolean [params.portfolioMargin]: set to True if you would like to fetch the leverage tiers for a portfolio margin account
8063
8221
  :returns dict: a dictionary of `leverage tiers structures <https://docs.ccxt.com/#/?id=leverage-tiers-structure>`, indexed by market symbols
8064
8222
  """
8065
8223
  self.load_markets()
@@ -8067,11 +8225,19 @@ class binance(Exchange, ImplicitAPI):
8067
8225
  type, params = self.handle_market_type_and_params('fetchLeverageTiers', None, params)
8068
8226
  subType = None
8069
8227
  subType, params = self.handle_sub_type_and_params('fetchLeverageTiers', None, params, 'linear')
8228
+ isPortfolioMargin = None
8229
+ isPortfolioMargin, params = self.handle_option_and_params_2(params, 'fetchLeverageTiers', 'papi', 'portfolioMargin', False)
8070
8230
  response = None
8071
8231
  if self.is_linear(type, subType):
8072
- response = self.fapiPrivateGetLeverageBracket(params)
8232
+ if isPortfolioMargin:
8233
+ response = self.papiGetUmLeverageBracket(params)
8234
+ else:
8235
+ response = self.fapiPrivateGetLeverageBracket(params)
8073
8236
  elif self.is_inverse(type, subType):
8074
- response = self.dapiPrivateV2GetLeverageBracket(params)
8237
+ if isPortfolioMargin:
8238
+ response = self.papiGetCmLeverageBracket(params)
8239
+ else:
8240
+ response = self.dapiPrivateV2GetLeverageBracket(params)
8075
8241
  else:
8076
8242
  raise NotSupported(self.id + ' fetchLeverageTiers() supports linear and inverse contracts only')
8077
8243
  #
@@ -8138,7 +8304,7 @@ class binance(Exchange, ImplicitAPI):
8138
8304
  #
8139
8305
  marketId = self.safe_string(info, 'symbol')
8140
8306
  market = self.safe_market(marketId, market, None, 'contract')
8141
- brackets = self.safe_value(info, 'brackets', [])
8307
+ brackets = self.safe_list(info, 'brackets', [])
8142
8308
  tiers = []
8143
8309
  for j in range(0, len(brackets)):
8144
8310
  bracket = brackets[j]
@@ -8333,8 +8499,11 @@ class binance(Exchange, ImplicitAPI):
8333
8499
  fetch account positions
8334
8500
  :see: https://binance-docs.github.io/apidocs/futures/en/#account-information-v2-user_data
8335
8501
  :see: https://binance-docs.github.io/apidocs/delivery/en/#account-information-user_data
8502
+ :see: https://binance-docs.github.io/apidocs/pm/en/#get-um-account-detail-user_data
8503
+ :see: https://binance-docs.github.io/apidocs/pm/en/#get-cm-account-detail-user_data
8336
8504
  :param str[]|None symbols: list of unified market symbols
8337
8505
  :param dict [params]: extra parameters specific to the exchange API endpoint
8506
+ :param boolean [params.portfolioMargin]: set to True if you would like to fetch positions in a portfolio margin account
8338
8507
  :returns dict: data on account positions
8339
8508
  """
8340
8509
  if symbols is not None:
@@ -8344,14 +8513,22 @@ class binance(Exchange, ImplicitAPI):
8344
8513
  self.load_leverage_brackets(False, params)
8345
8514
  defaultType = self.safe_string(self.options, 'defaultType', 'future')
8346
8515
  type = self.safe_string(params, 'type', defaultType)
8347
- query = self.omit(params, 'type')
8516
+ params = self.omit(params, 'type')
8348
8517
  subType = None
8349
- subType, query = self.handle_sub_type_and_params('fetchAccountPositions', None, params, 'linear')
8518
+ subType, params = self.handle_sub_type_and_params('fetchAccountPositions', None, params, 'linear')
8519
+ isPortfolioMargin = None
8520
+ isPortfolioMargin, params = self.handle_option_and_params_2(params, 'fetchAccountPositions', 'papi', 'portfolioMargin', False)
8350
8521
  response = None
8351
8522
  if self.is_linear(type, subType):
8352
- response = self.fapiPrivateV2GetAccount(query)
8523
+ if isPortfolioMargin:
8524
+ response = self.papiGetUmAccount(params)
8525
+ else:
8526
+ response = self.fapiPrivateV2GetAccount(params)
8353
8527
  elif self.is_inverse(type, subType):
8354
- response = self.dapiPrivateGetAccount(query)
8528
+ if isPortfolioMargin:
8529
+ response = self.papiGetCmAccount(params)
8530
+ else:
8531
+ response = self.dapiPrivateGetAccount(params)
8355
8532
  else:
8356
8533
  raise NotSupported(self.id + ' fetchPositions() supports linear and inverse contracts only')
8357
8534
  result = self.parse_account_positions(response)
@@ -8364,8 +8541,11 @@ class binance(Exchange, ImplicitAPI):
8364
8541
  fetch positions risk
8365
8542
  :see: https://binance-docs.github.io/apidocs/futures/en/#position-information-v2-user_data
8366
8543
  :see: https://binance-docs.github.io/apidocs/delivery/en/#position-information-user_data
8544
+ :see: https://binance-docs.github.io/apidocs/pm/en/#query-um-position-information-user_data
8545
+ :see: https://binance-docs.github.io/apidocs/pm/en/#query-cm-position-information-user_data
8367
8546
  :param str[]|None symbols: list of unified market symbols
8368
8547
  :param dict [params]: extra parameters specific to the exchange API endpoint
8548
+ :param boolean [params.portfolioMargin]: set to True if you would like to fetch positions for a portfolio margin account
8369
8549
  :returns dict: data on the positions risk
8370
8550
  """
8371
8551
  if symbols is not None:
@@ -8379,68 +8559,117 @@ class binance(Exchange, ImplicitAPI):
8379
8559
  type = self.safe_string(params, 'type', defaultType)
8380
8560
  subType = None
8381
8561
  subType, params = self.handle_sub_type_and_params('fetchPositionsRisk', None, params, 'linear')
8562
+ isPortfolioMargin = None
8563
+ isPortfolioMargin, params = self.handle_option_and_params_2(params, 'fetchPositionsRisk', 'papi', 'portfolioMargin', False)
8382
8564
  params = self.omit(params, 'type')
8383
8565
  response = None
8384
8566
  if self.is_linear(type, subType):
8385
- response = self.fapiPrivateV2GetPositionRisk(self.extend(request, params))
8386
- # ### Response examples ###
8387
- #
8388
- # For One-way position mode:
8389
- # [
8390
- # {
8391
- # "entryPrice": "0.00000",
8392
- # "marginType": "isolated",
8393
- # "isAutoAddMargin": "false",
8394
- # "isolatedMargin": "0.00000000",
8395
- # "leverage": "10",
8396
- # "liquidationPrice": "0",
8397
- # "markPrice": "6679.50671178",
8398
- # "maxNotionalValue": "20000000",
8399
- # "positionAmt": "0.000",
8400
- # "symbol": "BTCUSDT",
8401
- # "unRealizedProfit": "0.00000000",
8402
- # "positionSide": "BOTH",
8403
- # "updateTime": 0
8404
- # }
8405
- # ]
8406
- #
8407
- # For Hedge position mode:
8408
- # [
8409
- # {
8410
- # "entryPrice": "6563.66500",
8411
- # "marginType": "isolated",
8412
- # "isAutoAddMargin": "false",
8413
- # "isolatedMargin": "15517.54150468",
8414
- # "leverage": "10",
8415
- # "liquidationPrice": "5930.78",
8416
- # "markPrice": "6679.50671178",
8417
- # "maxNotionalValue": "20000000",
8418
- # "positionAmt": "20.000",
8419
- # "symbol": "BTCUSDT",
8420
- # "unRealizedProfit": "2316.83423560"
8421
- # "positionSide": "LONG",
8422
- # "updateTime": 1625474304765
8423
- # },
8424
- # {
8425
- # "entryPrice": "0.00000",
8426
- # "marginType": "isolated",
8427
- # "isAutoAddMargin": "false",
8428
- # "isolatedMargin": "5413.95799991",
8429
- # "leverage": "10",
8430
- # "liquidationPrice": "7189.95",
8431
- # "markPrice": "6679.50671178",
8432
- # "maxNotionalValue": "20000000",
8433
- # "positionAmt": "-10.000",
8434
- # "symbol": "BTCUSDT",
8435
- # "unRealizedProfit": "-1156.46711780",
8436
- # "positionSide": "SHORT",
8437
- # "updateTime": 0
8438
- # }
8439
- # ]
8567
+ if isPortfolioMargin:
8568
+ response = self.papiGetUmPositionRisk(self.extend(request, params))
8569
+ else:
8570
+ response = self.fapiPrivateV2GetPositionRisk(self.extend(request, params))
8440
8571
  elif self.is_inverse(type, subType):
8441
- response = self.dapiPrivateGetPositionRisk(self.extend(request, params))
8572
+ if isPortfolioMargin:
8573
+ response = self.papiGetCmPositionRisk(self.extend(request, params))
8574
+ else:
8575
+ response = self.dapiPrivateGetPositionRisk(self.extend(request, params))
8442
8576
  else:
8443
8577
  raise NotSupported(self.id + ' fetchPositionsRisk() supports linear and inverse contracts only')
8578
+ # ### Response examples ###
8579
+ #
8580
+ # For One-way position mode:
8581
+ #
8582
+ # [
8583
+ # {
8584
+ # "entryPrice": "0.00000",
8585
+ # "marginType": "isolated",
8586
+ # "isAutoAddMargin": "false",
8587
+ # "isolatedMargin": "0.00000000",
8588
+ # "leverage": "10",
8589
+ # "liquidationPrice": "0",
8590
+ # "markPrice": "6679.50671178",
8591
+ # "maxNotionalValue": "20000000",
8592
+ # "positionAmt": "0.000",
8593
+ # "symbol": "BTCUSDT",
8594
+ # "unRealizedProfit": "0.00000000",
8595
+ # "positionSide": "BOTH",
8596
+ # "updateTime": 0
8597
+ # }
8598
+ # ]
8599
+ #
8600
+ # For Hedge position mode:
8601
+ #
8602
+ # [
8603
+ # {
8604
+ # "entryPrice": "6563.66500",
8605
+ # "marginType": "isolated",
8606
+ # "isAutoAddMargin": "false",
8607
+ # "isolatedMargin": "15517.54150468",
8608
+ # "leverage": "10",
8609
+ # "liquidationPrice": "5930.78",
8610
+ # "markPrice": "6679.50671178",
8611
+ # "maxNotionalValue": "20000000",
8612
+ # "positionAmt": "20.000",
8613
+ # "symbol": "BTCUSDT",
8614
+ # "unRealizedProfit": "2316.83423560"
8615
+ # "positionSide": "LONG",
8616
+ # "updateTime": 1625474304765
8617
+ # },
8618
+ # {
8619
+ # "entryPrice": "0.00000",
8620
+ # "marginType": "isolated",
8621
+ # "isAutoAddMargin": "false",
8622
+ # "isolatedMargin": "5413.95799991",
8623
+ # "leverage": "10",
8624
+ # "liquidationPrice": "7189.95",
8625
+ # "markPrice": "6679.50671178",
8626
+ # "maxNotionalValue": "20000000",
8627
+ # "positionAmt": "-10.000",
8628
+ # "symbol": "BTCUSDT",
8629
+ # "unRealizedProfit": "-1156.46711780",
8630
+ # "positionSide": "SHORT",
8631
+ # "updateTime": 0
8632
+ # }
8633
+ # ]
8634
+ #
8635
+ # inverse portfolio margin:
8636
+ #
8637
+ # [
8638
+ # {
8639
+ # "symbol": "ETHUSD_PERP",
8640
+ # "positionAmt": "1",
8641
+ # "entryPrice": "2422.400000007",
8642
+ # "markPrice": "2424.51267823",
8643
+ # "unRealizedProfit": "0.0000036",
8644
+ # "liquidationPrice": "293.57678898",
8645
+ # "leverage": "100",
8646
+ # "positionSide": "LONG",
8647
+ # "updateTime": 1707371941861,
8648
+ # "maxQty": "15",
8649
+ # "notionalValue": "0.00412454",
8650
+ # "breakEvenPrice": "2423.368960034"
8651
+ # }
8652
+ # ]
8653
+ #
8654
+ # linear portfolio margin:
8655
+ #
8656
+ # [
8657
+ # {
8658
+ # "symbol": "BTCUSDT",
8659
+ # "positionAmt": "0.01",
8660
+ # "entryPrice": "44525.0",
8661
+ # "markPrice": "45464.1735922",
8662
+ # "unRealizedProfit": "9.39173592",
8663
+ # "liquidationPrice": "38007.16308568",
8664
+ # "leverage": "100",
8665
+ # "positionSide": "LONG",
8666
+ # "updateTime": 1707371879042,
8667
+ # "maxNotionalValue": "500000.0",
8668
+ # "notional": "454.64173592",
8669
+ # "breakEvenPrice": "44542.81"
8670
+ # }
8671
+ # ]
8672
+ #
8444
8673
  result = []
8445
8674
  for i in range(0, len(response)):
8446
8675
  parsed = self.parse_position_risk(response[i])
@@ -8453,10 +8682,14 @@ class binance(Exchange, ImplicitAPI):
8453
8682
  fetch the history of funding payments paid and received on self account
8454
8683
  :see: https://binance-docs.github.io/apidocs/futures/en/#get-income-history-user_data
8455
8684
  :see: https://binance-docs.github.io/apidocs/delivery/en/#get-income-history-user_data
8685
+ :see: https://binance-docs.github.io/apidocs/pm/en/#get-um-income-history-user_data
8686
+ :see: https://binance-docs.github.io/apidocs/pm/en/#get-cm-income-history-user_data
8456
8687
  :param str symbol: unified market symbol
8457
8688
  :param int [since]: the earliest time in ms to fetch funding history for
8458
8689
  :param int [limit]: the maximum number of funding history structures to retrieve
8459
8690
  :param dict [params]: extra parameters specific to the exchange API endpoint
8691
+ :param int [params.until]: timestamp in ms of the latest funding history entry
8692
+ :param boolean [params.portfolioMargin]: set to True if you would like to fetch the funding history for a portfolio margin account
8460
8693
  :returns dict: a `funding history structure <https://docs.ccxt.com/#/?id=funding-history-structure>`
8461
8694
  """
8462
8695
  self.load_markets()
@@ -8471,6 +8704,9 @@ class binance(Exchange, ImplicitAPI):
8471
8704
  raise NotSupported(self.id + ' fetchFundingHistory() supports swap contracts only')
8472
8705
  subType = None
8473
8706
  subType, params = self.handle_sub_type_and_params('fetchFundingHistory', market, params, 'linear')
8707
+ isPortfolioMargin = None
8708
+ isPortfolioMargin, params = self.handle_option_and_params_2(params, 'fetchFundingHistory', 'papi', 'portfolioMargin', False)
8709
+ request, params = self.handle_until_option('endTime', request, params)
8474
8710
  if since is not None:
8475
8711
  request['startTime'] = since
8476
8712
  if limit is not None:
@@ -8480,9 +8716,15 @@ class binance(Exchange, ImplicitAPI):
8480
8716
  params = self.omit(params, 'type')
8481
8717
  response = None
8482
8718
  if self.is_linear(type, subType):
8483
- response = self.fapiPrivateGetIncome(self.extend(request, params))
8719
+ if isPortfolioMargin:
8720
+ response = self.papiGetUmIncome(self.extend(request, params))
8721
+ else:
8722
+ response = self.fapiPrivateGetIncome(self.extend(request, params))
8484
8723
  elif self.is_inverse(type, subType):
8485
- response = self.dapiPrivateGetIncome(self.extend(request, params))
8724
+ if isPortfolioMargin:
8725
+ response = self.papiGetCmIncome(self.extend(request, params))
8726
+ else:
8727
+ response = self.dapiPrivateGetIncome(self.extend(request, params))
8486
8728
  else:
8487
8729
  raise NotSupported(self.id + ' fetchFundingHistory() supports linear and inverse contracts only')
8488
8730
  return self.parse_incomes(response, market, since, limit)
@@ -8492,9 +8734,12 @@ class binance(Exchange, ImplicitAPI):
8492
8734
  set the level of leverage for a market
8493
8735
  :see: https://binance-docs.github.io/apidocs/futures/en/#change-initial-leverage-trade
8494
8736
  :see: https://binance-docs.github.io/apidocs/delivery/en/#change-initial-leverage-trade
8737
+ :see: https://binance-docs.github.io/apidocs/pm/en/#change-um-initial-leverage-trade
8738
+ :see: https://binance-docs.github.io/apidocs/pm/en/#change-cm-initial-leverage-trade
8495
8739
  :param float leverage: the rate of leverage
8496
8740
  :param str symbol: unified market symbol
8497
8741
  :param dict [params]: extra parameters specific to the exchange API endpoint
8742
+ :param boolean [params.portfolioMargin]: set to True if you would like to set the leverage for a trading pair in a portfolio margin account
8498
8743
  :returns dict: response from the exchange
8499
8744
  """
8500
8745
  if symbol is None:
@@ -8509,11 +8754,19 @@ class binance(Exchange, ImplicitAPI):
8509
8754
  'symbol': market['id'],
8510
8755
  'leverage': leverage,
8511
8756
  }
8757
+ isPortfolioMargin = None
8758
+ isPortfolioMargin, params = self.handle_option_and_params_2(params, 'setLeverage', 'papi', 'portfolioMargin', False)
8512
8759
  response = None
8513
8760
  if market['linear']:
8514
- response = self.fapiPrivatePostLeverage(self.extend(request, params))
8761
+ if isPortfolioMargin:
8762
+ response = self.papiPostUmLeverage(self.extend(request, params))
8763
+ else:
8764
+ response = self.fapiPrivatePostLeverage(self.extend(request, params))
8515
8765
  elif market['inverse']:
8516
- response = self.dapiPrivatePostLeverage(self.extend(request, params))
8766
+ if isPortfolioMargin:
8767
+ response = self.papiPostCmLeverage(self.extend(request, params))
8768
+ else:
8769
+ response = self.dapiPrivatePostLeverage(self.extend(request, params))
8517
8770
  else:
8518
8771
  raise NotSupported(self.id + ' setLeverage() supports linear and inverse contracts only')
8519
8772
  return response
@@ -8572,14 +8825,17 @@ class binance(Exchange, ImplicitAPI):
8572
8825
  raise e
8573
8826
  return response
8574
8827
 
8575
- def set_position_mode(self, hedged, symbol: Str = None, params={}):
8828
+ def set_position_mode(self, hedged: bool, symbol: Str = None, params={}):
8576
8829
  """
8577
8830
  set hedged to True or False for a market
8578
8831
  :see: https://binance-docs.github.io/apidocs/futures/en/#change-position-mode-trade
8579
8832
  :see: https://binance-docs.github.io/apidocs/delivery/en/#change-position-mode-trade
8833
+ :see: https://binance-docs.github.io/apidocs/pm/en/#change-um-position-mode-trade
8834
+ :see: https://binance-docs.github.io/apidocs/pm/en/#change-cm-position-mode-trade
8580
8835
  :param bool hedged: set to True to use dualSidePosition
8581
8836
  :param str symbol: not used by binance setPositionMode()
8582
8837
  :param dict [params]: extra parameters specific to the exchange API endpoint
8838
+ :param boolean [params.portfolioMargin]: set to True if you would like to set the position mode for a portfolio margin account
8583
8839
  :returns dict: response from the exchange
8584
8840
  """
8585
8841
  defaultType = self.safe_string(self.options, 'defaultType', 'future')
@@ -8587,6 +8843,8 @@ class binance(Exchange, ImplicitAPI):
8587
8843
  params = self.omit(params, ['type'])
8588
8844
  subType = None
8589
8845
  subType, params = self.handle_sub_type_and_params('setPositionMode', None, params)
8846
+ isPortfolioMargin = None
8847
+ isPortfolioMargin, params = self.handle_option_and_params_2(params, 'setPositionMode', 'papi', 'portfolioMargin', False)
8590
8848
  dualSidePosition = None
8591
8849
  if hedged:
8592
8850
  dualSidePosition = 'true'
@@ -8597,10 +8855,15 @@ class binance(Exchange, ImplicitAPI):
8597
8855
  }
8598
8856
  response = None
8599
8857
  if self.is_inverse(type, subType):
8600
- response = self.dapiPrivatePostPositionSideDual(self.extend(request, params))
8858
+ if isPortfolioMargin:
8859
+ response = self.papiPostCmPositionSideDual(self.extend(request, params))
8860
+ else:
8861
+ response = self.dapiPrivatePostPositionSideDual(self.extend(request, params))
8601
8862
  else:
8602
- # default to future
8603
- response = self.fapiPrivatePostPositionSideDual(self.extend(request, params))
8863
+ if isPortfolioMargin:
8864
+ response = self.papiPostUmPositionSideDual(self.extend(request, params))
8865
+ else:
8866
+ response = self.fapiPrivatePostPositionSideDual(self.extend(request, params))
8604
8867
  #
8605
8868
  # {
8606
8869
  # "code": 200,
@@ -8784,12 +9047,15 @@ class binance(Exchange, ImplicitAPI):
8784
9047
  :see: https://binance-docs.github.io/apidocs/voptions/en/#account-funding-flow-user_data
8785
9048
  :see: https://binance-docs.github.io/apidocs/futures/en/#get-income-history-user_data
8786
9049
  :see: https://binance-docs.github.io/apidocs/delivery/en/#get-income-history-user_data
9050
+ :see: https://binance-docs.github.io/apidocs/pm/en/#get-um-income-history-user_data
9051
+ :see: https://binance-docs.github.io/apidocs/pm/en/#get-cm-income-history-user_data
8787
9052
  :param str code: unified currency code
8788
9053
  :param int [since]: timestamp in ms of the earliest ledger entry
8789
9054
  :param int [limit]: max number of ledger entrys to return
8790
9055
  :param dict [params]: extra parameters specific to the exchange API endpoint
8791
9056
  :param int [params.until]: timestamp in ms of the latest ledger entry
8792
- :param boolean [params.paginate]: default False, when True will automatically paginate by calling self endpoint multiple times. See in the docs all the [availble parameters](https://github.com/ccxt/ccxt/wiki/Manual#pagination-params)
9057
+ :param boolean [params.paginate]: default False, when True will automatically paginate by calling self endpoint multiple times. See in the docs all the [available parameters](https://github.com/ccxt/ccxt/wiki/Manual#pagination-params)
9058
+ :param boolean [params.portfolioMargin]: set to True if you would like to fetch the ledger for a portfolio margin account
8793
9059
  :returns dict: a `ledger structure <https://docs.ccxt.com/#/?id=ledger-structure>`
8794
9060
  """
8795
9061
  self.load_markets()
@@ -8813,15 +9079,23 @@ class binance(Exchange, ImplicitAPI):
8813
9079
  if until is not None:
8814
9080
  params = self.omit(params, 'until')
8815
9081
  request['endTime'] = until
9082
+ isPortfolioMargin = None
9083
+ isPortfolioMargin, params = self.handle_option_and_params_2(params, 'fetchLedger', 'papi', 'portfolioMargin', False)
8816
9084
  response = None
8817
9085
  if type == 'option':
8818
9086
  self.check_required_argument('fetchLedger', code, 'code')
8819
9087
  request['currency'] = currency['id']
8820
9088
  response = self.eapiPrivateGetBill(self.extend(request, params))
8821
9089
  elif self.is_linear(type, subType):
8822
- response = self.fapiPrivateGetIncome(self.extend(request, params))
9090
+ if isPortfolioMargin:
9091
+ response = self.papiGetUmIncome(self.extend(request, params))
9092
+ else:
9093
+ response = self.fapiPrivateGetIncome(self.extend(request, params))
8823
9094
  elif self.is_inverse(type, subType):
8824
- response = self.dapiPrivateGetIncome(self.extend(request, params))
9095
+ if isPortfolioMargin:
9096
+ response = self.papiGetCmIncome(self.extend(request, params))
9097
+ else:
9098
+ response = self.dapiPrivateGetIncome(self.extend(request, params))
8825
9099
  else:
8826
9100
  raise NotSupported(self.id + ' fetchLedger() supports contract wallets only')
8827
9101
  #
@@ -8837,7 +9111,7 @@ class binance(Exchange, ImplicitAPI):
8837
9111
  # }
8838
9112
  # ]
8839
9113
  #
8840
- # futures(fapi, dapi)
9114
+ # futures(fapi, dapi, papi)
8841
9115
  #
8842
9116
  # [
8843
9117
  # {
@@ -8866,7 +9140,7 @@ class binance(Exchange, ImplicitAPI):
8866
9140
  # "createDate": 1676621042489
8867
9141
  # }
8868
9142
  #
8869
- # futures(fapi, dapi)
9143
+ # futures(fapi, dapi, papi)
8870
9144
  #
8871
9145
  # {
8872
9146
  # "symbol": "",
@@ -8965,7 +9239,7 @@ class binance(Exchange, ImplicitAPI):
8965
9239
  isSpotOrMargin = (api.find('sapi') > -1 or api == 'private')
8966
9240
  marketType = 'spot' if isSpotOrMargin else 'future'
8967
9241
  defaultId = 'x-xcKtGhcu' if (not isSpotOrMargin) else 'x-R4BD3S82'
8968
- broker = self.safe_value(self.options, 'broker', {})
9242
+ broker = self.safe_dict(self.options, 'broker', {})
8969
9243
  brokerId = self.safe_string(broker, marketType, defaultId)
8970
9244
  params['newClientOrderId'] = brokerId + self.uuid22()
8971
9245
  query = None
@@ -8987,8 +9261,8 @@ class binance(Exchange, ImplicitAPI):
8987
9261
  query = self.urlencode_with_array_repeat(extendedParams)
8988
9262
  elif (path == 'batchOrders') or (path.find('sub-account') >= 0) or (path == 'capital/withdraw/apply') or (path.find('staking') >= 0):
8989
9263
  if (method == 'DELETE') and (path == 'batchOrders'):
8990
- orderidlist = self.safe_value(extendedParams, 'orderidlist', [])
8991
- origclientorderidlist = self.safe_value(extendedParams, 'origclientorderidlist', [])
9264
+ orderidlist = self.safe_list(extendedParams, 'orderidlist', [])
9265
+ origclientorderidlist = self.safe_list(extendedParams, 'origclientorderidlist', [])
8992
9266
  extendedParams = self.omit(extendedParams, ['orderidlist', 'origclientorderidlist'])
8993
9267
  query = self.rawencode(extendedParams)
8994
9268
  orderidlistLength = len(orderidlist)
@@ -9037,8 +9311,8 @@ class binance(Exchange, ImplicitAPI):
9037
9311
  elif url.startswith('https://papi.' + hostname + '/'):
9038
9312
  marketType = 'portfoliomargin'
9039
9313
  if marketType is not None:
9040
- exceptionsForMarketType = self.safe_value(self.exceptions, marketType, {})
9041
- return self.safe_value(exceptionsForMarketType, exactOrBroad, {})
9314
+ exceptionsForMarketType = self.safe_dict(self.exceptions, marketType, {})
9315
+ return self.safe_dict(exceptionsForMarketType, exactOrBroad, {})
9042
9316
  return {}
9043
9317
 
9044
9318
  def handle_errors(self, code, reason, url, method, headers, body, response, requestHeaders, requestBody):
@@ -9234,7 +9508,7 @@ class binance(Exchange, ImplicitAPI):
9234
9508
  # },
9235
9509
  # ]
9236
9510
  #
9237
- rate = self.safe_value(response, 0)
9511
+ rate = self.safe_dict(response, 0)
9238
9512
  return self.parse_borrow_rate(rate)
9239
9513
 
9240
9514
  def fetch_borrow_rate_history(self, code: str, since: Int = None, limit: Int = None, params={}):
@@ -9330,7 +9604,7 @@ class binance(Exchange, ImplicitAPI):
9330
9604
  # "success": True
9331
9605
  # }
9332
9606
  #
9333
- data = self.safe_value(response, 'data')
9607
+ data = self.safe_dict(response, 'data')
9334
9608
  giftcardCode = self.safe_string(data, 'code')
9335
9609
  id = self.safe_string(data, 'referenceNo')
9336
9610
  return {
@@ -9429,7 +9703,7 @@ class binance(Exchange, ImplicitAPI):
9429
9703
  # "total": 1
9430
9704
  # }
9431
9705
  #
9432
- rows = self.safe_value(response, 'rows')
9706
+ rows = self.safe_list(response, 'rows')
9433
9707
  interest = self.parse_borrow_interests(rows, market)
9434
9708
  return self.filter_by_currency_since_limit(interest, code, since, limit)
9435
9709
 
@@ -9454,9 +9728,11 @@ class binance(Exchange, ImplicitAPI):
9454
9728
  """
9455
9729
  repay borrowed margin and interest
9456
9730
  :see: https://binance-docs.github.io/apidocs/spot/en/#margin-account-borrow-repay-margin
9731
+ :see: https://binance-docs.github.io/apidocs/pm/en/#margin-account-repay-margin
9457
9732
  :param str code: unified currency code of the currency to repay
9458
9733
  :param float amount: the amount to repay
9459
9734
  :param dict [params]: extra parameters specific to the exchange API endpoint
9735
+ :param boolean [params.portfolioMargin]: set to True if you would like to repay margin in a portfolio margin account
9460
9736
  :returns dict: a `margin loan structure <https://docs.ccxt.com/#/?id=margin-loan-structure>`
9461
9737
  """
9462
9738
  self.load_markets()
@@ -9464,10 +9740,16 @@ class binance(Exchange, ImplicitAPI):
9464
9740
  request = {
9465
9741
  'asset': currency['id'],
9466
9742
  'amount': self.currency_to_precision(code, amount),
9467
- 'isIsolated': 'FALSE',
9468
- 'type': 'REPAY',
9469
9743
  }
9470
- response = self.sapiPostMarginBorrowRepay(self.extend(request, params))
9744
+ response = None
9745
+ isPortfolioMargin = None
9746
+ isPortfolioMargin, params = self.handle_option_and_params_2(params, 'repayCrossMargin', 'papi', 'portfolioMargin', False)
9747
+ if isPortfolioMargin:
9748
+ response = self.papiPostRepayLoan(self.extend(request, params))
9749
+ else:
9750
+ request['isIsolated'] = 'FALSE'
9751
+ request['type'] = 'REPAY'
9752
+ response = self.sapiPostMarginBorrowRepay(self.extend(request, params))
9471
9753
  #
9472
9754
  # {
9473
9755
  # "tranId": 108988250265,
@@ -9509,9 +9791,11 @@ class binance(Exchange, ImplicitAPI):
9509
9791
  """
9510
9792
  create a loan to borrow margin
9511
9793
  :see: https://binance-docs.github.io/apidocs/spot/en/#margin-account-borrow-repay-margin
9794
+ :see: https://binance-docs.github.io/apidocs/pm/en/#margin-account-borrow-margin
9512
9795
  :param str code: unified currency code of the currency to borrow
9513
9796
  :param float amount: the amount to borrow
9514
9797
  :param dict [params]: extra parameters specific to the exchange API endpoint
9798
+ :param boolean [params.portfolioMargin]: set to True if you would like to borrow margin in a portfolio margin account
9515
9799
  :returns dict: a `margin loan structure <https://docs.ccxt.com/#/?id=margin-loan-structure>`
9516
9800
  """
9517
9801
  self.load_markets()
@@ -9519,10 +9803,16 @@ class binance(Exchange, ImplicitAPI):
9519
9803
  request = {
9520
9804
  'asset': currency['id'],
9521
9805
  'amount': self.currency_to_precision(code, amount),
9522
- 'isIsolated': 'FALSE',
9523
- 'type': 'BORROW',
9524
9806
  }
9525
- response = self.sapiPostMarginBorrowRepay(self.extend(request, params))
9807
+ response = None
9808
+ isPortfolioMargin = None
9809
+ isPortfolioMargin, params = self.handle_option_and_params_2(params, 'borrowCrossMargin', 'papi', 'portfolioMargin', False)
9810
+ if isPortfolioMargin:
9811
+ response = self.papiPostMarginLoan(self.extend(request, params))
9812
+ else:
9813
+ request['isIsolated'] = 'FALSE'
9814
+ request['type'] = 'BORROW'
9815
+ response = self.sapiPostMarginBorrowRepay(self.extend(request, params))
9526
9816
  #
9527
9817
  # {
9528
9818
  # "tranId": 108988250265,