ccxt 4.4.61 → 4.4.62

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/dist/cjs/ccxt.js CHANGED
@@ -199,7 +199,7 @@ var xt$1 = require('./src/pro/xt.js');
199
199
 
200
200
  //-----------------------------------------------------------------------------
201
201
  // this is updated by vss.js when building
202
- const version = '4.4.61';
202
+ const version = '4.4.62';
203
203
  Exchange["default"].ccxtVersion = version;
204
204
  const exchanges = {
205
205
  'ace': ace,
@@ -74,6 +74,7 @@ class Exchange {
74
74
  };
75
75
  this.headers = {};
76
76
  this.origin = '*'; // CORS origin
77
+ this.MAX_VALUE = Number.MAX_VALUE;
77
78
  //
78
79
  this.agent = undefined; // maintained for backwards compatibility
79
80
  this.nodeHttpModuleLoaded = false;
@@ -360,18 +361,8 @@ class Exchange {
360
361
  if (this.api) {
361
362
  this.defineRestApi(this.api, 'request');
362
363
  }
363
- // init the request rate limiter
364
- this.initRestRateLimiter();
365
- // init predefined markets if any
366
- if (this.markets) {
367
- this.setMarkets(this.markets);
368
- }
369
364
  this.newUpdates = (this.options.newUpdates !== undefined) ? this.options.newUpdates : true;
370
365
  this.afterConstruct();
371
- const isSandbox = this.safeBool2(this.options, 'sandbox', 'testnet', false);
372
- if (isSandbox) {
373
- this.setSandboxMode(isSandbox);
374
- }
375
366
  }
376
367
  encodeURIComponent(...args) {
377
368
  // @ts-expect-error
@@ -401,22 +392,12 @@ class Exchange {
401
392
  }
402
393
  return result;
403
394
  }
404
- initRestRateLimiter() {
405
- if (this.rateLimit === undefined) {
406
- throw new Error(this.id + '.rateLimit property is not configured');
407
- }
408
- this.tokenBucket = this.extend({
409
- delay: 0.001,
410
- capacity: 1,
411
- cost: 1,
412
- maxCapacity: 1000,
413
- refillRate: (this.rateLimit > 0) ? 1 / this.rateLimit : Number.MAX_VALUE,
414
- }, this.tokenBucket);
415
- this.throttler = new Throttler(this.tokenBucket);
416
- }
417
395
  throttle(cost = undefined) {
418
396
  return this.throttler.throttle(cost);
419
397
  }
398
+ initThrottler() {
399
+ this.throttler = new Throttler(this.tokenBucket);
400
+ }
420
401
  defineRestApiEndpoint(methodName, uppercaseMethod, lowercaseMethod, camelcaseMethod, path, paths, config = {}) {
421
402
  const splitPath = path.split(/[^a-zA-Z0-9]/);
422
403
  const camelcaseSuffix = splitPath.map(this.capitalize).join('');
@@ -2339,8 +2320,39 @@ class Exchange {
2339
2320
  return timestamp;
2340
2321
  }
2341
2322
  afterConstruct() {
2323
+ // networks
2342
2324
  this.createNetworksByIdObject();
2343
2325
  this.featuresGenerator();
2326
+ // init predefined markets if any
2327
+ if (this.markets) {
2328
+ this.setMarkets(this.markets);
2329
+ }
2330
+ // init the request rate limiter
2331
+ this.initRestRateLimiter();
2332
+ // sanbox mode
2333
+ const isSandbox = this.safeBool2(this.options, 'sandbox', 'testnet', false);
2334
+ if (isSandbox) {
2335
+ this.setSandboxMode(isSandbox);
2336
+ }
2337
+ }
2338
+ initRestRateLimiter() {
2339
+ if (this.rateLimit === undefined || (this.id !== undefined && this.rateLimit === -1)) {
2340
+ throw new errors.ExchangeError(this.id + '.rateLimit property is not configured');
2341
+ }
2342
+ let refillRate = this.MAX_VALUE;
2343
+ if (this.rateLimit > 0) {
2344
+ refillRate = 1 / this.rateLimit;
2345
+ }
2346
+ const defaultBucket = {
2347
+ 'delay': 0.001,
2348
+ 'capacity': 1,
2349
+ 'cost': 1,
2350
+ 'maxCapacity': 1000,
2351
+ 'refillRate': refillRate,
2352
+ };
2353
+ const existingBucket = (this.tokenBucket === undefined) ? {} : this.tokenBucket;
2354
+ this.tokenBucket = this.extend(defaultBucket, existingBucket);
2355
+ this.initThrottler();
2344
2356
  }
2345
2357
  featuresGenerator() {
2346
2358
  //
@@ -4611,24 +4623,34 @@ class Exchange {
4611
4623
  * @param {string} [defaultValue] assigned programatically in the method calling handleMarketTypeAndParams
4612
4624
  * @returns {[string, object]} the market type and params with type and defaultType omitted
4613
4625
  */
4614
- const defaultType = this.safeString2(this.options, 'defaultType', 'type', 'spot');
4615
- if (defaultValue === undefined) { // defaultValue takes precendence over exchange wide defaultType
4616
- defaultValue = defaultType;
4626
+ // type from param
4627
+ const type = this.safeString2(params, 'defaultType', 'type');
4628
+ if (type !== undefined) {
4629
+ params = this.omit(params, ['defaultType', 'type']);
4630
+ return [type, params];
4631
+ }
4632
+ // type from market
4633
+ if (market !== undefined) {
4634
+ return [market['type'], params];
4635
+ }
4636
+ // type from default-argument
4637
+ if (defaultValue !== undefined) {
4638
+ return [defaultValue, params];
4617
4639
  }
4618
4640
  const methodOptions = this.safeDict(this.options, methodName);
4619
- let methodType = defaultValue;
4620
- if (methodOptions !== undefined) { // user defined methodType takes precedence over defaultValue
4641
+ if (methodOptions !== undefined) {
4621
4642
  if (typeof methodOptions === 'string') {
4622
- methodType = methodOptions;
4643
+ return [methodOptions, params];
4623
4644
  }
4624
4645
  else {
4625
- methodType = this.safeString2(methodOptions, 'defaultType', 'type', methodType);
4646
+ const typeFromMethod = this.safeString2(methodOptions, 'defaultType', 'type');
4647
+ if (typeFromMethod !== undefined) {
4648
+ return [typeFromMethod, params];
4649
+ }
4626
4650
  }
4627
4651
  }
4628
- const marketType = (market === undefined) ? methodType : market['type'];
4629
- const type = this.safeString2(params, 'defaultType', 'type', marketType);
4630
- params = this.omit(params, ['defaultType', 'type']);
4631
- return [type, params];
4652
+ const defaultType = this.safeString2(this.options, 'defaultType', 'type', 'spot');
4653
+ return [defaultType, params];
4632
4654
  }
4633
4655
  handleSubTypeAndParams(methodName, market = undefined, params = {}, defaultValue = undefined) {
4634
4656
  let subType = undefined;
@@ -4667,14 +4667,13 @@ class bingx extends bingx$1 {
4667
4667
  * @returns {object[]} a list of [order structures]{@link https://docs.ccxt.com/#/?id=order-structure}
4668
4668
  */
4669
4669
  async fetchCanceledAndClosedOrders(symbol = undefined, since = undefined, limit = undefined, params = {}) {
4670
- if (symbol === undefined) {
4671
- throw new errors.ArgumentsRequired(this.id + ' fetchClosedOrders() requires a symbol argument');
4672
- }
4673
4670
  await this.loadMarkets();
4674
- const market = this.market(symbol);
4675
- const request = {
4676
- 'symbol': market['id'],
4677
- };
4671
+ let market = undefined;
4672
+ const request = {};
4673
+ if (symbol !== undefined) {
4674
+ market = this.market(symbol);
4675
+ request['symbol'] = market['id'];
4676
+ }
4678
4677
  let type = undefined;
4679
4678
  let subType = undefined;
4680
4679
  let standard = undefined;
@@ -56,7 +56,9 @@ class bybit extends bybit$1 {
56
56
  'createTrailingAmountOrder': true,
57
57
  'createTriggerOrder': true,
58
58
  'editOrder': true,
59
+ 'editOrders': true,
59
60
  'fetchBalance': true,
61
+ 'fetchBidsAsks': 'emulated',
60
62
  'fetchBorrowInterest': false,
61
63
  'fetchBorrowRateHistories': false,
62
64
  'fetchBorrowRateHistory': false,
@@ -237,6 +239,7 @@ class bybit extends bybit$1 {
237
239
  'v5/spot-lever-token/reference': 5,
238
240
  // spot margin trade
239
241
  'v5/spot-margin-trade/data': 5,
242
+ 'v5/spot-margin-trade/collateral': 5,
240
243
  'v5/spot-cross-margin-trade/data': 5,
241
244
  'v5/spot-cross-margin-trade/pledge-token': 5,
242
245
  'v5/spot-cross-margin-trade/borrow-token': 5,
@@ -1222,6 +1225,9 @@ class bybit extends bybit$1 {
1222
1225
  'fetchOHLCV': {
1223
1226
  'limit': 1000,
1224
1227
  },
1228
+ 'editOrders': {
1229
+ 'max': 10,
1230
+ },
1225
1231
  },
1226
1232
  'spot': {
1227
1233
  'extends': 'default',
@@ -2524,6 +2530,20 @@ class bybit extends bybit$1 {
2524
2530
  const tickerList = this.safeList(result, 'list', []);
2525
2531
  return this.parseTickers(tickerList, parsedSymbols);
2526
2532
  }
2533
+ /**
2534
+ * @method
2535
+ * @name bybit#fetchBidsAsks
2536
+ * @description fetches the bid and ask price and volume for multiple markets
2537
+ * @see https://bybit-exchange.github.io/docs/v5/market/tickers
2538
+ * @param {string[]|undefined} symbols unified symbols of the markets to fetch the bids and asks for, all markets are returned if not assigned
2539
+ * @param {object} [params] extra parameters specific to the exchange API endpoint
2540
+ * @param {string} [params.subType] *contract only* 'linear', 'inverse'
2541
+ * @param {string} [params.baseCoin] *option only* base coin, default is 'BTC'
2542
+ * @returns {object} a dictionary of [ticker structures]{@link https://docs.ccxt.com/#/?id=ticker-structure}
2543
+ */
2544
+ async fetchBidsAsks(symbols = undefined, params = {}) {
2545
+ return await this.fetchTickers(symbols, params);
2546
+ }
2527
2547
  parseOHLCV(ohlcv, market = undefined) {
2528
2548
  //
2529
2549
  // [
@@ -4207,14 +4227,16 @@ class bybit extends bybit$1 {
4207
4227
  const price = this.safeValue(rawOrder, 'price');
4208
4228
  const orderParams = this.safeDict(rawOrder, 'params', {});
4209
4229
  const orderRequest = this.createOrderRequest(marketId, type, side, amount, price, orderParams, isUta);
4230
+ delete orderRequest['category'];
4210
4231
  ordersRequests.push(orderRequest);
4211
4232
  }
4212
4233
  const symbols = this.marketSymbols(orderSymbols, undefined, false, true, true);
4213
4234
  const market = this.market(symbols[0]);
4235
+ const unifiedMarginStatus = this.safeInteger(this.options, 'unifiedMarginStatus', 3);
4214
4236
  let category = undefined;
4215
4237
  [category, params] = this.getBybitType('createOrders', market, params);
4216
- if (category === 'inverse') {
4217
- throw new errors.NotSupported(this.id + ' createOrders does not allow inverse orders');
4238
+ if ((category === 'inverse') && (unifiedMarginStatus < 5)) {
4239
+ throw new errors.NotSupported(this.id + ' createOrders does not allow inverse orders for non UTA2.0 account');
4218
4240
  }
4219
4241
  const request = {
4220
4242
  'category': category,
@@ -4397,6 +4419,95 @@ class bybit extends bybit$1 {
4397
4419
  'id': this.safeString(result, 'orderId'),
4398
4420
  });
4399
4421
  }
4422
+ /**
4423
+ * @method
4424
+ * @name bybit#editOrders
4425
+ * @description edit a list of trade orders
4426
+ * @see https://bybit-exchange.github.io/docs/v5/order/batch-amend
4427
+ * @param {Array} orders list of orders to create, each object should contain the parameters required by createOrder, namely symbol, type, side, amount, price and params
4428
+ * @param {object} [params] extra parameters specific to the exchange API endpoint
4429
+ * @returns {object} an [order structure]{@link https://docs.ccxt.com/#/?id=order-structure}
4430
+ */
4431
+ async editOrders(orders, params = {}) {
4432
+ await this.loadMarkets();
4433
+ const ordersRequests = [];
4434
+ let orderSymbols = [];
4435
+ for (let i = 0; i < orders.length; i++) {
4436
+ const rawOrder = orders[i];
4437
+ const symbol = this.safeString(rawOrder, 'symbol');
4438
+ orderSymbols.push(symbol);
4439
+ const id = this.safeString(rawOrder, 'id');
4440
+ const type = this.safeString(rawOrder, 'type');
4441
+ const side = this.safeString(rawOrder, 'side');
4442
+ const amount = this.safeValue(rawOrder, 'amount');
4443
+ const price = this.safeValue(rawOrder, 'price');
4444
+ const orderParams = this.safeDict(rawOrder, 'params', {});
4445
+ const orderRequest = this.editOrderRequest(id, symbol, type, side, amount, price, orderParams);
4446
+ delete orderRequest['category'];
4447
+ ordersRequests.push(orderRequest);
4448
+ }
4449
+ orderSymbols = this.marketSymbols(orderSymbols, undefined, false, true, true);
4450
+ const market = this.market(orderSymbols[0]);
4451
+ const unifiedMarginStatus = this.safeInteger(this.options, 'unifiedMarginStatus', 3);
4452
+ let category = undefined;
4453
+ [category, params] = this.getBybitType('editOrders', market, params);
4454
+ if ((category === 'inverse') && (unifiedMarginStatus < 5)) {
4455
+ throw new errors.NotSupported(this.id + ' editOrders does not allow inverse orders for non UTA2.0 account');
4456
+ }
4457
+ const request = {
4458
+ 'category': category,
4459
+ 'request': ordersRequests,
4460
+ };
4461
+ const response = await this.privatePostV5OrderAmendBatch(this.extend(request, params));
4462
+ const result = this.safeDict(response, 'result', {});
4463
+ const data = this.safeList(result, 'list', []);
4464
+ const retInfo = this.safeDict(response, 'retExtInfo', {});
4465
+ const codes = this.safeList(retInfo, 'list', []);
4466
+ // extend the error with the unsuccessful orders
4467
+ for (let i = 0; i < codes.length; i++) {
4468
+ const code = codes[i];
4469
+ const retCode = this.safeInteger(code, 'code');
4470
+ if (retCode !== 0) {
4471
+ data[i] = this.extend(data[i], code);
4472
+ }
4473
+ }
4474
+ //
4475
+ // {
4476
+ // "retCode": 0,
4477
+ // "retMsg": "OK",
4478
+ // "result": {
4479
+ // "list": [
4480
+ // {
4481
+ // "category": "option",
4482
+ // "symbol": "ETH-30DEC22-500-C",
4483
+ // "orderId": "b551f227-7059-4fb5-a6a6-699c04dbd2f2",
4484
+ // "orderLinkId": ""
4485
+ // },
4486
+ // {
4487
+ // "category": "option",
4488
+ // "symbol": "ETH-30DEC22-700-C",
4489
+ // "orderId": "fa6a595f-1a57-483f-b9d3-30e9c8235a52",
4490
+ // "orderLinkId": ""
4491
+ // }
4492
+ // ]
4493
+ // },
4494
+ // "retExtInfo": {
4495
+ // "list": [
4496
+ // {
4497
+ // "code": 0,
4498
+ // "msg": "OK"
4499
+ // },
4500
+ // {
4501
+ // "code": 0,
4502
+ // "msg": "OK"
4503
+ // }
4504
+ // ]
4505
+ // },
4506
+ // "time": 1672222808060
4507
+ // }
4508
+ //
4509
+ return this.parseOrders(data);
4510
+ }
4400
4511
  cancelOrderRequest(id, symbol = undefined, params = {}) {
4401
4512
  const market = this.market(symbol);
4402
4513
  const request = {
@@ -976,9 +976,9 @@ class kraken extends kraken$1 {
976
976
  'high': this.safeString(high, 1),
977
977
  'low': this.safeString(low, 1),
978
978
  'bid': this.safeString(bid, 0),
979
- 'bidVolume': undefined,
979
+ 'bidVolume': this.safeString(bid, 2),
980
980
  'ask': this.safeString(ask, 0),
981
- 'askVolume': undefined,
981
+ 'askVolume': this.safeString(ask, 2),
982
982
  'vwap': vwap,
983
983
  'open': this.safeString(ticker, 'o'),
984
984
  'close': last,
@@ -34,6 +34,7 @@ class phemex extends phemex$1 {
34
34
  'cancelAllOrders': true,
35
35
  'cancelOrder': true,
36
36
  'closePosition': false,
37
+ 'createConvertTrade': true,
37
38
  'createOrder': true,
38
39
  'createReduceOnlyOrder': true,
39
40
  'createStopLimitOrder': true,
@@ -44,6 +45,9 @@ class phemex extends phemex$1 {
44
45
  'fetchBorrowRateHistories': false,
45
46
  'fetchBorrowRateHistory': false,
46
47
  'fetchClosedOrders': true,
48
+ 'fetchConvertQuote': true,
49
+ 'fetchConvertTrade': false,
50
+ 'fetchConvertTradeHistory': true,
47
51
  'fetchCrossBorrowRate': false,
48
52
  'fetchCrossBorrowRates': false,
49
53
  'fetchCurrencies': true,
@@ -1067,7 +1071,7 @@ class phemex extends phemex$1 {
1067
1071
  for (let i = 0; i < products.length; i++) {
1068
1072
  let market = products[i];
1069
1073
  const type = this.safeStringLower(market, 'type');
1070
- if ((type === 'perpetual') || (type === 'perpetualv2') || (type === 'PerpetualPilot')) {
1074
+ if ((type === 'perpetual') || (type === 'perpetualv2') || (type === 'perpetualpilot')) {
1071
1075
  const id = this.safeString(market, 'symbol');
1072
1076
  const riskLimitValues = this.safeValue(riskLimitsById, id, {});
1073
1077
  market = this.extend(market, riskLimitValues);
@@ -1258,7 +1262,7 @@ class phemex extends phemex$1 {
1258
1262
  precise.decimals = precise.decimals - scale;
1259
1263
  precise.reduce();
1260
1264
  const preciseString = precise.toString();
1261
- return this.parseToInt(preciseString);
1265
+ return this.parseToNumeric(preciseString);
1262
1266
  }
1263
1267
  toEv(amount, market = undefined) {
1264
1268
  if ((amount === undefined) || (market === undefined)) {
@@ -2625,7 +2629,6 @@ class phemex extends phemex$1 {
2625
2629
  const market = this.market(symbol);
2626
2630
  const requestSide = this.capitalize(side);
2627
2631
  type = this.capitalize(type);
2628
- const reduceOnly = this.safeBool(params, 'reduceOnly');
2629
2632
  const request = {
2630
2633
  // common
2631
2634
  'symbol': market['id'],
@@ -2725,8 +2728,10 @@ class phemex extends phemex$1 {
2725
2728
  let posSide = this.safeStringLower(params, 'posSide');
2726
2729
  if (posSide === undefined) {
2727
2730
  if (hedged) {
2731
+ const reduceOnly = this.safeBool(params, 'reduceOnly');
2728
2732
  if (reduceOnly) {
2729
2733
  side = (side === 'buy') ? 'sell' : 'buy';
2734
+ params = this.omit(params, 'reduceOnly');
2730
2735
  }
2731
2736
  posSide = (side === 'buy') ? 'Long' : 'Short';
2732
2737
  }
@@ -2841,7 +2846,6 @@ class phemex extends phemex$1 {
2841
2846
  }
2842
2847
  params = this.omit(params, 'stopLossPrice');
2843
2848
  }
2844
- params = this.omit(params, 'reduceOnly');
2845
2849
  let response = undefined;
2846
2850
  if (market['settle'] === 'USDT') {
2847
2851
  response = await this.privatePostGOrders(this.extend(request, params));
@@ -5066,6 +5070,224 @@ class phemex extends phemex$1 {
5066
5070
  'datetime': this.iso8601(timestamp),
5067
5071
  }, market);
5068
5072
  }
5073
+ /**
5074
+ * @method
5075
+ * @name phemex#fetchConvertQuote
5076
+ * @description fetch a quote for converting from one currency to another
5077
+ * @see https://phemex-docs.github.io/#rfq-quote
5078
+ * @param {string} fromCode the currency that you want to sell and convert from
5079
+ * @param {string} toCode the currency that you want to buy and convert into
5080
+ * @param {float} amount how much you want to trade in units of the from currency
5081
+ * @param {object} [params] extra parameters specific to the exchange API endpoint
5082
+ * @returns {object} a [conversion structure]{@link https://docs.ccxt.com/#/?id=conversion-structure}
5083
+ */
5084
+ async fetchConvertQuote(fromCode, toCode, amount = undefined, params = {}) {
5085
+ await this.loadMarkets();
5086
+ const fromCurrency = this.currency(fromCode);
5087
+ const toCurrency = this.currency(toCode);
5088
+ const valueScale = this.safeInteger(fromCurrency, 'valueScale');
5089
+ const request = {
5090
+ 'fromCurrency': fromCode,
5091
+ 'toCurrency': toCode,
5092
+ 'fromAmountEv': this.toEn(amount, valueScale),
5093
+ };
5094
+ const response = await this.privateGetAssetsQuote(this.extend(request, params));
5095
+ //
5096
+ // {
5097
+ // "code": 0,
5098
+ // "msg": "OK",
5099
+ // "data": {
5100
+ // "code": "GIF...AAA",
5101
+ // "quoteArgs": {
5102
+ // "origin": 10,
5103
+ // "price": "0.00000939",
5104
+ // "proceeds": "0.00000000",
5105
+ // "ttlMs": 7000,
5106
+ // "expireAt": 1739875826009,
5107
+ // "requestAt": 1739875818009,
5108
+ // "quoteAt": 1739875816594
5109
+ // }
5110
+ // }
5111
+ // }
5112
+ //
5113
+ const data = this.safeDict(response, 'data', {});
5114
+ return this.parseConversion(data, fromCurrency, toCurrency);
5115
+ }
5116
+ /**
5117
+ * @method
5118
+ * @name phemex#createConvertTrade
5119
+ * @description convert from one currency to another
5120
+ * @see https://phemex-docs.github.io/#convert
5121
+ * @param {string} id the id of the trade that you want to make
5122
+ * @param {string} fromCode the currency that you want to sell and convert from
5123
+ * @param {string} toCode the currency that you want to buy and convert into
5124
+ * @param {float} [amount] how much you want to trade in units of the from currency
5125
+ * @param {object} [params] extra parameters specific to the exchange API endpoint
5126
+ * @returns {object} a [conversion structure]{@link https://docs.ccxt.com/#/?id=conversion-structure}
5127
+ */
5128
+ async createConvertTrade(id, fromCode, toCode, amount = undefined, params = {}) {
5129
+ await this.loadMarkets();
5130
+ const fromCurrency = this.currency(fromCode);
5131
+ const toCurrency = this.currency(toCode);
5132
+ const valueScale = this.safeInteger(fromCurrency, 'valueScale');
5133
+ const request = {
5134
+ 'code': id,
5135
+ 'fromCurrency': fromCode,
5136
+ 'toCurrency': toCode,
5137
+ };
5138
+ if (amount !== undefined) {
5139
+ request['fromAmountEv'] = this.toEn(amount, valueScale);
5140
+ }
5141
+ const response = await this.privatePostAssetsConvert(this.extend(request, params));
5142
+ //
5143
+ // {
5144
+ // "code": 0,
5145
+ // "msg": "OK",
5146
+ // "data": {
5147
+ // "moveOp": 0,
5148
+ // "fromCurrency": "USDT",
5149
+ // "toCurrency": "BTC",
5150
+ // "fromAmountEv": 4000000000,
5151
+ // "toAmountEv": 41511,
5152
+ // "linkKey": "45c8ed8e-d3f4-472d-8262-e464e8c46247",
5153
+ // "status": 10
5154
+ // }
5155
+ // }
5156
+ //
5157
+ const data = this.safeDict(response, 'data', {});
5158
+ const fromCurrencyId = this.safeString(data, 'fromCurrency');
5159
+ const fromResult = this.safeCurrency(fromCurrencyId, fromCurrency);
5160
+ const toCurrencyId = this.safeString(data, 'toCurrency');
5161
+ const to = this.safeCurrency(toCurrencyId, toCurrency);
5162
+ return this.parseConversion(data, fromResult, to);
5163
+ }
5164
+ /**
5165
+ * @method
5166
+ * @name phemex#fetchConvertTradeHistory
5167
+ * @description fetch the users history of conversion trades
5168
+ * @see https://phemex-docs.github.io/#query-convert-history
5169
+ * @param {string} [code] the unified currency code
5170
+ * @param {int} [since] the earliest time in ms to fetch conversions for
5171
+ * @param {int} [limit] the maximum number of conversion structures to retrieve, default 20, max 200
5172
+ * @param {object} [params] extra parameters specific to the exchange API endpoint
5173
+ * @param {string} [params.until] the end time in ms
5174
+ * @param {string} [params.fromCurrency] the currency that you sold and converted from
5175
+ * @param {string} [params.toCurrency] the currency that you bought and converted into
5176
+ * @returns {object[]} a list of [conversion structures]{@link https://docs.ccxt.com/#/?id=conversion-structure}
5177
+ */
5178
+ async fetchConvertTradeHistory(code = undefined, since = undefined, limit = undefined, params = {}) {
5179
+ await this.loadMarkets();
5180
+ let request = {};
5181
+ if (code !== undefined) {
5182
+ request['fromCurrency'] = code;
5183
+ }
5184
+ if (since !== undefined) {
5185
+ request['startTime'] = since;
5186
+ }
5187
+ if (limit !== undefined) {
5188
+ request['limit'] = limit;
5189
+ }
5190
+ [request, params] = this.handleUntilOption('endTime', request, params);
5191
+ const response = await this.privateGetAssetsConvert(this.extend(request, params));
5192
+ //
5193
+ // {
5194
+ // "code": 0,
5195
+ // "msg": "OK",
5196
+ // "data": {
5197
+ // "total": 2,
5198
+ // "rows": [
5199
+ // {
5200
+ // "linkKey": "45c8ed8e-d3f4-472d-8262-e464e8c46247",
5201
+ // "createTime": 1739882294000,
5202
+ // "fromCurrency": "USDT",
5203
+ // "toCurrency": "BTC",
5204
+ // "fromAmountEv": 4000000000,
5205
+ // "toAmountEv": 41511,
5206
+ // "status": 10,
5207
+ // "conversionRate": 1037,
5208
+ // "errorCode": 0
5209
+ // },
5210
+ // ]
5211
+ // }
5212
+ // }
5213
+ //
5214
+ const data = this.safeDict(response, 'data', {});
5215
+ const rows = this.safeList(data, 'rows', []);
5216
+ return this.parseConversions(rows, code, 'fromCurrency', 'toCurrency', since, limit);
5217
+ }
5218
+ parseConversion(conversion, fromCurrency = undefined, toCurrency = undefined) {
5219
+ //
5220
+ // fetchConvertQuote
5221
+ //
5222
+ // {
5223
+ // "code": "GIF...AAA",
5224
+ // "quoteArgs": {
5225
+ // "origin": 10,
5226
+ // "price": "0.00000939",
5227
+ // "proceeds": "0.00000000",
5228
+ // "ttlMs": 7000,
5229
+ // "expireAt": 1739875826009,
5230
+ // "requestAt": 1739875818009,
5231
+ // "quoteAt": 1739875816594
5232
+ // }
5233
+ // }
5234
+ //
5235
+ // createConvertTrade
5236
+ //
5237
+ // {
5238
+ // "moveOp": 0,
5239
+ // "fromCurrency": "USDT",
5240
+ // "toCurrency": "BTC",
5241
+ // "fromAmountEv": 4000000000,
5242
+ // "toAmountEv": 41511,
5243
+ // "linkKey": "45c8ed8e-d3f4-472d-8262-e464e8c46247",
5244
+ // "status": 10
5245
+ // }
5246
+ //
5247
+ // fetchConvertTradeHistory
5248
+ //
5249
+ // {
5250
+ // "linkKey": "45c8ed8e-d3f4-472d-8262-e464e8c46247",
5251
+ // "createTime": 1739882294000,
5252
+ // "fromCurrency": "USDT",
5253
+ // "toCurrency": "BTC",
5254
+ // "fromAmountEv": 4000000000,
5255
+ // "toAmountEv": 41511,
5256
+ // "status": 10,
5257
+ // "conversionRate": 1037,
5258
+ // "errorCode": 0
5259
+ // }
5260
+ //
5261
+ const quoteArgs = this.safeDict(conversion, 'quoteArgs', {});
5262
+ const requestTime = this.safeInteger(quoteArgs, 'requestAt');
5263
+ const timestamp = this.safeInteger(conversion, 'createTime', requestTime);
5264
+ const fromCoin = this.safeString(conversion, 'fromCurrency', this.safeString(fromCurrency, 'code'));
5265
+ const fromCode = this.safeCurrencyCode(fromCoin, fromCurrency);
5266
+ const toCoin = this.safeString(conversion, 'toCurrency', this.safeString(toCurrency, 'code'));
5267
+ const toCode = this.safeCurrencyCode(toCoin, toCurrency);
5268
+ const fromValueScale = this.safeInteger(fromCurrency, 'valueScale');
5269
+ const toValueScale = this.safeInteger(toCurrency, 'valueScale');
5270
+ let fromAmount = this.fromEn(this.safeString(conversion, 'fromAmountEv'), fromValueScale);
5271
+ if (fromAmount === undefined && quoteArgs !== undefined) {
5272
+ fromAmount = this.fromEn(this.safeString(quoteArgs, 'origin'), fromValueScale);
5273
+ }
5274
+ let toAmount = this.fromEn(this.safeString(conversion, 'toAmountEv'), toValueScale);
5275
+ if (toAmount === undefined && quoteArgs !== undefined) {
5276
+ toAmount = this.fromEn(this.safeString(quoteArgs, 'proceeds'), toValueScale);
5277
+ }
5278
+ return {
5279
+ 'info': conversion,
5280
+ 'timestamp': timestamp,
5281
+ 'datetime': this.iso8601(timestamp),
5282
+ 'id': this.safeString(conversion, 'code'),
5283
+ 'fromCurrency': fromCode,
5284
+ 'fromAmount': this.parseNumber(fromAmount),
5285
+ 'toCurrency': toCode,
5286
+ 'toAmount': this.parseNumber(toAmount),
5287
+ 'price': this.safeNumber(quoteArgs, 'price'),
5288
+ 'fee': undefined,
5289
+ };
5290
+ }
5069
5291
  handleErrors(httpCode, reason, url, method, headers, body, response, requestHeaders, requestBody) {
5070
5292
  if (response === undefined) {
5071
5293
  return undefined; // fallback to default error handler