ccxt 4.4.52__py2.py3-none-any.whl → 4.4.57__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.
- ccxt/__init__.py +1 -3
- ccxt/abstract/binance.py +0 -1
- ccxt/abstract/binancecoinm.py +0 -1
- ccxt/abstract/binanceus.py +0 -1
- ccxt/abstract/binanceusdm.py +0 -1
- ccxt/ace.py +3 -0
- ccxt/alpaca.py +5 -0
- ccxt/ascendex.py +2 -1
- ccxt/async_support/__init__.py +1 -3
- ccxt/async_support/ace.py +3 -0
- ccxt/async_support/alpaca.py +5 -0
- ccxt/async_support/ascendex.py +2 -1
- ccxt/async_support/base/exchange.py +24 -7
- ccxt/async_support/bigone.py +5 -0
- ccxt/async_support/binance.py +54 -54
- ccxt/async_support/bingx.py +25 -29
- ccxt/async_support/bit2c.py +3 -0
- ccxt/async_support/bitbank.py +3 -0
- ccxt/async_support/bitbns.py +3 -0
- ccxt/async_support/bitfinex.py +6 -1
- ccxt/async_support/bitflyer.py +6 -1
- ccxt/async_support/bitget.py +9 -5
- ccxt/async_support/bithumb.py +3 -1
- ccxt/async_support/bitmart.py +22 -12
- ccxt/async_support/bitmex.py +99 -93
- ccxt/async_support/bitopro.py +7 -2
- ccxt/async_support/bitrue.py +4 -0
- ccxt/async_support/bitso.py +5 -2
- ccxt/async_support/bitstamp.py +3 -0
- ccxt/async_support/bitteam.py +5 -0
- ccxt/async_support/bitvavo.py +4 -0
- ccxt/async_support/blockchaincom.py +4 -0
- ccxt/async_support/blofin.py +3 -0
- ccxt/async_support/btcalpha.py +5 -0
- ccxt/async_support/btcbox.py +3 -2
- ccxt/async_support/btcmarkets.py +5 -0
- ccxt/async_support/btcturk.py +3 -0
- ccxt/async_support/bybit.py +13 -5
- ccxt/async_support/cex.py +2 -0
- ccxt/async_support/coinbase.py +20 -13
- ccxt/async_support/coinbaseexchange.py +5 -0
- ccxt/async_support/coinbaseinternational.py +23 -4
- ccxt/async_support/coincatch.py +3 -0
- ccxt/async_support/coincheck.py +2 -0
- ccxt/async_support/coinex.py +6 -2
- ccxt/async_support/coinlist.py +5 -0
- ccxt/async_support/coinmate.py +4 -0
- ccxt/async_support/coinmetro.py +9 -5
- ccxt/async_support/coinone.py +3 -0
- ccxt/async_support/coinsph.py +4 -0
- ccxt/async_support/coinspot.py +1 -0
- ccxt/async_support/cryptocom.py +5 -0
- ccxt/async_support/currencycom.py +3 -0
- ccxt/async_support/defx.py +5 -0
- ccxt/async_support/delta.py +4 -1
- ccxt/async_support/deribit.py +15 -28
- ccxt/async_support/digifinex.py +10 -5
- ccxt/async_support/ellipx.py +9 -5
- ccxt/async_support/exmo.py +7 -4
- ccxt/async_support/gate.py +5 -1
- ccxt/async_support/gemini.py +3 -0
- ccxt/async_support/hashkey.py +5 -4
- ccxt/async_support/hitbtc.py +6 -2
- ccxt/async_support/hollaex.py +7 -2
- ccxt/async_support/htx.py +8 -1
- ccxt/async_support/huobijp.py +5 -0
- ccxt/async_support/hyperliquid.py +9 -4
- ccxt/async_support/idex.py +5 -1
- ccxt/async_support/independentreserve.py +4 -0
- ccxt/async_support/indodax.py +3 -0
- ccxt/async_support/kraken.py +7 -5
- ccxt/async_support/krakenfutures.py +5 -2
- ccxt/async_support/kucoin.py +13 -6
- ccxt/async_support/kucoinfutures.py +5 -1
- ccxt/async_support/kuna.py +3 -0
- ccxt/async_support/latoken.py +4 -0
- ccxt/async_support/lbank.py +6 -1
- ccxt/async_support/luno.py +6 -1
- ccxt/async_support/mercado.py +4 -0
- ccxt/async_support/mexc.py +10 -9
- ccxt/async_support/ndax.py +6 -1
- ccxt/async_support/novadax.py +5 -0
- ccxt/async_support/oceanex.py +6 -2
- ccxt/async_support/okcoin.py +4 -0
- ccxt/async_support/okx.py +17 -5
- ccxt/async_support/onetrading.py +4 -0
- ccxt/async_support/oxfun.py +3 -0
- ccxt/async_support/p2b.py +3 -0
- ccxt/async_support/paradex.py +8 -2
- ccxt/async_support/phemex.py +10 -4
- ccxt/async_support/poloniex.py +6 -3
- ccxt/async_support/poloniexfutures.py +5 -1
- ccxt/async_support/probit.py +4 -0
- ccxt/async_support/timex.py +4 -0
- ccxt/async_support/tokocrypto.py +5 -0
- ccxt/async_support/tradeogre.py +2 -0
- ccxt/async_support/upbit.py +5 -2
- ccxt/async_support/vertex.py +6 -2
- ccxt/async_support/wavesexchange.py +20 -3
- ccxt/async_support/wazirx.py +2 -0
- ccxt/async_support/whitebit.py +5 -4
- ccxt/async_support/woo.py +15 -5
- ccxt/async_support/woofipro.py +23 -9
- ccxt/async_support/xt.py +5 -0
- ccxt/async_support/yobit.py +5 -2
- ccxt/async_support/zaif.py +2 -0
- ccxt/async_support/zonda.py +2 -0
- ccxt/base/exchange.py +96 -54
- ccxt/base/types.py +1 -1
- ccxt/bigone.py +5 -0
- ccxt/binance.py +54 -54
- ccxt/bingx.py +25 -29
- ccxt/bit2c.py +3 -0
- ccxt/bitbank.py +3 -0
- ccxt/bitbns.py +3 -0
- ccxt/bitfinex.py +6 -1
- ccxt/bitflyer.py +6 -1
- ccxt/bitget.py +9 -5
- ccxt/bithumb.py +3 -1
- ccxt/bitmart.py +22 -12
- ccxt/bitmex.py +99 -93
- ccxt/bitopro.py +7 -2
- ccxt/bitrue.py +4 -0
- ccxt/bitso.py +5 -2
- ccxt/bitstamp.py +3 -0
- ccxt/bitteam.py +5 -0
- ccxt/bitvavo.py +4 -0
- ccxt/blockchaincom.py +4 -0
- ccxt/blofin.py +3 -0
- ccxt/btcalpha.py +5 -0
- ccxt/btcbox.py +3 -2
- ccxt/btcmarkets.py +5 -0
- ccxt/btcturk.py +3 -0
- ccxt/bybit.py +13 -5
- ccxt/cex.py +2 -0
- ccxt/coinbase.py +20 -13
- ccxt/coinbaseexchange.py +5 -0
- ccxt/coinbaseinternational.py +23 -4
- ccxt/coincatch.py +3 -0
- ccxt/coincheck.py +2 -0
- ccxt/coinex.py +6 -2
- ccxt/coinlist.py +5 -0
- ccxt/coinmate.py +4 -0
- ccxt/coinmetro.py +9 -5
- ccxt/coinone.py +3 -0
- ccxt/coinsph.py +4 -0
- ccxt/coinspot.py +1 -0
- ccxt/cryptocom.py +5 -0
- ccxt/currencycom.py +3 -0
- ccxt/defx.py +5 -0
- ccxt/delta.py +4 -1
- ccxt/deribit.py +15 -28
- ccxt/digifinex.py +10 -5
- ccxt/ellipx.py +9 -5
- ccxt/exmo.py +7 -4
- ccxt/gate.py +5 -1
- ccxt/gemini.py +3 -0
- ccxt/hashkey.py +5 -4
- ccxt/hitbtc.py +6 -2
- ccxt/hollaex.py +7 -2
- ccxt/htx.py +8 -1
- ccxt/huobijp.py +5 -0
- ccxt/hyperliquid.py +9 -4
- ccxt/idex.py +5 -1
- ccxt/independentreserve.py +4 -0
- ccxt/indodax.py +3 -0
- ccxt/kraken.py +7 -5
- ccxt/krakenfutures.py +5 -2
- ccxt/kucoin.py +13 -6
- ccxt/kucoinfutures.py +5 -1
- ccxt/kuna.py +3 -0
- ccxt/latoken.py +4 -0
- ccxt/lbank.py +6 -1
- ccxt/luno.py +6 -1
- ccxt/mercado.py +4 -0
- ccxt/mexc.py +10 -9
- ccxt/ndax.py +6 -1
- ccxt/novadax.py +5 -0
- ccxt/oceanex.py +6 -2
- ccxt/okcoin.py +4 -0
- ccxt/okx.py +17 -5
- ccxt/onetrading.py +4 -0
- ccxt/oxfun.py +3 -0
- ccxt/p2b.py +3 -0
- ccxt/paradex.py +8 -2
- ccxt/phemex.py +10 -4
- ccxt/poloniex.py +6 -3
- ccxt/poloniexfutures.py +5 -1
- ccxt/pro/__init__.py +1 -1
- ccxt/pro/binance.py +2 -2
- ccxt/pro/bitcoincom.py +1 -4
- ccxt/pro/bitget.py +3 -3
- ccxt/pro/bitopro.py +1 -1
- ccxt/pro/bybit.py +7 -2
- ccxt/pro/cex.py +1 -1
- ccxt/pro/coincatch.py +3 -3
- ccxt/pro/mexc.py +3 -3
- ccxt/pro/okx.py +1 -1
- ccxt/probit.py +4 -0
- ccxt/test/tests_async.py +57 -30
- ccxt/test/tests_sync.py +57 -30
- ccxt/timex.py +4 -0
- ccxt/tokocrypto.py +5 -0
- ccxt/tradeogre.py +2 -0
- ccxt/upbit.py +5 -2
- ccxt/vertex.py +6 -2
- ccxt/wavesexchange.py +20 -3
- ccxt/wazirx.py +2 -0
- ccxt/whitebit.py +5 -4
- ccxt/woo.py +15 -5
- ccxt/woofipro.py +23 -9
- ccxt/xt.py +5 -0
- ccxt/yobit.py +5 -2
- ccxt/zaif.py +2 -0
- ccxt/zonda.py +2 -0
- {ccxt-4.4.52.dist-info → ccxt-4.4.57.dist-info}/METADATA +225 -143
- {ccxt-4.4.52.dist-info → ccxt-4.4.57.dist-info}/RECORD +220 -223
- ccxt/abstract/lykke.py +0 -29
- ccxt/async_support/lykke.py +0 -1370
- ccxt/lykke.py +0 -1370
- {ccxt-4.4.52.dist-info → ccxt-4.4.57.dist-info}/LICENSE.txt +0 -0
- {ccxt-4.4.52.dist-info → ccxt-4.4.57.dist-info}/WHEEL +0 -0
- {ccxt-4.4.52.dist-info → ccxt-4.4.57.dist-info}/top_level.txt +0 -0
ccxt/test/tests_sync.py
CHANGED
@@ -46,16 +46,16 @@ class testMainClass:
|
|
46
46
|
if self.request_tests and self.response_tests:
|
47
47
|
self.run_static_request_tests(exchange_id, symbol_argv)
|
48
48
|
self.run_static_response_tests(exchange_id, symbol_argv)
|
49
|
-
return
|
49
|
+
return True
|
50
50
|
if self.response_tests:
|
51
51
|
self.run_static_response_tests(exchange_id, symbol_argv)
|
52
|
-
return
|
52
|
+
return True
|
53
53
|
if self.request_tests:
|
54
54
|
self.run_static_request_tests(exchange_id, symbol_argv) # symbol here is the testname
|
55
|
-
return
|
55
|
+
return True
|
56
56
|
if self.id_tests:
|
57
57
|
self.run_broker_id_tests()
|
58
|
-
return
|
58
|
+
return True
|
59
59
|
new_line = '\n'
|
60
60
|
dump(new_line + '' + new_line + '' + '[INFO] TESTING ', self.ext, {
|
61
61
|
'exchange': exchange_id,
|
@@ -100,6 +100,7 @@ class testMainClass:
|
|
100
100
|
self.test_files = get_test_files_sync(properties, self.ws_tests)
|
101
101
|
else:
|
102
102
|
self.test_files = get_test_files(properties, self.ws_tests)
|
103
|
+
return True
|
103
104
|
|
104
105
|
def load_credentials_from_env(self, exchange):
|
105
106
|
exchange_id = exchange.id
|
@@ -122,8 +123,12 @@ class testMainClass:
|
|
122
123
|
keys_local = get_root_dir() + 'keys.local.json'
|
123
124
|
keys_global_exists = io_file_exists(keys_global)
|
124
125
|
keys_local_exists = io_file_exists(keys_local)
|
125
|
-
global_settings =
|
126
|
-
|
126
|
+
global_settings = {}
|
127
|
+
if keys_global_exists:
|
128
|
+
global_settings = io_file_read(keys_global)
|
129
|
+
local_settings = {}
|
130
|
+
if keys_local_exists:
|
131
|
+
local_settings = io_file_read(keys_local)
|
127
132
|
all_settings = exchange.deep_extend(global_settings, local_settings)
|
128
133
|
exchange_settings = exchange.safe_value(all_settings, exchange_id, {})
|
129
134
|
if exchange_settings:
|
@@ -174,7 +179,7 @@ class testMainClass:
|
|
174
179
|
exchange.options['checksum'] = False
|
175
180
|
# todo: temporary skip for php
|
176
181
|
if 'OrderBook' in method_name and self.ext == 'php':
|
177
|
-
return
|
182
|
+
return True
|
178
183
|
skipped_properties_for_method = self.get_skips(exchange, method_name)
|
179
184
|
is_load_markets = (method_name == 'loadMarkets')
|
180
185
|
is_fetch_currencies = (method_name == 'fetchCurrencies')
|
@@ -182,7 +187,7 @@ class testMainClass:
|
|
182
187
|
is_feature_test = (method_name == 'features')
|
183
188
|
# if this is a private test, and the implementation was already tested in public, then no need to re-test it in private test (exception is fetchCurrencies, because our approach in base exchange)
|
184
189
|
if not is_public and (method_name in self.checked_public_tests) and not is_fetch_currencies:
|
185
|
-
return
|
190
|
+
return True
|
186
191
|
skip_message = None
|
187
192
|
supported_by_exchange = (method_name in exchange.has) and exchange.has[method_name]
|
188
193
|
if not is_load_markets and (len(self.only_specific_tests) > 0 and not exchange.in_array(method_name, self.only_specific_tests)):
|
@@ -200,7 +205,7 @@ class testMainClass:
|
|
200
205
|
if skip_message:
|
201
206
|
if self.info:
|
202
207
|
dump(self.add_padding(skip_message, 25), name, method_name)
|
203
|
-
return
|
208
|
+
return True
|
204
209
|
if self.info:
|
205
210
|
args_stringified = '(' + exchange.json(args) + ')' # args.join() breaks when we provide a list of symbols or multidimensional array; "args.toString()" breaks bcz of "array to string conversion"
|
206
211
|
dump(self.add_padding('[INFO] TESTING', 25), name, method_name, args_stringified)
|
@@ -213,7 +218,7 @@ class testMainClass:
|
|
213
218
|
# add to the list of successed tests
|
214
219
|
if is_public:
|
215
220
|
self.checked_public_tests[method_name] = True
|
216
|
-
return
|
221
|
+
return True
|
217
222
|
|
218
223
|
def get_skips(self, exchange, method_name):
|
219
224
|
final_skips = {}
|
@@ -282,10 +287,10 @@ class testMainClass:
|
|
282
287
|
is_on_maintenance = (isinstance(e, OnMaintenance))
|
283
288
|
is_exchange_not_available = (isinstance(e, ExchangeNotAvailable))
|
284
289
|
should_fail = None
|
285
|
-
|
290
|
+
ret_success = None
|
286
291
|
if is_load_markets:
|
287
292
|
# if "loadMarkets" does not succeed, we must return "false" to caller method, to stop tests continual
|
288
|
-
|
293
|
+
ret_success = False
|
289
294
|
# we might not break exchange tests, if exchange is on maintenance at this moment
|
290
295
|
if is_on_maintenance:
|
291
296
|
should_fail = False
|
@@ -296,20 +301,19 @@ class testMainClass:
|
|
296
301
|
if is_exchange_not_available and not is_on_maintenance:
|
297
302
|
# break exchange tests if "ExchangeNotAvailable" exception is thrown, but it's not maintenance
|
298
303
|
should_fail = True
|
299
|
-
|
304
|
+
ret_success = False
|
300
305
|
else:
|
301
306
|
# in all other cases of OperationFailed, show Warning, but don't mark test as failed
|
302
307
|
should_fail = False
|
303
|
-
|
308
|
+
ret_success = True
|
304
309
|
# output the message
|
305
310
|
fail_type = '[TEST_FAILURE]' if should_fail else '[TEST_WARNING]'
|
306
311
|
dump(fail_type, 'Method could not be tested due to a repeated Network/Availability issues', ' | ', exchange.id, method_name, args_stringified, exception_message(e))
|
307
|
-
return
|
312
|
+
return ret_success
|
308
313
|
else:
|
309
314
|
# wait and retry again
|
310
315
|
# (increase wait time on every retry)
|
311
316
|
exchange.sleep((i + 1) * 1000)
|
312
|
-
continue
|
313
317
|
else:
|
314
318
|
# if it's loadMarkets, then fail test, because it's mandatory for tests
|
315
319
|
if is_load_markets:
|
@@ -373,6 +377,7 @@ class testMainClass:
|
|
373
377
|
tests['fetchPremiumIndexOHLCV'] = [symbol]
|
374
378
|
self.public_tests = tests
|
375
379
|
self.run_tests(exchange, tests, True)
|
380
|
+
return True
|
376
381
|
|
377
382
|
def run_tests(self, exchange, tests, is_public_test):
|
378
383
|
test_names = list(tests.keys())
|
@@ -397,6 +402,7 @@ class testMainClass:
|
|
397
402
|
dump('[TEST_FAILURE]', exchange.id, test_prefix_string, 'Failed methods : ' + errors_string)
|
398
403
|
if self.info:
|
399
404
|
dump(self.add_padding('[INFO] END ' + test_prefix_string + ' ' + exchange.id, 25))
|
405
|
+
return True
|
400
406
|
|
401
407
|
def load_exchange(self, exchange):
|
402
408
|
result = self.test_safe('loadMarkets', exchange, [], True)
|
@@ -517,11 +523,12 @@ class testMainClass:
|
|
517
523
|
if exchange.has['swap'] and swap_symbol is not None:
|
518
524
|
exchange.options['defaultType'] = 'swap'
|
519
525
|
self.run_private_tests(exchange, swap_symbol)
|
526
|
+
return True
|
520
527
|
|
521
528
|
def run_private_tests(self, exchange, symbol):
|
522
529
|
if not exchange.check_required_credentials(False):
|
523
530
|
dump('[INFO] Skipping private tests', 'Keys not found')
|
524
|
-
return
|
531
|
+
return True
|
525
532
|
code = self.get_exchange_code(exchange)
|
526
533
|
# if (exchange.deepExtendedTest) {
|
527
534
|
# test ('InvalidNonce', exchange, symbol);
|
@@ -591,14 +598,14 @@ class testMainClass:
|
|
591
598
|
proxy_test_name = self.proxy_test_file_name
|
592
599
|
# todo: temporary skip for sync py
|
593
600
|
if self.ext == 'py' and is_sync():
|
594
|
-
return
|
601
|
+
return True
|
595
602
|
# try proxy several times
|
596
603
|
max_retries = 3
|
597
604
|
exception = None
|
598
605
|
for j in range(0, max_retries):
|
599
606
|
try:
|
600
607
|
self.test_method(proxy_test_name, exchange, [], True)
|
601
|
-
return # if successfull, then end the test
|
608
|
+
return True # if successfull, then end the test
|
602
609
|
except Exception as e:
|
603
610
|
exception = e
|
604
611
|
exchange.sleep(j * 1000)
|
@@ -607,12 +614,13 @@ class testMainClass:
|
|
607
614
|
error_message = '[TEST_FAILURE] Failed ' + proxy_test_name + ' : ' + exception_message(exception)
|
608
615
|
# temporary comment the below, because c# transpilation failure
|
609
616
|
# throw new Exchange Error (errorMessage.toString ());
|
610
|
-
dump('[TEST_WARNING]' +
|
617
|
+
dump('[TEST_WARNING]' + error_message)
|
618
|
+
return True
|
611
619
|
|
612
620
|
def start_test(self, exchange, symbol):
|
613
621
|
# we do not need to test aliases
|
614
622
|
if exchange.alias:
|
615
|
-
return
|
623
|
+
return True
|
616
624
|
if self.sandbox or get_exchange_prop(exchange, 'sandbox'):
|
617
625
|
exchange.set_sandbox_mode(True)
|
618
626
|
try:
|
@@ -620,7 +628,7 @@ class testMainClass:
|
|
620
628
|
if not result:
|
621
629
|
if not is_sync():
|
622
630
|
close(exchange)
|
623
|
-
return
|
631
|
+
return True
|
624
632
|
# if (exchange.id === 'binance') {
|
625
633
|
# # we test proxies functionality just for one random exchange on each build, because proxy functionality is not exchange-specific, instead it's all done from base methods, so just one working sample would mean it works for all ccxt exchanges
|
626
634
|
# # this.testProxies (exchange);
|
@@ -795,13 +803,15 @@ class testMainClass:
|
|
795
803
|
return True # c# requ
|
796
804
|
|
797
805
|
def assert_new_and_stored_output(self, exchange, skip_keys, new_output, stored_output, strict_type_check=True, asserting_key=None):
|
806
|
+
res = True
|
798
807
|
try:
|
799
|
-
|
808
|
+
res = self.assert_new_and_stored_output_inner(exchange, skip_keys, new_output, stored_output, strict_type_check, asserting_key)
|
800
809
|
except Exception as e:
|
801
810
|
if self.info:
|
802
811
|
error_message = self.var_to_string(new_output) + '(calculated)' + ' != ' + self.var_to_string(stored_output) + '(stored)'
|
803
812
|
dump('[TEST_FAILURE_DETAIL]' + error_message)
|
804
813
|
raise e
|
814
|
+
return res
|
805
815
|
|
806
816
|
def var_to_string(self, obj=None):
|
807
817
|
new_string = None
|
@@ -830,11 +840,11 @@ class testMainClass:
|
|
830
840
|
if (stored_url_query is None) and (new_url_query is None):
|
831
841
|
# might be a get request without any query parameters
|
832
842
|
# example: https://api.gateio.ws/api/v4/delivery/usdt/positions
|
833
|
-
return
|
843
|
+
return True
|
834
844
|
stored_url_params = self.urlencoded_to_dict(stored_url_query)
|
835
845
|
new_url_params = self.urlencoded_to_dict(new_url_query)
|
836
846
|
self.assert_new_and_stored_output(exchange, skip_keys, new_url_params, stored_url_params)
|
837
|
-
return
|
847
|
+
return True
|
838
848
|
if type == 'json' and (stored_output is not None) and (new_output is not None):
|
839
849
|
if isinstance(stored_output, str):
|
840
850
|
stored_output = json_parse(stored_output)
|
@@ -851,6 +861,7 @@ class testMainClass:
|
|
851
861
|
stored_output = self.urlencoded_to_dict(stored_output)
|
852
862
|
new_output = self.urlencoded_to_dict(new_output)
|
853
863
|
self.assert_new_and_stored_output(exchange, skip_keys, new_output, stored_output)
|
864
|
+
return True
|
854
865
|
|
855
866
|
def assert_static_response_output(self, exchange, skip_keys, computed_result, stored_result):
|
856
867
|
self.assert_new_and_stored_output(exchange, skip_keys, computed_result, stored_result, False)
|
@@ -886,8 +897,9 @@ class testMainClass:
|
|
886
897
|
self.assert_static_request_output(exchange, type, skip_keys, data['url'], request_url, call_output, output)
|
887
898
|
except Exception as e:
|
888
899
|
self.request_tests_failed = True
|
889
|
-
error_message = '[' + self.lang + '][STATIC_REQUEST]' + '[' + exchange.id + ']' + '[' + method + ']' + '[' + data['description'] + ']' +
|
900
|
+
error_message = '[' + self.lang + '][STATIC_REQUEST]' + '[' + exchange.id + ']' + '[' + method + ']' + '[' + data['description'] + ']' + exception_message(e)
|
890
901
|
dump('[TEST_FAILURE]' + error_message)
|
902
|
+
return True
|
891
903
|
|
892
904
|
def test_response_statically(self, exchange, method, skip_keys, data):
|
893
905
|
expected_result = exchange.safe_value(data, 'parsedResponse')
|
@@ -901,9 +913,10 @@ class testMainClass:
|
|
901
913
|
self.assert_static_response_output(mocked_exchange, skip_keys, unified_result_sync, expected_result)
|
902
914
|
except Exception as e:
|
903
915
|
self.response_tests_failed = True
|
904
|
-
error_message = '[' + self.lang + '][STATIC_RESPONSE]' + '[' + exchange.id + ']' + '[' + method + ']' + '[' + data['description'] + ']' +
|
916
|
+
error_message = '[' + self.lang + '][STATIC_RESPONSE]' + '[' + exchange.id + ']' + '[' + method + ']' + '[' + data['description'] + ']' + exception_message(e)
|
905
917
|
dump('[TEST_FAILURE]' + error_message)
|
906
918
|
set_fetch_response(exchange, None) # reset state
|
919
|
+
return True
|
907
920
|
|
908
921
|
def init_offline_exchange(self, exchange_name):
|
909
922
|
markets = self.load_markets_from_file(exchange_name)
|
@@ -939,7 +952,8 @@ class testMainClass:
|
|
939
952
|
'leverageBrackets': {},
|
940
953
|
},
|
941
954
|
})
|
942
|
-
exchange.currencies = currencies
|
955
|
+
exchange.currencies = currencies
|
956
|
+
# not working in python if assigned in the config dict
|
943
957
|
return exchange
|
944
958
|
|
945
959
|
def test_exchange_request_statically(self, exchange_name, exchange_data, test_name=None):
|
@@ -987,6 +1001,9 @@ class testMainClass:
|
|
987
1001
|
is_disabled_c_sharp = exchange.safe_bool(result, 'disabledCS', False)
|
988
1002
|
if is_disabled_c_sharp and (self.lang == 'C#'):
|
989
1003
|
continue
|
1004
|
+
is_disabled_go = exchange.safe_bool(result, 'disabledGO', False)
|
1005
|
+
if is_disabled_go and (self.lang == 'GO'):
|
1006
|
+
continue
|
990
1007
|
type = exchange.safe_string(exchange_data, 'outputType')
|
991
1008
|
skip_keys = exchange.safe_value(exchange_data, 'skipKeys', [])
|
992
1009
|
self.test_request_statically(exchange, method, result, type, skip_keys)
|
@@ -1038,6 +1055,9 @@ class testMainClass:
|
|
1038
1055
|
continue
|
1039
1056
|
if (test_name is not None) and (test_name != description):
|
1040
1057
|
continue
|
1058
|
+
is_disabled_go = exchange.safe_bool(result, 'disabledGO', False)
|
1059
|
+
if is_disabled_go and (self.lang == 'GO'):
|
1060
|
+
continue
|
1041
1061
|
skip_keys = exchange.safe_value(exchange_data, 'skipKeys', [])
|
1042
1062
|
self.test_response_statically(exchange, method, skip_keys, result)
|
1043
1063
|
# reset options
|
@@ -1062,12 +1082,13 @@ class testMainClass:
|
|
1062
1082
|
|
1063
1083
|
def run_static_request_tests(self, target_exchange=None, test_name=None):
|
1064
1084
|
self.run_static_tests('request', target_exchange, test_name)
|
1085
|
+
return True
|
1065
1086
|
|
1066
1087
|
def run_static_tests(self, type, target_exchange=None, test_name=None):
|
1067
1088
|
folder = get_root_dir() + './ts/src/test/static/' + type + '/'
|
1068
1089
|
static_data = self.load_static_data(folder, target_exchange)
|
1069
1090
|
if static_data is None:
|
1070
|
-
return
|
1091
|
+
return True
|
1071
1092
|
exchanges = list(static_data.keys())
|
1072
1093
|
exchange = init_exchange('Exchange', {}) # tmp to do the calculations until we have the ast-transpiler transpiling this code
|
1073
1094
|
promises = []
|
@@ -1092,7 +1113,7 @@ class testMainClass:
|
|
1092
1113
|
self.request_tests_failed = True
|
1093
1114
|
else:
|
1094
1115
|
self.response_tests_failed = True
|
1095
|
-
error_message = '[' + self.lang + '][STATIC_REQUEST]' +
|
1116
|
+
error_message = '[' + self.lang + '][STATIC_REQUEST]' + exception_message(e)
|
1096
1117
|
dump('[TEST_FAILURE]' + error_message)
|
1097
1118
|
if self.request_tests_failed or self.response_tests_failed:
|
1098
1119
|
exit_script(1)
|
@@ -1106,6 +1127,7 @@ class testMainClass:
|
|
1106
1127
|
# --- Init of mockResponses tests functions------------------------------------
|
1107
1128
|
# -----------------------------------------------------------------------------
|
1108
1129
|
self.run_static_tests('response', exchange_name, test)
|
1130
|
+
return True
|
1109
1131
|
|
1110
1132
|
def run_broker_id_tests(self):
|
1111
1133
|
# -----------------------------------------------------------------------------
|
@@ -1116,6 +1138,7 @@ class testMainClass:
|
|
1116
1138
|
success_message = '[' + self.lang + '][TEST_SUCCESS] brokerId tests passed.'
|
1117
1139
|
dump('[INFO]' + success_message)
|
1118
1140
|
exit_script(0)
|
1141
|
+
return True
|
1119
1142
|
|
1120
1143
|
def test_binance(self):
|
1121
1144
|
exchange = self.init_offline_exchange('binance')
|
@@ -1369,6 +1392,7 @@ class testMainClass:
|
|
1369
1392
|
assert req_headers['X-SOURCE-KEY'] == id, 'bingx - id: ' + id + ' not in headers.'
|
1370
1393
|
if not is_sync():
|
1371
1394
|
close(exchange)
|
1395
|
+
return True
|
1372
1396
|
|
1373
1397
|
def test_phemex(self):
|
1374
1398
|
exchange = self.init_offline_exchange('phemex')
|
@@ -1383,6 +1407,7 @@ class testMainClass:
|
|
1383
1407
|
assert client_order_id.startswith(id_string), 'phemex - clOrdID: ' + client_order_id + ' does not start with id: ' + id_string
|
1384
1408
|
if not is_sync():
|
1385
1409
|
close(exchange)
|
1410
|
+
return True
|
1386
1411
|
|
1387
1412
|
def test_blofin(self):
|
1388
1413
|
exchange = self.init_offline_exchange('blofin')
|
@@ -1397,6 +1422,7 @@ class testMainClass:
|
|
1397
1422
|
assert broker_id.startswith(id_string), 'blofin - brokerId: ' + broker_id + ' does not start with id: ' + id_string
|
1398
1423
|
if not is_sync():
|
1399
1424
|
close(exchange)
|
1425
|
+
return True
|
1400
1426
|
|
1401
1427
|
def test_hyperliquid(self):
|
1402
1428
|
exchange = self.init_offline_exchange('hyperliquid')
|
@@ -1410,6 +1436,7 @@ class testMainClass:
|
|
1410
1436
|
assert broker_id == id, 'hyperliquid - brokerId: ' + broker_id + ' does not start with id: ' + id
|
1411
1437
|
if not is_sync():
|
1412
1438
|
close(exchange)
|
1439
|
+
return True
|
1413
1440
|
|
1414
1441
|
def test_coinbaseinternational(self):
|
1415
1442
|
exchange = self.init_offline_exchange('coinbaseinternational')
|
ccxt/timex.py
CHANGED
@@ -313,17 +313,20 @@ class timex(Exchange, ImplicitAPI):
|
|
313
313
|
'limit': 100, # todo
|
314
314
|
'daysBack': 100000, # todo
|
315
315
|
'untilDays': 100000, # todo
|
316
|
+
'symbolRequired': False,
|
316
317
|
},
|
317
318
|
'fetchOrder': {
|
318
319
|
'marginMode': False,
|
319
320
|
'trigger': False,
|
320
321
|
'trailing': False,
|
322
|
+
'symbolRequired': False,
|
321
323
|
},
|
322
324
|
'fetchOpenOrders': {
|
323
325
|
'marginMode': False,
|
324
326
|
'limit': 100, # todo
|
325
327
|
'trigger': False,
|
326
328
|
'trailing': False,
|
329
|
+
'symbolRequired': False,
|
327
330
|
},
|
328
331
|
'fetchOrders': None, # todo
|
329
332
|
'fetchClosedOrders': {
|
@@ -334,6 +337,7 @@ class timex(Exchange, ImplicitAPI):
|
|
334
337
|
'untilDays': 100000, # todo
|
335
338
|
'trigger': False,
|
336
339
|
'trailing': False,
|
340
|
+
'symbolRequired': False,
|
337
341
|
},
|
338
342
|
'fetchOHLCV': {
|
339
343
|
'limit': None,
|
ccxt/tokocrypto.py
CHANGED
@@ -650,17 +650,20 @@ class tokocrypto(Exchange, ImplicitAPI):
|
|
650
650
|
'limit': 1000,
|
651
651
|
'daysBack': 100000, # todo
|
652
652
|
'untilDays': 100000, # todo
|
653
|
+
'symbolRequired': True,
|
653
654
|
},
|
654
655
|
'fetchOrder': {
|
655
656
|
'marginMode': False,
|
656
657
|
'trigger': False,
|
657
658
|
'trailing': False,
|
659
|
+
'symbolRequired': False,
|
658
660
|
},
|
659
661
|
'fetchOpenOrders': {
|
660
662
|
'marginMode': False,
|
661
663
|
'limit': 1000,
|
662
664
|
'trigger': False,
|
663
665
|
'trailing': False,
|
666
|
+
'symbolRequired': True,
|
664
667
|
},
|
665
668
|
'fetchOrders': {
|
666
669
|
'marginMode': False,
|
@@ -669,6 +672,7 @@ class tokocrypto(Exchange, ImplicitAPI):
|
|
669
672
|
'untilDays': 100000,
|
670
673
|
'trigger': False,
|
671
674
|
'trailing': False,
|
675
|
+
'symbolRequired': True,
|
672
676
|
},
|
673
677
|
'fetchClosedOrders': {
|
674
678
|
'marginMode': False,
|
@@ -678,6 +682,7 @@ class tokocrypto(Exchange, ImplicitAPI):
|
|
678
682
|
'untilDays': 100000, # todo
|
679
683
|
'trigger': False,
|
680
684
|
'trailing': False,
|
685
|
+
'symbolRequired': True,
|
681
686
|
},
|
682
687
|
'fetchOHLCV': {
|
683
688
|
'limit': 1000,
|
ccxt/tradeogre.py
CHANGED
@@ -195,12 +195,14 @@ class tradeogre(Exchange, ImplicitAPI):
|
|
195
195
|
'marginMode': False,
|
196
196
|
'trigger': False,
|
197
197
|
'trailing': False,
|
198
|
+
'symbolRequired': False,
|
198
199
|
},
|
199
200
|
'fetchOpenOrders': {
|
200
201
|
'marginMode': False,
|
201
202
|
'limit': None,
|
202
203
|
'trigger': False,
|
203
204
|
'trailing': False,
|
205
|
+
'symbolRequired': False,
|
204
206
|
},
|
205
207
|
'fetchOrders': None,
|
206
208
|
'fetchClosedOrders': None,
|
ccxt/upbit.py
CHANGED
@@ -5,7 +5,7 @@
|
|
5
5
|
|
6
6
|
from ccxt.base.exchange import Exchange
|
7
7
|
from ccxt.abstract.upbit import ImplicitAPI
|
8
|
-
from ccxt.base.types import Balances, Currency, DepositAddress, Int, Market, Num, Order, OrderBook, OrderSide, OrderType, Str, Strings, Ticker, Tickers, Trade, TradingFeeInterface, Transaction
|
8
|
+
from ccxt.base.types import Balances, Currency, DepositAddress, Int, Market, Num, Order, OrderBook, OrderSide, OrderType, Str, Strings, Ticker, Tickers, OrderBooks, Trade, TradingFeeInterface, Transaction
|
9
9
|
from typing import List
|
10
10
|
from ccxt.base.errors import ExchangeError
|
11
11
|
from ccxt.base.errors import AuthenticationError
|
@@ -202,12 +202,14 @@ class upbit(Exchange, ImplicitAPI):
|
|
202
202
|
'marginMode': False,
|
203
203
|
'trigger': False,
|
204
204
|
'trailing': False,
|
205
|
+
'symbolRequired': False,
|
205
206
|
},
|
206
207
|
'fetchOpenOrders': {
|
207
208
|
'marginMode': True,
|
208
209
|
'limit': 100,
|
209
210
|
'trigger': False,
|
210
211
|
'trailing': False,
|
212
|
+
'symbolRequired': False,
|
211
213
|
},
|
212
214
|
'fetchOrders': None, # todo
|
213
215
|
'fetchClosedOrders': {
|
@@ -218,6 +220,7 @@ class upbit(Exchange, ImplicitAPI):
|
|
218
220
|
'untilDays': 7,
|
219
221
|
'trigger': False,
|
220
222
|
'trailing': False,
|
223
|
+
'symbolRequired': False,
|
221
224
|
},
|
222
225
|
'fetchOHLCV': {
|
223
226
|
'limit': 200,
|
@@ -588,7 +591,7 @@ class upbit(Exchange, ImplicitAPI):
|
|
588
591
|
#
|
589
592
|
return self.parse_balance(response)
|
590
593
|
|
591
|
-
def fetch_order_books(self, symbols: Strings = None, limit: Int = None, params={}):
|
594
|
+
def fetch_order_books(self, symbols: Strings = None, limit: Int = None, params={}) -> OrderBooks:
|
592
595
|
"""
|
593
596
|
|
594
597
|
https://docs.upbit.com/reference/%ED%98%B8%EA%B0%80-%EC%A0%95%EB%B3%B4-%EC%A1%B0%ED%9A%8C
|
ccxt/vertex.py
CHANGED
@@ -361,17 +361,20 @@ class vertex(Exchange, ImplicitAPI):
|
|
361
361
|
'limit': 500,
|
362
362
|
'daysBack': 100000, # todo
|
363
363
|
'untilDays': None,
|
364
|
+
'symbolRequired': False,
|
364
365
|
},
|
365
366
|
'fetchOrder': {
|
366
367
|
'marginMode': False,
|
367
368
|
'trigger': False,
|
368
369
|
'trailing': False,
|
370
|
+
'symbolRequired': True,
|
369
371
|
},
|
370
372
|
'fetchOpenOrders': {
|
371
373
|
'marginMode': False,
|
372
374
|
'limit': 500,
|
373
375
|
'trigger': True,
|
374
376
|
'trailing': False,
|
377
|
+
'symbolRequired': False,
|
375
378
|
},
|
376
379
|
'fetchOrders': None, # todo, only for trigger
|
377
380
|
'fetchClosedOrders': None, # todo through fetchOrders
|
@@ -2394,14 +2397,15 @@ class vertex(Exchange, ImplicitAPI):
|
|
2394
2397
|
'digests': ids,
|
2395
2398
|
'nonce': nonce,
|
2396
2399
|
}
|
2400
|
+
productIds = cancels['productIds']
|
2397
2401
|
marketIdNum = self.parse_to_numeric(marketId)
|
2398
2402
|
for i in range(0, len(ids)):
|
2399
|
-
|
2403
|
+
productIds.append(marketIdNum)
|
2400
2404
|
request = {
|
2401
2405
|
'cancel_orders': {
|
2402
2406
|
'tx': {
|
2403
2407
|
'sender': cancels['sender'],
|
2404
|
-
'productIds':
|
2408
|
+
'productIds': productIds,
|
2405
2409
|
'digests': cancels['digests'],
|
2406
2410
|
'nonce': self.number_to_string(cancels['nonce']),
|
2407
2411
|
},
|
ccxt/wavesexchange.py
CHANGED
@@ -388,17 +388,20 @@ class wavesexchange(Exchange, ImplicitAPI):
|
|
388
388
|
'limit': 100, # todo
|
389
389
|
'daysBack': 100000, # todo
|
390
390
|
'untilDays': 100000, # todo
|
391
|
+
'symbolRequired': False,
|
391
392
|
},
|
392
393
|
'fetchOrder': {
|
393
394
|
'marginMode': False,
|
394
395
|
'trigger': False,
|
395
396
|
'trailing': False,
|
397
|
+
'symbolRequired': False,
|
396
398
|
},
|
397
399
|
'fetchOpenOrders': {
|
398
400
|
'marginMode': False,
|
399
401
|
'limit': 100, # todo
|
400
402
|
'trigger': False,
|
401
403
|
'trailing': False,
|
404
|
+
'symbolRequired': False,
|
402
405
|
},
|
403
406
|
'fetchOrders': {
|
404
407
|
'marginMode': False,
|
@@ -407,6 +410,7 @@ class wavesexchange(Exchange, ImplicitAPI):
|
|
407
410
|
'untilDays': None,
|
408
411
|
'trigger': False,
|
409
412
|
'trailing': False,
|
413
|
+
'symbolRequired': True,
|
410
414
|
}, # todo
|
411
415
|
'fetchClosedOrders': {
|
412
416
|
'marginMode': False,
|
@@ -416,6 +420,7 @@ class wavesexchange(Exchange, ImplicitAPI):
|
|
416
420
|
'untilDays': 100000, # todo
|
417
421
|
'trigger': False,
|
418
422
|
'trailing': False,
|
423
|
+
'symbolRequired': False,
|
419
424
|
},
|
420
425
|
'fetchOHLCV': {
|
421
426
|
'limit': None, # todo
|
@@ -754,6 +759,7 @@ class wavesexchange(Exchange, ImplicitAPI):
|
|
754
759
|
raise AuthenticationError(self.id + ' apiKey must be a base58 encoded public key')
|
755
760
|
if len(hexSecretKeyBytes) != 64:
|
756
761
|
raise AuthenticationError(self.id + ' secret must be a base58 encoded private key')
|
762
|
+
return True
|
757
763
|
|
758
764
|
def sign(self, path, api='public', method='GET', params={}, headers=None, body=None):
|
759
765
|
query = self.omit(params, self.extract_params(path))
|
@@ -1007,6 +1013,7 @@ class wavesexchange(Exchange, ImplicitAPI):
|
|
1007
1013
|
:param int [since]: timestamp in ms of the earliest candle to fetch
|
1008
1014
|
:param int [limit]: the maximum amount of candles to fetch
|
1009
1015
|
:param dict [params]: extra parameters specific to the exchange API endpoint
|
1016
|
+
:param int [params.until]: timestamp in ms of the latest candle to fetch
|
1010
1017
|
:returns int[][]: A list of candles ordered, open, high, low, close, volume
|
1011
1018
|
"""
|
1012
1019
|
self.load_markets()
|
@@ -1017,19 +1024,29 @@ class wavesexchange(Exchange, ImplicitAPI):
|
|
1017
1024
|
'interval': self.safe_string(self.timeframes, timeframe, timeframe),
|
1018
1025
|
}
|
1019
1026
|
allowedCandles = self.safe_integer(self.options, 'allowedCandles', 1440)
|
1027
|
+
until = self.safe_integer(params, 'until')
|
1028
|
+
untilIsDefined = until is not None
|
1020
1029
|
if limit is None:
|
1021
1030
|
limit = allowedCandles
|
1022
1031
|
limit = min(allowedCandles, limit)
|
1023
1032
|
duration = self.parse_timeframe(timeframe) * 1000
|
1024
1033
|
if since is None:
|
1025
|
-
|
1034
|
+
now = self.milliseconds()
|
1035
|
+
timeEnd = until if untilIsDefined else now
|
1036
|
+
durationRoundedTimestamp = self.parse_to_int(timeEnd / duration) * duration
|
1026
1037
|
delta = (limit - 1) * duration
|
1027
1038
|
timeStart = durationRoundedTimestamp - delta
|
1028
1039
|
request['timeStart'] = str(timeStart)
|
1040
|
+
if untilIsDefined:
|
1041
|
+
request['timeEnd'] = str(until)
|
1029
1042
|
else:
|
1030
1043
|
request['timeStart'] = str(since)
|
1031
|
-
|
1032
|
-
|
1044
|
+
if untilIsDefined:
|
1045
|
+
request['timeEnd'] = str(until)
|
1046
|
+
else:
|
1047
|
+
timeEnd = self.sum(since, duration * limit)
|
1048
|
+
request['timeEnd'] = str(timeEnd)
|
1049
|
+
params = self.omit(params, 'until')
|
1033
1050
|
response = self.publicGetCandlesBaseIdQuoteId(self.extend(request, params))
|
1034
1051
|
#
|
1035
1052
|
# {
|
ccxt/wazirx.py
CHANGED
@@ -250,6 +250,7 @@ class wazirx(Exchange, ImplicitAPI):
|
|
250
250
|
'limit': None,
|
251
251
|
'trigger': False,
|
252
252
|
'trailing': False,
|
253
|
+
'symbolRequired': True,
|
253
254
|
},
|
254
255
|
'fetchOrders': {
|
255
256
|
'marginMode': False,
|
@@ -258,6 +259,7 @@ class wazirx(Exchange, ImplicitAPI):
|
|
258
259
|
'untilDays': 100000, # todo
|
259
260
|
'trigger': False,
|
260
261
|
'trailing': False,
|
262
|
+
'symbolRequired': True,
|
261
263
|
},
|
262
264
|
'fetchClosedOrders': None,
|
263
265
|
'fetchOHLCV': {
|
ccxt/whitebit.py
CHANGED
@@ -322,6 +322,7 @@ class whitebit(Exchange, ImplicitAPI):
|
|
322
322
|
'limit': 100,
|
323
323
|
'daysBack': None,
|
324
324
|
'untilDays': None,
|
325
|
+
'symbolRequired': False,
|
325
326
|
},
|
326
327
|
'fetchOrder': None,
|
327
328
|
'fetchOpenOrders': {
|
@@ -329,6 +330,7 @@ class whitebit(Exchange, ImplicitAPI):
|
|
329
330
|
'limit': 100,
|
330
331
|
'trigger': False,
|
331
332
|
'trailing': False,
|
333
|
+
'symbolRequired': False,
|
332
334
|
},
|
333
335
|
'fetchOrders': None, # todo
|
334
336
|
'fetchClosedOrders': {
|
@@ -339,6 +341,7 @@ class whitebit(Exchange, ImplicitAPI):
|
|
339
341
|
'untilDays': None,
|
340
342
|
'trigger': False,
|
341
343
|
'trailing': False,
|
344
|
+
'symbolRequired': False,
|
342
345
|
},
|
343
346
|
'fetchOHLCV': {
|
344
347
|
'limit': 1440,
|
@@ -1314,11 +1317,9 @@ class whitebit(Exchange, ImplicitAPI):
|
|
1314
1317
|
:param dict [params]: extra parameters specific to the exchange API endpoint
|
1315
1318
|
:returns dict: an `order structure <https://docs.ccxt.com/#/?id=order-structure>`
|
1316
1319
|
"""
|
1317
|
-
|
1318
|
-
'cost': cost,
|
1319
|
-
}
|
1320
|
+
params['cost'] = cost
|
1320
1321
|
# only buy side is supported
|
1321
|
-
return self.create_order(symbol, 'market', side, 0, None,
|
1322
|
+
return self.create_order(symbol, 'market', side, 0, None, params)
|
1322
1323
|
|
1323
1324
|
def create_market_buy_order_with_cost(self, symbol: str, cost: float, params={}) -> Order:
|
1324
1325
|
"""
|