ccxt 4.4.48__py2.py3-none-any.whl → 4.4.49__py2.py3-none-any.whl

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 (71) hide show
  1. ccxt/__init__.py +1 -1
  2. ccxt/async_support/__init__.py +1 -1
  3. ccxt/async_support/base/exchange.py +1 -1
  4. ccxt/async_support/bingx.py +73 -29
  5. ccxt/async_support/bitget.py +12 -10
  6. ccxt/async_support/coinsph.py +17 -8
  7. ccxt/async_support/deribit.py +82 -0
  8. ccxt/async_support/digifinex.py +125 -10
  9. ccxt/async_support/ellipx.py +61 -0
  10. ccxt/async_support/exmo.py +58 -0
  11. ccxt/async_support/hitbtc.py +99 -0
  12. ccxt/async_support/hollaex.py +73 -0
  13. ccxt/async_support/huobijp.py +73 -0
  14. ccxt/async_support/hyperliquid.py +22 -1
  15. ccxt/async_support/idex.py +71 -0
  16. ccxt/async_support/independentreserve.py +64 -0
  17. ccxt/async_support/indodax.py +61 -0
  18. ccxt/async_support/kuna.py +60 -1
  19. ccxt/async_support/latoken.py +64 -0
  20. ccxt/async_support/lbank.py +70 -0
  21. ccxt/async_support/luno.py +73 -0
  22. ccxt/async_support/lykke.py +64 -0
  23. ccxt/async_support/mercado.py +65 -0
  24. ccxt/async_support/mexc.py +1 -1
  25. ccxt/async_support/myokx.py +10 -0
  26. ccxt/async_support/ndax.py +71 -0
  27. ccxt/async_support/novadax.py +74 -0
  28. ccxt/async_support/oceanex.py +69 -0
  29. ccxt/async_support/okcoin.py +79 -2
  30. ccxt/async_support/onetrading.py +66 -0
  31. ccxt/async_support/oxfun.py +66 -0
  32. ccxt/async_support/p2b.py +63 -1
  33. ccxt/async_support/paradex.py +68 -0
  34. ccxt/base/exchange.py +1 -1
  35. ccxt/bingx.py +73 -29
  36. ccxt/bitget.py +12 -10
  37. ccxt/coinsph.py +17 -8
  38. ccxt/deribit.py +82 -0
  39. ccxt/digifinex.py +125 -10
  40. ccxt/ellipx.py +61 -0
  41. ccxt/exmo.py +58 -0
  42. ccxt/hitbtc.py +99 -0
  43. ccxt/hollaex.py +73 -0
  44. ccxt/huobijp.py +73 -0
  45. ccxt/hyperliquid.py +22 -1
  46. ccxt/idex.py +71 -0
  47. ccxt/independentreserve.py +64 -0
  48. ccxt/indodax.py +61 -0
  49. ccxt/kuna.py +60 -1
  50. ccxt/latoken.py +64 -0
  51. ccxt/lbank.py +70 -0
  52. ccxt/luno.py +73 -0
  53. ccxt/lykke.py +64 -0
  54. ccxt/mercado.py +65 -0
  55. ccxt/mexc.py +1 -1
  56. ccxt/myokx.py +10 -0
  57. ccxt/ndax.py +71 -0
  58. ccxt/novadax.py +74 -0
  59. ccxt/oceanex.py +69 -0
  60. ccxt/okcoin.py +79 -2
  61. ccxt/onetrading.py +66 -0
  62. ccxt/oxfun.py +66 -0
  63. ccxt/p2b.py +63 -1
  64. ccxt/paradex.py +68 -0
  65. ccxt/pro/__init__.py +1 -1
  66. ccxt/pro/bitmart.py +5 -0
  67. {ccxt-4.4.48.dist-info → ccxt-4.4.49.dist-info}/METADATA +4 -4
  68. {ccxt-4.4.48.dist-info → ccxt-4.4.49.dist-info}/RECORD +71 -71
  69. {ccxt-4.4.48.dist-info → ccxt-4.4.49.dist-info}/LICENSE.txt +0 -0
  70. {ccxt-4.4.48.dist-info → ccxt-4.4.49.dist-info}/WHEEL +0 -0
  71. {ccxt-4.4.48.dist-info → ccxt-4.4.49.dist-info}/top_level.txt +0 -0
ccxt/__init__.py CHANGED
@@ -22,7 +22,7 @@
22
22
 
23
23
  # ----------------------------------------------------------------------------
24
24
 
25
- __version__ = '4.4.48'
25
+ __version__ = '4.4.49'
26
26
 
27
27
  # ----------------------------------------------------------------------------
28
28
 
@@ -4,7 +4,7 @@
4
4
 
5
5
  # -----------------------------------------------------------------------------
6
6
 
7
- __version__ = '4.4.48'
7
+ __version__ = '4.4.49'
8
8
 
9
9
  # -----------------------------------------------------------------------------
10
10
 
@@ -2,7 +2,7 @@
2
2
 
3
3
  # -----------------------------------------------------------------------------
4
4
 
5
- __version__ = '4.4.48'
5
+ __version__ = '4.4.49'
6
6
 
7
7
  # -----------------------------------------------------------------------------
8
8
 
@@ -830,8 +830,8 @@ class bingx(Exchange, ImplicitAPI):
830
830
  # "symbols": [
831
831
  # {
832
832
  # "symbol": "GEAR-USDT",
833
- # "minQty": 735,
834
- # "maxQty": 2941177,
833
+ # "minQty": 735, # deprecated
834
+ # "maxQty": 2941177, # deprecated
835
835
  # "minNotional": 5,
836
836
  # "maxNotional": 20000,
837
837
  # "status": 1,
@@ -949,6 +949,9 @@ class bingx(Exchange, ImplicitAPI):
949
949
  isActive = True # spot active
950
950
  isInverse = None if (spot) else checkIsInverse
951
951
  isLinear = None if (spot) else checkIsLinear
952
+ minAmount = None
953
+ if not spot:
954
+ minAmount = self.safe_number_2(market, 'minQty', 'tradeMinQuantity')
952
955
  timeOnline = self.safe_integer(market, 'timeOnline')
953
956
  if timeOnline == 0:
954
957
  timeOnline = None
@@ -989,8 +992,8 @@ class bingx(Exchange, ImplicitAPI):
989
992
  'max': None,
990
993
  },
991
994
  'amount': {
992
- 'min': self.safe_number_2(market, 'minQty', 'tradeMinQuantity'),
993
- 'max': self.safe_number(market, 'maxQty'),
995
+ 'min': minAmount,
996
+ 'max': None,
994
997
  },
995
998
  'price': {
996
999
  'min': minTickSize,
@@ -1622,21 +1625,24 @@ class bingx(Exchange, ImplicitAPI):
1622
1625
  # }
1623
1626
  #
1624
1627
  data = self.safe_list(response, 'data', [])
1625
- rates = []
1626
- for i in range(0, len(data)):
1627
- entry = data[i]
1628
- marketId = self.safe_string(entry, 'symbol')
1629
- symbolInner = self.safe_symbol(marketId, market, '-', 'swap')
1630
- timestamp = self.safe_integer(entry, 'fundingTime')
1631
- rates.append({
1632
- 'info': entry,
1633
- 'symbol': symbolInner,
1634
- 'fundingRate': self.safe_number(entry, 'fundingRate'),
1635
- 'timestamp': timestamp,
1636
- 'datetime': self.iso8601(timestamp),
1637
- })
1638
- sorted = self.sort_by(rates, 'timestamp')
1639
- return self.filter_by_symbol_since_limit(sorted, market['symbol'], since, limit)
1628
+ return self.parse_funding_rate_histories(data, market, since, limit)
1629
+
1630
+ def parse_funding_rate_history(self, contract, market: Market = None):
1631
+ #
1632
+ # {
1633
+ # "symbol": "BTC-USDT",
1634
+ # "fundingRate": "0.0001",
1635
+ # "fundingTime": 1585684800000
1636
+ # }
1637
+ #
1638
+ timestamp = self.safe_integer(contract, 'fundingTime')
1639
+ return {
1640
+ 'info': contract,
1641
+ 'symbol': self.safe_symbol(self.safe_string(contract, 'symbol'), market, '-', 'swap'),
1642
+ 'fundingRate': self.safe_number(contract, 'fundingRate'),
1643
+ 'timestamp': timestamp,
1644
+ 'datetime': self.iso8601(timestamp),
1645
+ }
1640
1646
 
1641
1647
  async def fetch_open_interest(self, symbol: str, params={}):
1642
1648
  """
@@ -2275,12 +2281,13 @@ class bingx(Exchange, ImplicitAPI):
2275
2281
  else:
2276
2282
  linearSwapData = self.safe_dict(response, 'data', {})
2277
2283
  linearSwapBalance = self.safe_dict(linearSwapData, 'balance')
2278
- currencyId = self.safe_string(linearSwapBalance, 'asset')
2279
- code = self.safe_currency_code(currencyId)
2280
- account = self.account()
2281
- account['free'] = self.safe_string(linearSwapBalance, 'availableMargin')
2282
- account['used'] = self.safe_string(linearSwapBalance, 'usedMargin')
2283
- result[code] = account
2284
+ if linearSwapBalance:
2285
+ currencyId = self.safe_string(linearSwapBalance, 'asset')
2286
+ code = self.safe_currency_code(currencyId)
2287
+ account = self.account()
2288
+ account['free'] = self.safe_string(linearSwapBalance, 'availableMargin')
2289
+ account['used'] = self.safe_string(linearSwapBalance, 'usedMargin')
2290
+ result[code] = account
2284
2291
  return self.safe_balance(result)
2285
2292
 
2286
2293
  async def fetch_position_history(self, symbol: str, since: Int = None, limit: Int = None, params={}) -> List[Position]:
@@ -6161,6 +6168,37 @@ class bingx(Exchange, ImplicitAPI):
6161
6168
  'tierBased': False,
6162
6169
  }
6163
6170
 
6171
+ def custom_encode(self, params):
6172
+ sortedParams = self.keysort(params)
6173
+ keys = list(sortedParams.keys())
6174
+ adjustedValue = None
6175
+ result = None
6176
+ for i in range(0, len(keys)):
6177
+ key = keys[i]
6178
+ value = sortedParams[key]
6179
+ if isinstance(value, list):
6180
+ arrStr = None
6181
+ for j in range(0, len(value)):
6182
+ arrayElement = value[j]
6183
+ isString = (isinstance(arrayElement, str))
6184
+ if isString:
6185
+ if j > 0:
6186
+ arrStr += ',' + '"' + str(arrayElement) + '"'
6187
+ else:
6188
+ arrStr = '"' + str(arrayElement) + '"'
6189
+ else:
6190
+ if j > 0:
6191
+ arrStr += ',' + str(arrayElement)
6192
+ else:
6193
+ arrStr = str(arrayElement)
6194
+ adjustedValue = '[' + arrStr + ']'
6195
+ value = adjustedValue
6196
+ if i == 0:
6197
+ result = key + '=' + value
6198
+ else:
6199
+ result += '&' + key + '=' + value
6200
+ return result
6201
+
6164
6202
  def sign(self, path, section='public', method='GET', params={}, headers=None, body=None):
6165
6203
  type = section[0]
6166
6204
  version = section[1]
@@ -6189,16 +6227,22 @@ class bingx(Exchange, ImplicitAPI):
6189
6227
  elif access == 'private':
6190
6228
  self.check_required_credentials()
6191
6229
  isJsonContentType = (((type == 'subAccount') or (type == 'account/transfer')) and (method == 'POST'))
6192
- parsedParams = self.parse_params(params)
6193
- signature = self.hmac(self.encode(self.rawencode(parsedParams)), self.encode(self.secret), hashlib.sha256)
6230
+ parsedParams = None
6231
+ encodeRequest = None
6232
+ if isJsonContentType:
6233
+ encodeRequest = self.custom_encode(params)
6234
+ else:
6235
+ parsedParams = self.parse_params(params)
6236
+ encodeRequest = self.rawencode(parsedParams)
6237
+ signature = self.hmac(self.encode(encodeRequest), self.encode(self.secret), hashlib.sha256)
6194
6238
  headers = {
6195
6239
  'X-BX-APIKEY': self.apiKey,
6196
6240
  'X-SOURCE-KEY': self.safe_string(self.options, 'broker', 'CCXT'),
6197
6241
  }
6198
6242
  if isJsonContentType:
6199
6243
  headers['Content-Type'] = 'application/json'
6200
- parsedParams['signature'] = signature
6201
- body = self.json(parsedParams)
6244
+ params['signature'] = signature
6245
+ body = self.json(params)
6202
6246
  else:
6203
6247
  query = self.urlencode(parsedParams)
6204
6248
  url += '?' + query + '&' + 'signature=' + signature
@@ -1403,18 +1403,18 @@ class bitget(Exchange, ImplicitAPI):
1403
1403
  '1m': 30,
1404
1404
  '3m': 30,
1405
1405
  '5m': 30,
1406
- '10m': 52,
1406
+ '10m': 30,
1407
1407
  '15m': 52,
1408
- '30m': 52,
1408
+ '30m': 62,
1409
1409
  '1h': 83,
1410
1410
  '2h': 120,
1411
1411
  '4h': 240,
1412
1412
  '6h': 360,
1413
1413
  '12h': 360,
1414
- '1d': 360,
1415
- '3d': 1000,
1416
- '1w': 1000,
1417
- '1M': 1000,
1414
+ '1d': 300,
1415
+ '3d': 300,
1416
+ '1w': 300,
1417
+ '1M': 300,
1418
1418
  },
1419
1419
  },
1420
1420
  'fetchTrades': {
@@ -3464,6 +3464,7 @@ class bitget(Exchange, ImplicitAPI):
3464
3464
  :param int [limit]: the maximum amount of candles to fetch
3465
3465
  :param dict [params]: extra parameters specific to the exchange API endpoint
3466
3466
  :param int [params.until]: timestamp in ms of the latest candle to fetch
3467
+ :param boolean [params.useHistoryEndpoint]: whether to force to use historical endpoint(it has max limit of 200)
3467
3468
  :param boolean [params.paginate]: default False, when True will automatically paginate by calling self endpoint multiple times. See in the docs all the [available parameters](https://github.com/ccxt/ccxt/wiki/Manual#pagination-params)
3468
3469
  :param str [params.price]: *swap only* "mark"(to fetch mark price candles) or "index"(to fetch index price candles)
3469
3470
  :returns int[][]: A list of candles ordered, open, high, low, close, volume
@@ -3475,8 +3476,9 @@ class bitget(Exchange, ImplicitAPI):
3475
3476
  paginate = False
3476
3477
  paginate, params = self.handle_option_and_params(params, 'fetchOHLCV', 'paginate')
3477
3478
  if paginate:
3478
- return await self.fetch_paginated_call_deterministic('fetchOHLCV', symbol, since, limit, timeframe, params, maxLimitForHistoryEndpoint)
3479
+ return await self.fetch_paginated_call_deterministic('fetchOHLCV', symbol, since, limit, timeframe, params, maxLimitForRecentEndpoint)
3479
3480
  sandboxMode = self.safe_bool(self.options, 'sandboxMode', False)
3481
+ useHistoryEndpoint = self.safe_bool(params, 'useHistoryEndpoint', False)
3480
3482
  market = None
3481
3483
  if sandboxMode:
3482
3484
  sandboxSymbol = self.convert_symbol_for_sandbox(symbol)
@@ -3504,7 +3506,7 @@ class bitget(Exchange, ImplicitAPI):
3504
3506
  ohlcOptions = self.safe_dict(self.options, 'fetchOHLCV', {})
3505
3507
  retrievableDaysMap = self.safe_dict(ohlcOptions, 'maxDaysPerTimeframe', {})
3506
3508
  maxRetrievableDaysForRecent = self.safe_integer(retrievableDaysMap, timeframe, 30) # default to safe minimum
3507
- endpointTsBoundary = now - maxRetrievableDaysForRecent * msInDay
3509
+ endpointTsBoundary = now - (maxRetrievableDaysForRecent - 1) * msInDay
3508
3510
  if limitDefined:
3509
3511
  limit = min(limit, maxLimitForRecentEndpoint)
3510
3512
  request['limit'] = limit
@@ -3535,7 +3537,7 @@ class bitget(Exchange, ImplicitAPI):
3535
3537
  # make request
3536
3538
  if market['spot']:
3537
3539
  # checks if we need history endpoint
3538
- if historicalEndpointNeeded:
3540
+ if historicalEndpointNeeded or useHistoryEndpoint:
3539
3541
  response = await self.publicSpotGetV2SpotMarketHistoryCandles(self.extend(request, params))
3540
3542
  else:
3541
3543
  response = await self.publicSpotGetV2SpotMarketCandles(self.extend(request, params))
@@ -3559,7 +3561,7 @@ class bitget(Exchange, ImplicitAPI):
3559
3561
  elif priceType == 'index':
3560
3562
  response = await self.publicMixGetV2MixMarketHistoryIndexCandles(extended)
3561
3563
  else:
3562
- if historicalEndpointNeeded:
3564
+ if historicalEndpointNeeded or useHistoryEndpoint:
3563
3565
  response = await self.publicMixGetV2MixMarketHistoryCandles(extended)
3564
3566
  else:
3565
3567
  response = await self.publicMixGetV2MixMarketCandles(extended)
@@ -875,27 +875,36 @@ class coinsph(Exchange, ImplicitAPI):
875
875
  :param int [since]: timestamp in ms of the earliest candle to fetch
876
876
  :param int [limit]: the maximum amount of candles to fetch(default 500, max 1000)
877
877
  :param dict [params]: extra parameters specific to the exchange API endpoint
878
+ :param int [params.until]: timestamp in ms of the latest candle to fetch
878
879
  :returns int[][]: A list of candles ordered, open, high, low, close, volume
879
880
  """
880
881
  await self.load_markets()
881
882
  market = self.market(symbol)
882
883
  interval = self.safe_string(self.timeframes, timeframe)
884
+ until = self.safe_integer(params, 'until')
883
885
  request: dict = {
884
886
  'symbol': market['id'],
885
887
  'interval': interval,
886
888
  }
889
+ if limit is None:
890
+ limit = 1000
887
891
  if since is not None:
888
892
  request['startTime'] = since
889
- request['limit'] = 1000
890
893
  # since work properly only when it is "younger" than last "limit" candle
891
- if limit is not None:
892
- duration = self.parse_timeframe(timeframe) * 1000
893
- request['endTime'] = self.sum(since, duration * (limit - 1))
894
+ if until is not None:
895
+ request['endTime'] = until
894
896
  else:
895
- request['endTime'] = self.milliseconds()
896
- else:
897
- if limit is not None:
898
- request['limit'] = limit
897
+ duration = self.parse_timeframe(timeframe) * 1000
898
+ endTimeByLimit = self.sum(since, duration * (limit - 1))
899
+ now = self.milliseconds()
900
+ request['endTime'] = min(endTimeByLimit, now)
901
+ elif until is not None:
902
+ request['endTime'] = until
903
+ # since work properly only when it is "younger" than last "limit" candle
904
+ duration = self.parse_timeframe(timeframe) * 1000
905
+ request['startTime'] = until - (duration * (limit - 1))
906
+ request['limit'] = limit
907
+ params = self.omit(params, 'until')
899
908
  response = await self.publicGetOpenapiQuoteV1Klines(self.extend(request, params))
900
909
  #
901
910
  # [
@@ -291,6 +291,88 @@ class deribit(Exchange, ImplicitAPI):
291
291
  },
292
292
  },
293
293
  },
294
+ 'features': {
295
+ 'default': {
296
+ 'sandbox': True,
297
+ 'createOrder': {
298
+ 'marginMode': False,
299
+ 'triggerPrice': True, # todo
300
+ # todo implement
301
+ 'triggerPriceType': {
302
+ 'last': True,
303
+ 'mark': True,
304
+ 'index': True,
305
+ },
306
+ 'triggerDirection': False,
307
+ 'stopLossPrice': False, # todo
308
+ 'takeProfitPrice': False, # todo
309
+ 'attachedStopLossTakeProfit': None,
310
+ 'timeInForce': {
311
+ 'IOC': True,
312
+ 'FOK': True,
313
+ 'PO': True,
314
+ 'GTD': True,
315
+ },
316
+ 'hedged': False,
317
+ 'selfTradePrevention': False,
318
+ 'trailing': True, # todo
319
+ 'leverage': False,
320
+ 'marketBuyByCost': True, # todo
321
+ 'marketBuyRequiresPrice': False,
322
+ 'iceberg': True, # todo
323
+ },
324
+ 'createOrders': None,
325
+ 'fetchMyTrades': {
326
+ 'marginMode': False,
327
+ 'limit': 100, # todo: revise
328
+ 'daysBack': 100000,
329
+ 'untilDays': 100000,
330
+ },
331
+ 'fetchOrder': {
332
+ 'marginMode': False,
333
+ 'trigger': False,
334
+ 'trailing': False,
335
+ },
336
+ 'fetchOpenOrders': {
337
+ 'marginMode': False,
338
+ 'limit': None,
339
+ 'trigger': False,
340
+ 'trailing': False,
341
+ },
342
+ 'fetchOrders': None,
343
+ 'fetchClosedOrders': {
344
+ 'marginMode': False,
345
+ 'limit': 100,
346
+ 'daysBack': 100000,
347
+ 'daysBackCanceled': 1,
348
+ 'untilDays': 100000,
349
+ 'trigger': False,
350
+ 'trailing': False,
351
+ },
352
+ 'fetchOHLCV': {
353
+ 'limit': 1000, # todo: recheck
354
+ },
355
+ },
356
+ 'spot': {
357
+ 'extends': 'default',
358
+ },
359
+ 'swap': {
360
+ 'linear': {
361
+ 'extends': 'default',
362
+ },
363
+ 'inverse': {
364
+ 'extends': 'default',
365
+ },
366
+ },
367
+ 'future': {
368
+ 'linear': {
369
+ 'extends': 'default',
370
+ },
371
+ 'inverse': {
372
+ 'extends': 'default',
373
+ },
374
+ },
375
+ },
294
376
  'exceptions': {
295
377
  # 0 or absent Success, No error.
296
378
  '9999': PermissionDenied, # 'api_not_enabled' User didn't enable API for the Account.
@@ -254,6 +254,109 @@ class digifinex(Exchange, ImplicitAPI):
254
254
  },
255
255
  },
256
256
  },
257
+ 'features': {
258
+ 'default': {
259
+ 'sandbox': False,
260
+ 'createOrder': {
261
+ 'marginMode': True,
262
+ 'triggerPrice': False,
263
+ 'triggerPriceType': None,
264
+ 'triggerDirection': False,
265
+ 'stopLossPrice': False,
266
+ 'takeProfitPrice': False,
267
+ 'attachedStopLossTakeProfit': None,
268
+ 'timeInForce': {
269
+ 'IOC': False,
270
+ 'FOK': False,
271
+ 'PO': True,
272
+ 'GTD': False,
273
+ },
274
+ 'hedged': False,
275
+ 'selfTradePrevention': False,
276
+ 'trailing': False,
277
+ 'leverage': False,
278
+ 'marketBuyByCost': False,
279
+ 'marketBuyRequiresPrice': False,
280
+ 'iceberg': False,
281
+ },
282
+ 'createOrders': {
283
+ 'max': 10,
284
+ 'marginMode': True,
285
+ },
286
+ 'fetchMyTrades': {
287
+ 'marginMode': True,
288
+ 'limit': 500,
289
+ 'daysBack': 100000, # todo
290
+ 'untilDays': 30,
291
+ },
292
+ 'fetchOrder': {
293
+ 'marginMode': True,
294
+ 'trigger': False,
295
+ 'trailing': False,
296
+ 'marketType': True,
297
+ },
298
+ 'fetchOpenOrders': {
299
+ 'marginMode': True,
300
+ 'limit': None,
301
+ 'trigger': False,
302
+ 'trailing': False,
303
+ },
304
+ 'fetchOrders': {
305
+ 'marginMode': True,
306
+ 'limit': 100,
307
+ 'daysBack': 100000, # todo
308
+ 'untilDays': 30,
309
+ 'trigger': False,
310
+ 'trailing': False,
311
+ },
312
+ 'fetchClosedOrders': None,
313
+ 'fetchOHLCV': {
314
+ 'limit': 500,
315
+ },
316
+ },
317
+ 'spot': {
318
+ 'extends': 'default',
319
+ },
320
+ 'forDerivatives': {
321
+ 'extends': 'default',
322
+ 'createOrders': {
323
+ 'max': 20,
324
+ 'marginMode': False,
325
+ },
326
+ 'fetchMyTrades': {
327
+ 'marginMode': False,
328
+ 'limit': 100,
329
+ 'daysBack': 100000, # todo
330
+ 'untilDays': 100000, # todo
331
+ },
332
+ 'fetchOrder': {
333
+ 'marginMode': False,
334
+ },
335
+ 'fetchOpenOrders': {
336
+ 'marginMode': False,
337
+ 'limit': 100,
338
+ },
339
+ 'fetchOrders': {
340
+ 'marginMode': False,
341
+ 'daysBack': 100000, # todo
342
+ },
343
+ 'fetchOHLCV': {
344
+ 'limit': 100,
345
+ },
346
+ },
347
+ 'swap': {
348
+ 'linear': {
349
+ 'extends': 'forDerivatives',
350
+ },
351
+ 'inverse': {
352
+ 'extends': 'forDerivatives',
353
+ },
354
+ },
355
+ 'future': {
356
+ 'linear': None,
357
+ 'inverse': None,
358
+ },
359
+ },
257
360
  'fees': {
258
361
  'trading': {
259
362
  'tierBased': True,
@@ -1459,6 +1562,7 @@ class digifinex(Exchange, ImplicitAPI):
1459
1562
  :param int [since]: timestamp in ms of the earliest candle to fetch
1460
1563
  :param int [limit]: the maximum amount of candles to fetch
1461
1564
  :param dict [params]: extra parameters specific to the exchange API endpoint
1565
+ :param int [params.until]: timestamp in ms of the latest candle to fetch
1462
1566
  :returns int[][]: A list of candles ordered, open, high, low, close, volume
1463
1567
  """
1464
1568
  await self.load_markets()
@@ -1472,19 +1576,30 @@ class digifinex(Exchange, ImplicitAPI):
1472
1576
  request['limit'] = min(limit, 100)
1473
1577
  response = await self.publicSwapGetPublicCandles(self.extend(request, params))
1474
1578
  else:
1579
+ until = self.safe_integer(params, 'until')
1475
1580
  request['symbol'] = market['id']
1476
1581
  request['period'] = self.safe_string(self.timeframes, timeframe, timeframe)
1477
- if since is not None:
1478
- startTime = self.parse_to_int(since / 1000)
1582
+ startTime = since
1583
+ duration = self.parse_timeframe(timeframe)
1584
+ if startTime is None:
1585
+ if (limit is not None) or (until is not None):
1586
+ endTime = until if (until is not None) else self.milliseconds()
1587
+ startLimit = limit if (limit is not None) else 200
1588
+ startTime = endTime - (startLimit * duration * 1000)
1589
+ if startTime is not None:
1590
+ startTime = self.parse_to_int(startTime / 1000)
1479
1591
  request['start_time'] = startTime
1480
- if limit is not None:
1481
- duration = self.parse_timeframe(timeframe)
1482
- request['end_time'] = self.sum(startTime, limit * duration)
1483
- elif limit is not None:
1484
- endTime = self.seconds()
1485
- duration = self.parse_timeframe(timeframe)
1486
- auxLimit = limit # in c# -limit is mutating the arg
1487
- request['start_time'] = self.sum(endTime, -auxLimit * duration)
1592
+ if (limit is not None) or (until is not None):
1593
+ if until is not None:
1594
+ endByUntil = self.parse_to_int(until / 1000)
1595
+ if limit is not None:
1596
+ endByLimit = self.sum(startTime, limit * duration)
1597
+ request['end_time'] = min(endByLimit, endByUntil)
1598
+ else:
1599
+ request['end_time'] = endByUntil
1600
+ else:
1601
+ request['end_time'] = self.sum(startTime, limit * duration)
1602
+ params = self.omit(params, 'until')
1488
1603
  response = await self.publicSpotGetKline(self.extend(request, params))
1489
1604
  #
1490
1605
  # spot
@@ -235,6 +235,66 @@ class ellipx(Exchange, ImplicitAPI):
235
235
  'ETH': 'Ethereum',
236
236
  },
237
237
  },
238
+ 'features': {
239
+ 'spot': {
240
+ 'sandbox': False,
241
+ 'createOrder': {
242
+ 'marginMode': False,
243
+ 'triggerPrice': False,
244
+ 'triggerPriceType': None,
245
+ 'triggerDirection': False,
246
+ 'stopLossPrice': False,
247
+ 'takeProfitPrice': False,
248
+ 'attachedStopLossTakeProfit': None,
249
+ 'timeInForce': {
250
+ 'IOC': False,
251
+ 'FOK': False,
252
+ 'PO': False,
253
+ 'GTD': False,
254
+ },
255
+ 'hedged': False,
256
+ 'selfTradePrevention': False,
257
+ 'trailing': False,
258
+ 'leverage': False,
259
+ 'marketBuyByCost': True,
260
+ 'marketBuyRequiresPrice': False,
261
+ 'iceberg': False,
262
+ },
263
+ 'createOrders': None,
264
+ 'fetchMyTrades': None,
265
+ 'fetchOrder': {
266
+ 'marginMode': False,
267
+ 'trigger': False,
268
+ 'trailing': False,
269
+ },
270
+ 'fetchOpenOrders': {
271
+ 'marginMode': False,
272
+ 'limit': None,
273
+ 'trigger': False,
274
+ 'trailing': False,
275
+ },
276
+ 'fetchOrders': {
277
+ 'marginMode': False,
278
+ 'limit': None, # todo
279
+ 'daysBack': None, # todo
280
+ 'untilDays': None, # todo
281
+ 'trigger': False,
282
+ 'trailing': False,
283
+ },
284
+ 'fetchClosedOrders': None,
285
+ 'fetchOHLCV': {
286
+ 'limit': 100,
287
+ },
288
+ },
289
+ 'swap': {
290
+ 'linear': None,
291
+ 'inverse': None,
292
+ },
293
+ 'future': {
294
+ 'linear': None,
295
+ 'inverse': None,
296
+ },
297
+ },
238
298
  'commonCurrencies': {},
239
299
  'exceptions': {
240
300
  'exact': {
@@ -662,6 +722,7 @@ class ellipx(Exchange, ImplicitAPI):
662
722
  :param int [since]: timestamp in ms of the earliest candle to fetch
663
723
  :param int [limit]: the maximum amount of candles to fetch
664
724
  :param dict [params]: extra parameters specific to the API endpoint
725
+ :param int [params.until]: timestamp in ms of the earliest candle to fetch
665
726
  :returns OHLCV[]: A list of candles ordered, open, high, low, close, volume
666
727
  """
667
728
  await self.load_markets()