ccxt 4.4.51__py2.py3-none-any.whl → 4.4.53__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 -1
- 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 -1
- 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 +20 -3
- ccxt/async_support/bigone.py +5 -0
- ccxt/async_support/binance.py +55 -69
- 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 +8 -4
- 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 +10 -4
- ccxt/async_support/cex.py +2 -0
- ccxt/async_support/coinbase.py +19 -12
- ccxt/async_support/coinbaseexchange.py +5 -0
- ccxt/async_support/coinbaseinternational.py +21 -2
- ccxt/async_support/coincatch.py +3 -0
- ccxt/async_support/coincheck.py +2 -0
- ccxt/async_support/coinex.py +5 -1
- 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 +16 -5
- ccxt/async_support/digifinex.py +10 -5
- ccxt/async_support/ellipx.py +9 -5
- ccxt/async_support/exmo.py +6 -3
- 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 +6 -1
- 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 +6 -4
- 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/lykke.py +4 -0
- 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 +21 -7
- 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 +113 -50
- ccxt/base/types.py +1 -1
- ccxt/bigone.py +5 -0
- ccxt/binance.py +55 -69
- 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 +8 -4
- 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 +10 -4
- ccxt/cex.py +2 -0
- ccxt/coinbase.py +19 -12
- ccxt/coinbaseexchange.py +5 -0
- ccxt/coinbaseinternational.py +21 -2
- ccxt/coincatch.py +3 -0
- ccxt/coincheck.py +2 -0
- ccxt/coinex.py +5 -1
- 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 +16 -5
- ccxt/digifinex.py +10 -5
- ccxt/ellipx.py +9 -5
- ccxt/exmo.py +6 -3
- 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 +6 -1
- ccxt/idex.py +5 -1
- ccxt/independentreserve.py +4 -0
- ccxt/indodax.py +3 -0
- ccxt/kraken.py +6 -4
- 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/lykke.py +4 -0
- 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/bitcoincom.py +1 -4
- ccxt/pro/bitopro.py +1 -1
- ccxt/probit.py +4 -0
- ccxt/test/tests_async.py +57 -31
- ccxt/test/tests_sync.py +57 -31
- 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 +21 -7
- ccxt/xt.py +5 -0
- ccxt/yobit.py +5 -2
- ccxt/zaif.py +2 -0
- ccxt/zonda.py +2 -0
- {ccxt-4.4.51.dist-info → ccxt-4.4.53.dist-info}/METADATA +225 -140
- {ccxt-4.4.51.dist-info → ccxt-4.4.53.dist-info}/RECORD +215 -224
- ccxt/static_dependencies/ethereum/abi/py.typed +0 -0
- ccxt/static_dependencies/ethereum/account/py.typed +0 -0
- ccxt/static_dependencies/ethereum/hexbytes/py.typed +0 -0
- ccxt/static_dependencies/ethereum/typing/py.typed +0 -0
- ccxt/static_dependencies/ethereum/utils/py.typed +0 -0
- ccxt/static_dependencies/lark/py.typed +0 -0
- ccxt/static_dependencies/marshmallow/py.typed +0 -0
- ccxt/static_dependencies/marshmallow_dataclass/py.typed +0 -0
- ccxt/static_dependencies/marshmallow_oneofschema/py.typed +0 -0
- {ccxt-4.4.51.dist-info → ccxt-4.4.53.dist-info}/LICENSE.txt +0 -0
- {ccxt-4.4.51.dist-info → ccxt-4.4.53.dist-info}/WHEEL +0 -0
- {ccxt-4.4.51.dist-info → ccxt-4.4.53.dist-info}/top_level.txt +0 -0
ccxt/test/tests_sync.py
CHANGED
@@ -9,7 +9,6 @@ class testMainClass:
|
|
9
9
|
request_tests = False
|
10
10
|
ws_tests = False
|
11
11
|
response_tests = False
|
12
|
-
static_tests = False
|
13
12
|
info = False
|
14
13
|
verbose = False
|
15
14
|
debug = False
|
@@ -47,16 +46,16 @@ class testMainClass:
|
|
47
46
|
if self.request_tests and self.response_tests:
|
48
47
|
self.run_static_request_tests(exchange_id, symbol_argv)
|
49
48
|
self.run_static_response_tests(exchange_id, symbol_argv)
|
50
|
-
return
|
49
|
+
return True
|
51
50
|
if self.response_tests:
|
52
51
|
self.run_static_response_tests(exchange_id, symbol_argv)
|
53
|
-
return
|
52
|
+
return True
|
54
53
|
if self.request_tests:
|
55
54
|
self.run_static_request_tests(exchange_id, symbol_argv) # symbol here is the testname
|
56
|
-
return
|
55
|
+
return True
|
57
56
|
if self.id_tests:
|
58
57
|
self.run_broker_id_tests()
|
59
|
-
return
|
58
|
+
return True
|
60
59
|
new_line = '\n'
|
61
60
|
dump(new_line + '' + new_line + '' + '[INFO] TESTING ', self.ext, {
|
62
61
|
'exchange': exchange_id,
|
@@ -101,6 +100,7 @@ class testMainClass:
|
|
101
100
|
self.test_files = get_test_files_sync(properties, self.ws_tests)
|
102
101
|
else:
|
103
102
|
self.test_files = get_test_files(properties, self.ws_tests)
|
103
|
+
return True
|
104
104
|
|
105
105
|
def load_credentials_from_env(self, exchange):
|
106
106
|
exchange_id = exchange.id
|
@@ -123,8 +123,12 @@ class testMainClass:
|
|
123
123
|
keys_local = get_root_dir() + 'keys.local.json'
|
124
124
|
keys_global_exists = io_file_exists(keys_global)
|
125
125
|
keys_local_exists = io_file_exists(keys_local)
|
126
|
-
global_settings =
|
127
|
-
|
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)
|
128
132
|
all_settings = exchange.deep_extend(global_settings, local_settings)
|
129
133
|
exchange_settings = exchange.safe_value(all_settings, exchange_id, {})
|
130
134
|
if exchange_settings:
|
@@ -175,7 +179,7 @@ class testMainClass:
|
|
175
179
|
exchange.options['checksum'] = False
|
176
180
|
# todo: temporary skip for php
|
177
181
|
if 'OrderBook' in method_name and self.ext == 'php':
|
178
|
-
return
|
182
|
+
return True
|
179
183
|
skipped_properties_for_method = self.get_skips(exchange, method_name)
|
180
184
|
is_load_markets = (method_name == 'loadMarkets')
|
181
185
|
is_fetch_currencies = (method_name == 'fetchCurrencies')
|
@@ -183,7 +187,7 @@ class testMainClass:
|
|
183
187
|
is_feature_test = (method_name == 'features')
|
184
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)
|
185
189
|
if not is_public and (method_name in self.checked_public_tests) and not is_fetch_currencies:
|
186
|
-
return
|
190
|
+
return True
|
187
191
|
skip_message = None
|
188
192
|
supported_by_exchange = (method_name in exchange.has) and exchange.has[method_name]
|
189
193
|
if not is_load_markets and (len(self.only_specific_tests) > 0 and not exchange.in_array(method_name, self.only_specific_tests)):
|
@@ -201,7 +205,7 @@ class testMainClass:
|
|
201
205
|
if skip_message:
|
202
206
|
if self.info:
|
203
207
|
dump(self.add_padding(skip_message, 25), name, method_name)
|
204
|
-
return
|
208
|
+
return True
|
205
209
|
if self.info:
|
206
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"
|
207
211
|
dump(self.add_padding('[INFO] TESTING', 25), name, method_name, args_stringified)
|
@@ -214,7 +218,7 @@ class testMainClass:
|
|
214
218
|
# add to the list of successed tests
|
215
219
|
if is_public:
|
216
220
|
self.checked_public_tests[method_name] = True
|
217
|
-
return
|
221
|
+
return True
|
218
222
|
|
219
223
|
def get_skips(self, exchange, method_name):
|
220
224
|
final_skips = {}
|
@@ -283,10 +287,10 @@ class testMainClass:
|
|
283
287
|
is_on_maintenance = (isinstance(e, OnMaintenance))
|
284
288
|
is_exchange_not_available = (isinstance(e, ExchangeNotAvailable))
|
285
289
|
should_fail = None
|
286
|
-
|
290
|
+
ret_success = None
|
287
291
|
if is_load_markets:
|
288
292
|
# if "loadMarkets" does not succeed, we must return "false" to caller method, to stop tests continual
|
289
|
-
|
293
|
+
ret_success = False
|
290
294
|
# we might not break exchange tests, if exchange is on maintenance at this moment
|
291
295
|
if is_on_maintenance:
|
292
296
|
should_fail = False
|
@@ -297,20 +301,19 @@ class testMainClass:
|
|
297
301
|
if is_exchange_not_available and not is_on_maintenance:
|
298
302
|
# break exchange tests if "ExchangeNotAvailable" exception is thrown, but it's not maintenance
|
299
303
|
should_fail = True
|
300
|
-
|
304
|
+
ret_success = False
|
301
305
|
else:
|
302
306
|
# in all other cases of OperationFailed, show Warning, but don't mark test as failed
|
303
307
|
should_fail = False
|
304
|
-
|
308
|
+
ret_success = True
|
305
309
|
# output the message
|
306
310
|
fail_type = '[TEST_FAILURE]' if should_fail else '[TEST_WARNING]'
|
307
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))
|
308
|
-
return
|
312
|
+
return ret_success
|
309
313
|
else:
|
310
314
|
# wait and retry again
|
311
315
|
# (increase wait time on every retry)
|
312
316
|
exchange.sleep((i + 1) * 1000)
|
313
|
-
continue
|
314
317
|
else:
|
315
318
|
# if it's loadMarkets, then fail test, because it's mandatory for tests
|
316
319
|
if is_load_markets:
|
@@ -374,6 +377,7 @@ class testMainClass:
|
|
374
377
|
tests['fetchPremiumIndexOHLCV'] = [symbol]
|
375
378
|
self.public_tests = tests
|
376
379
|
self.run_tests(exchange, tests, True)
|
380
|
+
return True
|
377
381
|
|
378
382
|
def run_tests(self, exchange, tests, is_public_test):
|
379
383
|
test_names = list(tests.keys())
|
@@ -398,6 +402,7 @@ class testMainClass:
|
|
398
402
|
dump('[TEST_FAILURE]', exchange.id, test_prefix_string, 'Failed methods : ' + errors_string)
|
399
403
|
if self.info:
|
400
404
|
dump(self.add_padding('[INFO] END ' + test_prefix_string + ' ' + exchange.id, 25))
|
405
|
+
return True
|
401
406
|
|
402
407
|
def load_exchange(self, exchange):
|
403
408
|
result = self.test_safe('loadMarkets', exchange, [], True)
|
@@ -518,11 +523,12 @@ class testMainClass:
|
|
518
523
|
if exchange.has['swap'] and swap_symbol is not None:
|
519
524
|
exchange.options['defaultType'] = 'swap'
|
520
525
|
self.run_private_tests(exchange, swap_symbol)
|
526
|
+
return True
|
521
527
|
|
522
528
|
def run_private_tests(self, exchange, symbol):
|
523
529
|
if not exchange.check_required_credentials(False):
|
524
530
|
dump('[INFO] Skipping private tests', 'Keys not found')
|
525
|
-
return
|
531
|
+
return True
|
526
532
|
code = self.get_exchange_code(exchange)
|
527
533
|
# if (exchange.deepExtendedTest) {
|
528
534
|
# test ('InvalidNonce', exchange, symbol);
|
@@ -592,14 +598,14 @@ class testMainClass:
|
|
592
598
|
proxy_test_name = self.proxy_test_file_name
|
593
599
|
# todo: temporary skip for sync py
|
594
600
|
if self.ext == 'py' and is_sync():
|
595
|
-
return
|
601
|
+
return True
|
596
602
|
# try proxy several times
|
597
603
|
max_retries = 3
|
598
604
|
exception = None
|
599
605
|
for j in range(0, max_retries):
|
600
606
|
try:
|
601
607
|
self.test_method(proxy_test_name, exchange, [], True)
|
602
|
-
return # if successfull, then end the test
|
608
|
+
return True # if successfull, then end the test
|
603
609
|
except Exception as e:
|
604
610
|
exception = e
|
605
611
|
exchange.sleep(j * 1000)
|
@@ -608,12 +614,13 @@ class testMainClass:
|
|
608
614
|
error_message = '[TEST_FAILURE] Failed ' + proxy_test_name + ' : ' + exception_message(exception)
|
609
615
|
# temporary comment the below, because c# transpilation failure
|
610
616
|
# throw new Exchange Error (errorMessage.toString ());
|
611
|
-
dump('[TEST_WARNING]' +
|
617
|
+
dump('[TEST_WARNING]' + error_message)
|
618
|
+
return True
|
612
619
|
|
613
620
|
def start_test(self, exchange, symbol):
|
614
621
|
# we do not need to test aliases
|
615
622
|
if exchange.alias:
|
616
|
-
return
|
623
|
+
return True
|
617
624
|
if self.sandbox or get_exchange_prop(exchange, 'sandbox'):
|
618
625
|
exchange.set_sandbox_mode(True)
|
619
626
|
try:
|
@@ -621,7 +628,7 @@ class testMainClass:
|
|
621
628
|
if not result:
|
622
629
|
if not is_sync():
|
623
630
|
close(exchange)
|
624
|
-
return
|
631
|
+
return True
|
625
632
|
# if (exchange.id === 'binance') {
|
626
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
|
627
634
|
# # this.testProxies (exchange);
|
@@ -796,13 +803,15 @@ class testMainClass:
|
|
796
803
|
return True # c# requ
|
797
804
|
|
798
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
|
799
807
|
try:
|
800
|
-
|
808
|
+
res = self.assert_new_and_stored_output_inner(exchange, skip_keys, new_output, stored_output, strict_type_check, asserting_key)
|
801
809
|
except Exception as e:
|
802
810
|
if self.info:
|
803
811
|
error_message = self.var_to_string(new_output) + '(calculated)' + ' != ' + self.var_to_string(stored_output) + '(stored)'
|
804
812
|
dump('[TEST_FAILURE_DETAIL]' + error_message)
|
805
813
|
raise e
|
814
|
+
return res
|
806
815
|
|
807
816
|
def var_to_string(self, obj=None):
|
808
817
|
new_string = None
|
@@ -831,11 +840,11 @@ class testMainClass:
|
|
831
840
|
if (stored_url_query is None) and (new_url_query is None):
|
832
841
|
# might be a get request without any query parameters
|
833
842
|
# example: https://api.gateio.ws/api/v4/delivery/usdt/positions
|
834
|
-
return
|
843
|
+
return True
|
835
844
|
stored_url_params = self.urlencoded_to_dict(stored_url_query)
|
836
845
|
new_url_params = self.urlencoded_to_dict(new_url_query)
|
837
846
|
self.assert_new_and_stored_output(exchange, skip_keys, new_url_params, stored_url_params)
|
838
|
-
return
|
847
|
+
return True
|
839
848
|
if type == 'json' and (stored_output is not None) and (new_output is not None):
|
840
849
|
if isinstance(stored_output, str):
|
841
850
|
stored_output = json_parse(stored_output)
|
@@ -852,6 +861,7 @@ class testMainClass:
|
|
852
861
|
stored_output = self.urlencoded_to_dict(stored_output)
|
853
862
|
new_output = self.urlencoded_to_dict(new_output)
|
854
863
|
self.assert_new_and_stored_output(exchange, skip_keys, new_output, stored_output)
|
864
|
+
return True
|
855
865
|
|
856
866
|
def assert_static_response_output(self, exchange, skip_keys, computed_result, stored_result):
|
857
867
|
self.assert_new_and_stored_output(exchange, skip_keys, computed_result, stored_result, False)
|
@@ -887,8 +897,9 @@ class testMainClass:
|
|
887
897
|
self.assert_static_request_output(exchange, type, skip_keys, data['url'], request_url, call_output, output)
|
888
898
|
except Exception as e:
|
889
899
|
self.request_tests_failed = True
|
890
|
-
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)
|
891
901
|
dump('[TEST_FAILURE]' + error_message)
|
902
|
+
return True
|
892
903
|
|
893
904
|
def test_response_statically(self, exchange, method, skip_keys, data):
|
894
905
|
expected_result = exchange.safe_value(data, 'parsedResponse')
|
@@ -902,9 +913,10 @@ class testMainClass:
|
|
902
913
|
self.assert_static_response_output(mocked_exchange, skip_keys, unified_result_sync, expected_result)
|
903
914
|
except Exception as e:
|
904
915
|
self.response_tests_failed = True
|
905
|
-
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)
|
906
917
|
dump('[TEST_FAILURE]' + error_message)
|
907
918
|
set_fetch_response(exchange, None) # reset state
|
919
|
+
return True
|
908
920
|
|
909
921
|
def init_offline_exchange(self, exchange_name):
|
910
922
|
markets = self.load_markets_from_file(exchange_name)
|
@@ -940,7 +952,8 @@ class testMainClass:
|
|
940
952
|
'leverageBrackets': {},
|
941
953
|
},
|
942
954
|
})
|
943
|
-
exchange.currencies = currencies
|
955
|
+
exchange.currencies = currencies
|
956
|
+
# not working in python if assigned in the config dict
|
944
957
|
return exchange
|
945
958
|
|
946
959
|
def test_exchange_request_statically(self, exchange_name, exchange_data, test_name=None):
|
@@ -988,6 +1001,9 @@ class testMainClass:
|
|
988
1001
|
is_disabled_c_sharp = exchange.safe_bool(result, 'disabledCS', False)
|
989
1002
|
if is_disabled_c_sharp and (self.lang == 'C#'):
|
990
1003
|
continue
|
1004
|
+
is_disabled_go = exchange.safe_bool(result, 'disabledGO', False)
|
1005
|
+
if is_disabled_go and (self.lang == 'GO'):
|
1006
|
+
continue
|
991
1007
|
type = exchange.safe_string(exchange_data, 'outputType')
|
992
1008
|
skip_keys = exchange.safe_value(exchange_data, 'skipKeys', [])
|
993
1009
|
self.test_request_statically(exchange, method, result, type, skip_keys)
|
@@ -1039,6 +1055,9 @@ class testMainClass:
|
|
1039
1055
|
continue
|
1040
1056
|
if (test_name is not None) and (test_name != description):
|
1041
1057
|
continue
|
1058
|
+
is_disabled_go = exchange.safe_bool(result, 'disabledGO', False)
|
1059
|
+
if is_disabled_go and (self.lang == 'GO'):
|
1060
|
+
continue
|
1042
1061
|
skip_keys = exchange.safe_value(exchange_data, 'skipKeys', [])
|
1043
1062
|
self.test_response_statically(exchange, method, skip_keys, result)
|
1044
1063
|
# reset options
|
@@ -1063,12 +1082,13 @@ class testMainClass:
|
|
1063
1082
|
|
1064
1083
|
def run_static_request_tests(self, target_exchange=None, test_name=None):
|
1065
1084
|
self.run_static_tests('request', target_exchange, test_name)
|
1085
|
+
return True
|
1066
1086
|
|
1067
1087
|
def run_static_tests(self, type, target_exchange=None, test_name=None):
|
1068
1088
|
folder = get_root_dir() + './ts/src/test/static/' + type + '/'
|
1069
1089
|
static_data = self.load_static_data(folder, target_exchange)
|
1070
1090
|
if static_data is None:
|
1071
|
-
return
|
1091
|
+
return True
|
1072
1092
|
exchanges = list(static_data.keys())
|
1073
1093
|
exchange = init_exchange('Exchange', {}) # tmp to do the calculations until we have the ast-transpiler transpiling this code
|
1074
1094
|
promises = []
|
@@ -1093,7 +1113,7 @@ class testMainClass:
|
|
1093
1113
|
self.request_tests_failed = True
|
1094
1114
|
else:
|
1095
1115
|
self.response_tests_failed = True
|
1096
|
-
error_message = '[' + self.lang + '][STATIC_REQUEST]' +
|
1116
|
+
error_message = '[' + self.lang + '][STATIC_REQUEST]' + exception_message(e)
|
1097
1117
|
dump('[TEST_FAILURE]' + error_message)
|
1098
1118
|
if self.request_tests_failed or self.response_tests_failed:
|
1099
1119
|
exit_script(1)
|
@@ -1107,6 +1127,7 @@ class testMainClass:
|
|
1107
1127
|
# --- Init of mockResponses tests functions------------------------------------
|
1108
1128
|
# -----------------------------------------------------------------------------
|
1109
1129
|
self.run_static_tests('response', exchange_name, test)
|
1130
|
+
return True
|
1110
1131
|
|
1111
1132
|
def run_broker_id_tests(self):
|
1112
1133
|
# -----------------------------------------------------------------------------
|
@@ -1117,6 +1138,7 @@ class testMainClass:
|
|
1117
1138
|
success_message = '[' + self.lang + '][TEST_SUCCESS] brokerId tests passed.'
|
1118
1139
|
dump('[INFO]' + success_message)
|
1119
1140
|
exit_script(0)
|
1141
|
+
return True
|
1120
1142
|
|
1121
1143
|
def test_binance(self):
|
1122
1144
|
exchange = self.init_offline_exchange('binance')
|
@@ -1370,6 +1392,7 @@ class testMainClass:
|
|
1370
1392
|
assert req_headers['X-SOURCE-KEY'] == id, 'bingx - id: ' + id + ' not in headers.'
|
1371
1393
|
if not is_sync():
|
1372
1394
|
close(exchange)
|
1395
|
+
return True
|
1373
1396
|
|
1374
1397
|
def test_phemex(self):
|
1375
1398
|
exchange = self.init_offline_exchange('phemex')
|
@@ -1384,6 +1407,7 @@ class testMainClass:
|
|
1384
1407
|
assert client_order_id.startswith(id_string), 'phemex - clOrdID: ' + client_order_id + ' does not start with id: ' + id_string
|
1385
1408
|
if not is_sync():
|
1386
1409
|
close(exchange)
|
1410
|
+
return True
|
1387
1411
|
|
1388
1412
|
def test_blofin(self):
|
1389
1413
|
exchange = self.init_offline_exchange('blofin')
|
@@ -1398,6 +1422,7 @@ class testMainClass:
|
|
1398
1422
|
assert broker_id.startswith(id_string), 'blofin - brokerId: ' + broker_id + ' does not start with id: ' + id_string
|
1399
1423
|
if not is_sync():
|
1400
1424
|
close(exchange)
|
1425
|
+
return True
|
1401
1426
|
|
1402
1427
|
def test_hyperliquid(self):
|
1403
1428
|
exchange = self.init_offline_exchange('hyperliquid')
|
@@ -1411,6 +1436,7 @@ class testMainClass:
|
|
1411
1436
|
assert broker_id == id, 'hyperliquid - brokerId: ' + broker_id + ' does not start with id: ' + id
|
1412
1437
|
if not is_sync():
|
1413
1438
|
close(exchange)
|
1439
|
+
return True
|
1414
1440
|
|
1415
1441
|
def test_coinbaseinternational(self):
|
1416
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
|
"""
|