ccxt 4.4.52__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.
Files changed (224) hide show
  1. ccxt/__init__.py +1 -1
  2. ccxt/abstract/binance.py +0 -1
  3. ccxt/abstract/binancecoinm.py +0 -1
  4. ccxt/abstract/binanceus.py +0 -1
  5. ccxt/abstract/binanceusdm.py +0 -1
  6. ccxt/ace.py +3 -0
  7. ccxt/alpaca.py +5 -0
  8. ccxt/ascendex.py +2 -1
  9. ccxt/async_support/__init__.py +1 -1
  10. ccxt/async_support/ace.py +3 -0
  11. ccxt/async_support/alpaca.py +5 -0
  12. ccxt/async_support/ascendex.py +2 -1
  13. ccxt/async_support/base/exchange.py +20 -3
  14. ccxt/async_support/bigone.py +5 -0
  15. ccxt/async_support/binance.py +55 -69
  16. ccxt/async_support/bingx.py +25 -29
  17. ccxt/async_support/bit2c.py +3 -0
  18. ccxt/async_support/bitbank.py +3 -0
  19. ccxt/async_support/bitbns.py +3 -0
  20. ccxt/async_support/bitfinex.py +6 -1
  21. ccxt/async_support/bitflyer.py +6 -1
  22. ccxt/async_support/bitget.py +8 -4
  23. ccxt/async_support/bithumb.py +3 -1
  24. ccxt/async_support/bitmart.py +22 -12
  25. ccxt/async_support/bitmex.py +99 -93
  26. ccxt/async_support/bitopro.py +7 -2
  27. ccxt/async_support/bitrue.py +4 -0
  28. ccxt/async_support/bitso.py +5 -2
  29. ccxt/async_support/bitstamp.py +3 -0
  30. ccxt/async_support/bitteam.py +5 -0
  31. ccxt/async_support/bitvavo.py +4 -0
  32. ccxt/async_support/blockchaincom.py +4 -0
  33. ccxt/async_support/blofin.py +3 -0
  34. ccxt/async_support/btcalpha.py +5 -0
  35. ccxt/async_support/btcbox.py +3 -2
  36. ccxt/async_support/btcmarkets.py +5 -0
  37. ccxt/async_support/btcturk.py +3 -0
  38. ccxt/async_support/bybit.py +10 -4
  39. ccxt/async_support/cex.py +2 -0
  40. ccxt/async_support/coinbase.py +19 -12
  41. ccxt/async_support/coinbaseexchange.py +5 -0
  42. ccxt/async_support/coinbaseinternational.py +21 -2
  43. ccxt/async_support/coincatch.py +3 -0
  44. ccxt/async_support/coincheck.py +2 -0
  45. ccxt/async_support/coinex.py +5 -1
  46. ccxt/async_support/coinlist.py +5 -0
  47. ccxt/async_support/coinmate.py +4 -0
  48. ccxt/async_support/coinmetro.py +9 -5
  49. ccxt/async_support/coinone.py +3 -0
  50. ccxt/async_support/coinsph.py +4 -0
  51. ccxt/async_support/coinspot.py +1 -0
  52. ccxt/async_support/cryptocom.py +5 -0
  53. ccxt/async_support/currencycom.py +3 -0
  54. ccxt/async_support/defx.py +5 -0
  55. ccxt/async_support/delta.py +4 -1
  56. ccxt/async_support/deribit.py +7 -3
  57. ccxt/async_support/digifinex.py +10 -5
  58. ccxt/async_support/ellipx.py +9 -5
  59. ccxt/async_support/exmo.py +6 -3
  60. ccxt/async_support/gate.py +5 -1
  61. ccxt/async_support/gemini.py +3 -0
  62. ccxt/async_support/hashkey.py +5 -4
  63. ccxt/async_support/hitbtc.py +6 -2
  64. ccxt/async_support/hollaex.py +7 -2
  65. ccxt/async_support/htx.py +8 -1
  66. ccxt/async_support/huobijp.py +5 -0
  67. ccxt/async_support/hyperliquid.py +6 -1
  68. ccxt/async_support/idex.py +5 -1
  69. ccxt/async_support/independentreserve.py +4 -0
  70. ccxt/async_support/indodax.py +3 -0
  71. ccxt/async_support/kraken.py +6 -4
  72. ccxt/async_support/krakenfutures.py +5 -2
  73. ccxt/async_support/kucoin.py +13 -6
  74. ccxt/async_support/kucoinfutures.py +5 -1
  75. ccxt/async_support/kuna.py +3 -0
  76. ccxt/async_support/latoken.py +4 -0
  77. ccxt/async_support/lbank.py +6 -1
  78. ccxt/async_support/luno.py +6 -1
  79. ccxt/async_support/lykke.py +4 -0
  80. ccxt/async_support/mercado.py +4 -0
  81. ccxt/async_support/mexc.py +10 -9
  82. ccxt/async_support/ndax.py +6 -1
  83. ccxt/async_support/novadax.py +5 -0
  84. ccxt/async_support/oceanex.py +6 -2
  85. ccxt/async_support/okcoin.py +4 -0
  86. ccxt/async_support/okx.py +17 -5
  87. ccxt/async_support/onetrading.py +4 -0
  88. ccxt/async_support/oxfun.py +3 -0
  89. ccxt/async_support/p2b.py +3 -0
  90. ccxt/async_support/paradex.py +8 -2
  91. ccxt/async_support/phemex.py +10 -4
  92. ccxt/async_support/poloniex.py +6 -3
  93. ccxt/async_support/poloniexfutures.py +5 -1
  94. ccxt/async_support/probit.py +4 -0
  95. ccxt/async_support/timex.py +4 -0
  96. ccxt/async_support/tokocrypto.py +5 -0
  97. ccxt/async_support/tradeogre.py +2 -0
  98. ccxt/async_support/upbit.py +5 -2
  99. ccxt/async_support/vertex.py +6 -2
  100. ccxt/async_support/wavesexchange.py +20 -3
  101. ccxt/async_support/wazirx.py +2 -0
  102. ccxt/async_support/whitebit.py +5 -4
  103. ccxt/async_support/woo.py +15 -5
  104. ccxt/async_support/woofipro.py +21 -7
  105. ccxt/async_support/xt.py +5 -0
  106. ccxt/async_support/yobit.py +5 -2
  107. ccxt/async_support/zaif.py +2 -0
  108. ccxt/async_support/zonda.py +2 -0
  109. ccxt/base/exchange.py +92 -50
  110. ccxt/base/types.py +1 -1
  111. ccxt/bigone.py +5 -0
  112. ccxt/binance.py +55 -69
  113. ccxt/bingx.py +25 -29
  114. ccxt/bit2c.py +3 -0
  115. ccxt/bitbank.py +3 -0
  116. ccxt/bitbns.py +3 -0
  117. ccxt/bitfinex.py +6 -1
  118. ccxt/bitflyer.py +6 -1
  119. ccxt/bitget.py +8 -4
  120. ccxt/bithumb.py +3 -1
  121. ccxt/bitmart.py +22 -12
  122. ccxt/bitmex.py +99 -93
  123. ccxt/bitopro.py +7 -2
  124. ccxt/bitrue.py +4 -0
  125. ccxt/bitso.py +5 -2
  126. ccxt/bitstamp.py +3 -0
  127. ccxt/bitteam.py +5 -0
  128. ccxt/bitvavo.py +4 -0
  129. ccxt/blockchaincom.py +4 -0
  130. ccxt/blofin.py +3 -0
  131. ccxt/btcalpha.py +5 -0
  132. ccxt/btcbox.py +3 -2
  133. ccxt/btcmarkets.py +5 -0
  134. ccxt/btcturk.py +3 -0
  135. ccxt/bybit.py +10 -4
  136. ccxt/cex.py +2 -0
  137. ccxt/coinbase.py +19 -12
  138. ccxt/coinbaseexchange.py +5 -0
  139. ccxt/coinbaseinternational.py +21 -2
  140. ccxt/coincatch.py +3 -0
  141. ccxt/coincheck.py +2 -0
  142. ccxt/coinex.py +5 -1
  143. ccxt/coinlist.py +5 -0
  144. ccxt/coinmate.py +4 -0
  145. ccxt/coinmetro.py +9 -5
  146. ccxt/coinone.py +3 -0
  147. ccxt/coinsph.py +4 -0
  148. ccxt/coinspot.py +1 -0
  149. ccxt/cryptocom.py +5 -0
  150. ccxt/currencycom.py +3 -0
  151. ccxt/defx.py +5 -0
  152. ccxt/delta.py +4 -1
  153. ccxt/deribit.py +7 -3
  154. ccxt/digifinex.py +10 -5
  155. ccxt/ellipx.py +9 -5
  156. ccxt/exmo.py +6 -3
  157. ccxt/gate.py +5 -1
  158. ccxt/gemini.py +3 -0
  159. ccxt/hashkey.py +5 -4
  160. ccxt/hitbtc.py +6 -2
  161. ccxt/hollaex.py +7 -2
  162. ccxt/htx.py +8 -1
  163. ccxt/huobijp.py +5 -0
  164. ccxt/hyperliquid.py +6 -1
  165. ccxt/idex.py +5 -1
  166. ccxt/independentreserve.py +4 -0
  167. ccxt/indodax.py +3 -0
  168. ccxt/kraken.py +6 -4
  169. ccxt/krakenfutures.py +5 -2
  170. ccxt/kucoin.py +13 -6
  171. ccxt/kucoinfutures.py +5 -1
  172. ccxt/kuna.py +3 -0
  173. ccxt/latoken.py +4 -0
  174. ccxt/lbank.py +6 -1
  175. ccxt/luno.py +6 -1
  176. ccxt/lykke.py +4 -0
  177. ccxt/mercado.py +4 -0
  178. ccxt/mexc.py +10 -9
  179. ccxt/ndax.py +6 -1
  180. ccxt/novadax.py +5 -0
  181. ccxt/oceanex.py +6 -2
  182. ccxt/okcoin.py +4 -0
  183. ccxt/okx.py +17 -5
  184. ccxt/onetrading.py +4 -0
  185. ccxt/oxfun.py +3 -0
  186. ccxt/p2b.py +3 -0
  187. ccxt/paradex.py +8 -2
  188. ccxt/phemex.py +10 -4
  189. ccxt/poloniex.py +6 -3
  190. ccxt/poloniexfutures.py +5 -1
  191. ccxt/pro/__init__.py +1 -1
  192. ccxt/pro/bitcoincom.py +1 -4
  193. ccxt/pro/bitopro.py +1 -1
  194. ccxt/probit.py +4 -0
  195. ccxt/test/tests_async.py +57 -30
  196. ccxt/test/tests_sync.py +57 -30
  197. ccxt/timex.py +4 -0
  198. ccxt/tokocrypto.py +5 -0
  199. ccxt/tradeogre.py +2 -0
  200. ccxt/upbit.py +5 -2
  201. ccxt/vertex.py +6 -2
  202. ccxt/wavesexchange.py +20 -3
  203. ccxt/wazirx.py +2 -0
  204. ccxt/whitebit.py +5 -4
  205. ccxt/woo.py +15 -5
  206. ccxt/woofipro.py +21 -7
  207. ccxt/xt.py +5 -0
  208. ccxt/yobit.py +5 -2
  209. ccxt/zaif.py +2 -0
  210. ccxt/zonda.py +2 -0
  211. {ccxt-4.4.52.dist-info → ccxt-4.4.53.dist-info}/METADATA +225 -140
  212. {ccxt-4.4.52.dist-info → ccxt-4.4.53.dist-info}/RECORD +215 -224
  213. ccxt/static_dependencies/ethereum/abi/py.typed +0 -0
  214. ccxt/static_dependencies/ethereum/account/py.typed +0 -0
  215. ccxt/static_dependencies/ethereum/hexbytes/py.typed +0 -0
  216. ccxt/static_dependencies/ethereum/typing/py.typed +0 -0
  217. ccxt/static_dependencies/ethereum/utils/py.typed +0 -0
  218. ccxt/static_dependencies/lark/py.typed +0 -0
  219. ccxt/static_dependencies/marshmallow/py.typed +0 -0
  220. ccxt/static_dependencies/marshmallow_dataclass/py.typed +0 -0
  221. ccxt/static_dependencies/marshmallow_oneofschema/py.typed +0 -0
  222. {ccxt-4.4.52.dist-info → ccxt-4.4.53.dist-info}/LICENSE.txt +0 -0
  223. {ccxt-4.4.52.dist-info → ccxt-4.4.53.dist-info}/WHEEL +0 -0
  224. {ccxt-4.4.52.dist-info → ccxt-4.4.53.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 = io_file_read(keys_global) if keys_global_exists else {}
126
- local_settings = io_file_read(keys_local) if keys_local_exists else {}
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
- return_success = None
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
- return_success = False
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
- return_success = False
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
- return_success = True
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 return_success
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]' + str(error_message))
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
- return self.assert_new_and_stored_output_inner(exchange, skip_keys, new_output, stored_output, strict_type_check, asserting_key)
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'] + ']' + str(e)
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'] + ']' + str(e)
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 # not working in python if assigned in the config dict
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]' + str(e)
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
- cancels['productIds'].append(marketIdNum)
2403
+ productIds.append(marketIdNum)
2400
2404
  request = {
2401
2405
  'cancel_orders': {
2402
2406
  'tx': {
2403
2407
  'sender': cancels['sender'],
2404
- 'productIds': cancels['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
- durationRoundedTimestamp = self.parse_to_int(self.milliseconds() / duration) * duration
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
- timeEnd = self.sum(since, duration * limit)
1032
- request['timeEnd'] = str(timeEnd)
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
- req = {
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, self.extend(req, params))
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
  """