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.
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 +16 -5
  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 +113 -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 +16 -5
  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 -31
  196. ccxt/test/tests_sync.py +57 -31
  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.51.dist-info → ccxt-4.4.53.dist-info}/METADATA +225 -140
  212. {ccxt-4.4.51.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.51.dist-info → ccxt-4.4.53.dist-info}/LICENSE.txt +0 -0
  223. {ccxt-4.4.51.dist-info → ccxt-4.4.53.dist-info}/WHEEL +0 -0
  224. {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 = io_file_read(keys_global) if keys_global_exists else {}
127
- 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)
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
- return_success = None
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
- return_success = False
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
- return_success = False
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
- return_success = True
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 return_success
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]' + str(error_message))
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
- 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)
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'] + ']' + str(e)
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'] + ']' + str(e)
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 # 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
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]' + str(e)
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
- 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
  """