ccxt 4.4.48__py2.py3-none-any.whl → 4.4.50__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 (97) hide show
  1. ccxt/__init__.py +1 -1
  2. ccxt/abstract/binance.py +1 -0
  3. ccxt/abstract/binancecoinm.py +1 -0
  4. ccxt/abstract/binanceus.py +1 -0
  5. ccxt/abstract/binanceusdm.py +1 -0
  6. ccxt/alpaca.py +62 -1
  7. ccxt/async_support/__init__.py +1 -1
  8. ccxt/async_support/alpaca.py +62 -1
  9. ccxt/async_support/base/exchange.py +1 -1
  10. ccxt/async_support/binance.py +8 -4
  11. ccxt/async_support/bingx.py +73 -29
  12. ccxt/async_support/bitget.py +12 -10
  13. ccxt/async_support/blofin.py +1 -1
  14. ccxt/async_support/coinex.py +3 -3
  15. ccxt/async_support/coinsph.py +17 -8
  16. ccxt/async_support/deribit.py +82 -0
  17. ccxt/async_support/digifinex.py +125 -10
  18. ccxt/async_support/ellipx.py +61 -0
  19. ccxt/async_support/exmo.py +58 -0
  20. ccxt/async_support/hitbtc.py +99 -0
  21. ccxt/async_support/hollaex.py +85 -16
  22. ccxt/async_support/huobijp.py +73 -0
  23. ccxt/async_support/hyperliquid.py +22 -1
  24. ccxt/async_support/idex.py +71 -0
  25. ccxt/async_support/independentreserve.py +64 -0
  26. ccxt/async_support/indodax.py +61 -0
  27. ccxt/async_support/kucoin.py +47 -67
  28. ccxt/async_support/kuna.py +60 -1
  29. ccxt/async_support/latoken.py +64 -0
  30. ccxt/async_support/lbank.py +70 -0
  31. ccxt/async_support/luno.py +73 -0
  32. ccxt/async_support/lykke.py +64 -0
  33. ccxt/async_support/mercado.py +65 -0
  34. ccxt/async_support/mexc.py +4 -3
  35. ccxt/async_support/myokx.py +10 -0
  36. ccxt/async_support/ndax.py +71 -0
  37. ccxt/async_support/novadax.py +74 -0
  38. ccxt/async_support/oceanex.py +69 -0
  39. ccxt/async_support/okcoin.py +92 -7
  40. ccxt/async_support/onetrading.py +66 -0
  41. ccxt/async_support/oxfun.py +66 -0
  42. ccxt/async_support/p2b.py +63 -1
  43. ccxt/async_support/paradex.py +68 -0
  44. ccxt/async_support/paymium.py +42 -0
  45. ccxt/async_support/probit.py +68 -1
  46. ccxt/async_support/timex.py +67 -0
  47. ccxt/async_support/tokocrypto.py +81 -4
  48. ccxt/async_support/tradeogre.py +58 -1
  49. ccxt/async_support/whitebit.py +10 -4
  50. ccxt/base/exchange.py +62 -1
  51. ccxt/binance.py +8 -4
  52. ccxt/bingx.py +73 -29
  53. ccxt/bitget.py +12 -10
  54. ccxt/blofin.py +1 -1
  55. ccxt/coinex.py +3 -3
  56. ccxt/coinsph.py +17 -8
  57. ccxt/deribit.py +82 -0
  58. ccxt/digifinex.py +125 -10
  59. ccxt/ellipx.py +61 -0
  60. ccxt/exmo.py +58 -0
  61. ccxt/hitbtc.py +99 -0
  62. ccxt/hollaex.py +85 -16
  63. ccxt/huobijp.py +73 -0
  64. ccxt/hyperliquid.py +22 -1
  65. ccxt/idex.py +71 -0
  66. ccxt/independentreserve.py +64 -0
  67. ccxt/indodax.py +61 -0
  68. ccxt/kucoin.py +47 -67
  69. ccxt/kuna.py +60 -1
  70. ccxt/latoken.py +64 -0
  71. ccxt/lbank.py +70 -0
  72. ccxt/luno.py +73 -0
  73. ccxt/lykke.py +64 -0
  74. ccxt/mercado.py +65 -0
  75. ccxt/mexc.py +4 -3
  76. ccxt/myokx.py +10 -0
  77. ccxt/ndax.py +71 -0
  78. ccxt/novadax.py +74 -0
  79. ccxt/oceanex.py +69 -0
  80. ccxt/okcoin.py +92 -7
  81. ccxt/onetrading.py +66 -0
  82. ccxt/oxfun.py +66 -0
  83. ccxt/p2b.py +63 -1
  84. ccxt/paradex.py +68 -0
  85. ccxt/paymium.py +42 -0
  86. ccxt/pro/__init__.py +1 -1
  87. ccxt/pro/bitmart.py +5 -0
  88. ccxt/probit.py +68 -1
  89. ccxt/timex.py +67 -0
  90. ccxt/tokocrypto.py +81 -4
  91. ccxt/tradeogre.py +58 -1
  92. ccxt/whitebit.py +10 -4
  93. {ccxt-4.4.48.dist-info → ccxt-4.4.50.dist-info}/METADATA +4 -4
  94. {ccxt-4.4.48.dist-info → ccxt-4.4.50.dist-info}/RECORD +97 -97
  95. {ccxt-4.4.48.dist-info → ccxt-4.4.50.dist-info}/LICENSE.txt +0 -0
  96. {ccxt-4.4.48.dist-info → ccxt-4.4.50.dist-info}/WHEEL +0 -0
  97. {ccxt-4.4.48.dist-info → ccxt-4.4.50.dist-info}/top_level.txt +0 -0
ccxt/coinsph.py CHANGED
@@ -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
  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 = self.publicGetOpenapiQuoteV1Klines(self.extend(request, params))
900
909
  #
901
910
  # [
ccxt/deribit.py CHANGED
@@ -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.
ccxt/digifinex.py CHANGED
@@ -253,6 +253,109 @@ class digifinex(Exchange, ImplicitAPI):
253
253
  },
254
254
  },
255
255
  },
256
+ 'features': {
257
+ 'default': {
258
+ 'sandbox': False,
259
+ 'createOrder': {
260
+ 'marginMode': True,
261
+ 'triggerPrice': False,
262
+ 'triggerPriceType': None,
263
+ 'triggerDirection': False,
264
+ 'stopLossPrice': False,
265
+ 'takeProfitPrice': False,
266
+ 'attachedStopLossTakeProfit': None,
267
+ 'timeInForce': {
268
+ 'IOC': False,
269
+ 'FOK': False,
270
+ 'PO': True,
271
+ 'GTD': False,
272
+ },
273
+ 'hedged': False,
274
+ 'selfTradePrevention': False,
275
+ 'trailing': False,
276
+ 'leverage': False,
277
+ 'marketBuyByCost': False,
278
+ 'marketBuyRequiresPrice': False,
279
+ 'iceberg': False,
280
+ },
281
+ 'createOrders': {
282
+ 'max': 10,
283
+ 'marginMode': True,
284
+ },
285
+ 'fetchMyTrades': {
286
+ 'marginMode': True,
287
+ 'limit': 500,
288
+ 'daysBack': 100000, # todo
289
+ 'untilDays': 30,
290
+ },
291
+ 'fetchOrder': {
292
+ 'marginMode': True,
293
+ 'trigger': False,
294
+ 'trailing': False,
295
+ 'marketType': True,
296
+ },
297
+ 'fetchOpenOrders': {
298
+ 'marginMode': True,
299
+ 'limit': None,
300
+ 'trigger': False,
301
+ 'trailing': False,
302
+ },
303
+ 'fetchOrders': {
304
+ 'marginMode': True,
305
+ 'limit': 100,
306
+ 'daysBack': 100000, # todo
307
+ 'untilDays': 30,
308
+ 'trigger': False,
309
+ 'trailing': False,
310
+ },
311
+ 'fetchClosedOrders': None,
312
+ 'fetchOHLCV': {
313
+ 'limit': 500,
314
+ },
315
+ },
316
+ 'spot': {
317
+ 'extends': 'default',
318
+ },
319
+ 'forDerivatives': {
320
+ 'extends': 'default',
321
+ 'createOrders': {
322
+ 'max': 20,
323
+ 'marginMode': False,
324
+ },
325
+ 'fetchMyTrades': {
326
+ 'marginMode': False,
327
+ 'limit': 100,
328
+ 'daysBack': 100000, # todo
329
+ 'untilDays': 100000, # todo
330
+ },
331
+ 'fetchOrder': {
332
+ 'marginMode': False,
333
+ },
334
+ 'fetchOpenOrders': {
335
+ 'marginMode': False,
336
+ 'limit': 100,
337
+ },
338
+ 'fetchOrders': {
339
+ 'marginMode': False,
340
+ 'daysBack': 100000, # todo
341
+ },
342
+ 'fetchOHLCV': {
343
+ 'limit': 100,
344
+ },
345
+ },
346
+ 'swap': {
347
+ 'linear': {
348
+ 'extends': 'forDerivatives',
349
+ },
350
+ 'inverse': {
351
+ 'extends': 'forDerivatives',
352
+ },
353
+ },
354
+ 'future': {
355
+ 'linear': None,
356
+ 'inverse': None,
357
+ },
358
+ },
256
359
  'fees': {
257
360
  'trading': {
258
361
  'tierBased': True,
@@ -1458,6 +1561,7 @@ class digifinex(Exchange, ImplicitAPI):
1458
1561
  :param int [since]: timestamp in ms of the earliest candle to fetch
1459
1562
  :param int [limit]: the maximum amount of candles to fetch
1460
1563
  :param dict [params]: extra parameters specific to the exchange API endpoint
1564
+ :param int [params.until]: timestamp in ms of the latest candle to fetch
1461
1565
  :returns int[][]: A list of candles ordered, open, high, low, close, volume
1462
1566
  """
1463
1567
  self.load_markets()
@@ -1471,19 +1575,30 @@ class digifinex(Exchange, ImplicitAPI):
1471
1575
  request['limit'] = min(limit, 100)
1472
1576
  response = self.publicSwapGetPublicCandles(self.extend(request, params))
1473
1577
  else:
1578
+ until = self.safe_integer(params, 'until')
1474
1579
  request['symbol'] = market['id']
1475
1580
  request['period'] = self.safe_string(self.timeframes, timeframe, timeframe)
1476
- if since is not None:
1477
- startTime = self.parse_to_int(since / 1000)
1581
+ startTime = since
1582
+ duration = self.parse_timeframe(timeframe)
1583
+ if startTime is None:
1584
+ if (limit is not None) or (until is not None):
1585
+ endTime = until if (until is not None) else self.milliseconds()
1586
+ startLimit = limit if (limit is not None) else 200
1587
+ startTime = endTime - (startLimit * duration * 1000)
1588
+ if startTime is not None:
1589
+ startTime = self.parse_to_int(startTime / 1000)
1478
1590
  request['start_time'] = startTime
1479
- if limit is not None:
1480
- duration = self.parse_timeframe(timeframe)
1481
- request['end_time'] = self.sum(startTime, limit * duration)
1482
- elif limit is not None:
1483
- endTime = self.seconds()
1484
- duration = self.parse_timeframe(timeframe)
1485
- auxLimit = limit # in c# -limit is mutating the arg
1486
- request['start_time'] = self.sum(endTime, -auxLimit * duration)
1591
+ if (limit is not None) or (until is not None):
1592
+ if until is not None:
1593
+ endByUntil = self.parse_to_int(until / 1000)
1594
+ if limit is not None:
1595
+ endByLimit = self.sum(startTime, limit * duration)
1596
+ request['end_time'] = min(endByLimit, endByUntil)
1597
+ else:
1598
+ request['end_time'] = endByUntil
1599
+ else:
1600
+ request['end_time'] = self.sum(startTime, limit * duration)
1601
+ params = self.omit(params, 'until')
1487
1602
  response = self.publicSpotGetKline(self.extend(request, params))
1488
1603
  #
1489
1604
  # spot
ccxt/ellipx.py CHANGED
@@ -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
  self.load_markets()
ccxt/exmo.py CHANGED
@@ -220,6 +220,64 @@ class exmo(Exchange, ImplicitAPI):
220
220
  'fillResponseFromRequest': True,
221
221
  },
222
222
  },
223
+ 'features': {
224
+ 'spot': {
225
+ 'sandbox': False,
226
+ 'createOrder': {
227
+ 'marginMode': True, # todo revise
228
+ 'triggerPrice': True, # todo: endpoint lacks other features
229
+ 'triggerPriceType': None,
230
+ 'triggerDirection': False,
231
+ 'stopLossPrice': False,
232
+ 'takeProfitPrice': False,
233
+ 'attachedStopLossTakeProfit': None,
234
+ 'timeInForce': {
235
+ 'IOC': True,
236
+ 'FOK': True,
237
+ 'PO': True,
238
+ 'GTD': True,
239
+ },
240
+ 'hedged': False,
241
+ 'selfTradePrevention': False,
242
+ 'trailing': False,
243
+ 'leverage': True,
244
+ 'marketBuyByCost': True,
245
+ 'marketBuyRequiresPrice': False,
246
+ 'iceberg': False,
247
+ },
248
+ 'createOrders': None,
249
+ 'fetchMyTrades': {
250
+ 'marginMode': True,
251
+ 'limit': 100,
252
+ 'daysBack': None,
253
+ 'untilDays': None,
254
+ },
255
+ 'fetchOrder': {
256
+ 'marginMode': False,
257
+ 'trigger': False,
258
+ 'trailing': False,
259
+ },
260
+ 'fetchOpenOrders': {
261
+ 'marginMode': False,
262
+ 'limit': None,
263
+ 'trigger': False,
264
+ 'trailing': False,
265
+ },
266
+ 'fetchOrders': None,
267
+ 'fetchClosedOrders': None,
268
+ 'fetchOHLCV': {
269
+ 'limit': 1000, # todo, not in request
270
+ },
271
+ },
272
+ 'swap': {
273
+ 'linear': None,
274
+ 'inverse': None,
275
+ },
276
+ 'future': {
277
+ 'linear': None,
278
+ 'inverse': None,
279
+ },
280
+ },
223
281
  'commonCurrencies': {
224
282
  'GMT': 'GMT Token',
225
283
  },
ccxt/hitbtc.py CHANGED
@@ -304,6 +304,105 @@ class hitbtc(Exchange, ImplicitAPI):
304
304
  },
305
305
  },
306
306
  },
307
+ 'features': {
308
+ 'default': {
309
+ 'sandbox': True,
310
+ 'createOrder': {
311
+ 'marginMode': False,
312
+ 'triggerPrice': True,
313
+ 'triggerPriceType': None,
314
+ 'triggerDirection': False,
315
+ 'stopLossPrice': False, # todo
316
+ 'takeProfitPrice': False, # todo
317
+ 'attachedStopLossTakeProfit': None,
318
+ 'timeInForce': {
319
+ 'IOC': True,
320
+ 'FOK': True,
321
+ 'PO': True,
322
+ 'GTD': True,
323
+ },
324
+ 'hedged': False,
325
+ 'selfTradePrevention': False,
326
+ 'trailing': False,
327
+ 'leverage': False,
328
+ 'marketBuyByCost': False,
329
+ 'marketBuyRequiresPrice': False,
330
+ 'iceberg': True,
331
+ },
332
+ 'createOrders': None,
333
+ 'fetchMyTrades': {
334
+ 'marginMode': True,
335
+ 'limit': 1000,
336
+ 'daysBack': 100000,
337
+ 'untilDays': 100000,
338
+ 'marketType': True,
339
+ },
340
+ 'fetchOrder': {
341
+ 'marginMode': True,
342
+ 'trigger': False,
343
+ 'trailing': False,
344
+ 'marketType': True,
345
+ },
346
+ 'fetchOpenOrders': {
347
+ 'marginMode': True,
348
+ 'limit': 1000,
349
+ 'trigger': False,
350
+ 'trailing': False,
351
+ 'marketType': True,
352
+ },
353
+ 'fetchOrders': None,
354
+ 'fetchClosedOrders': {
355
+ 'marginMode': True,
356
+ 'limit': 1000,
357
+ 'daysBack': 100000, # todo
358
+ 'daysBackCanceled': 1, # todo
359
+ 'untilDays': 100000, # todo
360
+ 'trigger': False,
361
+ 'trailing': False,
362
+ 'marketType': True,
363
+ },
364
+ 'fetchOHLCV': {
365
+ 'limit': 1000,
366
+ },
367
+ },
368
+ 'spot': {
369
+ 'extends': 'default',
370
+ },
371
+ 'forDerivatives': {
372
+ 'extends': 'default',
373
+ 'createOrder': {
374
+ 'marginMode': True,
375
+ },
376
+ 'fetchOrder': {
377
+ 'marginMode': False,
378
+ },
379
+ 'fetchMyTrades': {
380
+ 'marginMode': False,
381
+ },
382
+ 'fetchOpenOrders': {
383
+ 'marginMode': False,
384
+ },
385
+ 'fetchClosedOrders': {
386
+ 'marginMode': False,
387
+ },
388
+ },
389
+ 'swap': {
390
+ 'linear': {
391
+ 'extends': 'forDerivatives',
392
+ },
393
+ 'inverse': {
394
+ 'extends': 'forDerivatives',
395
+ },
396
+ },
397
+ 'future': {
398
+ 'linear': {
399
+ 'extends': 'forDerivatives',
400
+ },
401
+ 'inverse': {
402
+ 'extends': 'forDerivatives',
403
+ },
404
+ },
405
+ },
307
406
  'timeframes': {
308
407
  '1m': 'M1',
309
408
  '3m': 'M3',
ccxt/hollaex.py CHANGED
@@ -174,6 +174,79 @@ class hollaex(Exchange, ImplicitAPI):
174
174
  },
175
175
  },
176
176
  },
177
+ 'features': {
178
+ 'spot': {
179
+ 'sandbox': True,
180
+ 'createOrder': {
181
+ 'marginMode': False,
182
+ 'triggerPrice': True,
183
+ 'triggerPriceType': None,
184
+ 'triggerDirection': False,
185
+ 'stopLossPrice': False, # todo
186
+ 'takeProfitPrice': False, # todo
187
+ 'attachedStopLossTakeProfit': None,
188
+ 'timeInForce': {
189
+ 'IOC': False,
190
+ 'FOK': False,
191
+ 'PO': True,
192
+ 'GTD': False,
193
+ },
194
+ 'hedged': False,
195
+ 'selfTradePrevention': False,
196
+ 'trailing': False,
197
+ 'leverage': False,
198
+ 'marketBuyByCost': False,
199
+ 'marketBuyRequiresPrice': False,
200
+ 'iceberg': False,
201
+ },
202
+ 'createOrders': None,
203
+ 'fetchMyTrades': {
204
+ 'marginMode': False,
205
+ 'limit': 100,
206
+ 'daysBack': 100000,
207
+ 'untilDays': 100000, # todo implement
208
+ },
209
+ 'fetchOrder': {
210
+ 'marginMode': False,
211
+ 'trigger': False,
212
+ 'trailing': False,
213
+ },
214
+ 'fetchOpenOrders': {
215
+ 'marginMode': False,
216
+ 'limit': 100,
217
+ 'trigger': False,
218
+ 'trailing': False,
219
+ },
220
+ 'fetchOrders': {
221
+ 'marginMode': False,
222
+ 'limit': 100,
223
+ 'daysBack': 100000, # todo
224
+ 'untilDays': 100000, # todo
225
+ 'trigger': False,
226
+ 'trailing': False,
227
+ },
228
+ 'fetchClosedOrders': {
229
+ 'marginMode': False,
230
+ 'limit': 100,
231
+ 'daysBack': 100000, # todo
232
+ 'daysBackCanceled': 1, # todo
233
+ 'untilDays': 100000, # todo
234
+ 'trigger': False,
235
+ 'trailing': False,
236
+ },
237
+ 'fetchOHLCV': {
238
+ 'limit': 1000, # todo: no limit in request
239
+ },
240
+ },
241
+ 'swap': {
242
+ 'linear': None,
243
+ 'inverse': None,
244
+ },
245
+ 'future': {
246
+ 'linear': None,
247
+ 'inverse': None,
248
+ },
249
+ },
177
250
  'fees': {
178
251
  'trading': {
179
252
  'tierBased': True,
@@ -756,7 +829,7 @@ class hollaex(Exchange, ImplicitAPI):
756
829
 
757
830
  def fetch_ohlcv(self, symbol: str, timeframe='1m', since: Int = None, limit: Int = None, params={}) -> List[list]:
758
831
  """
759
- fetches historical candlestick data containing the open, high, low, and close price, and the volume of a market
832
+ hollaex has large gaps between candles, so it's recommended to specify since
760
833
 
761
834
  https://apidocs.hollaex.com/#chart
762
835
 
@@ -765,6 +838,7 @@ class hollaex(Exchange, ImplicitAPI):
765
838
  :param int [since]: timestamp in ms of the earliest candle to fetch
766
839
  :param int [limit]: the maximum amount of candles to fetch
767
840
  :param dict [params]: extra parameters specific to the exchange API endpoint
841
+ :param int [params.until]: timestamp in ms of the latest candle to fetch
768
842
  :returns int[][]: A list of candles ordered, open, high, low, close, volume
769
843
  """
770
844
  self.load_markets()
@@ -773,22 +847,17 @@ class hollaex(Exchange, ImplicitAPI):
773
847
  'symbol': market['id'],
774
848
  'resolution': self.safe_string(self.timeframes, timeframe, timeframe),
775
849
  }
776
- duration = self.parse_timeframe(timeframe)
777
- if since is None:
778
- if limit is None:
779
- limit = 1000 # they have no defaults and can actually provide tens of thousands of bars in one request, but we should cap "default" at generous amount
780
- end = self.seconds()
781
- start = end - duration * limit
782
- request['to'] = end
783
- request['from'] = start
850
+ until = self.safe_integer(params, 'until')
851
+ end = self.seconds()
852
+ if until is not None:
853
+ end = self.parse_to_int(until / 1000)
854
+ defaultSpan = 2592000 # 30 days
855
+ if since is not None:
856
+ request['from'] = self.parse_to_int(since / 1000)
784
857
  else:
785
- if limit is None:
786
- request['from'] = self.parse_to_int(since / 1000)
787
- request['to'] = self.seconds()
788
- else:
789
- start = self.parse_to_int(since / 1000)
790
- request['from'] = start
791
- request['to'] = self.sum(start, duration * limit)
858
+ request['from'] = end - defaultSpan
859
+ request['to'] = end
860
+ params = self.omit(params, 'until')
792
861
  response = self.publicGetChart(self.extend(request, params))
793
862
  #
794
863
  # [