ccxt 4.2.39__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.

ccxt/binance.py CHANGED
@@ -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
 
@@ -5028,7 +5028,7 @@ class binance(Exchange, ImplicitAPI):
5028
5028
  cost = self.safe_string(order, 'cumBase', cost)
5029
5029
  type = self.safe_string_lower(order, 'type')
5030
5030
  side = self.safe_string_lower(order, 'side')
5031
- fills = self.safe_value(order, 'fills', [])
5031
+ fills = self.safe_list(order, 'fills', [])
5032
5032
  timeInForce = self.safe_string(order, 'timeInForce')
5033
5033
  if timeInForce == 'GTX':
5034
5034
  # GTX means "Good Till Crossing" and is an equivalent way of saying Post Only
@@ -5058,7 +5058,7 @@ class binance(Exchange, ImplicitAPI):
5058
5058
  'type': type,
5059
5059
  'timeInForce': timeInForce,
5060
5060
  'postOnly': postOnly,
5061
- 'reduceOnly': self.safe_value(order, 'reduceOnly'),
5061
+ 'reduceOnly': self.safe_bool(order, 'reduceOnly'),
5062
5062
  'side': side,
5063
5063
  'price': price,
5064
5064
  'triggerPrice': stopPrice,
@@ -5090,7 +5090,7 @@ class binance(Exchange, ImplicitAPI):
5090
5090
  side = self.safe_string(rawOrder, 'side')
5091
5091
  amount = self.safe_value(rawOrder, 'amount')
5092
5092
  price = self.safe_value(rawOrder, 'price')
5093
- orderParams = self.safe_value(rawOrder, 'params', {})
5093
+ orderParams = self.safe_dict(rawOrder, 'params', {})
5094
5094
  orderRequest = self.create_order_request(marketId, type, side, amount, price, orderParams)
5095
5095
  ordersRequests.append(orderRequest)
5096
5096
  orderSymbols = self.market_symbols(orderSymbols, None, False, True, True)
@@ -6241,11 +6241,11 @@ class binance(Exchange, ImplicitAPI):
6241
6241
  # },
6242
6242
  # ]
6243
6243
  # }
6244
- results = self.safe_value(response, 'userAssetDribblets', [])
6244
+ results = self.safe_list(response, 'userAssetDribblets', [])
6245
6245
  rows = self.safe_integer(response, 'total', 0)
6246
6246
  data = []
6247
6247
  for i in range(0, rows):
6248
- logs = self.safe_value(results[i], 'userAssetDribbletDetails', [])
6248
+ logs = self.safe_list(results[i], 'userAssetDribbletDetails', [])
6249
6249
  for j in range(0, len(logs)):
6250
6250
  logs[j]['isDustTrade'] = True
6251
6251
  data.append(logs[j])
@@ -6342,7 +6342,7 @@ class binance(Exchange, ImplicitAPI):
6342
6342
  currency = None
6343
6343
  response = None
6344
6344
  request = {}
6345
- legalMoney = self.safe_value(self.options, 'legalMoney', {})
6345
+ legalMoney = self.safe_dict(self.options, 'legalMoney', {})
6346
6346
  fiatOnly = self.safe_bool(params, 'fiat', False)
6347
6347
  params = self.omit(params, 'fiatOnly')
6348
6348
  until = self.safe_integer(params, 'until')
@@ -6441,7 +6441,7 @@ class binance(Exchange, ImplicitAPI):
6441
6441
  paginate, params = self.handle_option_and_params(params, 'fetchWithdrawals', 'paginate')
6442
6442
  if paginate:
6443
6443
  return self.fetch_paginated_call_dynamic('fetchWithdrawals', code, since, limit, params)
6444
- legalMoney = self.safe_value(self.options, 'legalMoney', {})
6444
+ legalMoney = self.safe_dict(self.options, 'legalMoney', {})
6445
6445
  fiatOnly = self.safe_bool(params, 'fiat', False)
6446
6446
  params = self.omit(params, 'fiatOnly')
6447
6447
  request = {}
@@ -6576,7 +6576,7 @@ class binance(Exchange, ImplicitAPI):
6576
6576
  'Refund Failed': 'failed',
6577
6577
  },
6578
6578
  }
6579
- statuses = self.safe_value(statusesByType, type, {})
6579
+ statuses = self.safe_dict(statusesByType, type, {})
6580
6580
  return self.safe_string(statuses, status, status)
6581
6581
 
6582
6582
  def parse_transaction(self, transaction, currency: Currency = None) -> Transaction:
@@ -6733,7 +6733,7 @@ class binance(Exchange, ImplicitAPI):
6733
6733
  type = self.safe_string(transfer, 'type')
6734
6734
  fromAccount = None
6735
6735
  toAccount = None
6736
- accountsById = self.safe_value(self.options, 'accountsById', {})
6736
+ accountsById = self.safe_dict(self.options, 'accountsById', {})
6737
6737
  if type is not None:
6738
6738
  parts = type.split('_')
6739
6739
  fromAccount = self.safe_value(parts, 0)
@@ -6768,20 +6768,16 @@ class binance(Exchange, ImplicitAPI):
6768
6768
  # }
6769
6769
  #
6770
6770
  marketId = self.safe_string(income, 'symbol')
6771
- symbol = self.safe_symbol(marketId, market, None, 'swap')
6772
- amount = self.safe_number(income, 'income')
6773
6771
  currencyId = self.safe_string(income, 'asset')
6774
- code = self.safe_currency_code(currencyId)
6775
- id = self.safe_string(income, 'tranId')
6776
6772
  timestamp = self.safe_integer(income, 'time')
6777
6773
  return {
6778
6774
  'info': income,
6779
- 'symbol': symbol,
6780
- 'code': code,
6775
+ 'symbol': self.safe_symbol(marketId, market, None, 'swap'),
6776
+ 'code': self.safe_currency_code(currencyId),
6781
6777
  'timestamp': timestamp,
6782
6778
  'datetime': self.iso8601(timestamp),
6783
- 'id': id,
6784
- 'amount': amount,
6779
+ 'id': self.safe_string(income, 'tranId'),
6780
+ 'amount': self.safe_number(income, 'income'),
6785
6781
  }
6786
6782
 
6787
6783
  def transfer(self, code: str, amount: float, fromAccount: str, toAccount: str, params={}) -> TransferEntry:
@@ -6822,7 +6818,7 @@ class binance(Exchange, ImplicitAPI):
6822
6818
  if toId == 'ISOLATED':
6823
6819
  if symbol is None:
6824
6820
  raise ArgumentsRequired(self.id + ' transfer() requires params["symbol"] when toAccount is ' + toAccount)
6825
- accountsById = self.safe_value(self.options, 'accountsById', {})
6821
+ accountsById = self.safe_dict(self.options, 'accountsById', {})
6826
6822
  fromIsolated = not (fromId in accountsById)
6827
6823
  toIsolated = not (toId in accountsById)
6828
6824
  if fromIsolated and (market is None):
@@ -6895,7 +6891,7 @@ class binance(Exchange, ImplicitAPI):
6895
6891
  defaultTo = 'spot' if (fromAccount == 'future') else 'future'
6896
6892
  toAccount = self.safe_string(params, 'toAccount', defaultTo)
6897
6893
  type = self.safe_string(params, 'type')
6898
- accountsByType = self.safe_value(self.options, 'accountsByType', {})
6894
+ accountsByType = self.safe_dict(self.options, 'accountsByType', {})
6899
6895
  fromId = self.safe_string(accountsByType, fromAccount)
6900
6896
  toId = self.safe_string(accountsByType, toAccount)
6901
6897
  if type is None:
@@ -6933,7 +6929,7 @@ class binance(Exchange, ImplicitAPI):
6933
6929
  # ]
6934
6930
  # }
6935
6931
  #
6936
- rows = self.safe_value(response, 'rows', [])
6932
+ rows = self.safe_list(response, 'rows', [])
6937
6933
  return self.parse_transfers(rows, currency, since, limit)
6938
6934
 
6939
6935
  def fetch_deposit_address(self, code: str, params={}):
@@ -6950,7 +6946,7 @@ class binance(Exchange, ImplicitAPI):
6950
6946
  'coin': currency['id'],
6951
6947
  # 'network': 'ETH', # 'BSC', 'XMR', you can get network and isDefault in networkList in the response of sapiGetCapitalConfigDetail
6952
6948
  }
6953
- networks = self.safe_value(self.options, 'networks', {})
6949
+ networks = self.safe_dict(self.options, 'networks', {})
6954
6950
  network = self.safe_string_upper(params, 'network') # self line allows the user to specify either ERC20 or ETH
6955
6951
  network = self.safe_string(networks, network, network) # handle ERC20>ETH alias
6956
6952
  if network is not None:
@@ -6976,7 +6972,7 @@ class binance(Exchange, ImplicitAPI):
6976
6972
  url = self.safe_string(response, 'url')
6977
6973
  impliedNetwork = None
6978
6974
  if url is not None:
6979
- reverseNetworks = self.safe_value(self.options, 'reverseNetworks', {})
6975
+ reverseNetworks = self.safe_dict(self.options, 'reverseNetworks', {})
6980
6976
  parts = url.split('/')
6981
6977
  topLevel = self.safe_string(parts, 2)
6982
6978
  if (topLevel == 'blockchair.com') or (topLevel == 'viewblock.io'):
@@ -6989,7 +6985,7 @@ class binance(Exchange, ImplicitAPI):
6989
6985
  'TRX': {'TRC20': 'TRX'},
6990
6986
  })
6991
6987
  if code in impliedNetworks:
6992
- conversion = self.safe_value(impliedNetworks, code, {})
6988
+ conversion = self.safe_dict(impliedNetworks, code, {})
6993
6989
  impliedNetwork = self.safe_string(conversion, impliedNetwork, impliedNetwork)
6994
6990
  tag = self.safe_string(response, 'tag', '')
6995
6991
  if len(tag) == 0:
@@ -7100,7 +7096,7 @@ class binance(Exchange, ImplicitAPI):
7100
7096
  entry = response[i]
7101
7097
  currencyId = self.safe_string(entry, 'coin')
7102
7098
  code = self.safe_currency_code(currencyId)
7103
- networkList = self.safe_value(entry, 'networkList', [])
7099
+ networkList = self.safe_list(entry, 'networkList', [])
7104
7100
  withdrawFees[code] = {}
7105
7101
  for j in range(0, len(networkList)):
7106
7102
  networkEntry = networkList[j]
@@ -7209,14 +7205,14 @@ class binance(Exchange, ImplicitAPI):
7209
7205
  # ]
7210
7206
  # }
7211
7207
  #
7212
- networkList = self.safe_value(fee, 'networkList', [])
7208
+ networkList = self.safe_list(fee, 'networkList', [])
7213
7209
  result = self.deposit_withdraw_fee(fee)
7214
7210
  for j in range(0, len(networkList)):
7215
7211
  networkEntry = networkList[j]
7216
7212
  networkId = self.safe_string(networkEntry, 'network')
7217
7213
  networkCode = self.network_id_to_code(networkId)
7218
7214
  withdrawFee = self.safe_number(networkEntry, 'withdrawFee')
7219
- isDefault = self.safe_value(networkEntry, 'isDefault')
7215
+ isDefault = self.safe_bool(networkEntry, 'isDefault')
7220
7216
  if isDefault is True:
7221
7217
  result['withdraw'] = {
7222
7218
  'fee': withdrawFee,
@@ -7259,7 +7255,7 @@ class binance(Exchange, ImplicitAPI):
7259
7255
  }
7260
7256
  if tag is not None:
7261
7257
  request['addressTag'] = tag
7262
- networks = self.safe_value(self.options, 'networks', {})
7258
+ networks = self.safe_dict(self.options, 'networks', {})
7263
7259
  network = self.safe_string_upper(params, 'network') # self line allows the user to specify either ERC20 or ETH
7264
7260
  network = self.safe_string(networks, network, network) # handle ERC20>ETH alias
7265
7261
  if network is not None:
@@ -7345,7 +7341,7 @@ class binance(Exchange, ImplicitAPI):
7345
7341
  #
7346
7342
  data = response
7347
7343
  if isinstance(data, list):
7348
- data = self.safe_value(data, 0, {})
7344
+ data = self.safe_dict(data, 0, {})
7349
7345
  return self.parse_trading_fee(data)
7350
7346
 
7351
7347
  def fetch_trading_fees(self, params={}):
@@ -7719,8 +7715,8 @@ class binance(Exchange, ImplicitAPI):
7719
7715
  }
7720
7716
 
7721
7717
  def parse_account_positions(self, account):
7722
- positions = self.safe_value(account, 'positions')
7723
- assets = self.safe_value(account, 'assets', [])
7718
+ positions = self.safe_list(account, 'positions')
7719
+ assets = self.safe_list(account, 'assets', [])
7724
7720
  balances = {}
7725
7721
  for i in range(0, len(assets)):
7726
7722
  entry = assets[i]
@@ -7738,18 +7734,22 @@ class binance(Exchange, ImplicitAPI):
7738
7734
  marketId = self.safe_string(position, 'symbol')
7739
7735
  market = self.safe_market(marketId, None, None, 'contract')
7740
7736
  code = market['quote'] if market['linear'] else market['base']
7741
- # sometimes not all the codes are correctly returned...
7742
- if code in balances:
7743
- parsed = self.parse_account_position(self.extend(position, {
7744
- 'crossMargin': balances[code]['crossMargin'],
7745
- 'crossWalletBalance': balances[code]['crossWalletBalance'],
7746
- }), market)
7747
- 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)
7748
7747
  return result
7749
7748
 
7750
7749
  def parse_account_position(self, position, market: Market = None):
7751
7750
  #
7752
7751
  # usdm
7752
+ #
7753
7753
  # {
7754
7754
  # "symbol": "BTCBUSD",
7755
7755
  # "initialMargin": "0",
@@ -7770,6 +7770,7 @@ class binance(Exchange, ImplicitAPI):
7770
7770
  # }
7771
7771
  #
7772
7772
  # coinm
7773
+ #
7773
7774
  # {
7774
7775
  # "symbol": "BTCUSD_210625",
7775
7776
  # "initialMargin": "0.00024393",
@@ -7788,6 +7789,46 @@ class binance(Exchange, ImplicitAPI):
7788
7789
  # "crossWalletBalance": "34",
7789
7790
  # }
7790
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
+ #
7791
7832
  marketId = self.safe_string(position, 'symbol')
7792
7833
  market = self.safe_market(marketId, market, None, 'contract')
7793
7834
  symbol = self.safe_string(market, 'symbol')
@@ -7816,8 +7857,8 @@ class binance(Exchange, ImplicitAPI):
7816
7857
  contractsString = Precise.string_div(entryNotional, contractSizeNew)
7817
7858
  contractsStringAbs = Precise.string_div(Precise.string_add(contractsString, '0.5'), '1', 0)
7818
7859
  contracts = self.parse_number(contractsStringAbs)
7819
- leverageBrackets = self.safe_value(self.options, 'leverageBrackets', {})
7820
- leverageBracket = self.safe_value(leverageBrackets, symbol, [])
7860
+ leverageBrackets = self.safe_dict(self.options, 'leverageBrackets', {})
7861
+ leverageBracket = self.safe_list(leverageBrackets, symbol, [])
7821
7862
  maintenanceMarginPercentageString = None
7822
7863
  for i in range(0, len(leverageBracket)):
7823
7864
  bracket = leverageBracket[i]
@@ -7830,7 +7871,7 @@ class binance(Exchange, ImplicitAPI):
7830
7871
  timestamp = self.safe_integer(position, 'updateTime')
7831
7872
  if timestamp == 0:
7832
7873
  timestamp = None
7833
- isolated = self.safe_value(position, 'isolated')
7874
+ isolated = self.safe_bool(position, 'isolated')
7834
7875
  marginMode = None
7835
7876
  collateralString = None
7836
7877
  walletBalance = None
@@ -7972,11 +8013,45 @@ class binance(Exchange, ImplicitAPI):
7972
8013
  # "isolatedWallet": "0.00268058"
7973
8014
  # }
7974
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
+ #
7975
8050
  marketId = self.safe_string(position, 'symbol')
7976
8051
  market = self.safe_market(marketId, market, None, 'contract')
7977
8052
  symbol = self.safe_string(market, 'symbol')
7978
- leverageBrackets = self.safe_value(self.options, 'leverageBrackets', {})
7979
- leverageBracket = self.safe_value(leverageBrackets, symbol, [])
8053
+ leverageBrackets = self.safe_dict(self.options, 'leverageBrackets', {})
8054
+ leverageBracket = self.safe_list(leverageBrackets, symbol, [])
7980
8055
  notionalString = self.safe_string_2(position, 'notional', 'notionalValue')
7981
8056
  notionalStringAbs = Precise.string_abs(notionalString)
7982
8057
  maintenanceMarginPercentageString = None
@@ -8009,7 +8084,7 @@ class binance(Exchange, ImplicitAPI):
8009
8084
  linear = ('notional' in position)
8010
8085
  if marginMode == 'cross':
8011
8086
  # calculate collateral
8012
- precision = self.safe_value(market, 'precision', {})
8087
+ precision = self.safe_dict(market, 'precision', {})
8013
8088
  if linear:
8014
8089
  # walletBalance = (liquidationPrice * (±1 + mmp) ± entryPrice) * contracts
8015
8090
  onePlusMaintenanceMarginPercentageString = None
@@ -8103,11 +8178,19 @@ class binance(Exchange, ImplicitAPI):
8103
8178
  query = self.omit(params, 'type')
8104
8179
  subType = None
8105
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)
8106
8183
  response = None
8107
8184
  if self.is_linear(type, subType):
8108
- response = self.fapiPrivateGetLeverageBracket(query)
8185
+ if isPortfolioMargin:
8186
+ response = self.papiGetUmLeverageBracket(query)
8187
+ else:
8188
+ response = self.fapiPrivateGetLeverageBracket(query)
8109
8189
  elif self.is_inverse(type, subType):
8110
- response = self.dapiPrivateV2GetLeverageBracket(query)
8190
+ if isPortfolioMargin:
8191
+ response = self.papiGetCmLeverageBracket(query)
8192
+ else:
8193
+ response = self.dapiPrivateV2GetLeverageBracket(query)
8111
8194
  else:
8112
8195
  raise NotSupported(self.id + ' loadLeverageBrackets() supports linear and inverse contracts only')
8113
8196
  self.options['leverageBrackets'] = {}
@@ -8115,7 +8198,7 @@ class binance(Exchange, ImplicitAPI):
8115
8198
  entry = response[i]
8116
8199
  marketId = self.safe_string(entry, 'symbol')
8117
8200
  symbol = self.safe_symbol(marketId, None, None, 'contract')
8118
- brackets = self.safe_value(entry, 'brackets', [])
8201
+ brackets = self.safe_list(entry, 'brackets', [])
8119
8202
  result = []
8120
8203
  for j in range(0, len(brackets)):
8121
8204
  bracket = brackets[j]
@@ -8130,8 +8213,11 @@ class binance(Exchange, ImplicitAPI):
8130
8213
  retrieve information on the maximum leverage, and maintenance margin for trades of varying trade sizes
8131
8214
  :see: https://binance-docs.github.io/apidocs/futures/en/#notional-and-leverage-brackets-user_data
8132
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
8133
8218
  :param str[]|None symbols: list of unified market symbols
8134
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
8135
8221
  :returns dict: a dictionary of `leverage tiers structures <https://docs.ccxt.com/#/?id=leverage-tiers-structure>`, indexed by market symbols
8136
8222
  """
8137
8223
  self.load_markets()
@@ -8139,11 +8225,19 @@ class binance(Exchange, ImplicitAPI):
8139
8225
  type, params = self.handle_market_type_and_params('fetchLeverageTiers', None, params)
8140
8226
  subType = None
8141
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)
8142
8230
  response = None
8143
8231
  if self.is_linear(type, subType):
8144
- response = self.fapiPrivateGetLeverageBracket(params)
8232
+ if isPortfolioMargin:
8233
+ response = self.papiGetUmLeverageBracket(params)
8234
+ else:
8235
+ response = self.fapiPrivateGetLeverageBracket(params)
8145
8236
  elif self.is_inverse(type, subType):
8146
- response = self.dapiPrivateV2GetLeverageBracket(params)
8237
+ if isPortfolioMargin:
8238
+ response = self.papiGetCmLeverageBracket(params)
8239
+ else:
8240
+ response = self.dapiPrivateV2GetLeverageBracket(params)
8147
8241
  else:
8148
8242
  raise NotSupported(self.id + ' fetchLeverageTiers() supports linear and inverse contracts only')
8149
8243
  #
@@ -8210,7 +8304,7 @@ class binance(Exchange, ImplicitAPI):
8210
8304
  #
8211
8305
  marketId = self.safe_string(info, 'symbol')
8212
8306
  market = self.safe_market(marketId, market, None, 'contract')
8213
- brackets = self.safe_value(info, 'brackets', [])
8307
+ brackets = self.safe_list(info, 'brackets', [])
8214
8308
  tiers = []
8215
8309
  for j in range(0, len(brackets)):
8216
8310
  bracket = brackets[j]
@@ -8405,8 +8499,11 @@ class binance(Exchange, ImplicitAPI):
8405
8499
  fetch account positions
8406
8500
  :see: https://binance-docs.github.io/apidocs/futures/en/#account-information-v2-user_data
8407
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
8408
8504
  :param str[]|None symbols: list of unified market symbols
8409
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
8410
8507
  :returns dict: data on account positions
8411
8508
  """
8412
8509
  if symbols is not None:
@@ -8416,14 +8513,22 @@ class binance(Exchange, ImplicitAPI):
8416
8513
  self.load_leverage_brackets(False, params)
8417
8514
  defaultType = self.safe_string(self.options, 'defaultType', 'future')
8418
8515
  type = self.safe_string(params, 'type', defaultType)
8419
- query = self.omit(params, 'type')
8516
+ params = self.omit(params, 'type')
8420
8517
  subType = None
8421
- 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)
8422
8521
  response = None
8423
8522
  if self.is_linear(type, subType):
8424
- response = self.fapiPrivateV2GetAccount(query)
8523
+ if isPortfolioMargin:
8524
+ response = self.papiGetUmAccount(params)
8525
+ else:
8526
+ response = self.fapiPrivateV2GetAccount(params)
8425
8527
  elif self.is_inverse(type, subType):
8426
- response = self.dapiPrivateGetAccount(query)
8528
+ if isPortfolioMargin:
8529
+ response = self.papiGetCmAccount(params)
8530
+ else:
8531
+ response = self.dapiPrivateGetAccount(params)
8427
8532
  else:
8428
8533
  raise NotSupported(self.id + ' fetchPositions() supports linear and inverse contracts only')
8429
8534
  result = self.parse_account_positions(response)
@@ -8436,8 +8541,11 @@ class binance(Exchange, ImplicitAPI):
8436
8541
  fetch positions risk
8437
8542
  :see: https://binance-docs.github.io/apidocs/futures/en/#position-information-v2-user_data
8438
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
8439
8546
  :param str[]|None symbols: list of unified market symbols
8440
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
8441
8549
  :returns dict: data on the positions risk
8442
8550
  """
8443
8551
  if symbols is not None:
@@ -8451,68 +8559,117 @@ class binance(Exchange, ImplicitAPI):
8451
8559
  type = self.safe_string(params, 'type', defaultType)
8452
8560
  subType = None
8453
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)
8454
8564
  params = self.omit(params, 'type')
8455
8565
  response = None
8456
8566
  if self.is_linear(type, subType):
8457
- response = self.fapiPrivateV2GetPositionRisk(self.extend(request, params))
8458
- # ### Response examples ###
8459
- #
8460
- # For One-way position mode:
8461
- # [
8462
- # {
8463
- # "entryPrice": "0.00000",
8464
- # "marginType": "isolated",
8465
- # "isAutoAddMargin": "false",
8466
- # "isolatedMargin": "0.00000000",
8467
- # "leverage": "10",
8468
- # "liquidationPrice": "0",
8469
- # "markPrice": "6679.50671178",
8470
- # "maxNotionalValue": "20000000",
8471
- # "positionAmt": "0.000",
8472
- # "symbol": "BTCUSDT",
8473
- # "unRealizedProfit": "0.00000000",
8474
- # "positionSide": "BOTH",
8475
- # "updateTime": 0
8476
- # }
8477
- # ]
8478
- #
8479
- # For Hedge position mode:
8480
- # [
8481
- # {
8482
- # "entryPrice": "6563.66500",
8483
- # "marginType": "isolated",
8484
- # "isAutoAddMargin": "false",
8485
- # "isolatedMargin": "15517.54150468",
8486
- # "leverage": "10",
8487
- # "liquidationPrice": "5930.78",
8488
- # "markPrice": "6679.50671178",
8489
- # "maxNotionalValue": "20000000",
8490
- # "positionAmt": "20.000",
8491
- # "symbol": "BTCUSDT",
8492
- # "unRealizedProfit": "2316.83423560"
8493
- # "positionSide": "LONG",
8494
- # "updateTime": 1625474304765
8495
- # },
8496
- # {
8497
- # "entryPrice": "0.00000",
8498
- # "marginType": "isolated",
8499
- # "isAutoAddMargin": "false",
8500
- # "isolatedMargin": "5413.95799991",
8501
- # "leverage": "10",
8502
- # "liquidationPrice": "7189.95",
8503
- # "markPrice": "6679.50671178",
8504
- # "maxNotionalValue": "20000000",
8505
- # "positionAmt": "-10.000",
8506
- # "symbol": "BTCUSDT",
8507
- # "unRealizedProfit": "-1156.46711780",
8508
- # "positionSide": "SHORT",
8509
- # "updateTime": 0
8510
- # }
8511
- # ]
8567
+ if isPortfolioMargin:
8568
+ response = self.papiGetUmPositionRisk(self.extend(request, params))
8569
+ else:
8570
+ response = self.fapiPrivateV2GetPositionRisk(self.extend(request, params))
8512
8571
  elif self.is_inverse(type, subType):
8513
- 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))
8514
8576
  else:
8515
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
+ #
8516
8673
  result = []
8517
8674
  for i in range(0, len(response)):
8518
8675
  parsed = self.parse_position_risk(response[i])
@@ -8525,10 +8682,14 @@ class binance(Exchange, ImplicitAPI):
8525
8682
  fetch the history of funding payments paid and received on self account
8526
8683
  :see: https://binance-docs.github.io/apidocs/futures/en/#get-income-history-user_data
8527
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
8528
8687
  :param str symbol: unified market symbol
8529
8688
  :param int [since]: the earliest time in ms to fetch funding history for
8530
8689
  :param int [limit]: the maximum number of funding history structures to retrieve
8531
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
8532
8693
  :returns dict: a `funding history structure <https://docs.ccxt.com/#/?id=funding-history-structure>`
8533
8694
  """
8534
8695
  self.load_markets()
@@ -8543,6 +8704,9 @@ class binance(Exchange, ImplicitAPI):
8543
8704
  raise NotSupported(self.id + ' fetchFundingHistory() supports swap contracts only')
8544
8705
  subType = None
8545
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)
8546
8710
  if since is not None:
8547
8711
  request['startTime'] = since
8548
8712
  if limit is not None:
@@ -8552,9 +8716,15 @@ class binance(Exchange, ImplicitAPI):
8552
8716
  params = self.omit(params, 'type')
8553
8717
  response = None
8554
8718
  if self.is_linear(type, subType):
8555
- 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))
8556
8723
  elif self.is_inverse(type, subType):
8557
- 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))
8558
8728
  else:
8559
8729
  raise NotSupported(self.id + ' fetchFundingHistory() supports linear and inverse contracts only')
8560
8730
  return self.parse_incomes(response, market, since, limit)
@@ -8877,12 +9047,15 @@ class binance(Exchange, ImplicitAPI):
8877
9047
  :see: https://binance-docs.github.io/apidocs/voptions/en/#account-funding-flow-user_data
8878
9048
  :see: https://binance-docs.github.io/apidocs/futures/en/#get-income-history-user_data
8879
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
8880
9052
  :param str code: unified currency code
8881
9053
  :param int [since]: timestamp in ms of the earliest ledger entry
8882
9054
  :param int [limit]: max number of ledger entrys to return
8883
9055
  :param dict [params]: extra parameters specific to the exchange API endpoint
8884
9056
  :param int [params.until]: timestamp in ms of the latest ledger entry
8885
- :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
8886
9059
  :returns dict: a `ledger structure <https://docs.ccxt.com/#/?id=ledger-structure>`
8887
9060
  """
8888
9061
  self.load_markets()
@@ -8906,15 +9079,23 @@ class binance(Exchange, ImplicitAPI):
8906
9079
  if until is not None:
8907
9080
  params = self.omit(params, 'until')
8908
9081
  request['endTime'] = until
9082
+ isPortfolioMargin = None
9083
+ isPortfolioMargin, params = self.handle_option_and_params_2(params, 'fetchLedger', 'papi', 'portfolioMargin', False)
8909
9084
  response = None
8910
9085
  if type == 'option':
8911
9086
  self.check_required_argument('fetchLedger', code, 'code')
8912
9087
  request['currency'] = currency['id']
8913
9088
  response = self.eapiPrivateGetBill(self.extend(request, params))
8914
9089
  elif self.is_linear(type, subType):
8915
- 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))
8916
9094
  elif self.is_inverse(type, subType):
8917
- 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))
8918
9099
  else:
8919
9100
  raise NotSupported(self.id + ' fetchLedger() supports contract wallets only')
8920
9101
  #
@@ -8930,7 +9111,7 @@ class binance(Exchange, ImplicitAPI):
8930
9111
  # }
8931
9112
  # ]
8932
9113
  #
8933
- # futures(fapi, dapi)
9114
+ # futures(fapi, dapi, papi)
8934
9115
  #
8935
9116
  # [
8936
9117
  # {
@@ -8959,7 +9140,7 @@ class binance(Exchange, ImplicitAPI):
8959
9140
  # "createDate": 1676621042489
8960
9141
  # }
8961
9142
  #
8962
- # futures(fapi, dapi)
9143
+ # futures(fapi, dapi, papi)
8963
9144
  #
8964
9145
  # {
8965
9146
  # "symbol": "",
@@ -9058,7 +9239,7 @@ class binance(Exchange, ImplicitAPI):
9058
9239
  isSpotOrMargin = (api.find('sapi') > -1 or api == 'private')
9059
9240
  marketType = 'spot' if isSpotOrMargin else 'future'
9060
9241
  defaultId = 'x-xcKtGhcu' if (not isSpotOrMargin) else 'x-R4BD3S82'
9061
- broker = self.safe_value(self.options, 'broker', {})
9242
+ broker = self.safe_dict(self.options, 'broker', {})
9062
9243
  brokerId = self.safe_string(broker, marketType, defaultId)
9063
9244
  params['newClientOrderId'] = brokerId + self.uuid22()
9064
9245
  query = None
@@ -9080,8 +9261,8 @@ class binance(Exchange, ImplicitAPI):
9080
9261
  query = self.urlencode_with_array_repeat(extendedParams)
9081
9262
  elif (path == 'batchOrders') or (path.find('sub-account') >= 0) or (path == 'capital/withdraw/apply') or (path.find('staking') >= 0):
9082
9263
  if (method == 'DELETE') and (path == 'batchOrders'):
9083
- orderidlist = self.safe_value(extendedParams, 'orderidlist', [])
9084
- origclientorderidlist = self.safe_value(extendedParams, 'origclientorderidlist', [])
9264
+ orderidlist = self.safe_list(extendedParams, 'orderidlist', [])
9265
+ origclientorderidlist = self.safe_list(extendedParams, 'origclientorderidlist', [])
9085
9266
  extendedParams = self.omit(extendedParams, ['orderidlist', 'origclientorderidlist'])
9086
9267
  query = self.rawencode(extendedParams)
9087
9268
  orderidlistLength = len(orderidlist)
@@ -9130,8 +9311,8 @@ class binance(Exchange, ImplicitAPI):
9130
9311
  elif url.startswith('https://papi.' + hostname + '/'):
9131
9312
  marketType = 'portfoliomargin'
9132
9313
  if marketType is not None:
9133
- exceptionsForMarketType = self.safe_value(self.exceptions, marketType, {})
9134
- return self.safe_value(exceptionsForMarketType, exactOrBroad, {})
9314
+ exceptionsForMarketType = self.safe_dict(self.exceptions, marketType, {})
9315
+ return self.safe_dict(exceptionsForMarketType, exactOrBroad, {})
9135
9316
  return {}
9136
9317
 
9137
9318
  def handle_errors(self, code, reason, url, method, headers, body, response, requestHeaders, requestBody):
@@ -9327,7 +9508,7 @@ class binance(Exchange, ImplicitAPI):
9327
9508
  # },
9328
9509
  # ]
9329
9510
  #
9330
- rate = self.safe_value(response, 0)
9511
+ rate = self.safe_dict(response, 0)
9331
9512
  return self.parse_borrow_rate(rate)
9332
9513
 
9333
9514
  def fetch_borrow_rate_history(self, code: str, since: Int = None, limit: Int = None, params={}):
@@ -9423,7 +9604,7 @@ class binance(Exchange, ImplicitAPI):
9423
9604
  # "success": True
9424
9605
  # }
9425
9606
  #
9426
- data = self.safe_value(response, 'data')
9607
+ data = self.safe_dict(response, 'data')
9427
9608
  giftcardCode = self.safe_string(data, 'code')
9428
9609
  id = self.safe_string(data, 'referenceNo')
9429
9610
  return {
@@ -9522,7 +9703,7 @@ class binance(Exchange, ImplicitAPI):
9522
9703
  # "total": 1
9523
9704
  # }
9524
9705
  #
9525
- rows = self.safe_value(response, 'rows')
9706
+ rows = self.safe_list(response, 'rows')
9526
9707
  interest = self.parse_borrow_interests(rows, market)
9527
9708
  return self.filter_by_currency_since_limit(interest, code, since, limit)
9528
9709
 
@@ -9547,9 +9728,11 @@ class binance(Exchange, ImplicitAPI):
9547
9728
  """
9548
9729
  repay borrowed margin and interest
9549
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
9550
9732
  :param str code: unified currency code of the currency to repay
9551
9733
  :param float amount: the amount to repay
9552
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
9553
9736
  :returns dict: a `margin loan structure <https://docs.ccxt.com/#/?id=margin-loan-structure>`
9554
9737
  """
9555
9738
  self.load_markets()
@@ -9557,10 +9740,16 @@ class binance(Exchange, ImplicitAPI):
9557
9740
  request = {
9558
9741
  'asset': currency['id'],
9559
9742
  'amount': self.currency_to_precision(code, amount),
9560
- 'isIsolated': 'FALSE',
9561
- 'type': 'REPAY',
9562
9743
  }
9563
- 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))
9564
9753
  #
9565
9754
  # {
9566
9755
  # "tranId": 108988250265,
@@ -9602,9 +9791,11 @@ class binance(Exchange, ImplicitAPI):
9602
9791
  """
9603
9792
  create a loan to borrow margin
9604
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
9605
9795
  :param str code: unified currency code of the currency to borrow
9606
9796
  :param float amount: the amount to borrow
9607
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
9608
9799
  :returns dict: a `margin loan structure <https://docs.ccxt.com/#/?id=margin-loan-structure>`
9609
9800
  """
9610
9801
  self.load_markets()
@@ -9612,10 +9803,16 @@ class binance(Exchange, ImplicitAPI):
9612
9803
  request = {
9613
9804
  'asset': currency['id'],
9614
9805
  'amount': self.currency_to_precision(code, amount),
9615
- 'isIsolated': 'FALSE',
9616
- 'type': 'BORROW',
9617
9806
  }
9618
- 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))
9619
9816
  #
9620
9817
  # {
9621
9818
  # "tranId": 108988250265,