ccxt 4.2.35__py2.py3-none-any.whl → 4.2.37__py2.py3-none-any.whl

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -4546,7 +4546,7 @@ class binance(Exchange, ImplicitAPI):
4546
4546
  if price is None:
4547
4547
  raise InvalidOrder(self.id + ' editOrder() requires a price argument for a ' + type + ' order')
4548
4548
  request['price'] = self.price_to_precision(symbol, price)
4549
- if timeInForceIsRequired:
4549
+ if timeInForceIsRequired and (self.safe_string(params, 'timeInForce') is None):
4550
4550
  request['timeInForce'] = self.options['defaultTimeInForce'] # 'GTC' = Good To Cancel(default), 'IOC' = Immediate Or Cancel
4551
4551
  if stopPriceIsRequired:
4552
4552
  if stopPrice is None:
@@ -4833,19 +4833,155 @@ class binance(Exchange, ImplicitAPI):
4833
4833
  # "lastTrade": {"id":"69","time":"1676084430567","price":"24.9","qty":"1.00"},
4834
4834
  # "mmp": False
4835
4835
  # }
4836
- # {
4836
+ #
4837
4837
  # cancelOrders/createOrders
4838
- # "code": -4005,
4839
- # "msg": "Quantity greater than max quantity."
4840
- # },
4838
+ #
4839
+ # {
4840
+ # "code": -4005,
4841
+ # "msg": "Quantity greater than max quantity."
4842
+ # }
4843
+ #
4844
+ # createOrder, fetchOpenOrders: portfolio margin linear swap and future
4845
+ #
4846
+ # {
4847
+ # "symbol": "BTCUSDT",
4848
+ # "side": "BUY",
4849
+ # "executedQty": "0.000",
4850
+ # "orderId": 258649539704,
4851
+ # "goodTillDate": 0,
4852
+ # "avgPrice": "0",
4853
+ # "origQty": "0.010",
4854
+ # "clientOrderId": "x-xcKtGhcu02573c6f15e544e990057b",
4855
+ # "positionSide": "BOTH",
4856
+ # "cumQty": "0.000",
4857
+ # "updateTime": 1707110415436,
4858
+ # "type": "LIMIT",
4859
+ # "reduceOnly": False,
4860
+ # "price": "35000.00",
4861
+ # "cumQuote": "0.00000",
4862
+ # "selfTradePreventionMode": "NONE",
4863
+ # "timeInForce": "GTC",
4864
+ # "status": "NEW"
4865
+ # }
4866
+ #
4867
+ # createOrder, fetchOpenOrders: portfolio margin inverse swap and future
4868
+ #
4869
+ # {
4870
+ # "symbol": "ETHUSD_PERP",
4871
+ # "side": "BUY",
4872
+ # "cumBase": "0",
4873
+ # "executedQty": "0",
4874
+ # "orderId": 71275227732,
4875
+ # "avgPrice": "0.00",
4876
+ # "origQty": "1",
4877
+ # "clientOrderId": "x-xcKtGhcuca5af3acfb5044198c5398",
4878
+ # "positionSide": "BOTH",
4879
+ # "cumQty": "0",
4880
+ # "updateTime": 1707110994334,
4881
+ # "type": "LIMIT",
4882
+ # "pair": "ETHUSD",
4883
+ # "reduceOnly": False,
4884
+ # "price": "2000",
4885
+ # "timeInForce": "GTC",
4886
+ # "status": "NEW"
4887
+ # }
4888
+ #
4889
+ # createOrder, fetchOpenOrders: portfolio margin linear swap and future conditional
4890
+ #
4891
+ # {
4892
+ # "newClientStrategyId": "x-xcKtGhcu27f109953d6e4dc0974006",
4893
+ # "strategyId": 3645916,
4894
+ # "strategyStatus": "NEW",
4895
+ # "strategyType": "STOP",
4896
+ # "origQty": "0.010",
4897
+ # "price": "35000.00",
4898
+ # "reduceOnly": False,
4899
+ # "side": "BUY",
4900
+ # "positionSide": "BOTH",
4901
+ # "stopPrice": "45000.00",
4902
+ # "symbol": "BTCUSDT",
4903
+ # "timeInForce": "GTC",
4904
+ # "bookTime": 1707112625879,
4905
+ # "updateTime": 1707112625879,
4906
+ # "workingType": "CONTRACT_PRICE",
4907
+ # "priceProtect": False,
4908
+ # "goodTillDate": 0,
4909
+ # "selfTradePreventionMode": "NONE"
4910
+ # }
4911
+ #
4912
+ # createOrder, fetchOpenOrders: portfolio margin inverse swap and future conditional
4913
+ #
4914
+ # {
4915
+ # "newClientStrategyId": "x-xcKtGhcuc6b86f053bb34933850739",
4916
+ # "strategyId": 1423462,
4917
+ # "strategyStatus": "NEW",
4918
+ # "strategyType": "STOP",
4919
+ # "origQty": "1",
4920
+ # "price": "2000",
4921
+ # "reduceOnly": False,
4922
+ # "side": "BUY",
4923
+ # "positionSide": "BOTH",
4924
+ # "stopPrice": "3000",
4925
+ # "symbol": "ETHUSD_PERP",
4926
+ # "timeInForce": "GTC",
4927
+ # "bookTime": 1707113098840,
4928
+ # "updateTime": 1707113098840,
4929
+ # "workingType": "CONTRACT_PRICE",
4930
+ # "priceProtect": False
4931
+ # }
4932
+ #
4933
+ # createOrder, cancelAllOrders: portfolio margin spot margin
4934
+ #
4935
+ # {
4936
+ # "clientOrderId": "x-R4BD3S82e9ef29d8346440f0b28b86",
4937
+ # "cummulativeQuoteQty": "0.00000000",
4938
+ # "executedQty": "0.00000000",
4939
+ # "fills": [],
4940
+ # "orderId": 24684460474,
4941
+ # "origQty": "0.00100000",
4942
+ # "price": "35000.00000000",
4943
+ # "selfTradePreventionMode": "EXPIRE_MAKER",
4944
+ # "side": "BUY",
4945
+ # "status": "NEW",
4946
+ # "symbol": "BTCUSDT",
4947
+ # "timeInForce": "GTC",
4948
+ # "transactTime": 1707113538870,
4949
+ # "type": "LIMIT"
4950
+ # }
4951
+ #
4952
+ # fetchOpenOrders: portfolio margin spot margin
4953
+ #
4954
+ # {
4955
+ # "symbol": "BTCUSDT",
4956
+ # "orderId": 24700763749,
4957
+ # "clientOrderId": "x-R4BD3S826f724c2a4af6425f98c7b6",
4958
+ # "price": "35000.00000000",
4959
+ # "origQty": "0.00100000",
4960
+ # "executedQty": "0.00000000",
4961
+ # "cummulativeQuoteQty": "0.00000000",
4962
+ # "status": "NEW",
4963
+ # "timeInForce": "GTC",
4964
+ # "type": "LIMIT",
4965
+ # "side": "BUY",
4966
+ # "stopPrice": "0.00000000",
4967
+ # "icebergQty": "0.00000000",
4968
+ # "time": 1707199187679,
4969
+ # "updateTime": 1707199187679,
4970
+ # "isWorking": True,
4971
+ # "accountId": 200180970,
4972
+ # "selfTradePreventionMode": "EXPIRE_MAKER",
4973
+ # "preventedMatchId": null,
4974
+ # "preventedQuantity": null
4975
+ # }
4841
4976
  #
4842
4977
  code = self.safe_string(order, 'code')
4843
4978
  if code is not None:
4844
4979
  # cancelOrders/createOrders might have a partial success
4845
4980
  return self.safe_order({'info': order, 'status': 'rejected'}, market)
4846
- status = self.parse_order_status(self.safe_string(order, 'status'))
4981
+ status = self.parse_order_status(self.safe_string_2(order, 'status', 'strategyStatus'))
4847
4982
  marketId = self.safe_string(order, 'symbol')
4848
- marketType = 'contract' if ('closePosition' in order) else 'spot'
4983
+ isContract = ('positionSide' in order) or ('cumQuote' in order)
4984
+ marketType = 'contract' if isContract else 'spot'
4849
4985
  symbol = self.safe_symbol(marketId, market, None, marketType)
4850
4986
  filled = self.safe_string(order, 'executedQty', '0')
4851
4987
  timestamp = self.safe_integer_n(order, ['time', 'createTime', 'workingTime', 'transactTime', 'updateTime']) # order of the keys matters here
@@ -4866,11 +5002,9 @@ class binance(Exchange, ImplicitAPI):
4866
5002
  # Note self is not the actual cost, since Binance futures uses leverage to calculate margins.
4867
5003
  cost = self.safe_string_2(order, 'cummulativeQuoteQty', 'cumQuote')
4868
5004
  cost = self.safe_string(order, 'cumBase', cost)
4869
- id = self.safe_string(order, 'orderId')
4870
5005
  type = self.safe_string_lower(order, 'type')
4871
5006
  side = self.safe_string_lower(order, 'side')
4872
5007
  fills = self.safe_value(order, 'fills', [])
4873
- clientOrderId = self.safe_string(order, 'clientOrderId')
4874
5008
  timeInForce = self.safe_string(order, 'timeInForce')
4875
5009
  if timeInForce == 'GTX':
4876
5010
  # GTX means "Good Till Crossing" and is an equivalent way of saying Post Only
@@ -4890,8 +5024,8 @@ class binance(Exchange, ImplicitAPI):
4890
5024
  }
4891
5025
  return self.safe_order({
4892
5026
  'info': order,
4893
- 'id': id,
4894
- 'clientOrderId': clientOrderId,
5027
+ 'id': self.safe_string_2(order, 'orderId', 'strategyId'),
5028
+ 'clientOrderId': self.safe_string_2(order, 'clientOrderId', 'newClientStrategyId'),
4895
5029
  'timestamp': timestamp,
4896
5030
  'datetime': self.iso8601(timestamp),
4897
5031
  'lastTradeTimestamp': lastTradeTimestamp,
@@ -4996,58 +5130,96 @@ class binance(Exchange, ImplicitAPI):
4996
5130
  :see: https://binance-docs.github.io/apidocs/voptions/en/#new-order-trade
4997
5131
  :see: https://binance-docs.github.io/apidocs/spot/en/#new-order-using-sor-trade
4998
5132
  :see: https://binance-docs.github.io/apidocs/spot/en/#test-new-order-using-sor-trade
5133
+ :see: https://binance-docs.github.io/apidocs/pm/en/#new-um-order-trade
5134
+ :see: https://binance-docs.github.io/apidocs/pm/en/#new-cm-order-trade
5135
+ :see: https://binance-docs.github.io/apidocs/pm/en/#new-margin-order-trade
5136
+ :see: https://binance-docs.github.io/apidocs/pm/en/#new-um-conditional-order-trade
5137
+ :see: https://binance-docs.github.io/apidocs/pm/en/#new-cm-conditional-order-trade
4999
5138
  :param str symbol: unified symbol of the market to create an order in
5000
5139
  :param str type: 'market' or 'limit' or 'STOP_LOSS' or 'STOP_LOSS_LIMIT' or 'TAKE_PROFIT' or 'TAKE_PROFIT_LIMIT' or 'STOP'
5001
5140
  :param str side: 'buy' or 'sell'
5002
- :param float amount: how much of currency you want to trade in units of base currency
5003
- :param float [price]: the price at which the order is to be fullfilled, in units of the quote currency, ignored in market orders
5141
+ :param float amount: how much of you want to trade in units of the base currency
5142
+ :param float [price]: the price that the order is to be fullfilled, in units of the quote currency, ignored in market orders
5004
5143
  :param dict [params]: extra parameters specific to the exchange API endpoint
5144
+ :param str [params.reduceOnly]: for swap and future reduceOnly is a string 'true' or 'false' that cant be sent with close position set to True or in hedge mode. For spot margin and option reduceOnly is a boolean.
5005
5145
  :param str [params.marginMode]: 'cross' or 'isolated', for spot margin trading
5006
5146
  :param boolean [params.sor]: *spot only* whether to use SOR(Smart Order Routing) or not, default is False
5007
5147
  :param boolean [params.test]: *spot only* whether to use the test endpoint or not, default is False
5008
5148
  :param float [params.trailingPercent]: the percent to trail away from the current market price
5009
5149
  :param float [params.trailingTriggerPrice]: the price to trigger a trailing order, default uses the price argument
5150
+ :param float [params.triggerPrice]: the price that a trigger order is triggered at
5151
+ :param float [params.stopLossPrice]: the price that a stop loss order is triggered at
5152
+ :param float [params.takeProfitPrice]: the price that a take profit order is triggered at
5153
+ :param boolean [params.portfolioMargin]: set to True if you would like to create an order in a portfolio margin account
5010
5154
  :returns dict: an `order structure <https://docs.ccxt.com/#/?id=order-structure>`
5011
5155
  """
5012
5156
  await self.load_markets()
5013
5157
  market = self.market(symbol)
5014
5158
  marketType = self.safe_string(params, 'type', market['type'])
5015
- marginMode, query = self.handle_margin_mode_and_params('createOrder', params)
5016
- sor = self.safe_value_2(params, 'sor', 'SOR', False)
5017
- params = self.omit(params, 'sor', 'SOR')
5159
+ marginMode = None
5160
+ marginMode, params = self.handle_margin_mode_and_params('createOrder', params)
5161
+ isPortfolioMargin = None
5162
+ isPortfolioMargin, params = self.handle_option_and_params_2(params, 'createOrder', 'papi', 'portfolioMargin', False)
5163
+ triggerPrice = self.safe_string_2(params, 'triggerPrice', 'stopPrice')
5164
+ stopLossPrice = self.safe_string(params, 'stopLossPrice')
5165
+ takeProfitPrice = self.safe_string(params, 'takeProfitPrice')
5166
+ trailingPercent = self.safe_string_2(params, 'trailingPercent', 'callbackRate')
5167
+ isTrailingPercentOrder = trailingPercent is not None
5168
+ isStopLoss = stopLossPrice is not None
5169
+ isTakeProfit = takeProfitPrice is not None
5170
+ isConditional = (triggerPrice is not None) or isTrailingPercentOrder or isStopLoss or isTakeProfit
5171
+ sor = self.safe_bool_2(params, 'sor', 'SOR', False)
5172
+ test = self.safe_bool(params, 'test', False)
5173
+ params = self.omit(params, ['sor', 'SOR', 'test'])
5174
+ if isPortfolioMargin:
5175
+ params['portfolioMargin'] = isPortfolioMargin
5018
5176
  request = self.create_order_request(symbol, type, side, amount, price, params)
5019
- method = 'privatePostOrder'
5020
- if sor:
5021
- method = 'privatePostSorOrder'
5177
+ response = None
5178
+ if market['option']:
5179
+ response = await self.eapiPrivatePostOrder(request)
5180
+ elif sor:
5181
+ if test:
5182
+ response = await self.privatePostSorOrderTest(request)
5183
+ else:
5184
+ response = await self.privatePostSorOrder(request)
5022
5185
  elif market['linear']:
5023
- method = 'fapiPrivatePostOrder'
5186
+ if isPortfolioMargin:
5187
+ if isConditional:
5188
+ response = await self.papiPostUmConditionalOrder(request)
5189
+ else:
5190
+ response = await self.papiPostUmOrder(request)
5191
+ else:
5192
+ response = await self.fapiPrivatePostOrder(request)
5024
5193
  elif market['inverse']:
5025
- method = 'dapiPrivatePostOrder'
5194
+ if isPortfolioMargin:
5195
+ if isConditional:
5196
+ response = await self.papiPostCmConditionalOrder(request)
5197
+ else:
5198
+ response = await self.papiPostCmOrder(request)
5199
+ else:
5200
+ response = await self.dapiPrivatePostOrder(request)
5026
5201
  elif marketType == 'margin' or marginMode is not None:
5027
- method = 'sapiPostMarginOrder'
5028
- if market['option']:
5029
- method = 'eapiPrivatePostOrder'
5030
- # support for testing orders
5031
- if market['spot'] or marketType == 'margin':
5032
- test = self.safe_bool(query, 'test', False)
5202
+ if isPortfolioMargin:
5203
+ response = await self.papiPostMarginOrder(request)
5204
+ else:
5205
+ response = await self.sapiPostMarginOrder(request)
5206
+ else:
5033
5207
  if test:
5034
- method += 'Test'
5035
- response = await getattr(self, method)(request)
5208
+ response = await self.privatePostOrderTest(request)
5209
+ else:
5210
+ response = await self.privatePostOrder(request)
5036
5211
  return self.parse_order(response, market)
5037
5212
 
5038
5213
  def create_order_request(self, symbol: str, type: OrderType, side: OrderSide, amount: float, price: float = None, params={}):
5039
5214
  """
5040
5215
  * @ignore
5041
- helper function to build request
5216
+ helper function to build the request
5042
5217
  :param str symbol: unified symbol of the market to create an order in
5043
- :param str type: 'market' or 'limit' or 'STOP_LOSS' or 'STOP_LOSS_LIMIT' or 'TAKE_PROFIT' or 'TAKE_PROFIT_LIMIT' or 'STOP'
5218
+ :param str type: 'market' or 'limit'
5044
5219
  :param str side: 'buy' or 'sell'
5045
- :param float amount: how much of currency you want to trade in units of base currency
5046
- :param float|None price: the price at which the order is to be fullfilled, in units of the quote currency, ignored in market orders
5047
- :param dict params: extra parameters specific to the exchange API endpoint
5048
- :param str|None params['marginMode']: 'cross' or 'isolated', for spot margin trading
5049
- :param float [params.trailingPercent]: the percent to trail away from the current market price
5050
- :param float [params.trailingTriggerPrice]: the price to trigger a trailing order, default uses the price argument
5220
+ :param float amount: how much you want to trade in units of the base currency
5221
+ :param float [price]: the price that the order is to be fullfilled, in units of the quote currency, ignored in market orders
5222
+ :param dict [params]: extra parameters specific to the exchange API endpoint
5051
5223
  :returns dict: request to be sent to the exchange
5052
5224
  """
5053
5225
  market = self.market(symbol)
@@ -5056,31 +5228,35 @@ class binance(Exchange, ImplicitAPI):
5056
5228
  initialUppercaseType = type.upper()
5057
5229
  isMarketOrder = initialUppercaseType == 'MARKET'
5058
5230
  isLimitOrder = initialUppercaseType == 'LIMIT'
5059
- postOnly = self.is_post_only(isMarketOrder, initialUppercaseType == 'LIMIT_MAKER', params)
5060
- triggerPrice = self.safe_value_2(params, 'triggerPrice', 'stopPrice')
5061
- stopLossPrice = self.safe_value(params, 'stopLossPrice', triggerPrice) # fallback to stopLoss
5062
- takeProfitPrice = self.safe_value(params, 'takeProfitPrice')
5063
- trailingDelta = self.safe_value(params, 'trailingDelta')
5231
+ request = {
5232
+ 'symbol': market['id'],
5233
+ 'side': side.upper(),
5234
+ }
5235
+ isPortfolioMargin = None
5236
+ isPortfolioMargin, params = self.handle_option_and_params_2(params, 'createOrder', 'papi', 'portfolioMargin', False)
5237
+ marginMode = None
5238
+ marginMode, params = self.handle_margin_mode_and_params('createOrder', params)
5239
+ if (marketType == 'margin') or (marginMode is not None) or market['option']:
5240
+ # for swap and future reduceOnly is a string that cant be sent with close position set to True or in hedge mode
5241
+ reduceOnly = self.safe_bool(params, 'reduceOnly', False)
5242
+ params = self.omit(params, 'reduceOnly')
5243
+ if market['option']:
5244
+ request['reduceOnly'] = reduceOnly
5245
+ else:
5246
+ if reduceOnly:
5247
+ request['sideEffectType'] = 'AUTO_REPAY'
5248
+ triggerPrice = self.safe_string_2(params, 'triggerPrice', 'stopPrice')
5249
+ stopLossPrice = self.safe_string(params, 'stopLossPrice', triggerPrice) # fallback to stopLoss
5250
+ takeProfitPrice = self.safe_string(params, 'takeProfitPrice')
5251
+ trailingDelta = self.safe_string(params, 'trailingDelta')
5064
5252
  trailingTriggerPrice = self.safe_string_2(params, 'trailingTriggerPrice', 'activationPrice', self.number_to_string(price))
5065
5253
  trailingPercent = self.safe_string_2(params, 'trailingPercent', 'callbackRate')
5066
5254
  isTrailingPercentOrder = trailingPercent is not None
5067
5255
  isStopLoss = stopLossPrice is not None or trailingDelta is not None
5068
5256
  isTakeProfit = takeProfitPrice is not None
5069
- params = self.omit(params, ['type', 'newClientOrderId', 'clientOrderId', 'postOnly', 'stopLossPrice', 'takeProfitPrice', 'stopPrice', 'triggerPrice', 'trailingTriggerPrice', 'trailingPercent'])
5070
- marginMode, query = self.handle_margin_mode_and_params('createOrder', params)
5071
- request = {
5072
- 'symbol': market['id'],
5073
- 'side': side.upper(),
5074
- }
5075
- if market['spot'] or marketType == 'margin':
5076
- # only supported for spot/margin api(all margin markets are spot markets)
5077
- if postOnly:
5078
- type = 'LIMIT_MAKER'
5079
- if marketType == 'margin' or marginMode is not None:
5080
- reduceOnly = self.safe_value(params, 'reduceOnly')
5081
- if reduceOnly:
5082
- request['sideEffectType'] = 'AUTO_REPAY'
5083
- params = self.omit(params, 'reduceOnly')
5257
+ isTriggerOrder = triggerPrice is not None
5258
+ isConditional = isTriggerOrder or isTrailingPercentOrder or isStopLoss or isTakeProfit
5259
+ isPortfolioMarginConditional = (isPortfolioMargin and isConditional)
5084
5260
  uppercaseType = type.upper()
5085
5261
  stopPrice = None
5086
5262
  if isTrailingPercentOrder:
@@ -5102,31 +5278,41 @@ class binance(Exchange, ImplicitAPI):
5102
5278
  uppercaseType = 'TAKE_PROFIT_MARKET' if market['contract'] else 'TAKE_PROFIT'
5103
5279
  elif isLimitOrder:
5104
5280
  uppercaseType = 'TAKE_PROFIT' if market['contract'] else 'TAKE_PROFIT_LIMIT'
5105
- if marginMode == 'isolated':
5106
- request['isIsolated'] = True
5107
- if clientOrderId is None:
5108
- broker = self.safe_value(self.options, 'broker', {})
5109
- defaultId = 'x-xcKtGhcu' if (market['contract']) else 'x-R4BD3S82'
5110
- brokerId = self.safe_string(broker, marketType, defaultId)
5111
- request['newClientOrderId'] = brokerId + self.uuid22()
5112
- else:
5113
- request['newClientOrderId'] = clientOrderId
5114
5281
  if (marketType == 'spot') or (marketType == 'margin'):
5115
- request['newOrderRespType'] = self.safe_value(self.options['newOrderRespType'], type, 'RESULT') # 'ACK' for order id, 'RESULT' for full order or 'FULL' for order with fills
5282
+ request['newOrderRespType'] = self.safe_string(self.options['newOrderRespType'], type, 'RESULT') # 'ACK' for order id, 'RESULT' for full order or 'FULL' for order with fills
5116
5283
  else:
5117
5284
  # swap, futures and options
5118
- request['newOrderRespType'] = 'RESULT' # "ACK", "RESULT", default "ACK"
5285
+ if not isPortfolioMargin:
5286
+ request['newOrderRespType'] = 'RESULT' # "ACK", "RESULT", default "ACK"
5119
5287
  if market['option']:
5120
5288
  if type == 'market':
5121
5289
  raise InvalidOrder(self.id + ' ' + type + ' is not a valid order type for the ' + symbol + ' market')
5122
5290
  else:
5123
- validOrderTypes = self.safe_value(market['info'], 'orderTypes')
5291
+ validOrderTypes = self.safe_list(market['info'], 'orderTypes')
5124
5292
  if not self.in_array(uppercaseType, validOrderTypes):
5125
5293
  if initialUppercaseType != uppercaseType:
5126
5294
  raise InvalidOrder(self.id + ' stopPrice parameter is not allowed for ' + symbol + ' ' + type + ' orders')
5127
5295
  else:
5128
5296
  raise InvalidOrder(self.id + ' ' + type + ' is not a valid order type for the ' + symbol + ' market')
5129
- request['type'] = uppercaseType
5297
+ clientOrderIdRequest = 'newClientStrategyId' if isPortfolioMarginConditional else 'newClientOrderId'
5298
+ if clientOrderId is None:
5299
+ broker = self.safe_dict(self.options, 'broker', {})
5300
+ defaultId = 'x-xcKtGhcu' if (market['contract']) else 'x-R4BD3S82'
5301
+ brokerId = self.safe_string(broker, marketType, defaultId)
5302
+ request[clientOrderIdRequest] = brokerId + self.uuid22()
5303
+ else:
5304
+ request[clientOrderIdRequest] = clientOrderId
5305
+ postOnly = None
5306
+ if not isPortfolioMargin:
5307
+ postOnly = self.is_post_only(isMarketOrder, initialUppercaseType == 'LIMIT_MAKER', params)
5308
+ if market['spot'] or marketType == 'margin':
5309
+ # only supported for spot/margin api(all margin markets are spot markets)
5310
+ if postOnly:
5311
+ uppercaseType = 'LIMIT_MAKER'
5312
+ if marginMode == 'isolated':
5313
+ request['isIsolated'] = True
5314
+ typeRequest = 'strategyType' if isPortfolioMarginConditional else 'type'
5315
+ request[typeRequest] = uppercaseType
5130
5316
  # additional required fields depending on the order type
5131
5317
  timeInForceIsRequired = False
5132
5318
  priceIsRequired = False
@@ -5154,9 +5340,9 @@ class binance(Exchange, ImplicitAPI):
5154
5340
  #
5155
5341
  if uppercaseType == 'MARKET':
5156
5342
  if market['spot']:
5157
- quoteOrderQty = self.safe_value(self.options, 'quoteOrderQty', True)
5343
+ quoteOrderQty = self.safe_bool(self.options, 'quoteOrderQty', True)
5158
5344
  if quoteOrderQty:
5159
- quoteOrderQtyNew = self.safe_value_2(query, 'quoteOrderQty', 'cost')
5345
+ quoteOrderQtyNew = self.safe_string_2(params, 'quoteOrderQty', 'cost')
5160
5346
  precision = market['precision']['price']
5161
5347
  if quoteOrderQtyNew is not None:
5162
5348
  request['quoteOrderQty'] = self.decimal_to_precision(quoteOrderQtyNew, TRUNCATE, precision, self.precisionMode)
@@ -5193,7 +5379,7 @@ class binance(Exchange, ImplicitAPI):
5193
5379
  stopPriceIsRequired = True
5194
5380
  priceIsRequired = True
5195
5381
  elif (uppercaseType == 'STOP_MARKET') or (uppercaseType == 'TAKE_PROFIT_MARKET'):
5196
- closePosition = self.safe_value(query, 'closePosition')
5382
+ closePosition = self.safe_bool(params, 'closePosition')
5197
5383
  if closePosition is None:
5198
5384
  quantityIsRequired = True
5199
5385
  stopPriceIsRequired = True
@@ -5202,15 +5388,15 @@ class binance(Exchange, ImplicitAPI):
5202
5388
  if trailingPercent is None:
5203
5389
  raise InvalidOrder(self.id + ' createOrder() requires a trailingPercent param for a ' + type + ' order')
5204
5390
  if quantityIsRequired:
5205
- request['quantity'] = self.amount_to_precision(symbol, amount)
5391
+ # portfolio margin has a different amount precision
5392
+ if isPortfolioMargin:
5393
+ request['quantity'] = self.parse_to_numeric(amount)
5394
+ else:
5395
+ request['quantity'] = self.amount_to_precision(symbol, amount)
5206
5396
  if priceIsRequired:
5207
5397
  if price is None:
5208
5398
  raise InvalidOrder(self.id + ' createOrder() requires a price argument for a ' + type + ' order')
5209
5399
  request['price'] = self.price_to_precision(symbol, price)
5210
- if timeInForceIsRequired:
5211
- request['timeInForce'] = self.options['defaultTimeInForce'] # 'GTC' = Good To Cancel(default), 'IOC' = Immediate Or Cancel
5212
- if market['contract'] and postOnly:
5213
- request['timeInForce'] = 'GTX'
5214
5400
  if stopPriceIsRequired:
5215
5401
  if market['contract']:
5216
5402
  if stopPrice is None:
@@ -5221,10 +5407,14 @@ class binance(Exchange, ImplicitAPI):
5221
5407
  raise InvalidOrder(self.id + ' createOrder() requires a stopPrice or trailingDelta param for a ' + type + ' order')
5222
5408
  if stopPrice is not None:
5223
5409
  request['stopPrice'] = self.price_to_precision(symbol, stopPrice)
5410
+ if timeInForceIsRequired and (self.safe_string(params, 'timeInForce') is None):
5411
+ request['timeInForce'] = self.options['defaultTimeInForce'] # 'GTC' = Good To Cancel(default), 'IOC' = Immediate Or Cancel
5412
+ if not isPortfolioMargin and market['contract'] and postOnly:
5413
+ request['timeInForce'] = 'GTX'
5224
5414
  # remove timeInForce from params because PO is only used by self.is_post_onlyand it's not a valid value for Binance
5225
5415
  if self.safe_string(params, 'timeInForce') == 'PO':
5226
- params = self.omit(params, ['timeInForce'])
5227
- requestParams = self.omit(params, ['quoteOrderQty', 'cost', 'stopPrice', 'test', 'type', 'newClientOrderId', 'clientOrderId', 'postOnly'])
5416
+ params = self.omit(params, 'timeInForce')
5417
+ requestParams = self.omit(params, ['type', 'newClientOrderId', 'clientOrderId', 'postOnly', 'stopLossPrice', 'takeProfitPrice', 'stopPrice', 'triggerPrice', 'trailingTriggerPrice', 'trailingPercent', 'quoteOrderQty', 'cost', 'test'])
5228
5418
  return self.extend(request, requestParams)
5229
5419
 
5230
5420
  async def create_market_order_with_cost(self, symbol: str, side: OrderSide, cost, params={}):
@@ -5453,21 +5643,28 @@ class binance(Exchange, ImplicitAPI):
5453
5643
 
5454
5644
  async def fetch_open_orders(self, symbol: Str = None, since: Int = None, limit: Int = None, params={}) -> List[Order]:
5455
5645
  """
5646
+ fetch all unfilled currently open orders
5456
5647
  :see: https://binance-docs.github.io/apidocs/spot/en/#cancel-an-existing-order-and-send-a-new-order-trade
5457
5648
  :see: https://binance-docs.github.io/apidocs/futures/en/#current-all-open-orders-user_data
5458
5649
  :see: https://binance-docs.github.io/apidocs/delivery/en/#current-all-open-orders-user_data
5459
5650
  :see: https://binance-docs.github.io/apidocs/voptions/en/#query-current-open-option-orders-user_data
5460
- fetch all unfilled currently open orders
5461
5651
  :see: https://binance-docs.github.io/apidocs/spot/en/#current-open-orders-user_data
5462
5652
  :see: https://binance-docs.github.io/apidocs/futures/en/#current-all-open-orders-user_data
5463
5653
  :see: https://binance-docs.github.io/apidocs/delivery/en/#current-all-open-orders-user_data
5464
5654
  :see: https://binance-docs.github.io/apidocs/voptions/en/#query-current-open-option-orders-user_data
5465
5655
  :see: https://binance-docs.github.io/apidocs/spot/en/#query-margin-account-39-s-open-orders-user_data
5656
+ :see: https://binance-docs.github.io/apidocs/pm/en/#query-all-current-um-open-orders-user_data
5657
+ :see: https://binance-docs.github.io/apidocs/pm/en/#query-all-current-cm-open-orders-user_data
5658
+ :see: https://binance-docs.github.io/apidocs/pm/en/#query-all-current-um-open-conditional-orders-user_data
5659
+ :see: https://binance-docs.github.io/apidocs/pm/en/#query-all-current-cm-open-conditional-orders-user_data
5660
+ :see: https://binance-docs.github.io/apidocs/pm/en/#query-current-margin-open-order-user_data
5466
5661
  :param str symbol: unified market symbol
5467
5662
  :param int [since]: the earliest time in ms to fetch open orders for
5468
5663
  :param int [limit]: the maximum number of open orders structures to retrieve
5469
5664
  :param dict [params]: extra parameters specific to the exchange API endpoint
5470
5665
  :param str [params.marginMode]: 'cross' or 'isolated', for spot margin trading
5666
+ :param boolean [params.portfolioMargin]: set to True if you would like to fetch open orders in the portfolio margin account
5667
+ :param boolean [params.stop]: set to True if you would like to fetch portfolio margin account conditional orders
5471
5668
  :returns Order[]: a list of `order structures <https://docs.ccxt.com/#/?id=order-structure>`
5472
5669
  """
5473
5670
  await self.load_markets()
@@ -5475,14 +5672,16 @@ class binance(Exchange, ImplicitAPI):
5475
5672
  type = None
5476
5673
  request = {}
5477
5674
  marginMode = None
5478
- query = None
5479
- marginMode, query = self.handle_margin_mode_and_params('fetchOpenOrders', params)
5675
+ marginMode, params = self.handle_margin_mode_and_params('fetchOpenOrders', params)
5676
+ isPortfolioMargin = None
5677
+ isPortfolioMargin, params = self.handle_option_and_params_2(params, 'fetchOpenOrders', 'papi', 'portfolioMargin', False)
5678
+ isConditional = self.safe_bool_2(params, 'stop', 'conditional')
5480
5679
  if symbol is not None:
5481
5680
  market = self.market(symbol)
5482
5681
  request['symbol'] = market['id']
5483
5682
  defaultType = self.safe_string_2(self.options, 'fetchOpenOrders', 'defaultType', 'spot')
5484
5683
  marketType = market['type'] if ('type' in market) else defaultType
5485
- type = self.safe_string(query, 'type', marketType)
5684
+ type = self.safe_string(params, 'type', marketType)
5486
5685
  elif self.options['warnOnFetchOpenOrdersWithoutSymbol']:
5487
5686
  symbols = self.symbols
5488
5687
  numSymbols = len(symbols)
@@ -5490,29 +5689,44 @@ class binance(Exchange, ImplicitAPI):
5490
5689
  raise ExchangeError(self.id + ' fetchOpenOrders() WARNING: fetching open orders without specifying a symbol is rate-limited to one call per ' + str(fetchOpenOrdersRateLimit) + ' seconds. Do not call self method frequently to avoid ban. Set ' + self.id + '.options["warnOnFetchOpenOrdersWithoutSymbol"] = False to suppress self warning message.')
5491
5690
  else:
5492
5691
  defaultType = self.safe_string_2(self.options, 'fetchOpenOrders', 'defaultType', 'spot')
5493
- type = self.safe_string(query, 'type', defaultType)
5692
+ type = self.safe_string(params, 'type', defaultType)
5494
5693
  subType = None
5495
- subType, query = self.handle_sub_type_and_params('fetchOpenOrders', market, query)
5496
- requestParams = self.omit(query, 'type')
5694
+ subType, params = self.handle_sub_type_and_params('fetchOpenOrders', market, params)
5695
+ params = self.omit(params, ['type', 'stop', 'conditional'])
5497
5696
  response = None
5498
5697
  if type == 'option':
5499
5698
  if since is not None:
5500
5699
  request['startTime'] = since
5501
5700
  if limit is not None:
5502
5701
  request['limit'] = limit
5503
- response = await self.eapiPrivateGetOpenOrders(self.extend(request, requestParams))
5702
+ response = await self.eapiPrivateGetOpenOrders(self.extend(request, params))
5504
5703
  elif self.is_linear(type, subType):
5505
- response = await self.fapiPrivateGetOpenOrders(self.extend(request, requestParams))
5704
+ if isPortfolioMargin:
5705
+ if isConditional:
5706
+ response = await self.papiGetUmConditionalOpenOrders(self.extend(request, params))
5707
+ else:
5708
+ response = await self.papiGetUmOpenOrders(self.extend(request, params))
5709
+ else:
5710
+ response = await self.fapiPrivateGetOpenOrders(self.extend(request, params))
5506
5711
  elif self.is_inverse(type, subType):
5507
- response = await self.dapiPrivateGetOpenOrders(self.extend(request, requestParams))
5712
+ if isPortfolioMargin:
5713
+ if isConditional:
5714
+ response = await self.papiGetCmConditionalOpenOrders(self.extend(request, params))
5715
+ else:
5716
+ response = await self.papiGetCmOpenOrders(self.extend(request, params))
5717
+ else:
5718
+ response = await self.dapiPrivateGetOpenOrders(self.extend(request, params))
5508
5719
  elif type == 'margin' or marginMode is not None:
5509
- if marginMode == 'isolated':
5510
- request['isIsolated'] = True
5511
- if symbol is None:
5512
- raise ArgumentsRequired(self.id + ' fetchOpenOrders() requires a symbol argument for isolated markets')
5513
- response = await self.sapiGetMarginOpenOrders(self.extend(request, requestParams))
5720
+ if isPortfolioMargin:
5721
+ response = await self.papiGetMarginOpenOrders(self.extend(request, params))
5722
+ else:
5723
+ if marginMode == 'isolated':
5724
+ request['isIsolated'] = True
5725
+ if symbol is None:
5726
+ raise ArgumentsRequired(self.id + ' fetchOpenOrders() requires a symbol argument for isolated markets')
5727
+ response = await self.sapiGetMarginOpenOrders(self.extend(request, params))
5514
5728
  else:
5515
- response = await self.privateGetOpenOrders(self.extend(request, requestParams))
5729
+ response = await self.privateGetOpenOrders(self.extend(request, params))
5516
5730
  return self.parse_orders(response, market, since, limit)
5517
5731
 
5518
5732
  async def fetch_closed_orders(self, symbol: Str = None, since: Int = None, limit: Int = None, params={}) -> List[Order]:
@@ -5608,40 +5822,66 @@ class binance(Exchange, ImplicitAPI):
5608
5822
 
5609
5823
  async def cancel_all_orders(self, symbol: Str = None, params={}):
5610
5824
  """
5825
+ cancel all open orders in a market
5611
5826
  :see: https://binance-docs.github.io/apidocs/spot/en/#cancel-all-open-orders-on-a-symbol-trade
5612
5827
  :see: https://binance-docs.github.io/apidocs/futures/en/#cancel-all-open-orders-trade
5613
5828
  :see: https://binance-docs.github.io/apidocs/delivery/en/#cancel-all-open-orders-trade
5614
5829
  :see: https://binance-docs.github.io/apidocs/voptions/en/#cancel-all-option-orders-on-specific-symbol-trade
5615
5830
  :see: https://binance-docs.github.io/apidocs/spot/en/#margin-account-cancel-order-trade
5616
- cancel all open orders in a market
5831
+ :see: https://binance-docs.github.io/apidocs/pm/en/#cancel-all-um-open-orders-trade
5832
+ :see: https://binance-docs.github.io/apidocs/pm/en/#cancel-all-cm-open-orders-trade
5833
+ :see: https://binance-docs.github.io/apidocs/pm/en/#cancel-all-um-open-conditional-orders-trade
5834
+ :see: https://binance-docs.github.io/apidocs/pm/en/#cancel-all-cm-open-conditional-orders-trade
5835
+ :see: https://binance-docs.github.io/apidocs/pm/en/#cancel-margin-account-all-open-orders-on-a-symbol-trade
5617
5836
  :param str symbol: unified market symbol of the market to cancel orders in
5618
5837
  :param dict [params]: extra parameters specific to the exchange API endpoint
5619
5838
  :param str [params.marginMode]: 'cross' or 'isolated', for spot margin trading
5839
+ :param boolean [params.portfolioMargin]: set to True if you would like to cancel orders in a portfolio margin account
5840
+ :param boolean [params.stop]: set to True if you would like to cancel portfolio margin account conditional orders
5620
5841
  :returns dict[]: a list of `order structures <https://docs.ccxt.com/#/?id=order-structure>`
5621
5842
  """
5622
5843
  if symbol is None:
5623
- raise ArgumentsRequired(self.id + ' cancelOrder() requires a symbol argument')
5844
+ raise ArgumentsRequired(self.id + ' cancelAllOrders() requires a symbol argument')
5624
5845
  await self.load_markets()
5625
5846
  market = self.market(symbol)
5626
5847
  request = {
5627
5848
  'symbol': market['id'],
5628
5849
  }
5850
+ isPortfolioMargin = None
5851
+ isPortfolioMargin, params = self.handle_option_and_params_2(params, 'cancelAllOrders', 'papi', 'portfolioMargin', False)
5852
+ isConditional = self.safe_bool_2(params, 'stop', 'conditional')
5629
5853
  type = self.safe_string(params, 'type', market['type'])
5630
- params = self.omit(params, ['type'])
5631
- marginMode, query = self.handle_margin_mode_and_params('cancelAllOrders', params)
5854
+ params = self.omit(params, ['type', 'stop', 'conditional'])
5855
+ marginMode = None
5856
+ marginMode, params = self.handle_margin_mode_and_params('cancelAllOrders', params)
5632
5857
  response = None
5633
5858
  if market['option']:
5634
- response = await self.eapiPrivateDeleteAllOpenOrders(self.extend(request, query))
5859
+ response = await self.eapiPrivateDeleteAllOpenOrders(self.extend(request, params))
5635
5860
  elif market['linear']:
5636
- response = await self.fapiPrivateDeleteAllOpenOrders(self.extend(request, query))
5861
+ if isPortfolioMargin:
5862
+ if isConditional:
5863
+ response = await self.papiDeleteUmConditionalAllOpenOrders(self.extend(request, params))
5864
+ else:
5865
+ response = await self.papiDeleteUmAllOpenOrders(self.extend(request, params))
5866
+ else:
5867
+ response = await self.fapiPrivateDeleteAllOpenOrders(self.extend(request, params))
5637
5868
  elif market['inverse']:
5638
- response = await self.dapiPrivateDeleteAllOpenOrders(self.extend(request, query))
5869
+ if isPortfolioMargin:
5870
+ if isConditional:
5871
+ response = await self.papiDeleteCmConditionalAllOpenOrders(self.extend(request, params))
5872
+ else:
5873
+ response = await self.papiDeleteCmAllOpenOrders(self.extend(request, params))
5874
+ else:
5875
+ response = await self.dapiPrivateDeleteAllOpenOrders(self.extend(request, params))
5639
5876
  elif (type == 'margin') or (marginMode is not None):
5640
- if marginMode == 'isolated':
5641
- request['isIsolated'] = True
5642
- response = await self.sapiDeleteMarginOpenOrders(self.extend(request, query))
5877
+ if isPortfolioMargin:
5878
+ response = await self.papiDeleteMarginAllOpenOrders(self.extend(request, params))
5879
+ else:
5880
+ if marginMode == 'isolated':
5881
+ request['isIsolated'] = True
5882
+ response = await self.sapiDeleteMarginOpenOrders(self.extend(request, params))
5643
5883
  else:
5644
- response = await self.privateDeleteOpenOrders(self.extend(request, query))
5884
+ response = await self.privateDeleteOpenOrders(self.extend(request, params))
5645
5885
  if isinstance(response, list):
5646
5886
  return self.parse_orders(response, market)
5647
5887
  else: