ccxt 4.3.85__py2.py3-none-any.whl → 4.3.87__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 +4 -1
- ccxt/abstract/hashkey.py +67 -0
- ccxt/async_support/__init__.py +4 -1
- ccxt/async_support/base/exchange.py +2 -2
- ccxt/async_support/binance.py +4 -2
- ccxt/async_support/bingx.py +5 -1
- ccxt/async_support/bitfinex.py +2 -2
- ccxt/async_support/hashkey.py +4061 -0
- ccxt/async_support/hyperliquid.py +80 -62
- ccxt/async_support/indodax.py +29 -8
- ccxt/async_support/kraken.py +32 -5
- ccxt/async_support/krakenfutures.py +10 -9
- ccxt/async_support/upbit.py +1 -1
- ccxt/base/errors.py +7 -1
- ccxt/base/exchange.py +2 -2
- ccxt/binance.py +4 -2
- ccxt/bingx.py +5 -1
- ccxt/bitfinex.py +2 -2
- ccxt/hashkey.py +4061 -0
- ccxt/hyperliquid.py +80 -62
- ccxt/indodax.py +29 -8
- ccxt/kraken.py +32 -5
- ccxt/krakenfutures.py +10 -9
- ccxt/pro/__init__.py +3 -1
- ccxt/pro/ascendex.py +41 -5
- ccxt/pro/bingx.py +13 -12
- ccxt/pro/bitget.py +143 -16
- ccxt/pro/hashkey.py +783 -0
- ccxt/pro/hyperliquid.py +118 -1
- ccxt/pro/mexc.py +13 -7
- ccxt/pro/p2b.py +30 -7
- ccxt/pro/poloniex.py +32 -3
- ccxt/pro/poloniexfutures.py +1 -0
- ccxt/pro/probit.py +2 -0
- ccxt/pro/upbit.py +44 -3
- ccxt/pro/vertex.py +1 -0
- ccxt/pro/wazirx.py +3 -0
- ccxt/pro/whitebit.py +9 -0
- ccxt/pro/woo.py +1 -0
- ccxt/pro/woofipro.py +1 -0
- ccxt/pro/xt.py +1 -0
- ccxt/test/tests_async.py +31 -31
- ccxt/test/tests_sync.py +31 -31
- ccxt/upbit.py +1 -1
- {ccxt-4.3.85.dist-info → ccxt-4.3.87.dist-info}/METADATA +9 -6
- {ccxt-4.3.85.dist-info → ccxt-4.3.87.dist-info}/RECORD +49 -45
- {ccxt-4.3.85.dist-info → ccxt-4.3.87.dist-info}/LICENSE.txt +0 -0
- {ccxt-4.3.85.dist-info → ccxt-4.3.87.dist-info}/WHEEL +0 -0
- {ccxt-4.3.85.dist-info → ccxt-4.3.87.dist-info}/top_level.txt +0 -0
ccxt/test/tests_async.py
CHANGED
@@ -143,24 +143,6 @@ class testMainClass(baseMainTestClass):
|
|
143
143
|
res += ' '
|
144
144
|
return message + res
|
145
145
|
|
146
|
-
def exchange_hint(self, exchange, market=None):
|
147
|
-
market_type = exchange.safe_string_2(exchange.options, 'defaultType', 'type', '')
|
148
|
-
market_sub_type = exchange.safe_string_2(exchange.options, 'defaultSubType', 'subType')
|
149
|
-
if market is not None:
|
150
|
-
market_type = market['type']
|
151
|
-
if market['linear']:
|
152
|
-
market_sub_type = 'linear'
|
153
|
-
elif market['inverse']:
|
154
|
-
market_sub_type = 'inverse'
|
155
|
-
elif exchange.safe_value(market, 'quanto'):
|
156
|
-
market_sub_type = 'quanto'
|
157
|
-
is_ws = ('ws' in exchange.has)
|
158
|
-
ws_flag = '(WS)' if is_ws else ''
|
159
|
-
result = exchange.id + ' ' + ws_flag + ' ' + market_type
|
160
|
-
if market_sub_type is not None:
|
161
|
-
result = result + ' [subType: ' + market_sub_type + '] '
|
162
|
-
return result
|
163
|
-
|
164
146
|
async def test_method(self, method_name, exchange, args, is_public):
|
165
147
|
# todo: temporary skip for c#
|
166
148
|
if 'OrderBook' in method_name and self.ext == 'cs':
|
@@ -188,19 +170,20 @@ class testMainClass(baseMainTestClass):
|
|
188
170
|
# exceptionally for `loadMarkets` call, we call it before it's even checked for "skip" as we need it to be called anyway (but can skip "test.loadMarket" for it)
|
189
171
|
if is_load_markets:
|
190
172
|
await exchange.load_markets(True)
|
173
|
+
name = exchange.id
|
191
174
|
if skip_message:
|
192
175
|
if self.info:
|
193
|
-
dump(self.add_padding(skip_message, 25),
|
176
|
+
dump(self.add_padding(skip_message, 25), name, method_name)
|
194
177
|
return
|
195
178
|
if self.info:
|
196
179
|
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"
|
197
|
-
dump(self.add_padding('[INFO] TESTING', 25),
|
180
|
+
dump(self.add_padding('[INFO] TESTING', 25), name, method_name, args_stringified)
|
198
181
|
if self.is_synchronous:
|
199
182
|
call_method_sync(self.test_files, method_name, exchange, skipped_properties_for_method, args)
|
200
183
|
else:
|
201
184
|
await call_method(self.test_files, method_name, exchange, skipped_properties_for_method, args)
|
202
185
|
if self.info:
|
203
|
-
dump(self.add_padding('[INFO] TESTING DONE', 25),
|
186
|
+
dump(self.add_padding('[INFO] TESTING DONE', 25), name, method_name)
|
204
187
|
# add to the list of successed tests
|
205
188
|
if is_public:
|
206
189
|
self.checked_public_tests[method_name] = True
|
@@ -294,7 +277,7 @@ class testMainClass(baseMainTestClass):
|
|
294
277
|
return_success = True
|
295
278
|
# output the message
|
296
279
|
fail_type = '[TEST_FAILURE]' if should_fail else '[TEST_WARNING]'
|
297
|
-
dump(fail_type, 'Method could not be tested due to a repeated Network/Availability issues', ' | ',
|
280
|
+
dump(fail_type, 'Method could not be tested due to a repeated Network/Availability issues', ' | ', exchange.id, method_name, args_stringified, exception_message(e))
|
298
281
|
return return_success
|
299
282
|
else:
|
300
283
|
# wait and retry again
|
@@ -304,21 +287,21 @@ class testMainClass(baseMainTestClass):
|
|
304
287
|
else:
|
305
288
|
# if it's loadMarkets, then fail test, because it's mandatory for tests
|
306
289
|
if is_load_markets:
|
307
|
-
dump('[TEST_FAILURE]', 'Exchange can not load markets', exception_message(e),
|
290
|
+
dump('[TEST_FAILURE]', 'Exchange can not load markets', exception_message(e), exchange.id, method_name, args_stringified)
|
308
291
|
return False
|
309
292
|
# if the specific arguments to the test method throws "NotSupported" exception
|
310
293
|
# then let's don't fail the test
|
311
294
|
if is_not_supported:
|
312
295
|
if self.info:
|
313
|
-
dump('[INFO] NOT_SUPPORTED', exception_message(e),
|
296
|
+
dump('[INFO] NOT_SUPPORTED', exception_message(e), exchange.id, method_name, args_stringified)
|
314
297
|
return True
|
315
298
|
# If public test faces authentication error, we don't break (see comments under `testSafe` method)
|
316
299
|
if is_public and is_auth_error:
|
317
300
|
if self.info:
|
318
|
-
dump('[INFO]', 'Authentication problem for public method', exception_message(e),
|
301
|
+
dump('[INFO]', 'Authentication problem for public method', exception_message(e), exchange.id, method_name, args_stringified)
|
319
302
|
return True
|
320
303
|
else:
|
321
|
-
dump('[TEST_FAILURE]', exception_message(e),
|
304
|
+
dump('[TEST_FAILURE]', exception_message(e), exchange.id, method_name, args_stringified)
|
322
305
|
return False
|
323
306
|
return True
|
324
307
|
|
@@ -384,9 +367,9 @@ class testMainClass(baseMainTestClass):
|
|
384
367
|
test_prefix_string = 'PUBLIC_TESTS' if is_public_test else 'PRIVATE_TESTS'
|
385
368
|
if len(failed_methods):
|
386
369
|
errors_string = ', '.join(failed_methods)
|
387
|
-
dump('[TEST_FAILURE]',
|
370
|
+
dump('[TEST_FAILURE]', exchange.id, test_prefix_string, 'Failed methods : ' + errors_string)
|
388
371
|
if self.info:
|
389
|
-
dump(self.add_padding('[INFO] END ' + test_prefix_string + ' ' +
|
372
|
+
dump(self.add_padding('[INFO] END ' + test_prefix_string + ' ' + exchange.id, 25))
|
390
373
|
|
391
374
|
async def load_exchange(self, exchange):
|
392
375
|
result = await self.test_safe('loadMarkets', exchange, [], True)
|
@@ -846,7 +829,7 @@ class testMainClass(baseMainTestClass):
|
|
846
829
|
self.assert_static_request_output(exchange, type, skip_keys, data['url'], request_url, call_output, output)
|
847
830
|
except Exception as e:
|
848
831
|
self.request_tests_failed = True
|
849
|
-
error_message = '[' + self.lang + '][STATIC_REQUEST_TEST_FAILURE]' + '[' +
|
832
|
+
error_message = '[' + self.lang + '][STATIC_REQUEST_TEST_FAILURE]' + '[' + exchange.id + ']' + '[' + method + ']' + '[' + data['description'] + ']' + str(e)
|
850
833
|
dump('[TEST_FAILURE]' + error_message)
|
851
834
|
|
852
835
|
async def test_response_statically(self, exchange, method, skip_keys, data):
|
@@ -861,7 +844,7 @@ class testMainClass(baseMainTestClass):
|
|
861
844
|
self.assert_static_response_output(mocked_exchange, skip_keys, unified_result_sync, expected_result)
|
862
845
|
except Exception as e:
|
863
846
|
self.response_tests_failed = True
|
864
|
-
error_message = '[' + self.lang + '][STATIC_RESPONSE_TEST_FAILURE]' + '[' +
|
847
|
+
error_message = '[' + self.lang + '][STATIC_RESPONSE_TEST_FAILURE]' + '[' + exchange.id + ']' + '[' + method + ']' + '[' + data['description'] + ']' + str(e)
|
865
848
|
dump('[TEST_FAILURE]' + error_message)
|
866
849
|
set_fetch_response(exchange, None) # reset state
|
867
850
|
|
@@ -937,6 +920,9 @@ class testMainClass(baseMainTestClass):
|
|
937
920
|
is_disabled = exchange.safe_bool(result, 'disabled', False)
|
938
921
|
if is_disabled:
|
939
922
|
continue
|
923
|
+
is_disabled_c_sharp = exchange.safe_bool(result, 'disabledCS', False)
|
924
|
+
if is_disabled_c_sharp and (self.lang == 'C#'):
|
925
|
+
continue
|
940
926
|
type = exchange.safe_string(exchange_data, 'outputType')
|
941
927
|
skip_keys = exchange.safe_value(exchange_data, 'skipKeys', [])
|
942
928
|
await self.test_request_statically(exchange, method, result, type, skip_keys)
|
@@ -1053,7 +1039,7 @@ class testMainClass(baseMainTestClass):
|
|
1053
1039
|
# -----------------------------------------------------------------------------
|
1054
1040
|
# --- Init of brokerId tests functions-----------------------------------------
|
1055
1041
|
# -----------------------------------------------------------------------------
|
1056
|
-
promises = [self.test_binance(), self.test_okx(), self.test_cryptocom(), self.test_bybit(), self.test_kucoin(), self.test_kucoinfutures(), self.test_bitget(), self.test_mexc(), self.test_htx(), self.test_woo(), self.test_bitmart(), self.test_coinex(), self.test_bingx(), self.test_phemex(), self.test_blofin(), self.test_hyperliquid(), self.test_coinbaseinternational(), self.test_coinbase_advanced(), self.test_woofi_pro(), self.test_oxfun(), self.test_xt(), self.test_vertex(), self.test_paradex()]
|
1042
|
+
promises = [self.test_binance(), self.test_okx(), self.test_cryptocom(), self.test_bybit(), self.test_kucoin(), self.test_kucoinfutures(), self.test_bitget(), self.test_mexc(), self.test_htx(), self.test_woo(), self.test_bitmart(), self.test_coinex(), self.test_bingx(), self.test_phemex(), self.test_blofin(), self.test_hyperliquid(), self.test_coinbaseinternational(), self.test_coinbase_advanced(), self.test_woofi_pro(), self.test_oxfun(), self.test_xt(), self.test_vertex(), self.test_paradex(), self.test_hashkey()]
|
1057
1043
|
await asyncio.gather(*promises)
|
1058
1044
|
success_message = '[' + self.lang + '][TEST_SUCCESS] brokerId tests passed.'
|
1059
1045
|
dump('[INFO]' + success_message)
|
@@ -1501,3 +1487,17 @@ class testMainClass(baseMainTestClass):
|
|
1501
1487
|
if not self.is_synchronous:
|
1502
1488
|
await close(exchange)
|
1503
1489
|
return True
|
1490
|
+
|
1491
|
+
async def test_hashkey(self):
|
1492
|
+
exchange = self.init_offline_exchange('hashkey')
|
1493
|
+
req_headers = None
|
1494
|
+
id = '10000700011'
|
1495
|
+
try:
|
1496
|
+
await exchange.create_order('BTC/USDT', 'limit', 'buy', 1, 20000)
|
1497
|
+
except Exception as e:
|
1498
|
+
# we expect an error here, we're only interested in the headers
|
1499
|
+
req_headers = exchange.last_request_headers
|
1500
|
+
assert req_headers['INPUT-SOURCE'] == id, 'hashkey - id: ' + id + ' not in headers.'
|
1501
|
+
if not self.is_synchronous:
|
1502
|
+
await close(exchange)
|
1503
|
+
return True
|
ccxt/test/tests_sync.py
CHANGED
@@ -140,24 +140,6 @@ class testMainClass(baseMainTestClass):
|
|
140
140
|
res += ' '
|
141
141
|
return message + res
|
142
142
|
|
143
|
-
def exchange_hint(self, exchange, market=None):
|
144
|
-
market_type = exchange.safe_string_2(exchange.options, 'defaultType', 'type', '')
|
145
|
-
market_sub_type = exchange.safe_string_2(exchange.options, 'defaultSubType', 'subType')
|
146
|
-
if market is not None:
|
147
|
-
market_type = market['type']
|
148
|
-
if market['linear']:
|
149
|
-
market_sub_type = 'linear'
|
150
|
-
elif market['inverse']:
|
151
|
-
market_sub_type = 'inverse'
|
152
|
-
elif exchange.safe_value(market, 'quanto'):
|
153
|
-
market_sub_type = 'quanto'
|
154
|
-
is_ws = ('ws' in exchange.has)
|
155
|
-
ws_flag = '(WS)' if is_ws else ''
|
156
|
-
result = exchange.id + ' ' + ws_flag + ' ' + market_type
|
157
|
-
if market_sub_type is not None:
|
158
|
-
result = result + ' [subType: ' + market_sub_type + '] '
|
159
|
-
return result
|
160
|
-
|
161
143
|
def test_method(self, method_name, exchange, args, is_public):
|
162
144
|
# todo: temporary skip for c#
|
163
145
|
if 'OrderBook' in method_name and self.ext == 'cs':
|
@@ -185,19 +167,20 @@ class testMainClass(baseMainTestClass):
|
|
185
167
|
# exceptionally for `loadMarkets` call, we call it before it's even checked for "skip" as we need it to be called anyway (but can skip "test.loadMarket" for it)
|
186
168
|
if is_load_markets:
|
187
169
|
exchange.load_markets(True)
|
170
|
+
name = exchange.id
|
188
171
|
if skip_message:
|
189
172
|
if self.info:
|
190
|
-
dump(self.add_padding(skip_message, 25),
|
173
|
+
dump(self.add_padding(skip_message, 25), name, method_name)
|
191
174
|
return
|
192
175
|
if self.info:
|
193
176
|
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"
|
194
|
-
dump(self.add_padding('[INFO] TESTING', 25),
|
177
|
+
dump(self.add_padding('[INFO] TESTING', 25), name, method_name, args_stringified)
|
195
178
|
if self.is_synchronous:
|
196
179
|
call_method_sync(self.test_files, method_name, exchange, skipped_properties_for_method, args)
|
197
180
|
else:
|
198
181
|
call_method(self.test_files, method_name, exchange, skipped_properties_for_method, args)
|
199
182
|
if self.info:
|
200
|
-
dump(self.add_padding('[INFO] TESTING DONE', 25),
|
183
|
+
dump(self.add_padding('[INFO] TESTING DONE', 25), name, method_name)
|
201
184
|
# add to the list of successed tests
|
202
185
|
if is_public:
|
203
186
|
self.checked_public_tests[method_name] = True
|
@@ -291,7 +274,7 @@ class testMainClass(baseMainTestClass):
|
|
291
274
|
return_success = True
|
292
275
|
# output the message
|
293
276
|
fail_type = '[TEST_FAILURE]' if should_fail else '[TEST_WARNING]'
|
294
|
-
dump(fail_type, 'Method could not be tested due to a repeated Network/Availability issues', ' | ',
|
277
|
+
dump(fail_type, 'Method could not be tested due to a repeated Network/Availability issues', ' | ', exchange.id, method_name, args_stringified, exception_message(e))
|
295
278
|
return return_success
|
296
279
|
else:
|
297
280
|
# wait and retry again
|
@@ -301,21 +284,21 @@ class testMainClass(baseMainTestClass):
|
|
301
284
|
else:
|
302
285
|
# if it's loadMarkets, then fail test, because it's mandatory for tests
|
303
286
|
if is_load_markets:
|
304
|
-
dump('[TEST_FAILURE]', 'Exchange can not load markets', exception_message(e),
|
287
|
+
dump('[TEST_FAILURE]', 'Exchange can not load markets', exception_message(e), exchange.id, method_name, args_stringified)
|
305
288
|
return False
|
306
289
|
# if the specific arguments to the test method throws "NotSupported" exception
|
307
290
|
# then let's don't fail the test
|
308
291
|
if is_not_supported:
|
309
292
|
if self.info:
|
310
|
-
dump('[INFO] NOT_SUPPORTED', exception_message(e),
|
293
|
+
dump('[INFO] NOT_SUPPORTED', exception_message(e), exchange.id, method_name, args_stringified)
|
311
294
|
return True
|
312
295
|
# If public test faces authentication error, we don't break (see comments under `testSafe` method)
|
313
296
|
if is_public and is_auth_error:
|
314
297
|
if self.info:
|
315
|
-
dump('[INFO]', 'Authentication problem for public method', exception_message(e),
|
298
|
+
dump('[INFO]', 'Authentication problem for public method', exception_message(e), exchange.id, method_name, args_stringified)
|
316
299
|
return True
|
317
300
|
else:
|
318
|
-
dump('[TEST_FAILURE]', exception_message(e),
|
301
|
+
dump('[TEST_FAILURE]', exception_message(e), exchange.id, method_name, args_stringified)
|
319
302
|
return False
|
320
303
|
return True
|
321
304
|
|
@@ -381,9 +364,9 @@ class testMainClass(baseMainTestClass):
|
|
381
364
|
test_prefix_string = 'PUBLIC_TESTS' if is_public_test else 'PRIVATE_TESTS'
|
382
365
|
if len(failed_methods):
|
383
366
|
errors_string = ', '.join(failed_methods)
|
384
|
-
dump('[TEST_FAILURE]',
|
367
|
+
dump('[TEST_FAILURE]', exchange.id, test_prefix_string, 'Failed methods : ' + errors_string)
|
385
368
|
if self.info:
|
386
|
-
dump(self.add_padding('[INFO] END ' + test_prefix_string + ' ' +
|
369
|
+
dump(self.add_padding('[INFO] END ' + test_prefix_string + ' ' + exchange.id, 25))
|
387
370
|
|
388
371
|
def load_exchange(self, exchange):
|
389
372
|
result = self.test_safe('loadMarkets', exchange, [], True)
|
@@ -843,7 +826,7 @@ class testMainClass(baseMainTestClass):
|
|
843
826
|
self.assert_static_request_output(exchange, type, skip_keys, data['url'], request_url, call_output, output)
|
844
827
|
except Exception as e:
|
845
828
|
self.request_tests_failed = True
|
846
|
-
error_message = '[' + self.lang + '][STATIC_REQUEST_TEST_FAILURE]' + '[' +
|
829
|
+
error_message = '[' + self.lang + '][STATIC_REQUEST_TEST_FAILURE]' + '[' + exchange.id + ']' + '[' + method + ']' + '[' + data['description'] + ']' + str(e)
|
847
830
|
dump('[TEST_FAILURE]' + error_message)
|
848
831
|
|
849
832
|
def test_response_statically(self, exchange, method, skip_keys, data):
|
@@ -858,7 +841,7 @@ class testMainClass(baseMainTestClass):
|
|
858
841
|
self.assert_static_response_output(mocked_exchange, skip_keys, unified_result_sync, expected_result)
|
859
842
|
except Exception as e:
|
860
843
|
self.response_tests_failed = True
|
861
|
-
error_message = '[' + self.lang + '][STATIC_RESPONSE_TEST_FAILURE]' + '[' +
|
844
|
+
error_message = '[' + self.lang + '][STATIC_RESPONSE_TEST_FAILURE]' + '[' + exchange.id + ']' + '[' + method + ']' + '[' + data['description'] + ']' + str(e)
|
862
845
|
dump('[TEST_FAILURE]' + error_message)
|
863
846
|
set_fetch_response(exchange, None) # reset state
|
864
847
|
|
@@ -934,6 +917,9 @@ class testMainClass(baseMainTestClass):
|
|
934
917
|
is_disabled = exchange.safe_bool(result, 'disabled', False)
|
935
918
|
if is_disabled:
|
936
919
|
continue
|
920
|
+
is_disabled_c_sharp = exchange.safe_bool(result, 'disabledCS', False)
|
921
|
+
if is_disabled_c_sharp and (self.lang == 'C#'):
|
922
|
+
continue
|
937
923
|
type = exchange.safe_string(exchange_data, 'outputType')
|
938
924
|
skip_keys = exchange.safe_value(exchange_data, 'skipKeys', [])
|
939
925
|
self.test_request_statically(exchange, method, result, type, skip_keys)
|
@@ -1050,7 +1036,7 @@ class testMainClass(baseMainTestClass):
|
|
1050
1036
|
# -----------------------------------------------------------------------------
|
1051
1037
|
# --- Init of brokerId tests functions-----------------------------------------
|
1052
1038
|
# -----------------------------------------------------------------------------
|
1053
|
-
promises = [self.test_binance(), self.test_okx(), self.test_cryptocom(), self.test_bybit(), self.test_kucoin(), self.test_kucoinfutures(), self.test_bitget(), self.test_mexc(), self.test_htx(), self.test_woo(), self.test_bitmart(), self.test_coinex(), self.test_bingx(), self.test_phemex(), self.test_blofin(), self.test_hyperliquid(), self.test_coinbaseinternational(), self.test_coinbase_advanced(), self.test_woofi_pro(), self.test_oxfun(), self.test_xt(), self.test_vertex(), self.test_paradex()]
|
1039
|
+
promises = [self.test_binance(), self.test_okx(), self.test_cryptocom(), self.test_bybit(), self.test_kucoin(), self.test_kucoinfutures(), self.test_bitget(), self.test_mexc(), self.test_htx(), self.test_woo(), self.test_bitmart(), self.test_coinex(), self.test_bingx(), self.test_phemex(), self.test_blofin(), self.test_hyperliquid(), self.test_coinbaseinternational(), self.test_coinbase_advanced(), self.test_woofi_pro(), self.test_oxfun(), self.test_xt(), self.test_vertex(), self.test_paradex(), self.test_hashkey()]
|
1054
1040
|
(promises)
|
1055
1041
|
success_message = '[' + self.lang + '][TEST_SUCCESS] brokerId tests passed.'
|
1056
1042
|
dump('[INFO]' + success_message)
|
@@ -1498,3 +1484,17 @@ class testMainClass(baseMainTestClass):
|
|
1498
1484
|
if not self.is_synchronous:
|
1499
1485
|
close(exchange)
|
1500
1486
|
return True
|
1487
|
+
|
1488
|
+
def test_hashkey(self):
|
1489
|
+
exchange = self.init_offline_exchange('hashkey')
|
1490
|
+
req_headers = None
|
1491
|
+
id = '10000700011'
|
1492
|
+
try:
|
1493
|
+
exchange.create_order('BTC/USDT', 'limit', 'buy', 1, 20000)
|
1494
|
+
except Exception as e:
|
1495
|
+
# we expect an error here, we're only interested in the headers
|
1496
|
+
req_headers = exchange.last_request_headers
|
1497
|
+
assert req_headers['INPUT-SOURCE'] == id, 'hashkey - id: ' + id + ' not in headers.'
|
1498
|
+
if not self.is_synchronous:
|
1499
|
+
close(exchange)
|
1500
|
+
return True
|
ccxt/upbit.py
CHANGED
@@ -1891,7 +1891,7 @@ class upbit(Exchange, ImplicitAPI):
|
|
1891
1891
|
body = self.json(params)
|
1892
1892
|
headers['Content-Type'] = 'application/json'
|
1893
1893
|
if hasQuery:
|
1894
|
-
auth = self.
|
1894
|
+
auth = self.rawencode(query)
|
1895
1895
|
if auth is not None:
|
1896
1896
|
hash = self.hash(self.encode(auth), 'sha512')
|
1897
1897
|
request['query_hash'] = hash
|
@@ -1,6 +1,6 @@
|
|
1
1
|
Metadata-Version: 2.1
|
2
2
|
Name: ccxt
|
3
|
-
Version: 4.3.
|
3
|
+
Version: 4.3.87
|
4
4
|
Summary: A JavaScript / TypeScript / Python / C# / PHP cryptocurrency trading library with support for 100+ exchanges
|
5
5
|
Home-page: https://ccxt.com
|
6
6
|
Author: Igor Kroitor
|
@@ -50,7 +50,7 @@ Requires-Dist: mypy (==1.6.1) ; extra == 'type'
|
|
50
50
|
|
51
51
|
# CCXT – CryptoCurrency eXchange Trading Library
|
52
52
|
|
53
|
-
[](https://travis-ci.com/ccxt/ccxt) [](https://npmjs.com/package/ccxt) [](https://pypi.python.org/pypi/ccxt) [](https://www.npmjs.com/package/ccxt) [](https://discord.gg/ccxt) [](https://travis-ci.com/ccxt/ccxt) [](https://npmjs.com/package/ccxt) [](https://pypi.python.org/pypi/ccxt) [](https://www.npmjs.com/package/ccxt) [](https://discord.gg/ccxt) [](https://github.com/ccxt/ccxt/wiki/Exchange-Markets) [](https://twitter.com/ccxt_official)
|
54
54
|
|
55
55
|
A JavaScript / Python / PHP / C# library for cryptocurrency trading and e-commerce with support for many bitcoin/ether/altcoin exchange markets and merchant APIs.
|
56
56
|
|
@@ -70,6 +70,7 @@ Current feature list:
|
|
70
70
|
|
71
71
|
|
72
72
|
## Sponsored Promotion
|
73
|
+
[](https://support.global.hashkey.com/hc/en-us/articles/15485543021468-Trade-Through-CCXT-to-Grab-up-to-1-000-USDT-30-Day-VIP5-Trial)
|
73
74
|
|
74
75
|
## See Also
|
75
76
|
|
@@ -96,6 +97,7 @@ Current feature list:
|
|
96
97
|
| [](https://www.coinex.com/register?refer_code=yw5fz) | coinex | [CoinEx](https://www.coinex.com/register?refer_code=yw5fz) | [](https://docs.coinex.com/api/v2) | cex | [](https://github.com/ccxt/ccxt/wiki/Certification) | [](https://ccxt.pro) | |
|
97
98
|
| [](https://crypto.com/exch/kdacthrnxt) | cryptocom | [Crypto.com](https://crypto.com/exch/kdacthrnxt) | [](https://exchange-docs.crypto.com/exchange/v1/rest-ws/index.html) | cex | [](https://github.com/ccxt/ccxt/wiki/Certification) | [](https://ccxt.pro) | [](https://crypto.com/exch/kdacthrnxt) |
|
98
99
|
| [](https://www.gate.io/signup/2436035) | gate | [Gate.io](https://www.gate.io/signup/2436035) | [](https://www.gate.io/docs/developers/apiv4/en/) | cex | [](https://github.com/ccxt/ccxt/wiki/Certification) | [](https://ccxt.pro) | [](https://www.gate.io/signup/2436035) |
|
100
|
+
| [](https://global.hashkey.com/en-US/register/invite?invite_code=82FQUN) | hashkey | [HashKey Global](https://global.hashkey.com/en-US/register/invite?invite_code=82FQUN) | [](https://hashkeyglobal-apidoc.readme.io/) | cex | [](https://github.com/ccxt/ccxt/wiki/Certification) | [](https://ccxt.pro) | |
|
99
101
|
| [](https://www.huobi.com/en-us/v/register/double-invite/?inviter_id=11343840&invite_code=6rmm2223) | htx | [HTX](https://www.huobi.com/en-us/v/register/double-invite/?inviter_id=11343840&invite_code=6rmm2223) | [](https://huobiapi.github.io/docs/spot/v1/en/) | cex | [](https://github.com/ccxt/ccxt/wiki/Certification) | [](https://ccxt.pro) | [](https://www.huobi.com/en-us/v/register/double-invite/?inviter_id=11343840&invite_code=6rmm2223) |
|
100
102
|
| [](https://www.kucoin.com/ucenter/signup?rcode=E5wkqe) | kucoin | [KuCoin](https://www.kucoin.com/ucenter/signup?rcode=E5wkqe) | [](https://docs.kucoin.com) | cex | [](https://github.com/ccxt/ccxt/wiki/Certification) | [](https://ccxt.pro) | |
|
101
103
|
| [](https://futures.kucoin.com/?rcode=E5wkqe) | kucoinfutures | [KuCoin Futures](https://futures.kucoin.com/?rcode=E5wkqe) | [](https://docs.kucoin.com/futures) | cex | [](https://github.com/ccxt/ccxt/wiki/Certification) | [](https://ccxt.pro) | |
|
@@ -106,7 +108,7 @@ Current feature list:
|
|
106
108
|
|
107
109
|
## Supported Cryptocurrency Exchanges
|
108
110
|
|
109
|
-
The CCXT library currently supports the following
|
111
|
+
The CCXT library currently supports the following 103 cryptocurrency exchange markets and trading APIs:
|
110
112
|
|
111
113
|
| logo | id | name | ver | type | certified | pro |
|
112
114
|
|---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------|-----------------------|-------------------------------------------------------------------------------------------------------|:------------------------------------------------------------------------------------------------------------------------------------------------:|------|-----------------------------------------------------------------------------------------------------------------------------|------------------------------------------------------------------------------|
|
@@ -165,6 +167,7 @@ The CCXT library currently supports the following 102 cryptocurrency exchange ma
|
|
165
167
|
| [](https://fmfw.io/referral/da948b21d6c92d69) | fmfwio | [FMFW.io](https://fmfw.io/referral/da948b21d6c92d69) | [](https://api.fmfw.io/) | cex | | |
|
166
168
|
| [](https://www.gate.io/signup/2436035) | gate | [Gate.io](https://www.gate.io/signup/2436035) | [](https://www.gate.io/docs/developers/apiv4/en/) | cex | [](https://github.com/ccxt/ccxt/wiki/Certification) | [](https://ccxt.pro) |
|
167
169
|
| [](https://gemini.com/) | gemini | [Gemini](https://gemini.com/) | [](https://docs.gemini.com/rest-api) | cex | | [](https://ccxt.pro) |
|
170
|
+
| [](https://global.hashkey.com/en-US/register/invite?invite_code=82FQUN) | hashkey | [HashKey Global](https://global.hashkey.com/en-US/register/invite?invite_code=82FQUN) | [](https://hashkeyglobal-apidoc.readme.io/) | cex | [](https://github.com/ccxt/ccxt/wiki/Certification) | [](https://ccxt.pro) |
|
168
171
|
| [](https://hitbtc.com/?ref_id=5a5d39a65d466) | hitbtc | [HitBTC](https://hitbtc.com/?ref_id=5a5d39a65d466) | [](https://api.hitbtc.com) | cex | | |
|
169
172
|
| [](https://pro.hollaex.com/signup?affiliation_code=QSWA6G) | hollaex | [HollaEx](https://pro.hollaex.com/signup?affiliation_code=QSWA6G) | [](https://apidocs.hollaex.com) | cex | | [](https://ccxt.pro) |
|
170
173
|
| [](https://www.huobi.com/en-us/v/register/double-invite/?inviter_id=11343840&invite_code=6rmm2223) | htx | [HTX](https://www.huobi.com/en-us/v/register/double-invite/?inviter_id=11343840&invite_code=6rmm2223) | [](https://huobiapi.github.io/docs/spot/v1/en/) | cex | [](https://github.com/ccxt/ccxt/wiki/Certification) | [](https://ccxt.pro) |
|
@@ -269,13 +272,13 @@ console.log(version, Object.keys(exchanges));
|
|
269
272
|
|
270
273
|
All-in-one browser bundle (dependencies included), served from a CDN of your choice:
|
271
274
|
|
272
|
-
* jsDelivr: https://cdn.jsdelivr.net/npm/ccxt@4.3.
|
273
|
-
* unpkg: https://unpkg.com/ccxt@4.3.
|
275
|
+
* jsDelivr: https://cdn.jsdelivr.net/npm/ccxt@4.3.87/dist/ccxt.browser.min.js
|
276
|
+
* unpkg: https://unpkg.com/ccxt@4.3.87/dist/ccxt.browser.min.js
|
274
277
|
|
275
278
|
CDNs are not updated in real-time and may have delays. Defaulting to the most recent version without specifying the version number is not recommended. Please, keep in mind that we are not responsible for the correct operation of those CDN servers.
|
276
279
|
|
277
280
|
```HTML
|
278
|
-
<script type="text/javascript" src="https://cdn.jsdelivr.net/npm/ccxt@4.3.
|
281
|
+
<script type="text/javascript" src="https://cdn.jsdelivr.net/npm/ccxt@4.3.87/dist/ccxt.browser.min.js"></script>
|
279
282
|
```
|
280
283
|
|
281
284
|
Creates a global `ccxt` object:
|