ccxt 4.2.35 → 4.2.37

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.
package/js/src/binance.js CHANGED
@@ -4738,7 +4738,7 @@ export default class binance extends Exchange {
4738
4738
  }
4739
4739
  request['price'] = this.priceToPrecision(symbol, price);
4740
4740
  }
4741
- if (timeInForceIsRequired) {
4741
+ if (timeInForceIsRequired && (this.safeString(params, 'timeInForce') === undefined)) {
4742
4742
  request['timeInForce'] = this.options['defaultTimeInForce']; // 'GTC' = Good To Cancel (default), 'IOC' = Immediate Or Cancel
4743
4743
  }
4744
4744
  if (stopPriceIsRequired) {
@@ -5043,20 +5043,156 @@ export default class binance extends Exchange {
5043
5043
  // "lastTrade": {"id":"69","time":"1676084430567","price":"24.9","qty":"1.00"},
5044
5044
  // "mmp": false
5045
5045
  // }
5046
- // {
5046
+ //
5047
5047
  // cancelOrders/createOrders
5048
- // "code": -4005,
5049
- // "msg": "Quantity greater than max quantity."
5050
- // },
5048
+ //
5049
+ // {
5050
+ // "code": -4005,
5051
+ // "msg": "Quantity greater than max quantity."
5052
+ // }
5053
+ //
5054
+ // createOrder, fetchOpenOrders: portfolio margin linear swap and future
5055
+ //
5056
+ // {
5057
+ // "symbol": "BTCUSDT",
5058
+ // "side": "BUY",
5059
+ // "executedQty": "0.000",
5060
+ // "orderId": 258649539704,
5061
+ // "goodTillDate": 0,
5062
+ // "avgPrice": "0",
5063
+ // "origQty": "0.010",
5064
+ // "clientOrderId": "x-xcKtGhcu02573c6f15e544e990057b",
5065
+ // "positionSide": "BOTH",
5066
+ // "cumQty": "0.000",
5067
+ // "updateTime": 1707110415436,
5068
+ // "type": "LIMIT",
5069
+ // "reduceOnly": false,
5070
+ // "price": "35000.00",
5071
+ // "cumQuote": "0.00000",
5072
+ // "selfTradePreventionMode": "NONE",
5073
+ // "timeInForce": "GTC",
5074
+ // "status": "NEW"
5075
+ // }
5076
+ //
5077
+ // createOrder, fetchOpenOrders: portfolio margin inverse swap and future
5078
+ //
5079
+ // {
5080
+ // "symbol": "ETHUSD_PERP",
5081
+ // "side": "BUY",
5082
+ // "cumBase": "0",
5083
+ // "executedQty": "0",
5084
+ // "orderId": 71275227732,
5085
+ // "avgPrice": "0.00",
5086
+ // "origQty": "1",
5087
+ // "clientOrderId": "x-xcKtGhcuca5af3acfb5044198c5398",
5088
+ // "positionSide": "BOTH",
5089
+ // "cumQty": "0",
5090
+ // "updateTime": 1707110994334,
5091
+ // "type": "LIMIT",
5092
+ // "pair": "ETHUSD",
5093
+ // "reduceOnly": false,
5094
+ // "price": "2000",
5095
+ // "timeInForce": "GTC",
5096
+ // "status": "NEW"
5097
+ // }
5098
+ //
5099
+ // createOrder, fetchOpenOrders: portfolio margin linear swap and future conditional
5100
+ //
5101
+ // {
5102
+ // "newClientStrategyId": "x-xcKtGhcu27f109953d6e4dc0974006",
5103
+ // "strategyId": 3645916,
5104
+ // "strategyStatus": "NEW",
5105
+ // "strategyType": "STOP",
5106
+ // "origQty": "0.010",
5107
+ // "price": "35000.00",
5108
+ // "reduceOnly": false,
5109
+ // "side": "BUY",
5110
+ // "positionSide": "BOTH",
5111
+ // "stopPrice": "45000.00",
5112
+ // "symbol": "BTCUSDT",
5113
+ // "timeInForce": "GTC",
5114
+ // "bookTime": 1707112625879,
5115
+ // "updateTime": 1707112625879,
5116
+ // "workingType": "CONTRACT_PRICE",
5117
+ // "priceProtect": false,
5118
+ // "goodTillDate": 0,
5119
+ // "selfTradePreventionMode": "NONE"
5120
+ // }
5121
+ //
5122
+ // createOrder, fetchOpenOrders: portfolio margin inverse swap and future conditional
5123
+ //
5124
+ // {
5125
+ // "newClientStrategyId": "x-xcKtGhcuc6b86f053bb34933850739",
5126
+ // "strategyId": 1423462,
5127
+ // "strategyStatus": "NEW",
5128
+ // "strategyType": "STOP",
5129
+ // "origQty": "1",
5130
+ // "price": "2000",
5131
+ // "reduceOnly": false,
5132
+ // "side": "BUY",
5133
+ // "positionSide": "BOTH",
5134
+ // "stopPrice": "3000",
5135
+ // "symbol": "ETHUSD_PERP",
5136
+ // "timeInForce": "GTC",
5137
+ // "bookTime": 1707113098840,
5138
+ // "updateTime": 1707113098840,
5139
+ // "workingType": "CONTRACT_PRICE",
5140
+ // "priceProtect": false
5141
+ // }
5142
+ //
5143
+ // createOrder, cancelAllOrders: portfolio margin spot margin
5144
+ //
5145
+ // {
5146
+ // "clientOrderId": "x-R4BD3S82e9ef29d8346440f0b28b86",
5147
+ // "cummulativeQuoteQty": "0.00000000",
5148
+ // "executedQty": "0.00000000",
5149
+ // "fills": [],
5150
+ // "orderId": 24684460474,
5151
+ // "origQty": "0.00100000",
5152
+ // "price": "35000.00000000",
5153
+ // "selfTradePreventionMode": "EXPIRE_MAKER",
5154
+ // "side": "BUY",
5155
+ // "status": "NEW",
5156
+ // "symbol": "BTCUSDT",
5157
+ // "timeInForce": "GTC",
5158
+ // "transactTime": 1707113538870,
5159
+ // "type": "LIMIT"
5160
+ // }
5161
+ //
5162
+ // fetchOpenOrders: portfolio margin spot margin
5163
+ //
5164
+ // {
5165
+ // "symbol": "BTCUSDT",
5166
+ // "orderId": 24700763749,
5167
+ // "clientOrderId": "x-R4BD3S826f724c2a4af6425f98c7b6",
5168
+ // "price": "35000.00000000",
5169
+ // "origQty": "0.00100000",
5170
+ // "executedQty": "0.00000000",
5171
+ // "cummulativeQuoteQty": "0.00000000",
5172
+ // "status": "NEW",
5173
+ // "timeInForce": "GTC",
5174
+ // "type": "LIMIT",
5175
+ // "side": "BUY",
5176
+ // "stopPrice": "0.00000000",
5177
+ // "icebergQty": "0.00000000",
5178
+ // "time": 1707199187679,
5179
+ // "updateTime": 1707199187679,
5180
+ // "isWorking": true,
5181
+ // "accountId": 200180970,
5182
+ // "selfTradePreventionMode": "EXPIRE_MAKER",
5183
+ // "preventedMatchId": null,
5184
+ // "preventedQuantity": null
5185
+ // }
5051
5186
  //
5052
5187
  const code = this.safeString(order, 'code');
5053
5188
  if (code !== undefined) {
5054
5189
  // cancelOrders/createOrders might have a partial success
5055
5190
  return this.safeOrder({ 'info': order, 'status': 'rejected' }, market);
5056
5191
  }
5057
- const status = this.parseOrderStatus(this.safeString(order, 'status'));
5192
+ const status = this.parseOrderStatus(this.safeString2(order, 'status', 'strategyStatus'));
5058
5193
  const marketId = this.safeString(order, 'symbol');
5059
- const marketType = ('closePosition' in order) ? 'contract' : 'spot';
5194
+ const isContract = ('positionSide' in order) || ('cumQuote' in order);
5195
+ const marketType = isContract ? 'contract' : 'spot';
5060
5196
  const symbol = this.safeSymbol(marketId, market, undefined, marketType);
5061
5197
  const filled = this.safeString(order, 'executedQty', '0');
5062
5198
  const timestamp = this.safeIntegerN(order, ['time', 'createTime', 'workingTime', 'transactTime', 'updateTime']); // order of the keys matters here
@@ -5081,11 +5217,9 @@ export default class binance extends Exchange {
5081
5217
  // Note this is not the actual cost, since Binance futures uses leverage to calculate margins.
5082
5218
  let cost = this.safeString2(order, 'cummulativeQuoteQty', 'cumQuote');
5083
5219
  cost = this.safeString(order, 'cumBase', cost);
5084
- const id = this.safeString(order, 'orderId');
5085
5220
  let type = this.safeStringLower(order, 'type');
5086
5221
  const side = this.safeStringLower(order, 'side');
5087
5222
  const fills = this.safeValue(order, 'fills', []);
5088
- const clientOrderId = this.safeString(order, 'clientOrderId');
5089
5223
  let timeInForce = this.safeString(order, 'timeInForce');
5090
5224
  if (timeInForce === 'GTX') {
5091
5225
  // GTX means "Good Till Crossing" and is an equivalent way of saying Post Only
@@ -5108,8 +5242,8 @@ export default class binance extends Exchange {
5108
5242
  }
5109
5243
  return this.safeOrder({
5110
5244
  'info': order,
5111
- 'id': id,
5112
- 'clientOrderId': clientOrderId,
5245
+ 'id': this.safeString2(order, 'orderId', 'strategyId'),
5246
+ 'clientOrderId': this.safeString2(order, 'clientOrderId', 'newClientStrategyId'),
5113
5247
  'timestamp': timestamp,
5114
5248
  'datetime': this.iso8601(timestamp),
5115
5249
  'lastTradeTimestamp': lastTradeTimestamp,
@@ -5223,50 +5357,105 @@ export default class binance extends Exchange {
5223
5357
  * @see https://binance-docs.github.io/apidocs/voptions/en/#new-order-trade
5224
5358
  * @see https://binance-docs.github.io/apidocs/spot/en/#new-order-using-sor-trade
5225
5359
  * @see https://binance-docs.github.io/apidocs/spot/en/#test-new-order-using-sor-trade
5360
+ * @see https://binance-docs.github.io/apidocs/pm/en/#new-um-order-trade
5361
+ * @see https://binance-docs.github.io/apidocs/pm/en/#new-cm-order-trade
5362
+ * @see https://binance-docs.github.io/apidocs/pm/en/#new-margin-order-trade
5363
+ * @see https://binance-docs.github.io/apidocs/pm/en/#new-um-conditional-order-trade
5364
+ * @see https://binance-docs.github.io/apidocs/pm/en/#new-cm-conditional-order-trade
5226
5365
  * @param {string} symbol unified symbol of the market to create an order in
5227
5366
  * @param {string} type 'market' or 'limit' or 'STOP_LOSS' or 'STOP_LOSS_LIMIT' or 'TAKE_PROFIT' or 'TAKE_PROFIT_LIMIT' or 'STOP'
5228
5367
  * @param {string} side 'buy' or 'sell'
5229
- * @param {float} amount how much of currency you want to trade in units of base currency
5230
- * @param {float} [price] the price at which the order is to be fullfilled, in units of the quote currency, ignored in market orders
5368
+ * @param {float} amount how much of you want to trade in units of the base currency
5369
+ * @param {float} [price] the price that the order is to be fullfilled, in units of the quote currency, ignored in market orders
5231
5370
  * @param {object} [params] extra parameters specific to the exchange API endpoint
5371
+ * @param {string} [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.
5232
5372
  * @param {string} [params.marginMode] 'cross' or 'isolated', for spot margin trading
5233
5373
  * @param {boolean} [params.sor] *spot only* whether to use SOR (Smart Order Routing) or not, default is false
5234
5374
  * @param {boolean} [params.test] *spot only* whether to use the test endpoint or not, default is false
5235
5375
  * @param {float} [params.trailingPercent] the percent to trail away from the current market price
5236
5376
  * @param {float} [params.trailingTriggerPrice] the price to trigger a trailing order, default uses the price argument
5377
+ * @param {float} [params.triggerPrice] the price that a trigger order is triggered at
5378
+ * @param {float} [params.stopLossPrice] the price that a stop loss order is triggered at
5379
+ * @param {float} [params.takeProfitPrice] the price that a take profit order is triggered at
5380
+ * @param {boolean} [params.portfolioMargin] set to true if you would like to create an order in a portfolio margin account
5237
5381
  * @returns {object} an [order structure]{@link https://docs.ccxt.com/#/?id=order-structure}
5238
5382
  */
5239
5383
  await this.loadMarkets();
5240
5384
  const market = this.market(symbol);
5241
5385
  const marketType = this.safeString(params, 'type', market['type']);
5242
- const [marginMode, query] = this.handleMarginModeAndParams('createOrder', params);
5243
- const sor = this.safeValue2(params, 'sor', 'SOR', false);
5244
- params = this.omit(params, 'sor', 'SOR');
5386
+ let marginMode = undefined;
5387
+ [marginMode, params] = this.handleMarginModeAndParams('createOrder', params);
5388
+ let isPortfolioMargin = undefined;
5389
+ [isPortfolioMargin, params] = this.handleOptionAndParams2(params, 'createOrder', 'papi', 'portfolioMargin', false);
5390
+ const triggerPrice = this.safeString2(params, 'triggerPrice', 'stopPrice');
5391
+ const stopLossPrice = this.safeString(params, 'stopLossPrice');
5392
+ const takeProfitPrice = this.safeString(params, 'takeProfitPrice');
5393
+ const trailingPercent = this.safeString2(params, 'trailingPercent', 'callbackRate');
5394
+ const isTrailingPercentOrder = trailingPercent !== undefined;
5395
+ const isStopLoss = stopLossPrice !== undefined;
5396
+ const isTakeProfit = takeProfitPrice !== undefined;
5397
+ const isConditional = (triggerPrice !== undefined) || isTrailingPercentOrder || isStopLoss || isTakeProfit;
5398
+ const sor = this.safeBool2(params, 'sor', 'SOR', false);
5399
+ const test = this.safeBool(params, 'test', false);
5400
+ params = this.omit(params, ['sor', 'SOR', 'test']);
5401
+ if (isPortfolioMargin) {
5402
+ params['portfolioMargin'] = isPortfolioMargin;
5403
+ }
5245
5404
  const request = this.createOrderRequest(symbol, type, side, amount, price, params);
5246
- let method = 'privatePostOrder';
5247
- if (sor) {
5248
- method = 'privatePostSorOrder';
5405
+ let response = undefined;
5406
+ if (market['option']) {
5407
+ response = await this.eapiPrivatePostOrder(request);
5408
+ }
5409
+ else if (sor) {
5410
+ if (test) {
5411
+ response = await this.privatePostSorOrderTest(request);
5412
+ }
5413
+ else {
5414
+ response = await this.privatePostSorOrder(request);
5415
+ }
5249
5416
  }
5250
5417
  else if (market['linear']) {
5251
- method = 'fapiPrivatePostOrder';
5418
+ if (isPortfolioMargin) {
5419
+ if (isConditional) {
5420
+ response = await this.papiPostUmConditionalOrder(request);
5421
+ }
5422
+ else {
5423
+ response = await this.papiPostUmOrder(request);
5424
+ }
5425
+ }
5426
+ else {
5427
+ response = await this.fapiPrivatePostOrder(request);
5428
+ }
5252
5429
  }
5253
5430
  else if (market['inverse']) {
5254
- method = 'dapiPrivatePostOrder';
5431
+ if (isPortfolioMargin) {
5432
+ if (isConditional) {
5433
+ response = await this.papiPostCmConditionalOrder(request);
5434
+ }
5435
+ else {
5436
+ response = await this.papiPostCmOrder(request);
5437
+ }
5438
+ }
5439
+ else {
5440
+ response = await this.dapiPrivatePostOrder(request);
5441
+ }
5255
5442
  }
5256
5443
  else if (marketType === 'margin' || marginMode !== undefined) {
5257
- method = 'sapiPostMarginOrder';
5258
- }
5259
- if (market['option']) {
5260
- method = 'eapiPrivatePostOrder';
5444
+ if (isPortfolioMargin) {
5445
+ response = await this.papiPostMarginOrder(request);
5446
+ }
5447
+ else {
5448
+ response = await this.sapiPostMarginOrder(request);
5449
+ }
5261
5450
  }
5262
- // support for testing orders
5263
- if (market['spot'] || marketType === 'margin') {
5264
- const test = this.safeBool(query, 'test', false);
5451
+ else {
5265
5452
  if (test) {
5266
- method += 'Test';
5453
+ response = await this.privatePostOrderTest(request);
5454
+ }
5455
+ else {
5456
+ response = await this.privatePostOrder(request);
5267
5457
  }
5268
5458
  }
5269
- const response = await this[method](request);
5270
5459
  return this.parseOrder(response, market);
5271
5460
  }
5272
5461
  createOrderRequest(symbol, type, side, amount, price = undefined, params = {}) {
@@ -5274,16 +5463,13 @@ export default class binance extends Exchange {
5274
5463
  * @method
5275
5464
  * @ignore
5276
5465
  * @name binance#createOrderRequest
5277
- * @description helper function to build request
5466
+ * @description helper function to build the request
5278
5467
  * @param {string} symbol unified symbol of the market to create an order in
5279
- * @param {string} type 'market' or 'limit' or 'STOP_LOSS' or 'STOP_LOSS_LIMIT' or 'TAKE_PROFIT' or 'TAKE_PROFIT_LIMIT' or 'STOP'
5468
+ * @param {string} type 'market' or 'limit'
5280
5469
  * @param {string} side 'buy' or 'sell'
5281
- * @param {float} amount how much of currency you want to trade in units of base currency
5282
- * @param {float|undefined} price the price at which the order is to be fullfilled, in units of the quote currency, ignored in market orders
5283
- * @param {object} params extra parameters specific to the exchange API endpoint
5284
- * @param {string|undefined} params.marginMode 'cross' or 'isolated', for spot margin trading
5285
- * @param {float} [params.trailingPercent] the percent to trail away from the current market price
5286
- * @param {float} [params.trailingTriggerPrice] the price to trigger a trailing order, default uses the price argument
5470
+ * @param {float} amount how much you want to trade in units of the base currency
5471
+ * @param {float} [price] the price that the order is to be fullfilled, in units of the quote currency, ignored in market orders
5472
+ * @param {object} [params] extra parameters specific to the exchange API endpoint
5287
5473
  * @returns {object} request to be sent to the exchange
5288
5474
  */
5289
5475
  const market = this.market(symbol);
@@ -5292,35 +5478,39 @@ export default class binance extends Exchange {
5292
5478
  const initialUppercaseType = type.toUpperCase();
5293
5479
  const isMarketOrder = initialUppercaseType === 'MARKET';
5294
5480
  const isLimitOrder = initialUppercaseType === 'LIMIT';
5295
- const postOnly = this.isPostOnly(isMarketOrder, initialUppercaseType === 'LIMIT_MAKER', params);
5296
- const triggerPrice = this.safeValue2(params, 'triggerPrice', 'stopPrice');
5297
- const stopLossPrice = this.safeValue(params, 'stopLossPrice', triggerPrice); // fallback to stopLoss
5298
- const takeProfitPrice = this.safeValue(params, 'takeProfitPrice');
5299
- const trailingDelta = this.safeValue(params, 'trailingDelta');
5300
- const trailingTriggerPrice = this.safeString2(params, 'trailingTriggerPrice', 'activationPrice', this.numberToString(price));
5301
- const trailingPercent = this.safeString2(params, 'trailingPercent', 'callbackRate');
5302
- const isTrailingPercentOrder = trailingPercent !== undefined;
5303
- const isStopLoss = stopLossPrice !== undefined || trailingDelta !== undefined;
5304
- const isTakeProfit = takeProfitPrice !== undefined;
5305
- params = this.omit(params, ['type', 'newClientOrderId', 'clientOrderId', 'postOnly', 'stopLossPrice', 'takeProfitPrice', 'stopPrice', 'triggerPrice', 'trailingTriggerPrice', 'trailingPercent']);
5306
- const [marginMode, query] = this.handleMarginModeAndParams('createOrder', params);
5307
5481
  const request = {
5308
5482
  'symbol': market['id'],
5309
5483
  'side': side.toUpperCase(),
5310
5484
  };
5311
- if (market['spot'] || marketType === 'margin') {
5312
- // only supported for spot/margin api (all margin markets are spot markets)
5313
- if (postOnly) {
5314
- type = 'LIMIT_MAKER';
5485
+ let isPortfolioMargin = undefined;
5486
+ [isPortfolioMargin, params] = this.handleOptionAndParams2(params, 'createOrder', 'papi', 'portfolioMargin', false);
5487
+ let marginMode = undefined;
5488
+ [marginMode, params] = this.handleMarginModeAndParams('createOrder', params);
5489
+ if ((marketType === 'margin') || (marginMode !== undefined) || market['option']) {
5490
+ // for swap and future reduceOnly is a string that cant be sent with close position set to true or in hedge mode
5491
+ const reduceOnly = this.safeBool(params, 'reduceOnly', false);
5492
+ params = this.omit(params, 'reduceOnly');
5493
+ if (market['option']) {
5494
+ request['reduceOnly'] = reduceOnly;
5315
5495
  }
5316
- }
5317
- if (marketType === 'margin' || marginMode !== undefined) {
5318
- const reduceOnly = this.safeValue(params, 'reduceOnly');
5319
- if (reduceOnly) {
5320
- request['sideEffectType'] = 'AUTO_REPAY';
5321
- params = this.omit(params, 'reduceOnly');
5496
+ else {
5497
+ if (reduceOnly) {
5498
+ request['sideEffectType'] = 'AUTO_REPAY';
5499
+ }
5322
5500
  }
5323
5501
  }
5502
+ const triggerPrice = this.safeString2(params, 'triggerPrice', 'stopPrice');
5503
+ const stopLossPrice = this.safeString(params, 'stopLossPrice', triggerPrice); // fallback to stopLoss
5504
+ const takeProfitPrice = this.safeString(params, 'takeProfitPrice');
5505
+ const trailingDelta = this.safeString(params, 'trailingDelta');
5506
+ const trailingTriggerPrice = this.safeString2(params, 'trailingTriggerPrice', 'activationPrice', this.numberToString(price));
5507
+ const trailingPercent = this.safeString2(params, 'trailingPercent', 'callbackRate');
5508
+ const isTrailingPercentOrder = trailingPercent !== undefined;
5509
+ const isStopLoss = stopLossPrice !== undefined || trailingDelta !== undefined;
5510
+ const isTakeProfit = takeProfitPrice !== undefined;
5511
+ const isTriggerOrder = triggerPrice !== undefined;
5512
+ const isConditional = isTriggerOrder || isTrailingPercentOrder || isStopLoss || isTakeProfit;
5513
+ const isPortfolioMarginConditional = (isPortfolioMargin && isConditional);
5324
5514
  let uppercaseType = type.toUpperCase();
5325
5515
  let stopPrice = undefined;
5326
5516
  if (isTrailingPercentOrder) {
@@ -5350,24 +5540,14 @@ export default class binance extends Exchange {
5350
5540
  uppercaseType = market['contract'] ? 'TAKE_PROFIT' : 'TAKE_PROFIT_LIMIT';
5351
5541
  }
5352
5542
  }
5353
- if (marginMode === 'isolated') {
5354
- request['isIsolated'] = true;
5355
- }
5356
- if (clientOrderId === undefined) {
5357
- const broker = this.safeValue(this.options, 'broker', {});
5358
- const defaultId = (market['contract']) ? 'x-xcKtGhcu' : 'x-R4BD3S82';
5359
- const brokerId = this.safeString(broker, marketType, defaultId);
5360
- request['newClientOrderId'] = brokerId + this.uuid22();
5361
- }
5362
- else {
5363
- request['newClientOrderId'] = clientOrderId;
5364
- }
5365
5543
  if ((marketType === 'spot') || (marketType === 'margin')) {
5366
- request['newOrderRespType'] = this.safeValue(this.options['newOrderRespType'], type, 'RESULT'); // 'ACK' for order id, 'RESULT' for full order or 'FULL' for order with fills
5544
+ request['newOrderRespType'] = this.safeString(this.options['newOrderRespType'], type, 'RESULT'); // 'ACK' for order id, 'RESULT' for full order or 'FULL' for order with fills
5367
5545
  }
5368
5546
  else {
5369
5547
  // swap, futures and options
5370
- request['newOrderRespType'] = 'RESULT'; // "ACK", "RESULT", default "ACK"
5548
+ if (!isPortfolioMargin) {
5549
+ request['newOrderRespType'] = 'RESULT'; // "ACK", "RESULT", default "ACK"
5550
+ }
5371
5551
  }
5372
5552
  if (market['option']) {
5373
5553
  if (type === 'market') {
@@ -5375,7 +5555,7 @@ export default class binance extends Exchange {
5375
5555
  }
5376
5556
  }
5377
5557
  else {
5378
- const validOrderTypes = this.safeValue(market['info'], 'orderTypes');
5558
+ const validOrderTypes = this.safeList(market['info'], 'orderTypes');
5379
5559
  if (!this.inArray(uppercaseType, validOrderTypes)) {
5380
5560
  if (initialUppercaseType !== uppercaseType) {
5381
5561
  throw new InvalidOrder(this.id + ' stopPrice parameter is not allowed for ' + symbol + ' ' + type + ' orders');
@@ -5385,7 +5565,31 @@ export default class binance extends Exchange {
5385
5565
  }
5386
5566
  }
5387
5567
  }
5388
- request['type'] = uppercaseType;
5568
+ const clientOrderIdRequest = isPortfolioMarginConditional ? 'newClientStrategyId' : 'newClientOrderId';
5569
+ if (clientOrderId === undefined) {
5570
+ const broker = this.safeDict(this.options, 'broker', {});
5571
+ const defaultId = (market['contract']) ? 'x-xcKtGhcu' : 'x-R4BD3S82';
5572
+ const brokerId = this.safeString(broker, marketType, defaultId);
5573
+ request[clientOrderIdRequest] = brokerId + this.uuid22();
5574
+ }
5575
+ else {
5576
+ request[clientOrderIdRequest] = clientOrderId;
5577
+ }
5578
+ let postOnly = undefined;
5579
+ if (!isPortfolioMargin) {
5580
+ postOnly = this.isPostOnly(isMarketOrder, initialUppercaseType === 'LIMIT_MAKER', params);
5581
+ if (market['spot'] || marketType === 'margin') {
5582
+ // only supported for spot/margin api (all margin markets are spot markets)
5583
+ if (postOnly) {
5584
+ uppercaseType = 'LIMIT_MAKER';
5585
+ }
5586
+ if (marginMode === 'isolated') {
5587
+ request['isIsolated'] = true;
5588
+ }
5589
+ }
5590
+ }
5591
+ const typeRequest = isPortfolioMarginConditional ? 'strategyType' : 'type';
5592
+ request[typeRequest] = uppercaseType;
5389
5593
  // additional required fields depending on the order type
5390
5594
  let timeInForceIsRequired = false;
5391
5595
  let priceIsRequired = false;
@@ -5413,9 +5617,9 @@ export default class binance extends Exchange {
5413
5617
  //
5414
5618
  if (uppercaseType === 'MARKET') {
5415
5619
  if (market['spot']) {
5416
- const quoteOrderQty = this.safeValue(this.options, 'quoteOrderQty', true);
5620
+ const quoteOrderQty = this.safeBool(this.options, 'quoteOrderQty', true);
5417
5621
  if (quoteOrderQty) {
5418
- const quoteOrderQtyNew = this.safeValue2(query, 'quoteOrderQty', 'cost');
5622
+ const quoteOrderQtyNew = this.safeString2(params, 'quoteOrderQty', 'cost');
5419
5623
  const precision = market['precision']['price'];
5420
5624
  if (quoteOrderQtyNew !== undefined) {
5421
5625
  request['quoteOrderQty'] = this.decimalToPrecision(quoteOrderQtyNew, TRUNCATE, precision, this.precisionMode);
@@ -5466,7 +5670,7 @@ export default class binance extends Exchange {
5466
5670
  priceIsRequired = true;
5467
5671
  }
5468
5672
  else if ((uppercaseType === 'STOP_MARKET') || (uppercaseType === 'TAKE_PROFIT_MARKET')) {
5469
- const closePosition = this.safeValue(query, 'closePosition');
5673
+ const closePosition = this.safeBool(params, 'closePosition');
5470
5674
  if (closePosition === undefined) {
5471
5675
  quantityIsRequired = true;
5472
5676
  }
@@ -5479,7 +5683,13 @@ export default class binance extends Exchange {
5479
5683
  }
5480
5684
  }
5481
5685
  if (quantityIsRequired) {
5482
- request['quantity'] = this.amountToPrecision(symbol, amount);
5686
+ // portfolio margin has a different amount precision
5687
+ if (isPortfolioMargin) {
5688
+ request['quantity'] = this.parseToNumeric(amount);
5689
+ }
5690
+ else {
5691
+ request['quantity'] = this.amountToPrecision(symbol, amount);
5692
+ }
5483
5693
  }
5484
5694
  if (priceIsRequired) {
5485
5695
  if (price === undefined) {
@@ -5487,12 +5697,6 @@ export default class binance extends Exchange {
5487
5697
  }
5488
5698
  request['price'] = this.priceToPrecision(symbol, price);
5489
5699
  }
5490
- if (timeInForceIsRequired) {
5491
- request['timeInForce'] = this.options['defaultTimeInForce']; // 'GTC' = Good To Cancel (default), 'IOC' = Immediate Or Cancel
5492
- }
5493
- if (market['contract'] && postOnly) {
5494
- request['timeInForce'] = 'GTX';
5495
- }
5496
5700
  if (stopPriceIsRequired) {
5497
5701
  if (market['contract']) {
5498
5702
  if (stopPrice === undefined) {
@@ -5509,11 +5713,17 @@ export default class binance extends Exchange {
5509
5713
  request['stopPrice'] = this.priceToPrecision(symbol, stopPrice);
5510
5714
  }
5511
5715
  }
5716
+ if (timeInForceIsRequired && (this.safeString(params, 'timeInForce') === undefined)) {
5717
+ request['timeInForce'] = this.options['defaultTimeInForce']; // 'GTC' = Good To Cancel (default), 'IOC' = Immediate Or Cancel
5718
+ }
5719
+ if (!isPortfolioMargin && market['contract'] && postOnly) {
5720
+ request['timeInForce'] = 'GTX';
5721
+ }
5512
5722
  // remove timeInForce from params because PO is only used by this.isPostOnly and it's not a valid value for Binance
5513
5723
  if (this.safeString(params, 'timeInForce') === 'PO') {
5514
- params = this.omit(params, ['timeInForce']);
5724
+ params = this.omit(params, 'timeInForce');
5515
5725
  }
5516
- const requestParams = this.omit(params, ['quoteOrderQty', 'cost', 'stopPrice', 'test', 'type', 'newClientOrderId', 'clientOrderId', 'postOnly']);
5726
+ const requestParams = this.omit(params, ['type', 'newClientOrderId', 'clientOrderId', 'postOnly', 'stopLossPrice', 'takeProfitPrice', 'stopPrice', 'triggerPrice', 'trailingTriggerPrice', 'trailingPercent', 'quoteOrderQty', 'cost', 'test']);
5517
5727
  return this.extend(request, requestParams);
5518
5728
  }
5519
5729
  async createMarketOrderWithCost(symbol, side, cost, params = {}) {
@@ -5779,21 +5989,28 @@ export default class binance extends Exchange {
5779
5989
  /**
5780
5990
  * @method
5781
5991
  * @name binance#fetchOpenOrders
5992
+ * @description fetch all unfilled currently open orders
5782
5993
  * @see https://binance-docs.github.io/apidocs/spot/en/#cancel-an-existing-order-and-send-a-new-order-trade
5783
5994
  * @see https://binance-docs.github.io/apidocs/futures/en/#current-all-open-orders-user_data
5784
5995
  * @see https://binance-docs.github.io/apidocs/delivery/en/#current-all-open-orders-user_data
5785
5996
  * @see https://binance-docs.github.io/apidocs/voptions/en/#query-current-open-option-orders-user_data
5786
- * @description fetch all unfilled currently open orders
5787
5997
  * @see https://binance-docs.github.io/apidocs/spot/en/#current-open-orders-user_data
5788
5998
  * @see https://binance-docs.github.io/apidocs/futures/en/#current-all-open-orders-user_data
5789
5999
  * @see https://binance-docs.github.io/apidocs/delivery/en/#current-all-open-orders-user_data
5790
6000
  * @see https://binance-docs.github.io/apidocs/voptions/en/#query-current-open-option-orders-user_data
5791
6001
  * @see https://binance-docs.github.io/apidocs/spot/en/#query-margin-account-39-s-open-orders-user_data
6002
+ * @see https://binance-docs.github.io/apidocs/pm/en/#query-all-current-um-open-orders-user_data
6003
+ * @see https://binance-docs.github.io/apidocs/pm/en/#query-all-current-cm-open-orders-user_data
6004
+ * @see https://binance-docs.github.io/apidocs/pm/en/#query-all-current-um-open-conditional-orders-user_data
6005
+ * @see https://binance-docs.github.io/apidocs/pm/en/#query-all-current-cm-open-conditional-orders-user_data
6006
+ * @see https://binance-docs.github.io/apidocs/pm/en/#query-current-margin-open-order-user_data
5792
6007
  * @param {string} symbol unified market symbol
5793
6008
  * @param {int} [since] the earliest time in ms to fetch open orders for
5794
6009
  * @param {int} [limit] the maximum number of open orders structures to retrieve
5795
6010
  * @param {object} [params] extra parameters specific to the exchange API endpoint
5796
6011
  * @param {string} [params.marginMode] 'cross' or 'isolated', for spot margin trading
6012
+ * @param {boolean} [params.portfolioMargin] set to true if you would like to fetch open orders in the portfolio margin account
6013
+ * @param {boolean} [params.stop] set to true if you would like to fetch portfolio margin account conditional orders
5797
6014
  * @returns {Order[]} a list of [order structures]{@link https://docs.ccxt.com/#/?id=order-structure}
5798
6015
  */
5799
6016
  await this.loadMarkets();
@@ -5801,14 +6018,16 @@ export default class binance extends Exchange {
5801
6018
  let type = undefined;
5802
6019
  const request = {};
5803
6020
  let marginMode = undefined;
5804
- let query = undefined;
5805
- [marginMode, query] = this.handleMarginModeAndParams('fetchOpenOrders', params);
6021
+ [marginMode, params] = this.handleMarginModeAndParams('fetchOpenOrders', params);
6022
+ let isPortfolioMargin = undefined;
6023
+ [isPortfolioMargin, params] = this.handleOptionAndParams2(params, 'fetchOpenOrders', 'papi', 'portfolioMargin', false);
6024
+ const isConditional = this.safeBool2(params, 'stop', 'conditional');
5806
6025
  if (symbol !== undefined) {
5807
6026
  market = this.market(symbol);
5808
6027
  request['symbol'] = market['id'];
5809
6028
  const defaultType = this.safeString2(this.options, 'fetchOpenOrders', 'defaultType', 'spot');
5810
6029
  const marketType = ('type' in market) ? market['type'] : defaultType;
5811
- type = this.safeString(query, 'type', marketType);
6030
+ type = this.safeString(params, 'type', marketType);
5812
6031
  }
5813
6032
  else if (this.options['warnOnFetchOpenOrdersWithoutSymbol']) {
5814
6033
  const symbols = this.symbols;
@@ -5818,11 +6037,11 @@ export default class binance extends Exchange {
5818
6037
  }
5819
6038
  else {
5820
6039
  const defaultType = this.safeString2(this.options, 'fetchOpenOrders', 'defaultType', 'spot');
5821
- type = this.safeString(query, 'type', defaultType);
6040
+ type = this.safeString(params, 'type', defaultType);
5822
6041
  }
5823
6042
  let subType = undefined;
5824
- [subType, query] = this.handleSubTypeAndParams('fetchOpenOrders', market, query);
5825
- const requestParams = this.omit(query, 'type');
6043
+ [subType, params] = this.handleSubTypeAndParams('fetchOpenOrders', market, params);
6044
+ params = this.omit(params, ['type', 'stop', 'conditional']);
5826
6045
  let response = undefined;
5827
6046
  if (type === 'option') {
5828
6047
  if (since !== undefined) {
@@ -5831,25 +6050,50 @@ export default class binance extends Exchange {
5831
6050
  if (limit !== undefined) {
5832
6051
  request['limit'] = limit;
5833
6052
  }
5834
- response = await this.eapiPrivateGetOpenOrders(this.extend(request, requestParams));
6053
+ response = await this.eapiPrivateGetOpenOrders(this.extend(request, params));
5835
6054
  }
5836
6055
  else if (this.isLinear(type, subType)) {
5837
- response = await this.fapiPrivateGetOpenOrders(this.extend(request, requestParams));
6056
+ if (isPortfolioMargin) {
6057
+ if (isConditional) {
6058
+ response = await this.papiGetUmConditionalOpenOrders(this.extend(request, params));
6059
+ }
6060
+ else {
6061
+ response = await this.papiGetUmOpenOrders(this.extend(request, params));
6062
+ }
6063
+ }
6064
+ else {
6065
+ response = await this.fapiPrivateGetOpenOrders(this.extend(request, params));
6066
+ }
5838
6067
  }
5839
6068
  else if (this.isInverse(type, subType)) {
5840
- response = await this.dapiPrivateGetOpenOrders(this.extend(request, requestParams));
6069
+ if (isPortfolioMargin) {
6070
+ if (isConditional) {
6071
+ response = await this.papiGetCmConditionalOpenOrders(this.extend(request, params));
6072
+ }
6073
+ else {
6074
+ response = await this.papiGetCmOpenOrders(this.extend(request, params));
6075
+ }
6076
+ }
6077
+ else {
6078
+ response = await this.dapiPrivateGetOpenOrders(this.extend(request, params));
6079
+ }
5841
6080
  }
5842
6081
  else if (type === 'margin' || marginMode !== undefined) {
5843
- if (marginMode === 'isolated') {
5844
- request['isIsolated'] = true;
5845
- if (symbol === undefined) {
5846
- throw new ArgumentsRequired(this.id + ' fetchOpenOrders() requires a symbol argument for isolated markets');
6082
+ if (isPortfolioMargin) {
6083
+ response = await this.papiGetMarginOpenOrders(this.extend(request, params));
6084
+ }
6085
+ else {
6086
+ if (marginMode === 'isolated') {
6087
+ request['isIsolated'] = true;
6088
+ if (symbol === undefined) {
6089
+ throw new ArgumentsRequired(this.id + ' fetchOpenOrders() requires a symbol argument for isolated markets');
6090
+ }
5847
6091
  }
6092
+ response = await this.sapiGetMarginOpenOrders(this.extend(request, params));
5848
6093
  }
5849
- response = await this.sapiGetMarginOpenOrders(this.extend(request, requestParams));
5850
6094
  }
5851
6095
  else {
5852
- response = await this.privateGetOpenOrders(this.extend(request, requestParams));
6096
+ response = await this.privateGetOpenOrders(this.extend(request, params));
5853
6097
  }
5854
6098
  return this.parseOrders(response, market, since, limit);
5855
6099
  }
@@ -5967,46 +6211,82 @@ export default class binance extends Exchange {
5967
6211
  /**
5968
6212
  * @method
5969
6213
  * @name binance#cancelAllOrders
6214
+ * @description cancel all open orders in a market
5970
6215
  * @see https://binance-docs.github.io/apidocs/spot/en/#cancel-all-open-orders-on-a-symbol-trade
5971
6216
  * @see https://binance-docs.github.io/apidocs/futures/en/#cancel-all-open-orders-trade
5972
6217
  * @see https://binance-docs.github.io/apidocs/delivery/en/#cancel-all-open-orders-trade
5973
6218
  * @see https://binance-docs.github.io/apidocs/voptions/en/#cancel-all-option-orders-on-specific-symbol-trade
5974
6219
  * @see https://binance-docs.github.io/apidocs/spot/en/#margin-account-cancel-order-trade
5975
- * @description cancel all open orders in a market
6220
+ * @see https://binance-docs.github.io/apidocs/pm/en/#cancel-all-um-open-orders-trade
6221
+ * @see https://binance-docs.github.io/apidocs/pm/en/#cancel-all-cm-open-orders-trade
6222
+ * @see https://binance-docs.github.io/apidocs/pm/en/#cancel-all-um-open-conditional-orders-trade
6223
+ * @see https://binance-docs.github.io/apidocs/pm/en/#cancel-all-cm-open-conditional-orders-trade
6224
+ * @see https://binance-docs.github.io/apidocs/pm/en/#cancel-margin-account-all-open-orders-on-a-symbol-trade
5976
6225
  * @param {string} symbol unified market symbol of the market to cancel orders in
5977
6226
  * @param {object} [params] extra parameters specific to the exchange API endpoint
5978
6227
  * @param {string} [params.marginMode] 'cross' or 'isolated', for spot margin trading
6228
+ * @param {boolean} [params.portfolioMargin] set to true if you would like to cancel orders in a portfolio margin account
6229
+ * @param {boolean} [params.stop] set to true if you would like to cancel portfolio margin account conditional orders
5979
6230
  * @returns {object[]} a list of [order structures]{@link https://docs.ccxt.com/#/?id=order-structure}
5980
6231
  */
5981
6232
  if (symbol === undefined) {
5982
- throw new ArgumentsRequired(this.id + ' cancelOrder() requires a symbol argument');
6233
+ throw new ArgumentsRequired(this.id + ' cancelAllOrders() requires a symbol argument');
5983
6234
  }
5984
6235
  await this.loadMarkets();
5985
6236
  const market = this.market(symbol);
5986
6237
  const request = {
5987
6238
  'symbol': market['id'],
5988
6239
  };
6240
+ let isPortfolioMargin = undefined;
6241
+ [isPortfolioMargin, params] = this.handleOptionAndParams2(params, 'cancelAllOrders', 'papi', 'portfolioMargin', false);
6242
+ const isConditional = this.safeBool2(params, 'stop', 'conditional');
5989
6243
  const type = this.safeString(params, 'type', market['type']);
5990
- params = this.omit(params, ['type']);
5991
- const [marginMode, query] = this.handleMarginModeAndParams('cancelAllOrders', params);
6244
+ params = this.omit(params, ['type', 'stop', 'conditional']);
6245
+ let marginMode = undefined;
6246
+ [marginMode, params] = this.handleMarginModeAndParams('cancelAllOrders', params);
5992
6247
  let response = undefined;
5993
6248
  if (market['option']) {
5994
- response = await this.eapiPrivateDeleteAllOpenOrders(this.extend(request, query));
6249
+ response = await this.eapiPrivateDeleteAllOpenOrders(this.extend(request, params));
5995
6250
  }
5996
6251
  else if (market['linear']) {
5997
- response = await this.fapiPrivateDeleteAllOpenOrders(this.extend(request, query));
6252
+ if (isPortfolioMargin) {
6253
+ if (isConditional) {
6254
+ response = await this.papiDeleteUmConditionalAllOpenOrders(this.extend(request, params));
6255
+ }
6256
+ else {
6257
+ response = await this.papiDeleteUmAllOpenOrders(this.extend(request, params));
6258
+ }
6259
+ }
6260
+ else {
6261
+ response = await this.fapiPrivateDeleteAllOpenOrders(this.extend(request, params));
6262
+ }
5998
6263
  }
5999
6264
  else if (market['inverse']) {
6000
- response = await this.dapiPrivateDeleteAllOpenOrders(this.extend(request, query));
6265
+ if (isPortfolioMargin) {
6266
+ if (isConditional) {
6267
+ response = await this.papiDeleteCmConditionalAllOpenOrders(this.extend(request, params));
6268
+ }
6269
+ else {
6270
+ response = await this.papiDeleteCmAllOpenOrders(this.extend(request, params));
6271
+ }
6272
+ }
6273
+ else {
6274
+ response = await this.dapiPrivateDeleteAllOpenOrders(this.extend(request, params));
6275
+ }
6001
6276
  }
6002
6277
  else if ((type === 'margin') || (marginMode !== undefined)) {
6003
- if (marginMode === 'isolated') {
6004
- request['isIsolated'] = true;
6278
+ if (isPortfolioMargin) {
6279
+ response = await this.papiDeleteMarginAllOpenOrders(this.extend(request, params));
6280
+ }
6281
+ else {
6282
+ if (marginMode === 'isolated') {
6283
+ request['isIsolated'] = true;
6284
+ }
6285
+ response = await this.sapiDeleteMarginOpenOrders(this.extend(request, params));
6005
6286
  }
6006
- response = await this.sapiDeleteMarginOpenOrders(this.extend(request, query));
6007
6287
  }
6008
6288
  else {
6009
- response = await this.privateDeleteOpenOrders(this.extend(request, query));
6289
+ response = await this.privateDeleteOpenOrders(this.extend(request, params));
6010
6290
  }
6011
6291
  if (Array.isArray(response)) {
6012
6292
  return this.parseOrders(response, market);