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.
package/js/src/binance.js CHANGED
@@ -3146,9 +3146,24 @@ export default class binance extends Exchange {
3146
3146
  let timestamp = undefined;
3147
3147
  const isolated = marginMode === 'isolated';
3148
3148
  const cross = (type === 'margin') || (marginMode === 'cross');
3149
- if (!isolated && ((type === 'spot') || cross)) {
3149
+ if (type === 'papi') {
3150
+ for (let i = 0; i < response.length; i++) {
3151
+ const entry = response[i];
3152
+ const account = this.account();
3153
+ const currencyId = this.safeString(entry, 'asset');
3154
+ const code = this.safeCurrencyCode(currencyId);
3155
+ const borrowed = this.safeString(entry, 'crossMarginBorrowed');
3156
+ const interest = this.safeString(entry, 'crossMarginInterest');
3157
+ account['free'] = this.safeString(entry, 'crossMarginFree');
3158
+ account['used'] = this.safeString(entry, 'crossMarginLocked');
3159
+ account['total'] = this.safeString(entry, 'crossMarginAsset');
3160
+ account['debt'] = Precise.stringAdd(borrowed, interest);
3161
+ result[code] = account;
3162
+ }
3163
+ }
3164
+ else if (!isolated && ((type === 'spot') || cross)) {
3150
3165
  timestamp = this.safeInteger(response, 'updateTime');
3151
- const balances = this.safeValue2(response, 'balances', 'userAssets', []);
3166
+ const balances = this.safeList2(response, 'balances', 'userAssets', []);
3152
3167
  for (let i = 0; i < balances.length; i++) {
3153
3168
  const balance = balances[i];
3154
3169
  const currencyId = this.safeString(balance, 'asset');
@@ -3165,13 +3180,13 @@ export default class binance extends Exchange {
3165
3180
  }
3166
3181
  }
3167
3182
  else if (isolated) {
3168
- const assets = this.safeValue(response, 'assets');
3183
+ const assets = this.safeList(response, 'assets');
3169
3184
  for (let i = 0; i < assets.length; i++) {
3170
3185
  const asset = assets[i];
3171
- const marketId = this.safeValue(asset, 'symbol');
3186
+ const marketId = this.safeString(asset, 'symbol');
3172
3187
  const symbol = this.safeSymbol(marketId, undefined, undefined, 'spot');
3173
- const base = this.safeValue(asset, 'baseAsset', {});
3174
- const quote = this.safeValue(asset, 'quoteAsset', {});
3188
+ const base = this.safeDict(asset, 'baseAsset', {});
3189
+ const quote = this.safeDict(asset, 'quoteAsset', {});
3175
3190
  const baseCode = this.safeCurrencyCode(this.safeString(base, 'asset'));
3176
3191
  const quoteCode = this.safeCurrencyCode(this.safeString(quote, 'asset'));
3177
3192
  const subResult = {};
@@ -3181,7 +3196,7 @@ export default class binance extends Exchange {
3181
3196
  }
3182
3197
  }
3183
3198
  else if (type === 'savings') {
3184
- const positionAmountVos = this.safeValue(response, 'positionAmountVos', []);
3199
+ const positionAmountVos = this.safeList(response, 'positionAmountVos', []);
3185
3200
  for (let i = 0; i < positionAmountVos.length; i++) {
3186
3201
  const entry = positionAmountVos[i];
3187
3202
  const currencyId = this.safeString(entry, 'asset');
@@ -3210,7 +3225,7 @@ export default class binance extends Exchange {
3210
3225
  else {
3211
3226
  let balances = response;
3212
3227
  if (!Array.isArray(response)) {
3213
- balances = this.safeValue(response, 'assets', []);
3228
+ balances = this.safeList(response, 'assets', []);
3214
3229
  }
3215
3230
  for (let i = 0; i < balances.length; i++) {
3216
3231
  const balance = balances[i];
@@ -3240,10 +3255,12 @@ export default class binance extends Exchange {
3240
3255
  * @see https://binance-docs.github.io/apidocs/futures/en/#account-information-v2-user_data // swap
3241
3256
  * @see https://binance-docs.github.io/apidocs/delivery/en/#account-information-user_data // future
3242
3257
  * @see https://binance-docs.github.io/apidocs/voptions/en/#option-account-information-trade // option
3258
+ * @see https://binance-docs.github.io/apidocs/pm/en/#account-balance-user_data // portfolio margin
3243
3259
  * @param {object} [params] extra parameters specific to the exchange API endpoint
3244
- * @param {string} [params.type] 'future', 'delivery', 'savings', 'funding', or 'spot'
3260
+ * @param {string} [params.type] 'future', 'delivery', 'savings', 'funding', or 'spot' or 'papi'
3245
3261
  * @param {string} [params.marginMode] 'cross' or 'isolated', for margin trading, uses this.options.defaultMarginMode if not passed, defaults to undefined/None/null
3246
3262
  * @param {string[]|undefined} [params.symbols] unified market symbols, only used in isolated margin mode
3263
+ * @param {boolean} [params.portfolioMargin] set to true if you would like to fetch the balance for a portfolio margin account
3247
3264
  * @returns {object} a [balance structure]{@link https://docs.ccxt.com/#/?id=balance-structure}
3248
3265
  */
3249
3266
  await this.loadMarkets();
@@ -3251,13 +3268,19 @@ export default class binance extends Exchange {
3251
3268
  let type = this.safeString(params, 'type', defaultType);
3252
3269
  let subType = undefined;
3253
3270
  [subType, params] = this.handleSubTypeAndParams('fetchBalance', undefined, params);
3271
+ let isPortfolioMargin = undefined;
3272
+ [isPortfolioMargin, params] = this.handleOptionAndParams2(params, 'fetchBalance', 'papi', 'portfolioMargin', false);
3254
3273
  let marginMode = undefined;
3255
3274
  let query = undefined;
3256
3275
  [marginMode, query] = this.handleMarginModeAndParams('fetchBalance', params);
3257
3276
  query = this.omit(query, 'type');
3258
3277
  let response = undefined;
3259
3278
  const request = {};
3260
- if (this.isLinear(type, subType)) {
3279
+ if (isPortfolioMargin || (type === 'papi')) {
3280
+ type = 'papi';
3281
+ response = await this.papiGetBalance(this.extend(request, query));
3282
+ }
3283
+ else if (this.isLinear(type, subType)) {
3261
3284
  type = 'linear';
3262
3285
  response = await this.fapiPrivateV2GetAccount(this.extend(request, query));
3263
3286
  }
@@ -3266,7 +3289,7 @@ export default class binance extends Exchange {
3266
3289
  response = await this.dapiPrivateGetAccount(this.extend(request, query));
3267
3290
  }
3268
3291
  else if (marginMode === 'isolated') {
3269
- const paramSymbols = this.safeValue(params, 'symbols');
3292
+ const paramSymbols = this.safeList(params, 'symbols');
3270
3293
  query = this.omit(query, 'symbols');
3271
3294
  if (paramSymbols !== undefined) {
3272
3295
  let symbols = '';
@@ -3482,6 +3505,26 @@ export default class binance extends Exchange {
3482
3505
  // }
3483
3506
  // ]
3484
3507
  //
3508
+ // portfolio margin
3509
+ //
3510
+ // [
3511
+ // {
3512
+ // "asset": "USDT",
3513
+ // "totalWalletBalance": "66.9923261",
3514
+ // "crossMarginAsset": "35.9697141",
3515
+ // "crossMarginBorrowed": "0.0",
3516
+ // "crossMarginFree": "35.9697141",
3517
+ // "crossMarginInterest": "0.0",
3518
+ // "crossMarginLocked": "0.0",
3519
+ // "umWalletBalance": "31.022612",
3520
+ // "umUnrealizedPNL": "0.0",
3521
+ // "cmWalletBalance": "0.0",
3522
+ // "cmUnrealizedPNL": "0.0",
3523
+ // "updateTime": 0,
3524
+ // "negativeBalance": "0.0"
3525
+ // },
3526
+ // ]
3527
+ //
3485
3528
  return this.parseBalanceCustom(response, type, marginMode);
3486
3529
  }
3487
3530
  async fetchOrderBook(symbol, limit = undefined, params = {}) {
@@ -4695,7 +4738,7 @@ export default class binance extends Exchange {
4695
4738
  }
4696
4739
  request['price'] = this.priceToPrecision(symbol, price);
4697
4740
  }
4698
- if (timeInForceIsRequired) {
4741
+ if (timeInForceIsRequired && (this.safeString(params, 'timeInForce') === undefined)) {
4699
4742
  request['timeInForce'] = this.options['defaultTimeInForce']; // 'GTC' = Good To Cancel (default), 'IOC' = Immediate Or Cancel
4700
4743
  }
4701
4744
  if (stopPriceIsRequired) {
@@ -5000,18 +5043,128 @@ export default class binance extends Exchange {
5000
5043
  // "lastTrade": {"id":"69","time":"1676084430567","price":"24.9","qty":"1.00"},
5001
5044
  // "mmp": false
5002
5045
  // }
5003
- // {
5046
+ //
5004
5047
  // cancelOrders/createOrders
5005
- // "code": -4005,
5006
- // "msg": "Quantity greater than max quantity."
5007
- // },
5048
+ //
5049
+ // {
5050
+ // "code": -4005,
5051
+ // "msg": "Quantity greater than max quantity."
5052
+ // }
5053
+ //
5054
+ // createOrder: 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: 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: 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: 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: 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
+ // }
5008
5161
  //
5009
5162
  const code = this.safeString(order, 'code');
5010
5163
  if (code !== undefined) {
5011
5164
  // cancelOrders/createOrders might have a partial success
5012
5165
  return this.safeOrder({ 'info': order, 'status': 'rejected' }, market);
5013
5166
  }
5014
- const status = this.parseOrderStatus(this.safeString(order, 'status'));
5167
+ const status = this.parseOrderStatus(this.safeString2(order, 'status', 'strategyStatus'));
5015
5168
  const marketId = this.safeString(order, 'symbol');
5016
5169
  const marketType = ('closePosition' in order) ? 'contract' : 'spot';
5017
5170
  const symbol = this.safeSymbol(marketId, market, undefined, marketType);
@@ -5038,11 +5191,9 @@ export default class binance extends Exchange {
5038
5191
  // Note this is not the actual cost, since Binance futures uses leverage to calculate margins.
5039
5192
  let cost = this.safeString2(order, 'cummulativeQuoteQty', 'cumQuote');
5040
5193
  cost = this.safeString(order, 'cumBase', cost);
5041
- const id = this.safeString(order, 'orderId');
5042
5194
  let type = this.safeStringLower(order, 'type');
5043
5195
  const side = this.safeStringLower(order, 'side');
5044
5196
  const fills = this.safeValue(order, 'fills', []);
5045
- const clientOrderId = this.safeString(order, 'clientOrderId');
5046
5197
  let timeInForce = this.safeString(order, 'timeInForce');
5047
5198
  if (timeInForce === 'GTX') {
5048
5199
  // GTX means "Good Till Crossing" and is an equivalent way of saying Post Only
@@ -5065,8 +5216,8 @@ export default class binance extends Exchange {
5065
5216
  }
5066
5217
  return this.safeOrder({
5067
5218
  'info': order,
5068
- 'id': id,
5069
- 'clientOrderId': clientOrderId,
5219
+ 'id': this.safeString2(order, 'orderId', 'strategyId'),
5220
+ 'clientOrderId': this.safeString2(order, 'clientOrderId', 'newClientStrategyId'),
5070
5221
  'timestamp': timestamp,
5071
5222
  'datetime': this.iso8601(timestamp),
5072
5223
  'lastTradeTimestamp': lastTradeTimestamp,
@@ -5180,50 +5331,105 @@ export default class binance extends Exchange {
5180
5331
  * @see https://binance-docs.github.io/apidocs/voptions/en/#new-order-trade
5181
5332
  * @see https://binance-docs.github.io/apidocs/spot/en/#new-order-using-sor-trade
5182
5333
  * @see https://binance-docs.github.io/apidocs/spot/en/#test-new-order-using-sor-trade
5334
+ * @see https://binance-docs.github.io/apidocs/pm/en/#new-um-order-trade
5335
+ * @see https://binance-docs.github.io/apidocs/pm/en/#new-cm-order-trade
5336
+ * @see https://binance-docs.github.io/apidocs/pm/en/#new-margin-order-trade
5337
+ * @see https://binance-docs.github.io/apidocs/pm/en/#new-um-conditional-order-trade
5338
+ * @see https://binance-docs.github.io/apidocs/pm/en/#new-cm-conditional-order-trade
5183
5339
  * @param {string} symbol unified symbol of the market to create an order in
5184
5340
  * @param {string} type 'market' or 'limit' or 'STOP_LOSS' or 'STOP_LOSS_LIMIT' or 'TAKE_PROFIT' or 'TAKE_PROFIT_LIMIT' or 'STOP'
5185
5341
  * @param {string} side 'buy' or 'sell'
5186
- * @param {float} amount how much of currency you want to trade in units of base currency
5187
- * @param {float} [price] the price at which the order is to be fullfilled, in units of the quote currency, ignored in market orders
5342
+ * @param {float} amount how much of you want to trade in units of the base currency
5343
+ * @param {float} [price] the price that the order is to be fullfilled, in units of the quote currency, ignored in market orders
5188
5344
  * @param {object} [params] extra parameters specific to the exchange API endpoint
5345
+ * @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.
5189
5346
  * @param {string} [params.marginMode] 'cross' or 'isolated', for spot margin trading
5190
5347
  * @param {boolean} [params.sor] *spot only* whether to use SOR (Smart Order Routing) or not, default is false
5191
5348
  * @param {boolean} [params.test] *spot only* whether to use the test endpoint or not, default is false
5192
5349
  * @param {float} [params.trailingPercent] the percent to trail away from the current market price
5193
5350
  * @param {float} [params.trailingTriggerPrice] the price to trigger a trailing order, default uses the price argument
5351
+ * @param {float} [params.triggerPrice] the price that a trigger order is triggered at
5352
+ * @param {float} [params.stopLossPrice] the price that a stop loss order is triggered at
5353
+ * @param {float} [params.takeProfitPrice] the price that a take profit order is triggered at
5354
+ * @param {boolean} [params.portfolioMargin] set to true if you would like to create an order in a portfolio margin account
5194
5355
  * @returns {object} an [order structure]{@link https://docs.ccxt.com/#/?id=order-structure}
5195
5356
  */
5196
5357
  await this.loadMarkets();
5197
5358
  const market = this.market(symbol);
5198
5359
  const marketType = this.safeString(params, 'type', market['type']);
5199
- const [marginMode, query] = this.handleMarginModeAndParams('createOrder', params);
5200
- const sor = this.safeValue2(params, 'sor', 'SOR', false);
5201
- params = this.omit(params, 'sor', 'SOR');
5360
+ let marginMode = undefined;
5361
+ [marginMode, params] = this.handleMarginModeAndParams('createOrder', params);
5362
+ let isPortfolioMargin = undefined;
5363
+ [isPortfolioMargin, params] = this.handleOptionAndParams2(params, 'createOrder', 'papi', 'portfolioMargin', false);
5364
+ const triggerPrice = this.safeString2(params, 'triggerPrice', 'stopPrice');
5365
+ const stopLossPrice = this.safeString(params, 'stopLossPrice');
5366
+ const takeProfitPrice = this.safeString(params, 'takeProfitPrice');
5367
+ const trailingPercent = this.safeString2(params, 'trailingPercent', 'callbackRate');
5368
+ const isTrailingPercentOrder = trailingPercent !== undefined;
5369
+ const isStopLoss = stopLossPrice !== undefined;
5370
+ const isTakeProfit = takeProfitPrice !== undefined;
5371
+ const isConditional = (triggerPrice !== undefined) || isTrailingPercentOrder || isStopLoss || isTakeProfit;
5372
+ const sor = this.safeBool2(params, 'sor', 'SOR', false);
5373
+ const test = this.safeBool(params, 'test', false);
5374
+ params = this.omit(params, ['sor', 'SOR', 'test']);
5375
+ if (isPortfolioMargin) {
5376
+ params['portfolioMargin'] = isPortfolioMargin;
5377
+ }
5202
5378
  const request = this.createOrderRequest(symbol, type, side, amount, price, params);
5203
- let method = 'privatePostOrder';
5204
- if (sor) {
5205
- method = 'privatePostSorOrder';
5379
+ let response = undefined;
5380
+ if (market['option']) {
5381
+ response = await this.eapiPrivatePostOrder(request);
5382
+ }
5383
+ else if (sor) {
5384
+ if (test) {
5385
+ response = await this.privatePostSorOrderTest(request);
5386
+ }
5387
+ else {
5388
+ response = await this.privatePostSorOrder(request);
5389
+ }
5206
5390
  }
5207
5391
  else if (market['linear']) {
5208
- method = 'fapiPrivatePostOrder';
5392
+ if (isPortfolioMargin) {
5393
+ if (isConditional) {
5394
+ response = await this.papiPostUmConditionalOrder(request);
5395
+ }
5396
+ else {
5397
+ response = await this.papiPostUmOrder(request);
5398
+ }
5399
+ }
5400
+ else {
5401
+ response = await this.fapiPrivatePostOrder(request);
5402
+ }
5209
5403
  }
5210
5404
  else if (market['inverse']) {
5211
- method = 'dapiPrivatePostOrder';
5405
+ if (isPortfolioMargin) {
5406
+ if (isConditional) {
5407
+ response = await this.papiPostCmConditionalOrder(request);
5408
+ }
5409
+ else {
5410
+ response = await this.papiPostCmOrder(request);
5411
+ }
5412
+ }
5413
+ else {
5414
+ response = await this.dapiPrivatePostOrder(request);
5415
+ }
5212
5416
  }
5213
5417
  else if (marketType === 'margin' || marginMode !== undefined) {
5214
- method = 'sapiPostMarginOrder';
5215
- }
5216
- if (market['option']) {
5217
- method = 'eapiPrivatePostOrder';
5418
+ if (isPortfolioMargin) {
5419
+ response = await this.papiPostMarginOrder(request);
5420
+ }
5421
+ else {
5422
+ response = await this.sapiPostMarginOrder(request);
5423
+ }
5218
5424
  }
5219
- // support for testing orders
5220
- if (market['spot'] || marketType === 'margin') {
5221
- const test = this.safeBool(query, 'test', false);
5425
+ else {
5222
5426
  if (test) {
5223
- method += 'Test';
5427
+ response = await this.privatePostOrderTest(request);
5428
+ }
5429
+ else {
5430
+ response = await this.privatePostOrder(request);
5224
5431
  }
5225
5432
  }
5226
- const response = await this[method](request);
5227
5433
  return this.parseOrder(response, market);
5228
5434
  }
5229
5435
  createOrderRequest(symbol, type, side, amount, price = undefined, params = {}) {
@@ -5231,16 +5437,13 @@ export default class binance extends Exchange {
5231
5437
  * @method
5232
5438
  * @ignore
5233
5439
  * @name binance#createOrderRequest
5234
- * @description helper function to build request
5440
+ * @description helper function to build the request
5235
5441
  * @param {string} symbol unified symbol of the market to create an order in
5236
- * @param {string} type 'market' or 'limit' or 'STOP_LOSS' or 'STOP_LOSS_LIMIT' or 'TAKE_PROFIT' or 'TAKE_PROFIT_LIMIT' or 'STOP'
5442
+ * @param {string} type 'market' or 'limit'
5237
5443
  * @param {string} side 'buy' or 'sell'
5238
- * @param {float} amount how much of currency you want to trade in units of base currency
5239
- * @param {float|undefined} price the price at which the order is to be fullfilled, in units of the quote currency, ignored in market orders
5240
- * @param {object} params extra parameters specific to the exchange API endpoint
5241
- * @param {string|undefined} params.marginMode 'cross' or 'isolated', for spot margin trading
5242
- * @param {float} [params.trailingPercent] the percent to trail away from the current market price
5243
- * @param {float} [params.trailingTriggerPrice] the price to trigger a trailing order, default uses the price argument
5444
+ * @param {float} amount how much you want to trade in units of the base currency
5445
+ * @param {float} [price] the price that the order is to be fullfilled, in units of the quote currency, ignored in market orders
5446
+ * @param {object} [params] extra parameters specific to the exchange API endpoint
5244
5447
  * @returns {object} request to be sent to the exchange
5245
5448
  */
5246
5449
  const market = this.market(symbol);
@@ -5249,35 +5452,54 @@ export default class binance extends Exchange {
5249
5452
  const initialUppercaseType = type.toUpperCase();
5250
5453
  const isMarketOrder = initialUppercaseType === 'MARKET';
5251
5454
  const isLimitOrder = initialUppercaseType === 'LIMIT';
5252
- const postOnly = this.isPostOnly(isMarketOrder, initialUppercaseType === 'LIMIT_MAKER', params);
5253
- const triggerPrice = this.safeValue2(params, 'triggerPrice', 'stopPrice');
5254
- const stopLossPrice = this.safeValue(params, 'stopLossPrice', triggerPrice); // fallback to stopLoss
5255
- const takeProfitPrice = this.safeValue(params, 'takeProfitPrice');
5256
- const trailingDelta = this.safeValue(params, 'trailingDelta');
5257
- const trailingTriggerPrice = this.safeString2(params, 'trailingTriggerPrice', 'activationPrice', this.numberToString(price));
5258
- const trailingPercent = this.safeString2(params, 'trailingPercent', 'callbackRate');
5259
- const isTrailingPercentOrder = trailingPercent !== undefined;
5260
- const isStopLoss = stopLossPrice !== undefined || trailingDelta !== undefined;
5261
- const isTakeProfit = takeProfitPrice !== undefined;
5262
- params = this.omit(params, ['type', 'newClientOrderId', 'clientOrderId', 'postOnly', 'stopLossPrice', 'takeProfitPrice', 'stopPrice', 'triggerPrice', 'trailingTriggerPrice', 'trailingPercent']);
5263
- const [marginMode, query] = this.handleMarginModeAndParams('createOrder', params);
5264
5455
  const request = {
5265
5456
  'symbol': market['id'],
5266
5457
  'side': side.toUpperCase(),
5267
5458
  };
5268
- if (market['spot'] || marketType === 'margin') {
5269
- // only supported for spot/margin api (all margin markets are spot markets)
5270
- if (postOnly) {
5271
- type = 'LIMIT_MAKER';
5459
+ let isPortfolioMargin = undefined;
5460
+ [isPortfolioMargin, params] = this.handleOptionAndParams2(params, 'createOrder', 'papi', 'portfolioMargin', false);
5461
+ let marginMode = undefined;
5462
+ [marginMode, params] = this.handleMarginModeAndParams('createOrder', params);
5463
+ if ((marketType === 'margin') || (marginMode !== undefined) || market['option']) {
5464
+ // for swap and future reduceOnly is a string that cant be sent with close position set to true or in hedge mode
5465
+ const reduceOnly = this.safeBool(params, 'reduceOnly', false);
5466
+ params = this.omit(params, 'reduceOnly');
5467
+ if (market['option']) {
5468
+ request['reduceOnly'] = reduceOnly;
5469
+ }
5470
+ else {
5471
+ if (reduceOnly) {
5472
+ request['sideEffectType'] = 'AUTO_REPAY';
5473
+ }
5272
5474
  }
5273
5475
  }
5274
- if (marketType === 'margin' || marginMode !== undefined) {
5275
- const reduceOnly = this.safeValue(params, 'reduceOnly');
5276
- if (reduceOnly) {
5277
- request['sideEffectType'] = 'AUTO_REPAY';
5278
- params = this.omit(params, 'reduceOnly');
5476
+ if (!isPortfolioMargin) {
5477
+ const postOnly = this.isPostOnly(isMarketOrder, initialUppercaseType === 'LIMIT_MAKER', params);
5478
+ if (market['spot'] || marketType === 'margin') {
5479
+ // only supported for spot/margin api (all margin markets are spot markets)
5480
+ if (postOnly) {
5481
+ type = 'LIMIT_MAKER';
5482
+ }
5483
+ if (marginMode === 'isolated') {
5484
+ request['isIsolated'] = true;
5485
+ }
5486
+ }
5487
+ if (market['contract'] && postOnly) {
5488
+ request['timeInForce'] = 'GTX';
5279
5489
  }
5280
5490
  }
5491
+ const triggerPrice = this.safeString2(params, 'triggerPrice', 'stopPrice');
5492
+ const stopLossPrice = this.safeString(params, 'stopLossPrice', triggerPrice); // fallback to stopLoss
5493
+ const takeProfitPrice = this.safeString(params, 'takeProfitPrice');
5494
+ const trailingDelta = this.safeString(params, 'trailingDelta');
5495
+ const trailingTriggerPrice = this.safeString2(params, 'trailingTriggerPrice', 'activationPrice', this.numberToString(price));
5496
+ const trailingPercent = this.safeString2(params, 'trailingPercent', 'callbackRate');
5497
+ const isTrailingPercentOrder = trailingPercent !== undefined;
5498
+ const isStopLoss = stopLossPrice !== undefined || trailingDelta !== undefined;
5499
+ const isTakeProfit = takeProfitPrice !== undefined;
5500
+ const isTriggerOrder = triggerPrice !== undefined;
5501
+ const isConditional = isTriggerOrder || isTrailingPercentOrder || isStopLoss || isTakeProfit;
5502
+ const isPortfolioMarginConditional = (isPortfolioMargin && isConditional);
5281
5503
  let uppercaseType = type.toUpperCase();
5282
5504
  let stopPrice = undefined;
5283
5505
  if (isTrailingPercentOrder) {
@@ -5307,24 +5529,14 @@ export default class binance extends Exchange {
5307
5529
  uppercaseType = market['contract'] ? 'TAKE_PROFIT' : 'TAKE_PROFIT_LIMIT';
5308
5530
  }
5309
5531
  }
5310
- if (marginMode === 'isolated') {
5311
- request['isIsolated'] = true;
5312
- }
5313
- if (clientOrderId === undefined) {
5314
- const broker = this.safeValue(this.options, 'broker', {});
5315
- const defaultId = (market['contract']) ? 'x-xcKtGhcu' : 'x-R4BD3S82';
5316
- const brokerId = this.safeString(broker, marketType, defaultId);
5317
- request['newClientOrderId'] = brokerId + this.uuid22();
5318
- }
5319
- else {
5320
- request['newClientOrderId'] = clientOrderId;
5321
- }
5322
5532
  if ((marketType === 'spot') || (marketType === 'margin')) {
5323
- request['newOrderRespType'] = this.safeValue(this.options['newOrderRespType'], type, 'RESULT'); // 'ACK' for order id, 'RESULT' for full order or 'FULL' for order with fills
5533
+ request['newOrderRespType'] = this.safeString(this.options['newOrderRespType'], type, 'RESULT'); // 'ACK' for order id, 'RESULT' for full order or 'FULL' for order with fills
5324
5534
  }
5325
5535
  else {
5326
5536
  // swap, futures and options
5327
- request['newOrderRespType'] = 'RESULT'; // "ACK", "RESULT", default "ACK"
5537
+ if (!isPortfolioMargin) {
5538
+ request['newOrderRespType'] = 'RESULT'; // "ACK", "RESULT", default "ACK"
5539
+ }
5328
5540
  }
5329
5541
  if (market['option']) {
5330
5542
  if (type === 'market') {
@@ -5332,7 +5544,7 @@ export default class binance extends Exchange {
5332
5544
  }
5333
5545
  }
5334
5546
  else {
5335
- const validOrderTypes = this.safeValue(market['info'], 'orderTypes');
5547
+ const validOrderTypes = this.safeList(market['info'], 'orderTypes');
5336
5548
  if (!this.inArray(uppercaseType, validOrderTypes)) {
5337
5549
  if (initialUppercaseType !== uppercaseType) {
5338
5550
  throw new InvalidOrder(this.id + ' stopPrice parameter is not allowed for ' + symbol + ' ' + type + ' orders');
@@ -5342,7 +5554,18 @@ export default class binance extends Exchange {
5342
5554
  }
5343
5555
  }
5344
5556
  }
5345
- request['type'] = uppercaseType;
5557
+ const clientOrderIdRequest = isPortfolioMarginConditional ? 'newClientStrategyId' : 'newClientOrderId';
5558
+ if (clientOrderId === undefined) {
5559
+ const broker = this.safeDict(this.options, 'broker', {});
5560
+ const defaultId = (market['contract']) ? 'x-xcKtGhcu' : 'x-R4BD3S82';
5561
+ const brokerId = this.safeString(broker, marketType, defaultId);
5562
+ request[clientOrderIdRequest] = brokerId + this.uuid22();
5563
+ }
5564
+ else {
5565
+ request[clientOrderIdRequest] = clientOrderId;
5566
+ }
5567
+ const typeRequest = isPortfolioMarginConditional ? 'strategyType' : 'type';
5568
+ request[typeRequest] = uppercaseType;
5346
5569
  // additional required fields depending on the order type
5347
5570
  let timeInForceIsRequired = false;
5348
5571
  let priceIsRequired = false;
@@ -5370,9 +5593,9 @@ export default class binance extends Exchange {
5370
5593
  //
5371
5594
  if (uppercaseType === 'MARKET') {
5372
5595
  if (market['spot']) {
5373
- const quoteOrderQty = this.safeValue(this.options, 'quoteOrderQty', true);
5596
+ const quoteOrderQty = this.safeBool(this.options, 'quoteOrderQty', true);
5374
5597
  if (quoteOrderQty) {
5375
- const quoteOrderQtyNew = this.safeValue2(query, 'quoteOrderQty', 'cost');
5598
+ const quoteOrderQtyNew = this.safeString2(params, 'quoteOrderQty', 'cost');
5376
5599
  const precision = market['precision']['price'];
5377
5600
  if (quoteOrderQtyNew !== undefined) {
5378
5601
  request['quoteOrderQty'] = this.decimalToPrecision(quoteOrderQtyNew, TRUNCATE, precision, this.precisionMode);
@@ -5423,7 +5646,7 @@ export default class binance extends Exchange {
5423
5646
  priceIsRequired = true;
5424
5647
  }
5425
5648
  else if ((uppercaseType === 'STOP_MARKET') || (uppercaseType === 'TAKE_PROFIT_MARKET')) {
5426
- const closePosition = this.safeValue(query, 'closePosition');
5649
+ const closePosition = this.safeBool(params, 'closePosition');
5427
5650
  if (closePosition === undefined) {
5428
5651
  quantityIsRequired = true;
5429
5652
  }
@@ -5436,7 +5659,13 @@ export default class binance extends Exchange {
5436
5659
  }
5437
5660
  }
5438
5661
  if (quantityIsRequired) {
5439
- request['quantity'] = this.amountToPrecision(symbol, amount);
5662
+ // portfolio margin has a different amount precision
5663
+ if (isPortfolioMargin) {
5664
+ request['quantity'] = this.parseToNumeric(amount);
5665
+ }
5666
+ else {
5667
+ request['quantity'] = this.amountToPrecision(symbol, amount);
5668
+ }
5440
5669
  }
5441
5670
  if (priceIsRequired) {
5442
5671
  if (price === undefined) {
@@ -5447,9 +5676,6 @@ export default class binance extends Exchange {
5447
5676
  if (timeInForceIsRequired) {
5448
5677
  request['timeInForce'] = this.options['defaultTimeInForce']; // 'GTC' = Good To Cancel (default), 'IOC' = Immediate Or Cancel
5449
5678
  }
5450
- if (market['contract'] && postOnly) {
5451
- request['timeInForce'] = 'GTX';
5452
- }
5453
5679
  if (stopPriceIsRequired) {
5454
5680
  if (market['contract']) {
5455
5681
  if (stopPrice === undefined) {
@@ -5466,11 +5692,26 @@ export default class binance extends Exchange {
5466
5692
  request['stopPrice'] = this.priceToPrecision(symbol, stopPrice);
5467
5693
  }
5468
5694
  }
5695
+ if (!isPortfolioMargin) {
5696
+ const postOnly = this.isPostOnly(isMarketOrder, initialUppercaseType === 'LIMIT_MAKER', params);
5697
+ if (market['spot'] || marketType === 'margin') {
5698
+ // only supported for spot/margin api (all margin markets are spot markets)
5699
+ if (postOnly) {
5700
+ type = 'LIMIT_MAKER';
5701
+ }
5702
+ if (marginMode === 'isolated') {
5703
+ request['isIsolated'] = true;
5704
+ }
5705
+ }
5706
+ if (market['contract'] && postOnly) {
5707
+ request['timeInForce'] = 'GTX';
5708
+ }
5709
+ }
5469
5710
  // remove timeInForce from params because PO is only used by this.isPostOnly and it's not a valid value for Binance
5470
5711
  if (this.safeString(params, 'timeInForce') === 'PO') {
5471
- params = this.omit(params, ['timeInForce']);
5712
+ params = this.omit(params, 'timeInForce');
5472
5713
  }
5473
- const requestParams = this.omit(params, ['quoteOrderQty', 'cost', 'stopPrice', 'test', 'type', 'newClientOrderId', 'clientOrderId', 'postOnly']);
5714
+ const requestParams = this.omit(params, ['type', 'newClientOrderId', 'clientOrderId', 'postOnly', 'stopLossPrice', 'takeProfitPrice', 'stopPrice', 'triggerPrice', 'trailingTriggerPrice', 'trailingPercent', 'quoteOrderQty', 'cost', 'test']);
5474
5715
  return this.extend(request, requestParams);
5475
5716
  }
5476
5717
  async createMarketOrderWithCost(symbol, side, cost, params = {}) {