ccxt 4.2.34 → 4.2.36

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.
@@ -3143,9 +3143,24 @@ class binance extends binance$1 {
3143
3143
  let timestamp = undefined;
3144
3144
  const isolated = marginMode === 'isolated';
3145
3145
  const cross = (type === 'margin') || (marginMode === 'cross');
3146
- if (!isolated && ((type === 'spot') || cross)) {
3146
+ if (type === 'papi') {
3147
+ for (let i = 0; i < response.length; i++) {
3148
+ const entry = response[i];
3149
+ const account = this.account();
3150
+ const currencyId = this.safeString(entry, 'asset');
3151
+ const code = this.safeCurrencyCode(currencyId);
3152
+ const borrowed = this.safeString(entry, 'crossMarginBorrowed');
3153
+ const interest = this.safeString(entry, 'crossMarginInterest');
3154
+ account['free'] = this.safeString(entry, 'crossMarginFree');
3155
+ account['used'] = this.safeString(entry, 'crossMarginLocked');
3156
+ account['total'] = this.safeString(entry, 'crossMarginAsset');
3157
+ account['debt'] = Precise["default"].stringAdd(borrowed, interest);
3158
+ result[code] = account;
3159
+ }
3160
+ }
3161
+ else if (!isolated && ((type === 'spot') || cross)) {
3147
3162
  timestamp = this.safeInteger(response, 'updateTime');
3148
- const balances = this.safeValue2(response, 'balances', 'userAssets', []);
3163
+ const balances = this.safeList2(response, 'balances', 'userAssets', []);
3149
3164
  for (let i = 0; i < balances.length; i++) {
3150
3165
  const balance = balances[i];
3151
3166
  const currencyId = this.safeString(balance, 'asset');
@@ -3162,13 +3177,13 @@ class binance extends binance$1 {
3162
3177
  }
3163
3178
  }
3164
3179
  else if (isolated) {
3165
- const assets = this.safeValue(response, 'assets');
3180
+ const assets = this.safeList(response, 'assets');
3166
3181
  for (let i = 0; i < assets.length; i++) {
3167
3182
  const asset = assets[i];
3168
- const marketId = this.safeValue(asset, 'symbol');
3183
+ const marketId = this.safeString(asset, 'symbol');
3169
3184
  const symbol = this.safeSymbol(marketId, undefined, undefined, 'spot');
3170
- const base = this.safeValue(asset, 'baseAsset', {});
3171
- const quote = this.safeValue(asset, 'quoteAsset', {});
3185
+ const base = this.safeDict(asset, 'baseAsset', {});
3186
+ const quote = this.safeDict(asset, 'quoteAsset', {});
3172
3187
  const baseCode = this.safeCurrencyCode(this.safeString(base, 'asset'));
3173
3188
  const quoteCode = this.safeCurrencyCode(this.safeString(quote, 'asset'));
3174
3189
  const subResult = {};
@@ -3178,7 +3193,7 @@ class binance extends binance$1 {
3178
3193
  }
3179
3194
  }
3180
3195
  else if (type === 'savings') {
3181
- const positionAmountVos = this.safeValue(response, 'positionAmountVos', []);
3196
+ const positionAmountVos = this.safeList(response, 'positionAmountVos', []);
3182
3197
  for (let i = 0; i < positionAmountVos.length; i++) {
3183
3198
  const entry = positionAmountVos[i];
3184
3199
  const currencyId = this.safeString(entry, 'asset');
@@ -3207,7 +3222,7 @@ class binance extends binance$1 {
3207
3222
  else {
3208
3223
  let balances = response;
3209
3224
  if (!Array.isArray(response)) {
3210
- balances = this.safeValue(response, 'assets', []);
3225
+ balances = this.safeList(response, 'assets', []);
3211
3226
  }
3212
3227
  for (let i = 0; i < balances.length; i++) {
3213
3228
  const balance = balances[i];
@@ -3237,10 +3252,12 @@ class binance extends binance$1 {
3237
3252
  * @see https://binance-docs.github.io/apidocs/futures/en/#account-information-v2-user_data // swap
3238
3253
  * @see https://binance-docs.github.io/apidocs/delivery/en/#account-information-user_data // future
3239
3254
  * @see https://binance-docs.github.io/apidocs/voptions/en/#option-account-information-trade // option
3255
+ * @see https://binance-docs.github.io/apidocs/pm/en/#account-balance-user_data // portfolio margin
3240
3256
  * @param {object} [params] extra parameters specific to the exchange API endpoint
3241
- * @param {string} [params.type] 'future', 'delivery', 'savings', 'funding', or 'spot'
3257
+ * @param {string} [params.type] 'future', 'delivery', 'savings', 'funding', or 'spot' or 'papi'
3242
3258
  * @param {string} [params.marginMode] 'cross' or 'isolated', for margin trading, uses this.options.defaultMarginMode if not passed, defaults to undefined/None/null
3243
3259
  * @param {string[]|undefined} [params.symbols] unified market symbols, only used in isolated margin mode
3260
+ * @param {boolean} [params.portfolioMargin] set to true if you would like to fetch the balance for a portfolio margin account
3244
3261
  * @returns {object} a [balance structure]{@link https://docs.ccxt.com/#/?id=balance-structure}
3245
3262
  */
3246
3263
  await this.loadMarkets();
@@ -3248,13 +3265,19 @@ class binance extends binance$1 {
3248
3265
  let type = this.safeString(params, 'type', defaultType);
3249
3266
  let subType = undefined;
3250
3267
  [subType, params] = this.handleSubTypeAndParams('fetchBalance', undefined, params);
3268
+ let isPortfolioMargin = undefined;
3269
+ [isPortfolioMargin, params] = this.handleOptionAndParams2(params, 'fetchBalance', 'papi', 'portfolioMargin', false);
3251
3270
  let marginMode = undefined;
3252
3271
  let query = undefined;
3253
3272
  [marginMode, query] = this.handleMarginModeAndParams('fetchBalance', params);
3254
3273
  query = this.omit(query, 'type');
3255
3274
  let response = undefined;
3256
3275
  const request = {};
3257
- if (this.isLinear(type, subType)) {
3276
+ if (isPortfolioMargin || (type === 'papi')) {
3277
+ type = 'papi';
3278
+ response = await this.papiGetBalance(this.extend(request, query));
3279
+ }
3280
+ else if (this.isLinear(type, subType)) {
3258
3281
  type = 'linear';
3259
3282
  response = await this.fapiPrivateV2GetAccount(this.extend(request, query));
3260
3283
  }
@@ -3263,7 +3286,7 @@ class binance extends binance$1 {
3263
3286
  response = await this.dapiPrivateGetAccount(this.extend(request, query));
3264
3287
  }
3265
3288
  else if (marginMode === 'isolated') {
3266
- const paramSymbols = this.safeValue(params, 'symbols');
3289
+ const paramSymbols = this.safeList(params, 'symbols');
3267
3290
  query = this.omit(query, 'symbols');
3268
3291
  if (paramSymbols !== undefined) {
3269
3292
  let symbols = '';
@@ -3479,6 +3502,26 @@ class binance extends binance$1 {
3479
3502
  // }
3480
3503
  // ]
3481
3504
  //
3505
+ // portfolio margin
3506
+ //
3507
+ // [
3508
+ // {
3509
+ // "asset": "USDT",
3510
+ // "totalWalletBalance": "66.9923261",
3511
+ // "crossMarginAsset": "35.9697141",
3512
+ // "crossMarginBorrowed": "0.0",
3513
+ // "crossMarginFree": "35.9697141",
3514
+ // "crossMarginInterest": "0.0",
3515
+ // "crossMarginLocked": "0.0",
3516
+ // "umWalletBalance": "31.022612",
3517
+ // "umUnrealizedPNL": "0.0",
3518
+ // "cmWalletBalance": "0.0",
3519
+ // "cmUnrealizedPNL": "0.0",
3520
+ // "updateTime": 0,
3521
+ // "negativeBalance": "0.0"
3522
+ // },
3523
+ // ]
3524
+ //
3482
3525
  return this.parseBalanceCustom(response, type, marginMode);
3483
3526
  }
3484
3527
  async fetchOrderBook(symbol, limit = undefined, params = {}) {
@@ -4692,7 +4735,7 @@ class binance extends binance$1 {
4692
4735
  }
4693
4736
  request['price'] = this.priceToPrecision(symbol, price);
4694
4737
  }
4695
- if (timeInForceIsRequired) {
4738
+ if (timeInForceIsRequired && (this.safeString(params, 'timeInForce') === undefined)) {
4696
4739
  request['timeInForce'] = this.options['defaultTimeInForce']; // 'GTC' = Good To Cancel (default), 'IOC' = Immediate Or Cancel
4697
4740
  }
4698
4741
  if (stopPriceIsRequired) {
@@ -4997,18 +5040,128 @@ class binance extends binance$1 {
4997
5040
  // "lastTrade": {"id":"69","time":"1676084430567","price":"24.9","qty":"1.00"},
4998
5041
  // "mmp": false
4999
5042
  // }
5000
- // {
5043
+ //
5001
5044
  // cancelOrders/createOrders
5002
- // "code": -4005,
5003
- // "msg": "Quantity greater than max quantity."
5004
- // },
5045
+ //
5046
+ // {
5047
+ // "code": -4005,
5048
+ // "msg": "Quantity greater than max quantity."
5049
+ // }
5050
+ //
5051
+ // createOrder: portfolio margin linear swap and future
5052
+ //
5053
+ // {
5054
+ // "symbol": "BTCUSDT",
5055
+ // "side": "BUY",
5056
+ // "executedQty": "0.000",
5057
+ // "orderId": 258649539704,
5058
+ // "goodTillDate": 0,
5059
+ // "avgPrice": "0",
5060
+ // "origQty": "0.010",
5061
+ // "clientOrderId": "x-xcKtGhcu02573c6f15e544e990057b",
5062
+ // "positionSide": "BOTH",
5063
+ // "cumQty": "0.000",
5064
+ // "updateTime": 1707110415436,
5065
+ // "type": "LIMIT",
5066
+ // "reduceOnly": false,
5067
+ // "price": "35000.00",
5068
+ // "cumQuote": "0.00000",
5069
+ // "selfTradePreventionMode": "NONE",
5070
+ // "timeInForce": "GTC",
5071
+ // "status": "NEW"
5072
+ // }
5073
+ //
5074
+ // createOrder: portfolio margin inverse swap and future
5075
+ //
5076
+ // {
5077
+ // "symbol": "ETHUSD_PERP",
5078
+ // "side": "BUY",
5079
+ // "cumBase": "0",
5080
+ // "executedQty": "0",
5081
+ // "orderId": 71275227732,
5082
+ // "avgPrice": "0.00",
5083
+ // "origQty": "1",
5084
+ // "clientOrderId": "x-xcKtGhcuca5af3acfb5044198c5398",
5085
+ // "positionSide": "BOTH",
5086
+ // "cumQty": "0",
5087
+ // "updateTime": 1707110994334,
5088
+ // "type": "LIMIT",
5089
+ // "pair": "ETHUSD",
5090
+ // "reduceOnly": false,
5091
+ // "price": "2000",
5092
+ // "timeInForce": "GTC",
5093
+ // "status": "NEW"
5094
+ // }
5095
+ //
5096
+ // createOrder: portfolio margin linear swap and future conditional
5097
+ //
5098
+ // {
5099
+ // "newClientStrategyId": "x-xcKtGhcu27f109953d6e4dc0974006",
5100
+ // "strategyId": 3645916,
5101
+ // "strategyStatus": "NEW",
5102
+ // "strategyType": "STOP",
5103
+ // "origQty": "0.010",
5104
+ // "price": "35000.00",
5105
+ // "reduceOnly": false,
5106
+ // "side": "BUY",
5107
+ // "positionSide": "BOTH",
5108
+ // "stopPrice": "45000.00",
5109
+ // "symbol": "BTCUSDT",
5110
+ // "timeInForce": "GTC",
5111
+ // "bookTime": 1707112625879,
5112
+ // "updateTime": 1707112625879,
5113
+ // "workingType": "CONTRACT_PRICE",
5114
+ // "priceProtect": false,
5115
+ // "goodTillDate": 0,
5116
+ // "selfTradePreventionMode": "NONE"
5117
+ // }
5118
+ //
5119
+ // createOrder: portfolio margin inverse swap and future conditional
5120
+ //
5121
+ // {
5122
+ // "newClientStrategyId": "x-xcKtGhcuc6b86f053bb34933850739",
5123
+ // "strategyId": 1423462,
5124
+ // "strategyStatus": "NEW",
5125
+ // "strategyType": "STOP",
5126
+ // "origQty": "1",
5127
+ // "price": "2000",
5128
+ // "reduceOnly": false,
5129
+ // "side": "BUY",
5130
+ // "positionSide": "BOTH",
5131
+ // "stopPrice": "3000",
5132
+ // "symbol": "ETHUSD_PERP",
5133
+ // "timeInForce": "GTC",
5134
+ // "bookTime": 1707113098840,
5135
+ // "updateTime": 1707113098840,
5136
+ // "workingType": "CONTRACT_PRICE",
5137
+ // "priceProtect": false
5138
+ // }
5139
+ //
5140
+ // createOrder: portfolio margin spot margin
5141
+ //
5142
+ // {
5143
+ // "clientOrderId": "x-R4BD3S82e9ef29d8346440f0b28b86",
5144
+ // "cummulativeQuoteQty": "0.00000000",
5145
+ // "executedQty": "0.00000000",
5146
+ // "fills": [],
5147
+ // "orderId": 24684460474,
5148
+ // "origQty": "0.00100000",
5149
+ // "price": "35000.00000000",
5150
+ // "selfTradePreventionMode": "EXPIRE_MAKER",
5151
+ // "side": "BUY",
5152
+ // "status": "NEW",
5153
+ // "symbol": "BTCUSDT",
5154
+ // "timeInForce": "GTC",
5155
+ // "transactTime": 1707113538870,
5156
+ // "type": "LIMIT"
5157
+ // }
5005
5158
  //
5006
5159
  const code = this.safeString(order, 'code');
5007
5160
  if (code !== undefined) {
5008
5161
  // cancelOrders/createOrders might have a partial success
5009
5162
  return this.safeOrder({ 'info': order, 'status': 'rejected' }, market);
5010
5163
  }
5011
- const status = this.parseOrderStatus(this.safeString(order, 'status'));
5164
+ const status = this.parseOrderStatus(this.safeString2(order, 'status', 'strategyStatus'));
5012
5165
  const marketId = this.safeString(order, 'symbol');
5013
5166
  const marketType = ('closePosition' in order) ? 'contract' : 'spot';
5014
5167
  const symbol = this.safeSymbol(marketId, market, undefined, marketType);
@@ -5035,11 +5188,9 @@ class binance extends binance$1 {
5035
5188
  // Note this is not the actual cost, since Binance futures uses leverage to calculate margins.
5036
5189
  let cost = this.safeString2(order, 'cummulativeQuoteQty', 'cumQuote');
5037
5190
  cost = this.safeString(order, 'cumBase', cost);
5038
- const id = this.safeString(order, 'orderId');
5039
5191
  let type = this.safeStringLower(order, 'type');
5040
5192
  const side = this.safeStringLower(order, 'side');
5041
5193
  const fills = this.safeValue(order, 'fills', []);
5042
- const clientOrderId = this.safeString(order, 'clientOrderId');
5043
5194
  let timeInForce = this.safeString(order, 'timeInForce');
5044
5195
  if (timeInForce === 'GTX') {
5045
5196
  // GTX means "Good Till Crossing" and is an equivalent way of saying Post Only
@@ -5062,8 +5213,8 @@ class binance extends binance$1 {
5062
5213
  }
5063
5214
  return this.safeOrder({
5064
5215
  'info': order,
5065
- 'id': id,
5066
- 'clientOrderId': clientOrderId,
5216
+ 'id': this.safeString2(order, 'orderId', 'strategyId'),
5217
+ 'clientOrderId': this.safeString2(order, 'clientOrderId', 'newClientStrategyId'),
5067
5218
  'timestamp': timestamp,
5068
5219
  'datetime': this.iso8601(timestamp),
5069
5220
  'lastTradeTimestamp': lastTradeTimestamp,
@@ -5177,50 +5328,105 @@ class binance extends binance$1 {
5177
5328
  * @see https://binance-docs.github.io/apidocs/voptions/en/#new-order-trade
5178
5329
  * @see https://binance-docs.github.io/apidocs/spot/en/#new-order-using-sor-trade
5179
5330
  * @see https://binance-docs.github.io/apidocs/spot/en/#test-new-order-using-sor-trade
5331
+ * @see https://binance-docs.github.io/apidocs/pm/en/#new-um-order-trade
5332
+ * @see https://binance-docs.github.io/apidocs/pm/en/#new-cm-order-trade
5333
+ * @see https://binance-docs.github.io/apidocs/pm/en/#new-margin-order-trade
5334
+ * @see https://binance-docs.github.io/apidocs/pm/en/#new-um-conditional-order-trade
5335
+ * @see https://binance-docs.github.io/apidocs/pm/en/#new-cm-conditional-order-trade
5180
5336
  * @param {string} symbol unified symbol of the market to create an order in
5181
5337
  * @param {string} type 'market' or 'limit' or 'STOP_LOSS' or 'STOP_LOSS_LIMIT' or 'TAKE_PROFIT' or 'TAKE_PROFIT_LIMIT' or 'STOP'
5182
5338
  * @param {string} side 'buy' or 'sell'
5183
- * @param {float} amount how much of currency you want to trade in units of base currency
5184
- * @param {float} [price] the price at which the order is to be fullfilled, in units of the quote currency, ignored in market orders
5339
+ * @param {float} amount how much of you want to trade in units of the base currency
5340
+ * @param {float} [price] the price that the order is to be fullfilled, in units of the quote currency, ignored in market orders
5185
5341
  * @param {object} [params] extra parameters specific to the exchange API endpoint
5342
+ * @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.
5186
5343
  * @param {string} [params.marginMode] 'cross' or 'isolated', for spot margin trading
5187
5344
  * @param {boolean} [params.sor] *spot only* whether to use SOR (Smart Order Routing) or not, default is false
5188
5345
  * @param {boolean} [params.test] *spot only* whether to use the test endpoint or not, default is false
5189
5346
  * @param {float} [params.trailingPercent] the percent to trail away from the current market price
5190
5347
  * @param {float} [params.trailingTriggerPrice] the price to trigger a trailing order, default uses the price argument
5348
+ * @param {float} [params.triggerPrice] the price that a trigger order is triggered at
5349
+ * @param {float} [params.stopLossPrice] the price that a stop loss order is triggered at
5350
+ * @param {float} [params.takeProfitPrice] the price that a take profit order is triggered at
5351
+ * @param {boolean} [params.portfolioMargin] set to true if you would like to create an order in a portfolio margin account
5191
5352
  * @returns {object} an [order structure]{@link https://docs.ccxt.com/#/?id=order-structure}
5192
5353
  */
5193
5354
  await this.loadMarkets();
5194
5355
  const market = this.market(symbol);
5195
5356
  const marketType = this.safeString(params, 'type', market['type']);
5196
- const [marginMode, query] = this.handleMarginModeAndParams('createOrder', params);
5197
- const sor = this.safeValue2(params, 'sor', 'SOR', false);
5198
- params = this.omit(params, 'sor', 'SOR');
5357
+ let marginMode = undefined;
5358
+ [marginMode, params] = this.handleMarginModeAndParams('createOrder', params);
5359
+ let isPortfolioMargin = undefined;
5360
+ [isPortfolioMargin, params] = this.handleOptionAndParams2(params, 'createOrder', 'papi', 'portfolioMargin', false);
5361
+ const triggerPrice = this.safeString2(params, 'triggerPrice', 'stopPrice');
5362
+ const stopLossPrice = this.safeString(params, 'stopLossPrice');
5363
+ const takeProfitPrice = this.safeString(params, 'takeProfitPrice');
5364
+ const trailingPercent = this.safeString2(params, 'trailingPercent', 'callbackRate');
5365
+ const isTrailingPercentOrder = trailingPercent !== undefined;
5366
+ const isStopLoss = stopLossPrice !== undefined;
5367
+ const isTakeProfit = takeProfitPrice !== undefined;
5368
+ const isConditional = (triggerPrice !== undefined) || isTrailingPercentOrder || isStopLoss || isTakeProfit;
5369
+ const sor = this.safeBool2(params, 'sor', 'SOR', false);
5370
+ const test = this.safeBool(params, 'test', false);
5371
+ params = this.omit(params, ['sor', 'SOR', 'test']);
5372
+ if (isPortfolioMargin) {
5373
+ params['portfolioMargin'] = isPortfolioMargin;
5374
+ }
5199
5375
  const request = this.createOrderRequest(symbol, type, side, amount, price, params);
5200
- let method = 'privatePostOrder';
5201
- if (sor) {
5202
- method = 'privatePostSorOrder';
5376
+ let response = undefined;
5377
+ if (market['option']) {
5378
+ response = await this.eapiPrivatePostOrder(request);
5379
+ }
5380
+ else if (sor) {
5381
+ if (test) {
5382
+ response = await this.privatePostSorOrderTest(request);
5383
+ }
5384
+ else {
5385
+ response = await this.privatePostSorOrder(request);
5386
+ }
5203
5387
  }
5204
5388
  else if (market['linear']) {
5205
- method = 'fapiPrivatePostOrder';
5389
+ if (isPortfolioMargin) {
5390
+ if (isConditional) {
5391
+ response = await this.papiPostUmConditionalOrder(request);
5392
+ }
5393
+ else {
5394
+ response = await this.papiPostUmOrder(request);
5395
+ }
5396
+ }
5397
+ else {
5398
+ response = await this.fapiPrivatePostOrder(request);
5399
+ }
5206
5400
  }
5207
5401
  else if (market['inverse']) {
5208
- method = 'dapiPrivatePostOrder';
5402
+ if (isPortfolioMargin) {
5403
+ if (isConditional) {
5404
+ response = await this.papiPostCmConditionalOrder(request);
5405
+ }
5406
+ else {
5407
+ response = await this.papiPostCmOrder(request);
5408
+ }
5409
+ }
5410
+ else {
5411
+ response = await this.dapiPrivatePostOrder(request);
5412
+ }
5209
5413
  }
5210
5414
  else if (marketType === 'margin' || marginMode !== undefined) {
5211
- method = 'sapiPostMarginOrder';
5212
- }
5213
- if (market['option']) {
5214
- method = 'eapiPrivatePostOrder';
5415
+ if (isPortfolioMargin) {
5416
+ response = await this.papiPostMarginOrder(request);
5417
+ }
5418
+ else {
5419
+ response = await this.sapiPostMarginOrder(request);
5420
+ }
5215
5421
  }
5216
- // support for testing orders
5217
- if (market['spot'] || marketType === 'margin') {
5218
- const test = this.safeBool(query, 'test', false);
5422
+ else {
5219
5423
  if (test) {
5220
- method += 'Test';
5424
+ response = await this.privatePostOrderTest(request);
5425
+ }
5426
+ else {
5427
+ response = await this.privatePostOrder(request);
5221
5428
  }
5222
5429
  }
5223
- const response = await this[method](request);
5224
5430
  return this.parseOrder(response, market);
5225
5431
  }
5226
5432
  createOrderRequest(symbol, type, side, amount, price = undefined, params = {}) {
@@ -5228,16 +5434,13 @@ class binance extends binance$1 {
5228
5434
  * @method
5229
5435
  * @ignore
5230
5436
  * @name binance#createOrderRequest
5231
- * @description helper function to build request
5437
+ * @description helper function to build the request
5232
5438
  * @param {string} symbol unified symbol of the market to create an order in
5233
- * @param {string} type 'market' or 'limit' or 'STOP_LOSS' or 'STOP_LOSS_LIMIT' or 'TAKE_PROFIT' or 'TAKE_PROFIT_LIMIT' or 'STOP'
5439
+ * @param {string} type 'market' or 'limit'
5234
5440
  * @param {string} side 'buy' or 'sell'
5235
- * @param {float} amount how much of currency you want to trade in units of base currency
5236
- * @param {float|undefined} price the price at which the order is to be fullfilled, in units of the quote currency, ignored in market orders
5237
- * @param {object} params extra parameters specific to the exchange API endpoint
5238
- * @param {string|undefined} params.marginMode 'cross' or 'isolated', for spot margin trading
5239
- * @param {float} [params.trailingPercent] the percent to trail away from the current market price
5240
- * @param {float} [params.trailingTriggerPrice] the price to trigger a trailing order, default uses the price argument
5441
+ * @param {float} amount how much you want to trade in units of the base currency
5442
+ * @param {float} [price] the price that the order is to be fullfilled, in units of the quote currency, ignored in market orders
5443
+ * @param {object} [params] extra parameters specific to the exchange API endpoint
5241
5444
  * @returns {object} request to be sent to the exchange
5242
5445
  */
5243
5446
  const market = this.market(symbol);
@@ -5246,35 +5449,54 @@ class binance extends binance$1 {
5246
5449
  const initialUppercaseType = type.toUpperCase();
5247
5450
  const isMarketOrder = initialUppercaseType === 'MARKET';
5248
5451
  const isLimitOrder = initialUppercaseType === 'LIMIT';
5249
- const postOnly = this.isPostOnly(isMarketOrder, initialUppercaseType === 'LIMIT_MAKER', params);
5250
- const triggerPrice = this.safeValue2(params, 'triggerPrice', 'stopPrice');
5251
- const stopLossPrice = this.safeValue(params, 'stopLossPrice', triggerPrice); // fallback to stopLoss
5252
- const takeProfitPrice = this.safeValue(params, 'takeProfitPrice');
5253
- const trailingDelta = this.safeValue(params, 'trailingDelta');
5254
- const trailingTriggerPrice = this.safeString2(params, 'trailingTriggerPrice', 'activationPrice', this.numberToString(price));
5255
- const trailingPercent = this.safeString2(params, 'trailingPercent', 'callbackRate');
5256
- const isTrailingPercentOrder = trailingPercent !== undefined;
5257
- const isStopLoss = stopLossPrice !== undefined || trailingDelta !== undefined;
5258
- const isTakeProfit = takeProfitPrice !== undefined;
5259
- params = this.omit(params, ['type', 'newClientOrderId', 'clientOrderId', 'postOnly', 'stopLossPrice', 'takeProfitPrice', 'stopPrice', 'triggerPrice', 'trailingTriggerPrice', 'trailingPercent']);
5260
- const [marginMode, query] = this.handleMarginModeAndParams('createOrder', params);
5261
5452
  const request = {
5262
5453
  'symbol': market['id'],
5263
5454
  'side': side.toUpperCase(),
5264
5455
  };
5265
- if (market['spot'] || marketType === 'margin') {
5266
- // only supported for spot/margin api (all margin markets are spot markets)
5267
- if (postOnly) {
5268
- type = 'LIMIT_MAKER';
5456
+ let isPortfolioMargin = undefined;
5457
+ [isPortfolioMargin, params] = this.handleOptionAndParams2(params, 'createOrder', 'papi', 'portfolioMargin', false);
5458
+ let marginMode = undefined;
5459
+ [marginMode, params] = this.handleMarginModeAndParams('createOrder', params);
5460
+ if ((marketType === 'margin') || (marginMode !== undefined) || market['option']) {
5461
+ // for swap and future reduceOnly is a string that cant be sent with close position set to true or in hedge mode
5462
+ const reduceOnly = this.safeBool(params, 'reduceOnly', false);
5463
+ params = this.omit(params, 'reduceOnly');
5464
+ if (market['option']) {
5465
+ request['reduceOnly'] = reduceOnly;
5466
+ }
5467
+ else {
5468
+ if (reduceOnly) {
5469
+ request['sideEffectType'] = 'AUTO_REPAY';
5470
+ }
5269
5471
  }
5270
5472
  }
5271
- if (marketType === 'margin' || marginMode !== undefined) {
5272
- const reduceOnly = this.safeValue(params, 'reduceOnly');
5273
- if (reduceOnly) {
5274
- request['sideEffectType'] = 'AUTO_REPAY';
5275
- params = this.omit(params, 'reduceOnly');
5473
+ if (!isPortfolioMargin) {
5474
+ const postOnly = this.isPostOnly(isMarketOrder, initialUppercaseType === 'LIMIT_MAKER', params);
5475
+ if (market['spot'] || marketType === 'margin') {
5476
+ // only supported for spot/margin api (all margin markets are spot markets)
5477
+ if (postOnly) {
5478
+ type = 'LIMIT_MAKER';
5479
+ }
5480
+ if (marginMode === 'isolated') {
5481
+ request['isIsolated'] = true;
5482
+ }
5483
+ }
5484
+ if (market['contract'] && postOnly) {
5485
+ request['timeInForce'] = 'GTX';
5276
5486
  }
5277
5487
  }
5488
+ const triggerPrice = this.safeString2(params, 'triggerPrice', 'stopPrice');
5489
+ const stopLossPrice = this.safeString(params, 'stopLossPrice', triggerPrice); // fallback to stopLoss
5490
+ const takeProfitPrice = this.safeString(params, 'takeProfitPrice');
5491
+ const trailingDelta = this.safeString(params, 'trailingDelta');
5492
+ const trailingTriggerPrice = this.safeString2(params, 'trailingTriggerPrice', 'activationPrice', this.numberToString(price));
5493
+ const trailingPercent = this.safeString2(params, 'trailingPercent', 'callbackRate');
5494
+ const isTrailingPercentOrder = trailingPercent !== undefined;
5495
+ const isStopLoss = stopLossPrice !== undefined || trailingDelta !== undefined;
5496
+ const isTakeProfit = takeProfitPrice !== undefined;
5497
+ const isTriggerOrder = triggerPrice !== undefined;
5498
+ const isConditional = isTriggerOrder || isTrailingPercentOrder || isStopLoss || isTakeProfit;
5499
+ const isPortfolioMarginConditional = (isPortfolioMargin && isConditional);
5278
5500
  let uppercaseType = type.toUpperCase();
5279
5501
  let stopPrice = undefined;
5280
5502
  if (isTrailingPercentOrder) {
@@ -5304,24 +5526,14 @@ class binance extends binance$1 {
5304
5526
  uppercaseType = market['contract'] ? 'TAKE_PROFIT' : 'TAKE_PROFIT_LIMIT';
5305
5527
  }
5306
5528
  }
5307
- if (marginMode === 'isolated') {
5308
- request['isIsolated'] = true;
5309
- }
5310
- if (clientOrderId === undefined) {
5311
- const broker = this.safeValue(this.options, 'broker', {});
5312
- const defaultId = (market['contract']) ? 'x-xcKtGhcu' : 'x-R4BD3S82';
5313
- const brokerId = this.safeString(broker, marketType, defaultId);
5314
- request['newClientOrderId'] = brokerId + this.uuid22();
5315
- }
5316
- else {
5317
- request['newClientOrderId'] = clientOrderId;
5318
- }
5319
5529
  if ((marketType === 'spot') || (marketType === 'margin')) {
5320
- request['newOrderRespType'] = this.safeValue(this.options['newOrderRespType'], type, 'RESULT'); // 'ACK' for order id, 'RESULT' for full order or 'FULL' for order with fills
5530
+ request['newOrderRespType'] = this.safeString(this.options['newOrderRespType'], type, 'RESULT'); // 'ACK' for order id, 'RESULT' for full order or 'FULL' for order with fills
5321
5531
  }
5322
5532
  else {
5323
5533
  // swap, futures and options
5324
- request['newOrderRespType'] = 'RESULT'; // "ACK", "RESULT", default "ACK"
5534
+ if (!isPortfolioMargin) {
5535
+ request['newOrderRespType'] = 'RESULT'; // "ACK", "RESULT", default "ACK"
5536
+ }
5325
5537
  }
5326
5538
  if (market['option']) {
5327
5539
  if (type === 'market') {
@@ -5329,7 +5541,7 @@ class binance extends binance$1 {
5329
5541
  }
5330
5542
  }
5331
5543
  else {
5332
- const validOrderTypes = this.safeValue(market['info'], 'orderTypes');
5544
+ const validOrderTypes = this.safeList(market['info'], 'orderTypes');
5333
5545
  if (!this.inArray(uppercaseType, validOrderTypes)) {
5334
5546
  if (initialUppercaseType !== uppercaseType) {
5335
5547
  throw new errors.InvalidOrder(this.id + ' stopPrice parameter is not allowed for ' + symbol + ' ' + type + ' orders');
@@ -5339,7 +5551,18 @@ class binance extends binance$1 {
5339
5551
  }
5340
5552
  }
5341
5553
  }
5342
- request['type'] = uppercaseType;
5554
+ const clientOrderIdRequest = isPortfolioMarginConditional ? 'newClientStrategyId' : 'newClientOrderId';
5555
+ if (clientOrderId === undefined) {
5556
+ const broker = this.safeDict(this.options, 'broker', {});
5557
+ const defaultId = (market['contract']) ? 'x-xcKtGhcu' : 'x-R4BD3S82';
5558
+ const brokerId = this.safeString(broker, marketType, defaultId);
5559
+ request[clientOrderIdRequest] = brokerId + this.uuid22();
5560
+ }
5561
+ else {
5562
+ request[clientOrderIdRequest] = clientOrderId;
5563
+ }
5564
+ const typeRequest = isPortfolioMarginConditional ? 'strategyType' : 'type';
5565
+ request[typeRequest] = uppercaseType;
5343
5566
  // additional required fields depending on the order type
5344
5567
  let timeInForceIsRequired = false;
5345
5568
  let priceIsRequired = false;
@@ -5367,9 +5590,9 @@ class binance extends binance$1 {
5367
5590
  //
5368
5591
  if (uppercaseType === 'MARKET') {
5369
5592
  if (market['spot']) {
5370
- const quoteOrderQty = this.safeValue(this.options, 'quoteOrderQty', true);
5593
+ const quoteOrderQty = this.safeBool(this.options, 'quoteOrderQty', true);
5371
5594
  if (quoteOrderQty) {
5372
- const quoteOrderQtyNew = this.safeValue2(query, 'quoteOrderQty', 'cost');
5595
+ const quoteOrderQtyNew = this.safeString2(params, 'quoteOrderQty', 'cost');
5373
5596
  const precision = market['precision']['price'];
5374
5597
  if (quoteOrderQtyNew !== undefined) {
5375
5598
  request['quoteOrderQty'] = this.decimalToPrecision(quoteOrderQtyNew, number.TRUNCATE, precision, this.precisionMode);
@@ -5420,7 +5643,7 @@ class binance extends binance$1 {
5420
5643
  priceIsRequired = true;
5421
5644
  }
5422
5645
  else if ((uppercaseType === 'STOP_MARKET') || (uppercaseType === 'TAKE_PROFIT_MARKET')) {
5423
- const closePosition = this.safeValue(query, 'closePosition');
5646
+ const closePosition = this.safeBool(params, 'closePosition');
5424
5647
  if (closePosition === undefined) {
5425
5648
  quantityIsRequired = true;
5426
5649
  }
@@ -5433,7 +5656,13 @@ class binance extends binance$1 {
5433
5656
  }
5434
5657
  }
5435
5658
  if (quantityIsRequired) {
5436
- request['quantity'] = this.amountToPrecision(symbol, amount);
5659
+ // portfolio margin has a different amount precision
5660
+ if (isPortfolioMargin) {
5661
+ request['quantity'] = this.parseToNumeric(amount);
5662
+ }
5663
+ else {
5664
+ request['quantity'] = this.amountToPrecision(symbol, amount);
5665
+ }
5437
5666
  }
5438
5667
  if (priceIsRequired) {
5439
5668
  if (price === undefined) {
@@ -5444,9 +5673,6 @@ class binance extends binance$1 {
5444
5673
  if (timeInForceIsRequired) {
5445
5674
  request['timeInForce'] = this.options['defaultTimeInForce']; // 'GTC' = Good To Cancel (default), 'IOC' = Immediate Or Cancel
5446
5675
  }
5447
- if (market['contract'] && postOnly) {
5448
- request['timeInForce'] = 'GTX';
5449
- }
5450
5676
  if (stopPriceIsRequired) {
5451
5677
  if (market['contract']) {
5452
5678
  if (stopPrice === undefined) {
@@ -5463,11 +5689,26 @@ class binance extends binance$1 {
5463
5689
  request['stopPrice'] = this.priceToPrecision(symbol, stopPrice);
5464
5690
  }
5465
5691
  }
5692
+ if (!isPortfolioMargin) {
5693
+ const postOnly = this.isPostOnly(isMarketOrder, initialUppercaseType === 'LIMIT_MAKER', params);
5694
+ if (market['spot'] || marketType === 'margin') {
5695
+ // only supported for spot/margin api (all margin markets are spot markets)
5696
+ if (postOnly) {
5697
+ type = 'LIMIT_MAKER';
5698
+ }
5699
+ if (marginMode === 'isolated') {
5700
+ request['isIsolated'] = true;
5701
+ }
5702
+ }
5703
+ if (market['contract'] && postOnly) {
5704
+ request['timeInForce'] = 'GTX';
5705
+ }
5706
+ }
5466
5707
  // remove timeInForce from params because PO is only used by this.isPostOnly and it's not a valid value for Binance
5467
5708
  if (this.safeString(params, 'timeInForce') === 'PO') {
5468
- params = this.omit(params, ['timeInForce']);
5709
+ params = this.omit(params, 'timeInForce');
5469
5710
  }
5470
- const requestParams = this.omit(params, ['quoteOrderQty', 'cost', 'stopPrice', 'test', 'type', 'newClientOrderId', 'clientOrderId', 'postOnly']);
5711
+ const requestParams = this.omit(params, ['type', 'newClientOrderId', 'clientOrderId', 'postOnly', 'stopLossPrice', 'takeProfitPrice', 'stopPrice', 'triggerPrice', 'trailingTriggerPrice', 'trailingPercent', 'quoteOrderQty', 'cost', 'test']);
5471
5712
  return this.extend(request, requestParams);
5472
5713
  }
5473
5714
  async createMarketOrderWithCost(symbol, side, cost, params = {}) {