ccxt 4.2.95__py2.py3-none-any.whl → 4.2.97__py2.py3-none-any.whl
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- ccxt/__init__.py +1 -1
- ccxt/abstract/coinbase.py +1 -0
- ccxt/async_support/__init__.py +1 -1
- ccxt/async_support/base/exchange.py +1 -1
- ccxt/async_support/binance.py +9 -9
- ccxt/async_support/coinbase.py +590 -102
- ccxt/async_support/deribit.py +7 -3
- ccxt/async_support/gemini.py +26 -11
- ccxt/async_support/okx.py +3 -2
- ccxt/async_support/poloniexfutures.py +4 -1
- ccxt/base/exchange.py +17 -5
- ccxt/binance.py +9 -9
- ccxt/coinbase.py +590 -102
- ccxt/deribit.py +7 -3
- ccxt/gemini.py +26 -11
- ccxt/okx.py +3 -2
- ccxt/poloniexfutures.py +4 -1
- ccxt/pro/__init__.py +1 -1
- ccxt/pro/binance.py +9 -5
- ccxt/pro/coinbase.py +19 -4
- ccxt/pro/poloniexfutures.py +5 -2
- ccxt/test/base/test_order_book.py +21 -15
- ccxt/test/test_async.py +32 -6
- ccxt/test/test_sync.py +32 -6
- {ccxt-4.2.95.dist-info → ccxt-4.2.97.dist-info}/METADATA +4 -4
- {ccxt-4.2.95.dist-info → ccxt-4.2.97.dist-info}/RECORD +28 -28
- {ccxt-4.2.95.dist-info → ccxt-4.2.97.dist-info}/WHEEL +0 -0
- {ccxt-4.2.95.dist-info → ccxt-4.2.97.dist-info}/top_level.txt +0 -0
ccxt/deribit.py
CHANGED
@@ -1184,13 +1184,17 @@ class deribit(Exchange, ImplicitAPI):
|
|
1184
1184
|
"""
|
1185
1185
|
fetches price tickers for multiple markets, statistical information calculated over the past 24 hours for each market
|
1186
1186
|
:see: https://docs.deribit.com/#public-get_book_summary_by_currency
|
1187
|
-
:param str[]
|
1187
|
+
:param str[] [symbols]: unified symbols of the markets to fetch the ticker for, all market tickers are returned if not assigned
|
1188
1188
|
:param dict [params]: extra parameters specific to the exchange API endpoint
|
1189
|
+
:param str [params.code]: *required* the currency code to fetch the tickers for, eg. 'BTC', 'ETH'
|
1189
1190
|
:returns dict: a dictionary of `ticker structures <https://docs.ccxt.com/#/?id=ticker-structure>`
|
1190
1191
|
"""
|
1191
1192
|
self.load_markets()
|
1192
1193
|
symbols = self.market_symbols(symbols)
|
1193
|
-
code = self.
|
1194
|
+
code = self.safe_string_2(params, 'code', 'currency')
|
1195
|
+
params = self.omit(params, ['code'])
|
1196
|
+
if code is None:
|
1197
|
+
raise ArgumentsRequired(self.id + ' fetchTickers requires a currency/code(eg: BTC/ETH/USDT) parameter to fetch tickers for')
|
1194
1198
|
currency = self.currency(code)
|
1195
1199
|
request = {
|
1196
1200
|
'currency': currency['id'],
|
@@ -1226,7 +1230,7 @@ class deribit(Exchange, ImplicitAPI):
|
|
1226
1230
|
# "testnet": False
|
1227
1231
|
# }
|
1228
1232
|
#
|
1229
|
-
result = self.
|
1233
|
+
result = self.safe_list(response, 'result', [])
|
1230
1234
|
tickers = {}
|
1231
1235
|
for i in range(0, len(result)):
|
1232
1236
|
ticker = self.parse_ticker(result[i])
|
ccxt/gemini.py
CHANGED
@@ -299,7 +299,13 @@ class gemini(Exchange, ImplicitAPI):
|
|
299
299
|
'ATOM': 'cosmos',
|
300
300
|
'DOT': 'polkadot',
|
301
301
|
},
|
302
|
-
'nonce': 'milliseconds', # if getting a Network 400 error change to seconds
|
302
|
+
'nonce': 'milliseconds', # if getting a Network 400 error change to seconds,
|
303
|
+
'conflictingMarkets': {
|
304
|
+
'paxgusd': {
|
305
|
+
'base': 'PAXG',
|
306
|
+
'quote': 'USD',
|
307
|
+
},
|
308
|
+
},
|
303
309
|
},
|
304
310
|
})
|
305
311
|
|
@@ -661,16 +667,25 @@ class gemini(Exchange, ImplicitAPI):
|
|
661
667
|
marketIdUpper = marketId.upper()
|
662
668
|
isPerp = (marketIdUpper.find('PERP') >= 0)
|
663
669
|
marketIdWithoutPerp = marketIdUpper.replace('PERP', '')
|
664
|
-
|
665
|
-
|
666
|
-
|
667
|
-
|
668
|
-
|
669
|
-
|
670
|
-
|
671
|
-
|
672
|
-
|
673
|
-
|
670
|
+
conflictingMarkets = self.safe_dict(self.options, 'conflictingMarkets', {})
|
671
|
+
lowerCaseId = marketIdWithoutPerp.lower()
|
672
|
+
if lowerCaseId in conflictingMarkets:
|
673
|
+
conflictingMarket = conflictingMarkets[lowerCaseId]
|
674
|
+
baseId = conflictingMarket['base']
|
675
|
+
quoteId = conflictingMarket['quote']
|
676
|
+
if isPerp:
|
677
|
+
settleId = conflictingMarket['quote']
|
678
|
+
else:
|
679
|
+
quoteCurrencies = self.handle_option('fetchMarketsFromAPI', 'quoteCurrencies', [])
|
680
|
+
for i in range(0, len(quoteCurrencies)):
|
681
|
+
quoteCurrency = quoteCurrencies[i]
|
682
|
+
if marketIdWithoutPerp.endswith(quoteCurrency):
|
683
|
+
quoteLength = self.parse_to_int(-1 * len(quoteCurrency))
|
684
|
+
baseId = marketIdWithoutPerp[0:quoteLength]
|
685
|
+
quoteId = quoteCurrency
|
686
|
+
if isPerp:
|
687
|
+
settleId = quoteCurrency # always same
|
688
|
+
break
|
674
689
|
base = self.safe_currency_code(baseId)
|
675
690
|
quote = self.safe_currency_code(quoteId)
|
676
691
|
settle = self.safe_currency_code(settleId)
|
ccxt/okx.py
CHANGED
@@ -8,6 +8,7 @@ from ccxt.abstract.okx import ImplicitAPI
|
|
8
8
|
import hashlib
|
9
9
|
from ccxt.base.types import Account, Balances, Conversion, Currencies, Currency, Greeks, Int, Leverage, MarginModification, Market, MarketInterface, Num, Option, OptionChain, Order, OrderBook, OrderRequest, OrderSide, OrderType, Str, Strings, Ticker, Tickers, Trade, TradingFeeInterface, Transaction, TransferEntry
|
10
10
|
from typing import List
|
11
|
+
from typing import Any
|
11
12
|
from ccxt.base.errors import ExchangeError
|
12
13
|
from ccxt.base.errors import AuthenticationError
|
13
14
|
from ccxt.base.errors import PermissionDenied
|
@@ -1129,13 +1130,13 @@ class okx(Exchange, ImplicitAPI):
|
|
1129
1130
|
},
|
1130
1131
|
})
|
1131
1132
|
|
1132
|
-
def handle_market_type_and_params(self, methodName, market=None, params={}):
|
1133
|
+
def handle_market_type_and_params(self, methodName: str, market: Market = None, params={}, defaultValue=None) -> Any:
|
1133
1134
|
instType = self.safe_string(params, 'instType')
|
1134
1135
|
params = self.omit(params, 'instType')
|
1135
1136
|
type = self.safe_string(params, 'type')
|
1136
1137
|
if (type is None) and (instType is not None):
|
1137
1138
|
params['type'] = instType
|
1138
|
-
return super(okx, self).handle_market_type_and_params(methodName, market, params)
|
1139
|
+
return super(okx, self).handle_market_type_and_params(methodName, market, params, defaultValue)
|
1139
1140
|
|
1140
1141
|
def convert_to_instrument_type(self, type):
|
1141
1142
|
exchangeTypes = self.safe_dict(self.options, 'exchangeType', {})
|
ccxt/poloniexfutures.py
CHANGED
@@ -382,7 +382,10 @@ class poloniexfutures(Exchange, ImplicitAPI):
|
|
382
382
|
#
|
383
383
|
marketId = self.safe_string(ticker, 'symbol')
|
384
384
|
symbol = self.safe_symbol(marketId, market)
|
385
|
-
|
385
|
+
timestampString = self.safe_string(ticker, 'ts')
|
386
|
+
# check timestamp bcz bug: https://app.travis-ci.com/github/ccxt/ccxt/builds/269959181#L4011
|
387
|
+
multiplier = 0.00001 if (len(timestampString) == 18) else 0.000001
|
388
|
+
timestamp = self.safe_integer_product(ticker, 'ts', multiplier)
|
386
389
|
last = self.safe_string_2(ticker, 'price', 'lastPrice')
|
387
390
|
percentage = Precise.string_mul(self.safe_string(ticker, 'priceChgPct'), '100')
|
388
391
|
return self.safe_ticker({
|
ccxt/pro/__init__.py
CHANGED
ccxt/pro/binance.py
CHANGED
@@ -9,10 +9,10 @@ import hashlib
|
|
9
9
|
from ccxt.base.types import Balances, Int, Num, Order, OrderBook, OrderSide, OrderType, Position, Str, Strings, Ticker, Tickers, Trade
|
10
10
|
from ccxt.async_support.base.ws.client import Client
|
11
11
|
from typing import List
|
12
|
-
from ccxt.base.errors import ExchangeError
|
13
12
|
from ccxt.base.errors import ArgumentsRequired
|
14
13
|
from ccxt.base.errors import BadRequest
|
15
14
|
from ccxt.base.errors import NotSupported
|
15
|
+
from ccxt.base.errors import InvalidNonce
|
16
16
|
from ccxt.base.precise import Precise
|
17
17
|
|
18
18
|
|
@@ -400,8 +400,10 @@ class binance(ccxt.async_support.binance):
|
|
400
400
|
if nonce < orderbook['nonce']:
|
401
401
|
client.resolve(orderbook, messageHash)
|
402
402
|
else:
|
403
|
-
|
404
|
-
|
403
|
+
checksum = self.safe_bool(self.options, 'checksum', True)
|
404
|
+
if checksum:
|
405
|
+
# todo: client.reject from handleOrderBookMessage properly
|
406
|
+
raise InvalidNonce(self.id + ' handleOrderBook received an out-of-order nonce')
|
405
407
|
else:
|
406
408
|
# future
|
407
409
|
# 4. Drop any event where u is < lastUpdateId in the snapshot
|
@@ -413,8 +415,10 @@ class binance(ccxt.async_support.binance):
|
|
413
415
|
if nonce <= orderbook['nonce']:
|
414
416
|
client.resolve(orderbook, messageHash)
|
415
417
|
else:
|
416
|
-
|
417
|
-
|
418
|
+
checksum = self.safe_bool(self.options, 'checksum', True)
|
419
|
+
if checksum:
|
420
|
+
# todo: client.reject from handleOrderBookMessage properly
|
421
|
+
raise InvalidNonce(self.id + ' handleOrderBook received an out-of-order nonce')
|
418
422
|
except Exception as e:
|
419
423
|
del self.orderbooks[symbol]
|
420
424
|
del client.subscriptions[messageHash]
|
ccxt/pro/coinbase.py
CHANGED
@@ -136,6 +136,11 @@ class coinbase(ccxt.async_support.coinbase):
|
|
136
136
|
# "low_52_w": "15460",
|
137
137
|
# "high_52_w": "48240",
|
138
138
|
# "price_percent_chg_24_h": "-4.15775596190603"
|
139
|
+
# new 2024-04-12
|
140
|
+
# "best_bid":"21835.29",
|
141
|
+
# "best_bid_quantity": "0.02000000",
|
142
|
+
# "best_ask":"23011.18",
|
143
|
+
# "best_ask_quantity": "0.01500000"
|
139
144
|
# }
|
140
145
|
# ]
|
141
146
|
# }
|
@@ -161,6 +166,11 @@ class coinbase(ccxt.async_support.coinbase):
|
|
161
166
|
# "low_52_w": "0.04908",
|
162
167
|
# "high_52_w": "0.1801",
|
163
168
|
# "price_percent_chg_24_h": "0.50177456859626"
|
169
|
+
# new 2024-04-12
|
170
|
+
# "best_bid":"0.07989",
|
171
|
+
# "best_bid_quantity": "500.0",
|
172
|
+
# "best_ask":"0.08308",
|
173
|
+
# "best_ask_quantity": "300.0"
|
164
174
|
# }
|
165
175
|
# ]
|
166
176
|
# }
|
@@ -209,6 +219,11 @@ class coinbase(ccxt.async_support.coinbase):
|
|
209
219
|
# "low_52_w": "0.04908",
|
210
220
|
# "high_52_w": "0.1801",
|
211
221
|
# "price_percent_chg_24_h": "0.50177456859626"
|
222
|
+
# new 2024-04-12
|
223
|
+
# "best_bid":"0.07989",
|
224
|
+
# "best_bid_quantity": "500.0",
|
225
|
+
# "best_ask":"0.08308",
|
226
|
+
# "best_ask_quantity": "300.0"
|
212
227
|
# }
|
213
228
|
#
|
214
229
|
marketId = self.safe_string(ticker, 'product_id')
|
@@ -221,10 +236,10 @@ class coinbase(ccxt.async_support.coinbase):
|
|
221
236
|
'datetime': self.iso8601(timestamp),
|
222
237
|
'high': self.safe_string(ticker, 'high_24_h'),
|
223
238
|
'low': self.safe_string(ticker, 'low_24_h'),
|
224
|
-
'bid':
|
225
|
-
'bidVolume':
|
226
|
-
'ask':
|
227
|
-
'askVolume':
|
239
|
+
'bid': self.safe_string(ticker, 'best_bid'),
|
240
|
+
'bidVolume': self.safe_string(ticker, 'best_bid_quantity'),
|
241
|
+
'ask': self.safe_string(ticker, 'best_ask'),
|
242
|
+
'askVolume': self.safe_string(ticker, 'best_ask_quantity'),
|
228
243
|
'vwap': None,
|
229
244
|
'open': None,
|
230
245
|
'close': last,
|
ccxt/pro/poloniexfutures.py
CHANGED
@@ -8,9 +8,9 @@ from ccxt.async_support.base.ws.cache import ArrayCache, ArrayCacheBySymbolById
|
|
8
8
|
from ccxt.base.types import Balances, Int, Order, OrderBook, Str, Ticker, Trade
|
9
9
|
from ccxt.async_support.base.ws.client import Client
|
10
10
|
from typing import List
|
11
|
-
from ccxt.base.errors import ExchangeError
|
12
11
|
from ccxt.base.errors import AuthenticationError
|
13
12
|
from ccxt.base.errors import BadRequest
|
13
|
+
from ccxt.base.errors import InvalidNonce
|
14
14
|
|
15
15
|
|
16
16
|
class poloniexfutures(ccxt.async_support.poloniexfutures):
|
@@ -817,7 +817,10 @@ class poloniexfutures(ccxt.async_support.poloniexfutures):
|
|
817
817
|
sequence = self.safe_integer(delta, 'sequence')
|
818
818
|
nonce = self.safe_integer(orderbook, 'nonce')
|
819
819
|
if nonce != sequence - 1:
|
820
|
-
|
820
|
+
checksum = self.safe_bool(self.options, 'checksum', True)
|
821
|
+
if checksum:
|
822
|
+
# todo: client.reject from handleOrderBookMessage properly
|
823
|
+
raise InvalidNonce(self.id + ' watchOrderBook received an out-of-order nonce')
|
821
824
|
change = self.safe_string(delta, 'change')
|
822
825
|
splitChange = change.split(',')
|
823
826
|
price = self.safe_number(splitChange, 0)
|
@@ -24,7 +24,9 @@ def test_order_book(exchange, skipped_properties, method, orderbook, symbol):
|
|
24
24
|
'datetime': '2017-09-01T00:00:00',
|
25
25
|
'nonce': 134234234,
|
26
26
|
}
|
27
|
-
empty_allowed_for = ['
|
27
|
+
empty_allowed_for = ['nonce']
|
28
|
+
# turn into copy: https://discord.com/channels/690203284119617602/921046068555313202/1220626834887282728
|
29
|
+
orderbook = exchange.deep_extend({}, orderbook)
|
28
30
|
test_shared_methods.assert_structure(exchange, skipped_properties, method, orderbook, format, empty_allowed_for)
|
29
31
|
test_shared_methods.assert_timestamp_and_datetime(exchange, skipped_properties, method, orderbook)
|
30
32
|
test_shared_methods.assert_symbol(exchange, skipped_properties, method, orderbook, 'symbol', symbol)
|
@@ -37,24 +39,28 @@ def test_order_book(exchange, skipped_properties, method, orderbook, symbol):
|
|
37
39
|
bids_length = len(bids)
|
38
40
|
for i in range(0, bids_length):
|
39
41
|
current_bid_string = exchange.safe_string(bids[i], 0)
|
40
|
-
|
41
|
-
|
42
|
-
|
43
|
-
|
44
|
-
|
45
|
-
|
46
|
-
|
42
|
+
if not ('compareToNextItem' in skipped_properties):
|
43
|
+
next_i = i + 1
|
44
|
+
if bids_length > next_i:
|
45
|
+
next_bid_string = exchange.safe_string(bids[next_i], 0)
|
46
|
+
assert Precise.string_gt(current_bid_string, next_bid_string), 'current bid should be > than the next one: ' + current_bid_string + '>' + next_bid_string + log_text
|
47
|
+
if not ('compareToZero' in skipped_properties):
|
48
|
+
# compare price & volume to zero
|
49
|
+
test_shared_methods.assert_greater(exchange, skipped_properties, method, bids[i], 0, '0')
|
50
|
+
test_shared_methods.assert_greater(exchange, skipped_properties, method, bids[i], 1, '0')
|
47
51
|
asks = orderbook['asks']
|
48
52
|
asks_length = len(asks)
|
49
53
|
for i in range(0, asks_length):
|
50
54
|
current_ask_string = exchange.safe_string(asks[i], 0)
|
51
|
-
|
52
|
-
|
53
|
-
|
54
|
-
|
55
|
-
|
56
|
-
|
57
|
-
|
55
|
+
if not ('compareToNextItem' in skipped_properties):
|
56
|
+
next_i = i + 1
|
57
|
+
if asks_length > next_i:
|
58
|
+
next_ask_string = exchange.safe_string(asks[next_i], 0)
|
59
|
+
assert Precise.string_lt(current_ask_string, next_ask_string), 'current ask should be < than the next one: ' + current_ask_string + '<' + next_ask_string + log_text
|
60
|
+
if not ('compareToZero' in skipped_properties):
|
61
|
+
# compare price & volume to zero
|
62
|
+
test_shared_methods.assert_greater(exchange, skipped_properties, method, asks[i], 0, '0')
|
63
|
+
test_shared_methods.assert_greater(exchange, skipped_properties, method, asks[i], 1, '0')
|
58
64
|
if not ('spread' in skipped_properties):
|
59
65
|
if bids_length and asks_length:
|
60
66
|
first_bid = exchange.safe_string(bids[0], 0)
|
ccxt/test/test_async.py
CHANGED
@@ -422,9 +422,13 @@ class testMainClass(baseMainTestClass):
|
|
422
422
|
return result
|
423
423
|
|
424
424
|
async def test_method(self, method_name, exchange, args, is_public):
|
425
|
+
# todo: temporary skip for c#
|
426
|
+
if 'OrderBook' in method_name and self.ext == 'cs':
|
427
|
+
exchange.options['checksum'] = False
|
425
428
|
# todo: temporary skip for php
|
426
429
|
if 'OrderBook' in method_name and self.ext == 'php':
|
427
430
|
return
|
431
|
+
skipped_properties_for_method = self.get_skips(exchange, method_name)
|
428
432
|
is_load_markets = (method_name == 'loadMarkets')
|
429
433
|
is_fetch_currencies = (method_name == 'fetchCurrencies')
|
430
434
|
is_proxy_test = (method_name == self.proxy_test_file_name)
|
@@ -437,7 +441,7 @@ class testMainClass(baseMainTestClass):
|
|
437
441
|
skip_message = '[INFO] IGNORED_TEST'
|
438
442
|
elif not is_load_markets and not supported_by_exchange and not is_proxy_test:
|
439
443
|
skip_message = '[INFO] UNSUPPORTED_TEST' # keep it aligned with the longest message
|
440
|
-
elif
|
444
|
+
elif isinstance(skipped_properties_for_method, str):
|
441
445
|
skip_message = '[INFO] SKIPPED_TEST'
|
442
446
|
elif not (method_name in self.test_files):
|
443
447
|
skip_message = '[INFO] UNIMPLEMENTED_TEST'
|
@@ -451,15 +455,24 @@ class testMainClass(baseMainTestClass):
|
|
451
455
|
if self.info:
|
452
456
|
args_stringified = '(' + ','.join(args) + ')'
|
453
457
|
dump(self.add_padding('[INFO] TESTING', 25), self.exchange_hint(exchange), method_name, args_stringified)
|
454
|
-
await call_method(self.test_files, method_name, exchange,
|
458
|
+
await call_method(self.test_files, method_name, exchange, skipped_properties_for_method, args)
|
455
459
|
# if it was passed successfully, add to the list of successfull tests
|
456
460
|
if is_public:
|
457
461
|
self.checked_public_tests[method_name] = True
|
458
462
|
return
|
459
463
|
|
460
464
|
def get_skips(self, exchange, method_name):
|
461
|
-
|
462
|
-
|
465
|
+
final_skips = {}
|
466
|
+
# check the exact method (i.e. `fetchTrades`) and language-specific (i.e. `fetchTrades.php`)
|
467
|
+
method_names = [method_name, method_name + '.' + self.ext]
|
468
|
+
for i in range(0, len(method_names)):
|
469
|
+
m_name = method_names[i]
|
470
|
+
if m_name in self.skipped_methods:
|
471
|
+
# if whole method is skipped, by assigning a string to it, i.e. "fetchOrders":"blabla"
|
472
|
+
if isinstance(self.skipped_methods[m_name], str):
|
473
|
+
return self.skipped_methods[m_name]
|
474
|
+
else:
|
475
|
+
final_skips = exchange.deep_extend(final_skips, self.skipped_methods[m_name])
|
463
476
|
# get "object-specific" skips
|
464
477
|
object_skips = {
|
465
478
|
'orderBook': ['fetchOrderBook', 'fetchOrderBooks', 'fetchL2OrderBook', 'watchOrderBook', 'watchOrderBookForSymbols'],
|
@@ -475,9 +488,19 @@ class testMainClass(baseMainTestClass):
|
|
475
488
|
object_name = object_names[i]
|
476
489
|
object_methods = object_skips[object_name]
|
477
490
|
if exchange.in_array(method_name, object_methods):
|
491
|
+
# if whole object is skipped, by assigning a string to it, i.e. "orderBook":"blabla"
|
492
|
+
if (object_name in self.skipped_methods) and (isinstance(self.skipped_methods[object_name], str)):
|
493
|
+
return self.skipped_methods[object_name]
|
478
494
|
extra_skips = exchange.safe_dict(self.skipped_methods, object_name, {})
|
479
|
-
|
480
|
-
|
495
|
+
final_skips = exchange.deep_extend(final_skips, extra_skips)
|
496
|
+
# extend related skips
|
497
|
+
# - if 'timestamp' is skipped, we should do so for 'datetime' too
|
498
|
+
# - if 'bid' is skipped, skip 'ask' too
|
499
|
+
if ('timestamp' in final_skips) and not ('datetime' in final_skips):
|
500
|
+
final_skips['datetime'] = final_skips['timestamp']
|
501
|
+
if ('bid' in final_skips) and not ('ask' in final_skips):
|
502
|
+
final_skips['ask'] = final_skips['bid']
|
503
|
+
return final_skips
|
481
504
|
|
482
505
|
async def test_safe(self, method_name, exchange, args=[], is_public=False):
|
483
506
|
# `testSafe` method does not throw an exception, instead mutes it. The reason we
|
@@ -569,11 +592,14 @@ class testMainClass(baseMainTestClass):
|
|
569
592
|
if self.ws_tests:
|
570
593
|
tests = {
|
571
594
|
'watchOHLCV': [symbol],
|
595
|
+
'watchOHLCVForSymbols': [symbol],
|
572
596
|
'watchTicker': [symbol],
|
573
597
|
'watchTickers': [symbol],
|
574
598
|
'watchBidsAsks': [symbol],
|
575
599
|
'watchOrderBook': [symbol],
|
600
|
+
'watchOrderBookForSymbols': [[symbol]],
|
576
601
|
'watchTrades': [symbol],
|
602
|
+
'watchTradesForSymbols': [[symbol]],
|
577
603
|
}
|
578
604
|
market = exchange.market(symbol)
|
579
605
|
is_spot = market['spot']
|
ccxt/test/test_sync.py
CHANGED
@@ -421,9 +421,13 @@ class testMainClass(baseMainTestClass):
|
|
421
421
|
return result
|
422
422
|
|
423
423
|
def test_method(self, method_name, exchange, args, is_public):
|
424
|
+
# todo: temporary skip for c#
|
425
|
+
if 'OrderBook' in method_name and self.ext == 'cs':
|
426
|
+
exchange.options['checksum'] = False
|
424
427
|
# todo: temporary skip for php
|
425
428
|
if 'OrderBook' in method_name and self.ext == 'php':
|
426
429
|
return
|
430
|
+
skipped_properties_for_method = self.get_skips(exchange, method_name)
|
427
431
|
is_load_markets = (method_name == 'loadMarkets')
|
428
432
|
is_fetch_currencies = (method_name == 'fetchCurrencies')
|
429
433
|
is_proxy_test = (method_name == self.proxy_test_file_name)
|
@@ -436,7 +440,7 @@ class testMainClass(baseMainTestClass):
|
|
436
440
|
skip_message = '[INFO] IGNORED_TEST'
|
437
441
|
elif not is_load_markets and not supported_by_exchange and not is_proxy_test:
|
438
442
|
skip_message = '[INFO] UNSUPPORTED_TEST' # keep it aligned with the longest message
|
439
|
-
elif
|
443
|
+
elif isinstance(skipped_properties_for_method, str):
|
440
444
|
skip_message = '[INFO] SKIPPED_TEST'
|
441
445
|
elif not (method_name in self.test_files):
|
442
446
|
skip_message = '[INFO] UNIMPLEMENTED_TEST'
|
@@ -450,15 +454,24 @@ class testMainClass(baseMainTestClass):
|
|
450
454
|
if self.info:
|
451
455
|
args_stringified = '(' + ','.join(args) + ')'
|
452
456
|
dump(self.add_padding('[INFO] TESTING', 25), self.exchange_hint(exchange), method_name, args_stringified)
|
453
|
-
call_method(self.test_files, method_name, exchange,
|
457
|
+
call_method(self.test_files, method_name, exchange, skipped_properties_for_method, args)
|
454
458
|
# if it was passed successfully, add to the list of successfull tests
|
455
459
|
if is_public:
|
456
460
|
self.checked_public_tests[method_name] = True
|
457
461
|
return
|
458
462
|
|
459
463
|
def get_skips(self, exchange, method_name):
|
460
|
-
|
461
|
-
|
464
|
+
final_skips = {}
|
465
|
+
# check the exact method (i.e. `fetchTrades`) and language-specific (i.e. `fetchTrades.php`)
|
466
|
+
method_names = [method_name, method_name + '.' + self.ext]
|
467
|
+
for i in range(0, len(method_names)):
|
468
|
+
m_name = method_names[i]
|
469
|
+
if m_name in self.skipped_methods:
|
470
|
+
# if whole method is skipped, by assigning a string to it, i.e. "fetchOrders":"blabla"
|
471
|
+
if isinstance(self.skipped_methods[m_name], str):
|
472
|
+
return self.skipped_methods[m_name]
|
473
|
+
else:
|
474
|
+
final_skips = exchange.deep_extend(final_skips, self.skipped_methods[m_name])
|
462
475
|
# get "object-specific" skips
|
463
476
|
object_skips = {
|
464
477
|
'orderBook': ['fetchOrderBook', 'fetchOrderBooks', 'fetchL2OrderBook', 'watchOrderBook', 'watchOrderBookForSymbols'],
|
@@ -474,9 +487,19 @@ class testMainClass(baseMainTestClass):
|
|
474
487
|
object_name = object_names[i]
|
475
488
|
object_methods = object_skips[object_name]
|
476
489
|
if exchange.in_array(method_name, object_methods):
|
490
|
+
# if whole object is skipped, by assigning a string to it, i.e. "orderBook":"blabla"
|
491
|
+
if (object_name in self.skipped_methods) and (isinstance(self.skipped_methods[object_name], str)):
|
492
|
+
return self.skipped_methods[object_name]
|
477
493
|
extra_skips = exchange.safe_dict(self.skipped_methods, object_name, {})
|
478
|
-
|
479
|
-
|
494
|
+
final_skips = exchange.deep_extend(final_skips, extra_skips)
|
495
|
+
# extend related skips
|
496
|
+
# - if 'timestamp' is skipped, we should do so for 'datetime' too
|
497
|
+
# - if 'bid' is skipped, skip 'ask' too
|
498
|
+
if ('timestamp' in final_skips) and not ('datetime' in final_skips):
|
499
|
+
final_skips['datetime'] = final_skips['timestamp']
|
500
|
+
if ('bid' in final_skips) and not ('ask' in final_skips):
|
501
|
+
final_skips['ask'] = final_skips['bid']
|
502
|
+
return final_skips
|
480
503
|
|
481
504
|
def test_safe(self, method_name, exchange, args=[], is_public=False):
|
482
505
|
# `testSafe` method does not throw an exception, instead mutes it. The reason we
|
@@ -568,11 +591,14 @@ class testMainClass(baseMainTestClass):
|
|
568
591
|
if self.ws_tests:
|
569
592
|
tests = {
|
570
593
|
'watchOHLCV': [symbol],
|
594
|
+
'watchOHLCVForSymbols': [symbol],
|
571
595
|
'watchTicker': [symbol],
|
572
596
|
'watchTickers': [symbol],
|
573
597
|
'watchBidsAsks': [symbol],
|
574
598
|
'watchOrderBook': [symbol],
|
599
|
+
'watchOrderBookForSymbols': [[symbol]],
|
575
600
|
'watchTrades': [symbol],
|
601
|
+
'watchTradesForSymbols': [[symbol]],
|
576
602
|
}
|
577
603
|
market = exchange.market(symbol)
|
578
604
|
is_spot = market['spot']
|
@@ -1,6 +1,6 @@
|
|
1
1
|
Metadata-Version: 2.1
|
2
2
|
Name: ccxt
|
3
|
-
Version: 4.2.
|
3
|
+
Version: 4.2.97
|
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
|
@@ -261,13 +261,13 @@ console.log(version, Object.keys(exchanges));
|
|
261
261
|
|
262
262
|
All-in-one browser bundle (dependencies included), served from a CDN of your choice:
|
263
263
|
|
264
|
-
* jsDelivr: https://cdn.jsdelivr.net/npm/ccxt@4.2.
|
265
|
-
* unpkg: https://unpkg.com/ccxt@4.2.
|
264
|
+
* jsDelivr: https://cdn.jsdelivr.net/npm/ccxt@4.2.97/dist/ccxt.browser.js
|
265
|
+
* unpkg: https://unpkg.com/ccxt@4.2.97/dist/ccxt.browser.js
|
266
266
|
|
267
267
|
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.
|
268
268
|
|
269
269
|
```HTML
|
270
|
-
<script type="text/javascript" src="https://cdn.jsdelivr.net/npm/ccxt@4.2.
|
270
|
+
<script type="text/javascript" src="https://cdn.jsdelivr.net/npm/ccxt@4.2.97/dist/ccxt.browser.js"></script>
|
271
271
|
```
|
272
272
|
|
273
273
|
Creates a global `ccxt` object:
|