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