ccxt 4.4.48 → 4.4.49

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.
Files changed (75) hide show
  1. package/README.md +3 -3
  2. package/dist/ccxt.browser.min.js +12 -12
  3. package/dist/cjs/ccxt.js +1 -1
  4. package/dist/cjs/src/bingx.js +88 -30
  5. package/dist/cjs/src/bitget.js +12 -10
  6. package/dist/cjs/src/coinsph.js +18 -9
  7. package/dist/cjs/src/deribit.js +82 -0
  8. package/dist/cjs/src/digifinex.js +131 -11
  9. package/dist/cjs/src/ellipx.js +61 -0
  10. package/dist/cjs/src/exmo.js +58 -0
  11. package/dist/cjs/src/hitbtc.js +99 -0
  12. package/dist/cjs/src/hollaex.js +73 -0
  13. package/dist/cjs/src/huobijp.js +73 -0
  14. package/dist/cjs/src/hyperliquid.js +22 -1
  15. package/dist/cjs/src/idex.js +71 -0
  16. package/dist/cjs/src/independentreserve.js +64 -0
  17. package/dist/cjs/src/indodax.js +61 -0
  18. package/dist/cjs/src/kuna.js +60 -1
  19. package/dist/cjs/src/latoken.js +64 -0
  20. package/dist/cjs/src/lbank.js +70 -0
  21. package/dist/cjs/src/luno.js +73 -0
  22. package/dist/cjs/src/lykke.js +64 -0
  23. package/dist/cjs/src/mercado.js +65 -0
  24. package/dist/cjs/src/mexc.js +1 -1
  25. package/dist/cjs/src/myokx.js +10 -0
  26. package/dist/cjs/src/ndax.js +71 -0
  27. package/dist/cjs/src/novadax.js +74 -0
  28. package/dist/cjs/src/oceanex.js +69 -0
  29. package/dist/cjs/src/okcoin.js +79 -2
  30. package/dist/cjs/src/onetrading.js +66 -0
  31. package/dist/cjs/src/oxfun.js +66 -0
  32. package/dist/cjs/src/p2b.js +63 -1
  33. package/dist/cjs/src/paradex.js +68 -0
  34. package/dist/cjs/src/pro/bitmart.js +6 -0
  35. package/js/ccxt.d.ts +1 -1
  36. package/js/ccxt.js +1 -1
  37. package/js/src/bingx.d.ts +8 -0
  38. package/js/src/bingx.js +88 -30
  39. package/js/src/bitget.d.ts +1 -0
  40. package/js/src/bitget.js +12 -10
  41. package/js/src/coinsph.d.ts +1 -0
  42. package/js/src/coinsph.js +18 -9
  43. package/js/src/deribit.js +82 -0
  44. package/js/src/digifinex.d.ts +1 -0
  45. package/js/src/digifinex.js +131 -11
  46. package/js/src/ellipx.d.ts +1 -0
  47. package/js/src/ellipx.js +61 -0
  48. package/js/src/exmo.js +58 -0
  49. package/js/src/hitbtc.js +99 -0
  50. package/js/src/hollaex.js +73 -0
  51. package/js/src/huobijp.js +73 -0
  52. package/js/src/hyperliquid.d.ts +1 -0
  53. package/js/src/hyperliquid.js +22 -1
  54. package/js/src/idex.js +71 -0
  55. package/js/src/independentreserve.js +64 -0
  56. package/js/src/indodax.js +61 -0
  57. package/js/src/kuna.js +60 -1
  58. package/js/src/latoken.js +64 -0
  59. package/js/src/lbank.js +70 -0
  60. package/js/src/luno.js +73 -0
  61. package/js/src/lykke.js +64 -0
  62. package/js/src/mercado.js +65 -0
  63. package/js/src/mexc.js +1 -1
  64. package/js/src/myokx.js +10 -0
  65. package/js/src/ndax.js +71 -0
  66. package/js/src/novadax.js +74 -0
  67. package/js/src/oceanex.js +69 -0
  68. package/js/src/okcoin.js +79 -2
  69. package/js/src/onetrading.js +66 -0
  70. package/js/src/oxfun.js +66 -0
  71. package/js/src/p2b.js +63 -1
  72. package/js/src/paradex.js +68 -0
  73. package/js/src/pro/bitmart.d.ts +1 -0
  74. package/js/src/pro/bitmart.js +6 -0
  75. package/package.json +2 -2
package/dist/cjs/ccxt.js CHANGED
@@ -202,7 +202,7 @@ var xt$1 = require('./src/pro/xt.js');
202
202
 
203
203
  //-----------------------------------------------------------------------------
204
204
  // this is updated by vss.js when building
205
- const version = '4.4.48';
205
+ const version = '4.4.49';
206
206
  Exchange["default"].ccxtVersion = version;
207
207
  const exchanges = {
208
208
  'ace': ace,
@@ -821,8 +821,8 @@ class bingx extends bingx$1 {
821
821
  // "symbols": [
822
822
  // {
823
823
  // "symbol": "GEAR-USDT",
824
- // "minQty": 735,
825
- // "maxQty": 2941177,
824
+ // "minQty": 735, // deprecated
825
+ // "maxQty": 2941177, // deprecated
826
826
  // "minNotional": 5,
827
827
  // "maxNotional": 20000,
828
828
  // "status": 1,
@@ -946,6 +946,10 @@ class bingx extends bingx$1 {
946
946
  }
947
947
  const isInverse = (spot) ? undefined : checkIsInverse;
948
948
  const isLinear = (spot) ? undefined : checkIsLinear;
949
+ let minAmount = undefined;
950
+ if (!spot) {
951
+ minAmount = this.safeNumber2(market, 'minQty', 'tradeMinQuantity');
952
+ }
949
953
  let timeOnline = this.safeInteger(market, 'timeOnline');
950
954
  if (timeOnline === 0) {
951
955
  timeOnline = undefined;
@@ -987,8 +991,8 @@ class bingx extends bingx$1 {
987
991
  'max': undefined,
988
992
  },
989
993
  'amount': {
990
- 'min': this.safeNumber2(market, 'minQty', 'tradeMinQuantity'),
991
- 'max': this.safeNumber(market, 'maxQty'),
994
+ 'min': minAmount,
995
+ 'max': undefined,
992
996
  },
993
997
  'price': {
994
998
  'min': minTickSize,
@@ -1656,22 +1660,24 @@ class bingx extends bingx$1 {
1656
1660
  // }
1657
1661
  //
1658
1662
  const data = this.safeList(response, 'data', []);
1659
- const rates = [];
1660
- for (let i = 0; i < data.length; i++) {
1661
- const entry = data[i];
1662
- const marketId = this.safeString(entry, 'symbol');
1663
- const symbolInner = this.safeSymbol(marketId, market, '-', 'swap');
1664
- const timestamp = this.safeInteger(entry, 'fundingTime');
1665
- rates.push({
1666
- 'info': entry,
1667
- 'symbol': symbolInner,
1668
- 'fundingRate': this.safeNumber(entry, 'fundingRate'),
1669
- 'timestamp': timestamp,
1670
- 'datetime': this.iso8601(timestamp),
1671
- });
1672
- }
1673
- const sorted = this.sortBy(rates, 'timestamp');
1674
- return this.filterBySymbolSinceLimit(sorted, market['symbol'], since, limit);
1663
+ return this.parseFundingRateHistories(data, market, since, limit);
1664
+ }
1665
+ parseFundingRateHistory(contract, market = undefined) {
1666
+ //
1667
+ // {
1668
+ // "symbol": "BTC-USDT",
1669
+ // "fundingRate": "0.0001",
1670
+ // "fundingTime": 1585684800000
1671
+ // }
1672
+ //
1673
+ const timestamp = this.safeInteger(contract, 'fundingTime');
1674
+ return {
1675
+ 'info': contract,
1676
+ 'symbol': this.safeSymbol(this.safeString(contract, 'symbol'), market, '-', 'swap'),
1677
+ 'fundingRate': this.safeNumber(contract, 'fundingRate'),
1678
+ 'timestamp': timestamp,
1679
+ 'datetime': this.iso8601(timestamp),
1680
+ };
1675
1681
  }
1676
1682
  /**
1677
1683
  * @method
@@ -2343,12 +2349,14 @@ class bingx extends bingx$1 {
2343
2349
  else {
2344
2350
  const linearSwapData = this.safeDict(response, 'data', {});
2345
2351
  const linearSwapBalance = this.safeDict(linearSwapData, 'balance');
2346
- const currencyId = this.safeString(linearSwapBalance, 'asset');
2347
- const code = this.safeCurrencyCode(currencyId);
2348
- const account = this.account();
2349
- account['free'] = this.safeString(linearSwapBalance, 'availableMargin');
2350
- account['used'] = this.safeString(linearSwapBalance, 'usedMargin');
2351
- result[code] = account;
2352
+ if (linearSwapBalance) {
2353
+ const currencyId = this.safeString(linearSwapBalance, 'asset');
2354
+ const code = this.safeCurrencyCode(currencyId);
2355
+ const account = this.account();
2356
+ account['free'] = this.safeString(linearSwapBalance, 'availableMargin');
2357
+ account['used'] = this.safeString(linearSwapBalance, 'usedMargin');
2358
+ result[code] = account;
2359
+ }
2352
2360
  }
2353
2361
  return this.safeBalance(result);
2354
2362
  }
@@ -6452,6 +6460,48 @@ class bingx extends bingx$1 {
6452
6460
  'tierBased': false,
6453
6461
  };
6454
6462
  }
6463
+ customEncode(params) {
6464
+ const sortedParams = this.keysort(params);
6465
+ const keys = Object.keys(sortedParams);
6466
+ let adjustedValue = undefined;
6467
+ let result = undefined;
6468
+ for (let i = 0; i < keys.length; i++) {
6469
+ const key = keys[i];
6470
+ let value = sortedParams[key];
6471
+ if (Array.isArray(value)) {
6472
+ let arrStr = undefined;
6473
+ for (let j = 0; j < value.length; j++) {
6474
+ const arrayElement = value[j];
6475
+ const isString = (typeof arrayElement === 'string');
6476
+ if (isString) {
6477
+ if (j > 0) {
6478
+ arrStr += ',' + '"' + arrayElement.toString() + '"';
6479
+ }
6480
+ else {
6481
+ arrStr = '"' + arrayElement.toString() + '"';
6482
+ }
6483
+ }
6484
+ else {
6485
+ if (j > 0) {
6486
+ arrStr += ',' + arrayElement.toString();
6487
+ }
6488
+ else {
6489
+ arrStr = arrayElement.toString();
6490
+ }
6491
+ }
6492
+ }
6493
+ adjustedValue = '[' + arrStr + ']';
6494
+ value = adjustedValue;
6495
+ }
6496
+ if (i === 0) {
6497
+ result = key + '=' + value;
6498
+ }
6499
+ else {
6500
+ result += '&' + key + '=' + value;
6501
+ }
6502
+ }
6503
+ return result;
6504
+ }
6455
6505
  sign(path, section = 'public', method = 'GET', params = {}, headers = undefined, body = undefined) {
6456
6506
  let type = section[0];
6457
6507
  let version = section[1];
@@ -6487,16 +6537,24 @@ class bingx extends bingx$1 {
6487
6537
  else if (access === 'private') {
6488
6538
  this.checkRequiredCredentials();
6489
6539
  const isJsonContentType = (((type === 'subAccount') || (type === 'account/transfer')) && (method === 'POST'));
6490
- const parsedParams = this.parseParams(params);
6491
- const signature = this.hmac(this.encode(this.rawencode(parsedParams)), this.encode(this.secret), sha256.sha256);
6540
+ let parsedParams = undefined;
6541
+ let encodeRequest = undefined;
6542
+ if (isJsonContentType) {
6543
+ encodeRequest = this.customEncode(params);
6544
+ }
6545
+ else {
6546
+ parsedParams = this.parseParams(params);
6547
+ encodeRequest = this.rawencode(parsedParams);
6548
+ }
6549
+ const signature = this.hmac(this.encode(encodeRequest), this.encode(this.secret), sha256.sha256);
6492
6550
  headers = {
6493
6551
  'X-BX-APIKEY': this.apiKey,
6494
6552
  'X-SOURCE-KEY': this.safeString(this.options, 'broker', 'CCXT'),
6495
6553
  };
6496
6554
  if (isJsonContentType) {
6497
6555
  headers['Content-Type'] = 'application/json';
6498
- parsedParams['signature'] = signature;
6499
- body = this.json(parsedParams);
6556
+ params['signature'] = signature;
6557
+ body = this.json(params);
6500
6558
  }
6501
6559
  else {
6502
6560
  const query = this.urlencode(parsedParams);
@@ -1381,18 +1381,18 @@ class bitget extends bitget$1 {
1381
1381
  '1m': 30,
1382
1382
  '3m': 30,
1383
1383
  '5m': 30,
1384
- '10m': 52,
1384
+ '10m': 30,
1385
1385
  '15m': 52,
1386
- '30m': 52,
1386
+ '30m': 62,
1387
1387
  '1h': 83,
1388
1388
  '2h': 120,
1389
1389
  '4h': 240,
1390
1390
  '6h': 360,
1391
1391
  '12h': 360,
1392
- '1d': 360,
1393
- '3d': 1000,
1394
- '1w': 1000,
1395
- '1M': 1000,
1392
+ '1d': 300,
1393
+ '3d': 300,
1394
+ '1w': 300,
1395
+ '1M': 300,
1396
1396
  },
1397
1397
  },
1398
1398
  'fetchTrades': {
@@ -3554,6 +3554,7 @@ class bitget extends bitget$1 {
3554
3554
  * @param {int} [limit] the maximum amount of candles to fetch
3555
3555
  * @param {object} [params] extra parameters specific to the exchange API endpoint
3556
3556
  * @param {int} [params.until] timestamp in ms of the latest candle to fetch
3557
+ * @param {boolean} [params.useHistoryEndpoint] whether to force to use historical endpoint (it has max limit of 200)
3557
3558
  * @param {boolean} [params.paginate] default false, when true will automatically paginate by calling this endpoint multiple times. See in the docs all the [available parameters](https://github.com/ccxt/ccxt/wiki/Manual#pagination-params)
3558
3559
  * @param {string} [params.price] *swap only* "mark" (to fetch mark price candles) or "index" (to fetch index price candles)
3559
3560
  * @returns {int[][]} A list of candles ordered as timestamp, open, high, low, close, volume
@@ -3566,9 +3567,10 @@ class bitget extends bitget$1 {
3566
3567
  let paginate = false;
3567
3568
  [paginate, params] = this.handleOptionAndParams(params, 'fetchOHLCV', 'paginate');
3568
3569
  if (paginate) {
3569
- return await this.fetchPaginatedCallDeterministic('fetchOHLCV', symbol, since, limit, timeframe, params, maxLimitForHistoryEndpoint);
3570
+ return await this.fetchPaginatedCallDeterministic('fetchOHLCV', symbol, since, limit, timeframe, params, maxLimitForRecentEndpoint);
3570
3571
  }
3571
3572
  const sandboxMode = this.safeBool(this.options, 'sandboxMode', false);
3573
+ const useHistoryEndpoint = this.safeBool(params, 'useHistoryEndpoint', false);
3572
3574
  let market = undefined;
3573
3575
  if (sandboxMode) {
3574
3576
  const sandboxSymbol = this.convertSymbolForSandbox(symbol);
@@ -3598,7 +3600,7 @@ class bitget extends bitget$1 {
3598
3600
  const ohlcOptions = this.safeDict(this.options, 'fetchOHLCV', {});
3599
3601
  const retrievableDaysMap = this.safeDict(ohlcOptions, 'maxDaysPerTimeframe', {});
3600
3602
  const maxRetrievableDaysForRecent = this.safeInteger(retrievableDaysMap, timeframe, 30); // default to safe minimum
3601
- const endpointTsBoundary = now - maxRetrievableDaysForRecent * msInDay;
3603
+ const endpointTsBoundary = now - (maxRetrievableDaysForRecent - 1) * msInDay;
3602
3604
  if (limitDefined) {
3603
3605
  limit = Math.min(limit, maxLimitForRecentEndpoint);
3604
3606
  request['limit'] = limit;
@@ -3637,7 +3639,7 @@ class bitget extends bitget$1 {
3637
3639
  // make request
3638
3640
  if (market['spot']) {
3639
3641
  // checks if we need history endpoint
3640
- if (historicalEndpointNeeded) {
3642
+ if (historicalEndpointNeeded || useHistoryEndpoint) {
3641
3643
  response = await this.publicSpotGetV2SpotMarketHistoryCandles(this.extend(request, params));
3642
3644
  }
3643
3645
  else {
@@ -3669,7 +3671,7 @@ class bitget extends bitget$1 {
3669
3671
  response = await this.publicMixGetV2MixMarketHistoryIndexCandles(extended);
3670
3672
  }
3671
3673
  else {
3672
- if (historicalEndpointNeeded) {
3674
+ if (historicalEndpointNeeded || useHistoryEndpoint) {
3673
3675
  response = await this.publicMixGetV2MixMarketHistoryCandles(extended);
3674
3676
  }
3675
3677
  else {
@@ -874,33 +874,42 @@ class coinsph extends coinsph$1 {
874
874
  * @param {int} [since] timestamp in ms of the earliest candle to fetch
875
875
  * @param {int} [limit] the maximum amount of candles to fetch (default 500, max 1000)
876
876
  * @param {object} [params] extra parameters specific to the exchange API endpoint
877
+ * @param {int} [params.until] timestamp in ms of the latest candle to fetch
877
878
  * @returns {int[][]} A list of candles ordered as timestamp, open, high, low, close, volume
878
879
  */
879
880
  async fetchOHLCV(symbol, timeframe = '1m', since = undefined, limit = undefined, params = {}) {
880
881
  await this.loadMarkets();
881
882
  const market = this.market(symbol);
882
883
  const interval = this.safeString(this.timeframes, timeframe);
884
+ const until = this.safeInteger(params, 'until');
883
885
  const request = {
884
886
  'symbol': market['id'],
885
887
  'interval': interval,
886
888
  };
889
+ if (limit === undefined) {
890
+ limit = 1000;
891
+ }
887
892
  if (since !== undefined) {
888
893
  request['startTime'] = since;
889
- request['limit'] = 1000;
890
894
  // since work properly only when it is "younger" than last "limit" candle
891
- if (limit !== undefined) {
892
- const duration = this.parseTimeframe(timeframe) * 1000;
893
- request['endTime'] = this.sum(since, duration * (limit - 1));
895
+ if (until !== undefined) {
896
+ request['endTime'] = until;
894
897
  }
895
898
  else {
896
- request['endTime'] = this.milliseconds();
899
+ const duration = this.parseTimeframe(timeframe) * 1000;
900
+ const endTimeByLimit = this.sum(since, duration * (limit - 1));
901
+ const now = this.milliseconds();
902
+ request['endTime'] = Math.min(endTimeByLimit, now);
897
903
  }
898
904
  }
899
- else {
900
- if (limit !== undefined) {
901
- request['limit'] = limit;
902
- }
905
+ else if (until !== undefined) {
906
+ request['endTime'] = until;
907
+ // since work properly only when it is "younger" than last "limit" candle
908
+ const duration = this.parseTimeframe(timeframe) * 1000;
909
+ request['startTime'] = until - (duration * (limit - 1));
903
910
  }
911
+ request['limit'] = limit;
912
+ params = this.omit(params, 'until');
904
913
  const response = await this.publicGetOpenapiQuoteV1Klines(this.extend(request, params));
905
914
  //
906
915
  // [
@@ -278,6 +278,88 @@ class deribit extends deribit$1 {
278
278
  },
279
279
  },
280
280
  },
281
+ 'features': {
282
+ 'default': {
283
+ 'sandbox': true,
284
+ 'createOrder': {
285
+ 'marginMode': false,
286
+ 'triggerPrice': true,
287
+ // todo implement
288
+ 'triggerPriceType': {
289
+ 'last': true,
290
+ 'mark': true,
291
+ 'index': true,
292
+ },
293
+ 'triggerDirection': false,
294
+ 'stopLossPrice': false,
295
+ 'takeProfitPrice': false,
296
+ 'attachedStopLossTakeProfit': undefined,
297
+ 'timeInForce': {
298
+ 'IOC': true,
299
+ 'FOK': true,
300
+ 'PO': true,
301
+ 'GTD': true,
302
+ },
303
+ 'hedged': false,
304
+ 'selfTradePrevention': false,
305
+ 'trailing': true,
306
+ 'leverage': false,
307
+ 'marketBuyByCost': true,
308
+ 'marketBuyRequiresPrice': false,
309
+ 'iceberg': true, // todo
310
+ },
311
+ 'createOrders': undefined,
312
+ 'fetchMyTrades': {
313
+ 'marginMode': false,
314
+ 'limit': 100,
315
+ 'daysBack': 100000,
316
+ 'untilDays': 100000,
317
+ },
318
+ 'fetchOrder': {
319
+ 'marginMode': false,
320
+ 'trigger': false,
321
+ 'trailing': false,
322
+ },
323
+ 'fetchOpenOrders': {
324
+ 'marginMode': false,
325
+ 'limit': undefined,
326
+ 'trigger': false,
327
+ 'trailing': false,
328
+ },
329
+ 'fetchOrders': undefined,
330
+ 'fetchClosedOrders': {
331
+ 'marginMode': false,
332
+ 'limit': 100,
333
+ 'daysBack': 100000,
334
+ 'daysBackCanceled': 1,
335
+ 'untilDays': 100000,
336
+ 'trigger': false,
337
+ 'trailing': false,
338
+ },
339
+ 'fetchOHLCV': {
340
+ 'limit': 1000, // todo: recheck
341
+ },
342
+ },
343
+ 'spot': {
344
+ 'extends': 'default',
345
+ },
346
+ 'swap': {
347
+ 'linear': {
348
+ 'extends': 'default',
349
+ },
350
+ 'inverse': {
351
+ 'extends': 'default',
352
+ },
353
+ },
354
+ 'future': {
355
+ 'linear': {
356
+ 'extends': 'default',
357
+ },
358
+ 'inverse': {
359
+ 'extends': 'default',
360
+ },
361
+ },
362
+ },
281
363
  'exceptions': {
282
364
  // 0 or absent Success, No error.
283
365
  '9999': errors.PermissionDenied,
@@ -234,6 +234,109 @@ class digifinex extends digifinex$1 {
234
234
  },
235
235
  },
236
236
  },
237
+ 'features': {
238
+ 'default': {
239
+ 'sandbox': false,
240
+ 'createOrder': {
241
+ 'marginMode': true,
242
+ 'triggerPrice': false,
243
+ 'triggerPriceType': undefined,
244
+ 'triggerDirection': false,
245
+ 'stopLossPrice': false,
246
+ 'takeProfitPrice': false,
247
+ 'attachedStopLossTakeProfit': undefined,
248
+ 'timeInForce': {
249
+ 'IOC': false,
250
+ 'FOK': false,
251
+ 'PO': true,
252
+ 'GTD': false,
253
+ },
254
+ 'hedged': false,
255
+ 'selfTradePrevention': false,
256
+ 'trailing': false,
257
+ 'leverage': false,
258
+ 'marketBuyByCost': false,
259
+ 'marketBuyRequiresPrice': false,
260
+ 'iceberg': false,
261
+ },
262
+ 'createOrders': {
263
+ 'max': 10,
264
+ 'marginMode': true,
265
+ },
266
+ 'fetchMyTrades': {
267
+ 'marginMode': true,
268
+ 'limit': 500,
269
+ 'daysBack': 100000,
270
+ 'untilDays': 30,
271
+ },
272
+ 'fetchOrder': {
273
+ 'marginMode': true,
274
+ 'trigger': false,
275
+ 'trailing': false,
276
+ 'marketType': true,
277
+ },
278
+ 'fetchOpenOrders': {
279
+ 'marginMode': true,
280
+ 'limit': undefined,
281
+ 'trigger': false,
282
+ 'trailing': false,
283
+ },
284
+ 'fetchOrders': {
285
+ 'marginMode': true,
286
+ 'limit': 100,
287
+ 'daysBack': 100000,
288
+ 'untilDays': 30,
289
+ 'trigger': false,
290
+ 'trailing': false,
291
+ },
292
+ 'fetchClosedOrders': undefined,
293
+ 'fetchOHLCV': {
294
+ 'limit': 500,
295
+ },
296
+ },
297
+ 'spot': {
298
+ 'extends': 'default',
299
+ },
300
+ 'forDerivatives': {
301
+ 'extends': 'default',
302
+ 'createOrders': {
303
+ 'max': 20,
304
+ 'marginMode': false,
305
+ },
306
+ 'fetchMyTrades': {
307
+ 'marginMode': false,
308
+ 'limit': 100,
309
+ 'daysBack': 100000,
310
+ 'untilDays': 100000, // todo
311
+ },
312
+ 'fetchOrder': {
313
+ 'marginMode': false,
314
+ },
315
+ 'fetchOpenOrders': {
316
+ 'marginMode': false,
317
+ 'limit': 100,
318
+ },
319
+ 'fetchOrders': {
320
+ 'marginMode': false,
321
+ 'daysBack': 100000, // todo
322
+ },
323
+ 'fetchOHLCV': {
324
+ 'limit': 100,
325
+ },
326
+ },
327
+ 'swap': {
328
+ 'linear': {
329
+ 'extends': 'forDerivatives',
330
+ },
331
+ 'inverse': {
332
+ 'extends': 'forDerivatives',
333
+ },
334
+ },
335
+ 'future': {
336
+ 'linear': undefined,
337
+ 'inverse': undefined,
338
+ },
339
+ },
237
340
  'fees': {
238
341
  'trading': {
239
342
  'tierBased': true,
@@ -1501,6 +1604,7 @@ class digifinex extends digifinex$1 {
1501
1604
  * @param {int} [since] timestamp in ms of the earliest candle to fetch
1502
1605
  * @param {int} [limit] the maximum amount of candles to fetch
1503
1606
  * @param {object} [params] extra parameters specific to the exchange API endpoint
1607
+ * @param {int} [params.until] timestamp in ms of the latest candle to fetch
1504
1608
  * @returns {int[][]} A list of candles ordered as timestamp, open, high, low, close, volume
1505
1609
  */
1506
1610
  async fetchOHLCV(symbol, timeframe = '1m', since = undefined, limit = undefined, params = {}) {
@@ -1517,22 +1621,38 @@ class digifinex extends digifinex$1 {
1517
1621
  response = await this.publicSwapGetPublicCandles(this.extend(request, params));
1518
1622
  }
1519
1623
  else {
1624
+ const until = this.safeInteger(params, 'until');
1520
1625
  request['symbol'] = market['id'];
1521
1626
  request['period'] = this.safeString(this.timeframes, timeframe, timeframe);
1522
- if (since !== undefined) {
1523
- const startTime = this.parseToInt(since / 1000);
1524
- request['start_time'] = startTime;
1525
- if (limit !== undefined) {
1526
- const duration = this.parseTimeframe(timeframe);
1527
- request['end_time'] = this.sum(startTime, limit * duration);
1627
+ let startTime = since;
1628
+ const duration = this.parseTimeframe(timeframe);
1629
+ if (startTime === undefined) {
1630
+ if ((limit !== undefined) || (until !== undefined)) {
1631
+ const endTime = (until !== undefined) ? until : this.milliseconds();
1632
+ const startLimit = (limit !== undefined) ? limit : 200;
1633
+ startTime = endTime - (startLimit * duration * 1000);
1528
1634
  }
1529
1635
  }
1530
- else if (limit !== undefined) {
1531
- const endTime = this.seconds();
1532
- const duration = this.parseTimeframe(timeframe);
1533
- const auxLimit = limit; // in c# -limit is mutating the arg
1534
- request['start_time'] = this.sum(endTime, -auxLimit * duration);
1636
+ if (startTime !== undefined) {
1637
+ startTime = this.parseToInt(startTime / 1000);
1638
+ request['start_time'] = startTime;
1639
+ if ((limit !== undefined) || (until !== undefined)) {
1640
+ if (until !== undefined) {
1641
+ const endByUntil = this.parseToInt(until / 1000);
1642
+ if (limit !== undefined) {
1643
+ const endByLimit = this.sum(startTime, limit * duration);
1644
+ request['end_time'] = Math.min(endByLimit, endByUntil);
1645
+ }
1646
+ else {
1647
+ request['end_time'] = endByUntil;
1648
+ }
1649
+ }
1650
+ else {
1651
+ request['end_time'] = this.sum(startTime, limit * duration);
1652
+ }
1653
+ }
1535
1654
  }
1655
+ params = this.omit(params, 'until');
1536
1656
  response = await this.publicSpotGetKline(this.extend(request, params));
1537
1657
  }
1538
1658
  //
@@ -230,6 +230,66 @@ class ellipx extends ellipx$1 {
230
230
  'ETH': 'Ethereum',
231
231
  },
232
232
  },
233
+ 'features': {
234
+ 'spot': {
235
+ 'sandbox': false,
236
+ 'createOrder': {
237
+ 'marginMode': false,
238
+ 'triggerPrice': false,
239
+ 'triggerPriceType': undefined,
240
+ 'triggerDirection': false,
241
+ 'stopLossPrice': false,
242
+ 'takeProfitPrice': false,
243
+ 'attachedStopLossTakeProfit': undefined,
244
+ 'timeInForce': {
245
+ 'IOC': false,
246
+ 'FOK': false,
247
+ 'PO': false,
248
+ 'GTD': false,
249
+ },
250
+ 'hedged': false,
251
+ 'selfTradePrevention': false,
252
+ 'trailing': false,
253
+ 'leverage': false,
254
+ 'marketBuyByCost': true,
255
+ 'marketBuyRequiresPrice': false,
256
+ 'iceberg': false,
257
+ },
258
+ 'createOrders': undefined,
259
+ 'fetchMyTrades': undefined,
260
+ 'fetchOrder': {
261
+ 'marginMode': false,
262
+ 'trigger': false,
263
+ 'trailing': false,
264
+ },
265
+ 'fetchOpenOrders': {
266
+ 'marginMode': false,
267
+ 'limit': undefined,
268
+ 'trigger': false,
269
+ 'trailing': false,
270
+ },
271
+ 'fetchOrders': {
272
+ 'marginMode': false,
273
+ 'limit': undefined,
274
+ 'daysBack': undefined,
275
+ 'untilDays': undefined,
276
+ 'trigger': false,
277
+ 'trailing': false,
278
+ },
279
+ 'fetchClosedOrders': undefined,
280
+ 'fetchOHLCV': {
281
+ 'limit': 100,
282
+ },
283
+ },
284
+ 'swap': {
285
+ 'linear': undefined,
286
+ 'inverse': undefined,
287
+ },
288
+ 'future': {
289
+ 'linear': undefined,
290
+ 'inverse': undefined,
291
+ },
292
+ },
233
293
  'commonCurrencies': {},
234
294
  'exceptions': {
235
295
  'exact': {
@@ -664,6 +724,7 @@ class ellipx extends ellipx$1 {
664
724
  * @param {int} [since] timestamp in ms of the earliest candle to fetch
665
725
  * @param {int} [limit] the maximum amount of candles to fetch
666
726
  * @param {object} [params] extra parameters specific to the API endpoint
727
+ * @param {int} [params.until] timestamp in ms of the earliest candle to fetch
667
728
  * @returns {OHLCV[]} A list of candles ordered as timestamp, open, high, low, close, volume
668
729
  */
669
730
  async fetchOHLCV(symbol, timeframe = '1m', since = undefined, limit = undefined, params = {}) {