gate-io-api 0.0.65__py3-none-any.whl → 0.0.100__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.
Potentially problematic release.
This version of gate-io-api might be problematic. Click here for more details.
- gate/ccxt/__init__.py +2 -1
- gate/ccxt/abstract/gate.py +62 -18
- gate/ccxt/async_support/__init__.py +2 -1
- gate/ccxt/async_support/base/exchange.py +165 -27
- gate/ccxt/async_support/base/throttler.py +1 -1
- gate/ccxt/async_support/base/ws/client.py +194 -64
- gate/ccxt/async_support/base/ws/future.py +27 -50
- gate/ccxt/async_support/gate.py +356 -253
- gate/ccxt/base/decimal_to_precision.py +14 -10
- gate/ccxt/base/errors.py +6 -0
- gate/ccxt/base/exchange.py +606 -119
- gate/ccxt/base/types.py +4 -0
- gate/ccxt/gate.py +356 -253
- gate/ccxt/pro/__init__.py +2 -89
- gate/ccxt/pro/gate.py +14 -7
- {gate_io_api-0.0.65.dist-info → gate_io_api-0.0.100.dist-info}/METADATA +70 -25
- {gate_io_api-0.0.65.dist-info → gate_io_api-0.0.100.dist-info}/RECORD +18 -19
- gate/ccxt/async_support/base/ws/aiohttp_client.py +0 -147
- {gate_io_api-0.0.65.dist-info → gate_io_api-0.0.100.dist-info}/WHEEL +0 -0
gate/ccxt/base/exchange.py
CHANGED
|
@@ -4,7 +4,7 @@
|
|
|
4
4
|
|
|
5
5
|
# -----------------------------------------------------------------------------
|
|
6
6
|
|
|
7
|
-
__version__ = '4.
|
|
7
|
+
__version__ = '4.5.15'
|
|
8
8
|
|
|
9
9
|
# -----------------------------------------------------------------------------
|
|
10
10
|
|
|
@@ -33,7 +33,7 @@ from ccxt.base.decimal_to_precision import decimal_to_precision
|
|
|
33
33
|
from ccxt.base.decimal_to_precision import DECIMAL_PLACES, TICK_SIZE, NO_PADDING, TRUNCATE, ROUND, ROUND_UP, ROUND_DOWN, SIGNIFICANT_DIGITS
|
|
34
34
|
from ccxt.base.decimal_to_precision import number_to_string
|
|
35
35
|
from ccxt.base.precise import Precise
|
|
36
|
-
from ccxt.base.types import ConstructorArgs, BalanceAccount, Currency, IndexType, OrderSide, OrderType, Trade, OrderRequest, Market, MarketType, Str, Num, Strings, CancellationRequest, Bool
|
|
36
|
+
from ccxt.base.types import ConstructorArgs, BalanceAccount, Currency, IndexType, OrderSide, OrderType, Trade, OrderRequest, Market, MarketType, Str, Num, Strings, CancellationRequest, Bool, Order
|
|
37
37
|
|
|
38
38
|
# -----------------------------------------------------------------------------
|
|
39
39
|
|
|
@@ -50,6 +50,11 @@ from cryptography.hazmat.primitives.serialization import load_pem_private_key
|
|
|
50
50
|
from ccxt.static_dependencies import ecdsa
|
|
51
51
|
from ccxt.static_dependencies import keccak
|
|
52
52
|
|
|
53
|
+
try:
|
|
54
|
+
import coincurve
|
|
55
|
+
except ImportError:
|
|
56
|
+
coincurve = None
|
|
57
|
+
|
|
53
58
|
# eddsa signing
|
|
54
59
|
try:
|
|
55
60
|
import axolotl_curve25519 as eddsa
|
|
@@ -229,6 +234,7 @@ class Exchange(object):
|
|
|
229
234
|
'chrome100': 'Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_7) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/100.0.4896.75 Safari/537.36',
|
|
230
235
|
}
|
|
231
236
|
headers = None
|
|
237
|
+
returnResponseHeaders = False
|
|
232
238
|
origin = '*' # CORS origin
|
|
233
239
|
MAX_VALUE = float('inf')
|
|
234
240
|
#
|
|
@@ -310,7 +316,7 @@ class Exchange(object):
|
|
|
310
316
|
bidsasks = None
|
|
311
317
|
base_currencies = None
|
|
312
318
|
quote_currencies = None
|
|
313
|
-
currencies =
|
|
319
|
+
currencies = {}
|
|
314
320
|
options = None # Python does not allow to define properties in run-time with setattr
|
|
315
321
|
isSandboxModeEnabled = False
|
|
316
322
|
accounts = None
|
|
@@ -578,6 +584,8 @@ class Exchange(object):
|
|
|
578
584
|
if self.verbose:
|
|
579
585
|
self.log("\nfetch Response:", self.id, method, url, http_status_code, "ResponseHeaders:", headers, "ResponseBody:", http_response)
|
|
580
586
|
self.logger.debug("%s %s, Response: %s %s %s", method, url, http_status_code, headers, http_response)
|
|
587
|
+
if json_response and not isinstance(json_response, list) and self.returnResponseHeaders:
|
|
588
|
+
json_response['responseHeaders'] = headers
|
|
581
589
|
response.raise_for_status()
|
|
582
590
|
|
|
583
591
|
except Timeout as e:
|
|
@@ -905,6 +913,10 @@ class Exchange(object):
|
|
|
905
913
|
def keysort(dictionary):
|
|
906
914
|
return collections.OrderedDict(sorted(dictionary.items(), key=lambda t: t[0]))
|
|
907
915
|
|
|
916
|
+
@staticmethod
|
|
917
|
+
def sort(array):
|
|
918
|
+
return sorted(array)
|
|
919
|
+
|
|
908
920
|
@staticmethod
|
|
909
921
|
def extend(*args):
|
|
910
922
|
if args is not None:
|
|
@@ -955,6 +967,11 @@ class Exchange(object):
|
|
|
955
967
|
def groupBy(array, key):
|
|
956
968
|
return Exchange.group_by(array, key)
|
|
957
969
|
|
|
970
|
+
|
|
971
|
+
@staticmethod
|
|
972
|
+
def index_by_safe(array, key):
|
|
973
|
+
return Exchange.index_by(array, key) # wrapper for go
|
|
974
|
+
|
|
958
975
|
@staticmethod
|
|
959
976
|
def index_by(array, key):
|
|
960
977
|
result = {}
|
|
@@ -1036,7 +1053,7 @@ class Exchange(object):
|
|
|
1036
1053
|
return _urlencode.urlencode(result, quote_via=_urlencode.quote)
|
|
1037
1054
|
|
|
1038
1055
|
@staticmethod
|
|
1039
|
-
def rawencode(params={}):
|
|
1056
|
+
def rawencode(params={}, sort=False):
|
|
1040
1057
|
return _urlencode.unquote(Exchange.urlencode(params))
|
|
1041
1058
|
|
|
1042
1059
|
@staticmethod
|
|
@@ -1296,6 +1313,10 @@ class Exchange(object):
|
|
|
1296
1313
|
elif algoType == 'ES':
|
|
1297
1314
|
rawSignature = Exchange.ecdsa(token, secret, 'p256', algorithm)
|
|
1298
1315
|
signature = Exchange.base16_to_binary(rawSignature['r'].rjust(64, "0") + rawSignature['s'].rjust(64, "0"))
|
|
1316
|
+
elif algoType == 'Ed':
|
|
1317
|
+
signature = Exchange.eddsa(token.encode('utf-8'), secret, 'ed25519', True)
|
|
1318
|
+
# here the signature is already a urlencoded base64-encoded string
|
|
1319
|
+
return token + '.' + signature
|
|
1299
1320
|
else:
|
|
1300
1321
|
signature = Exchange.hmac(Exchange.encode(token), secret, algos[algorithm], 'binary')
|
|
1301
1322
|
return token + '.' + Exchange.urlencode_base64(signature)
|
|
@@ -1366,9 +1387,9 @@ class Exchange(object):
|
|
|
1366
1387
|
return msgHash
|
|
1367
1388
|
|
|
1368
1389
|
@staticmethod
|
|
1369
|
-
def starknet_sign (
|
|
1390
|
+
def starknet_sign (msg_hash, pri):
|
|
1370
1391
|
# // TODO: unify to ecdsa
|
|
1371
|
-
r, s = message_signature(
|
|
1392
|
+
r, s = message_signature(msg_hash, pri)
|
|
1372
1393
|
return Exchange.json([hex(r), hex(s)])
|
|
1373
1394
|
|
|
1374
1395
|
@staticmethod
|
|
@@ -1385,6 +1406,21 @@ class Exchange(object):
|
|
|
1385
1406
|
|
|
1386
1407
|
@staticmethod
|
|
1387
1408
|
def ecdsa(request, secret, algorithm='p256', hash=None, fixed_length=False):
|
|
1409
|
+
"""
|
|
1410
|
+
ECDSA signing with support for multiple algorithms and coincurve for SECP256K1.
|
|
1411
|
+
Args:
|
|
1412
|
+
request: The message to sign
|
|
1413
|
+
secret: The private key (hex string or PEM format)
|
|
1414
|
+
algorithm: The elliptic curve algorithm ('p192', 'p224', 'p256', 'p384', 'p521', 'secp256k1')
|
|
1415
|
+
hash: The hash function to use (defaults to algorithm-specific hash)
|
|
1416
|
+
fixed_length: Whether to ensure fixed-length signatures (for deterministic signing)
|
|
1417
|
+
Note: coincurve produces non-deterministic signatures
|
|
1418
|
+
Returns:
|
|
1419
|
+
dict: {'r': r_value, 's': s_value, 'v': v_value}
|
|
1420
|
+
Note:
|
|
1421
|
+
If coincurve is not available or fails for SECP256K1, the method automatically
|
|
1422
|
+
falls back to the standard ecdsa implementation.
|
|
1423
|
+
"""
|
|
1388
1424
|
# your welcome - frosty00
|
|
1389
1425
|
algorithms = {
|
|
1390
1426
|
'p192': [ecdsa.NIST192p, 'sha256'],
|
|
@@ -1396,6 +1432,14 @@ class Exchange(object):
|
|
|
1396
1432
|
}
|
|
1397
1433
|
if algorithm not in algorithms:
|
|
1398
1434
|
raise ArgumentsRequired(algorithm + ' is not a supported algorithm')
|
|
1435
|
+
# Use coincurve for SECP256K1 if available
|
|
1436
|
+
if algorithm == 'secp256k1' and coincurve is not None:
|
|
1437
|
+
try:
|
|
1438
|
+
return Exchange._ecdsa_secp256k1_coincurve(request, secret, hash, fixed_length)
|
|
1439
|
+
except Exception:
|
|
1440
|
+
# If coincurve fails, fall back to ecdsa implementation
|
|
1441
|
+
pass
|
|
1442
|
+
# Fall back to original ecdsa implementation for other algorithms or when deterministic signing is needed
|
|
1399
1443
|
curve_info = algorithms[algorithm]
|
|
1400
1444
|
hash_function = getattr(hashlib, curve_info[1])
|
|
1401
1445
|
encoded_request = Exchange.encode(request)
|
|
@@ -1429,12 +1473,69 @@ class Exchange(object):
|
|
|
1429
1473
|
'v': v,
|
|
1430
1474
|
}
|
|
1431
1475
|
|
|
1476
|
+
|
|
1432
1477
|
@staticmethod
|
|
1433
|
-
def
|
|
1478
|
+
def _ecdsa_secp256k1_coincurve(request, secret, hash=None, fixed_length=False):
|
|
1479
|
+
"""
|
|
1480
|
+
Use coincurve library for SECP256K1 ECDSA signing.
|
|
1481
|
+
This method provides faster SECP256K1 signing using the coincurve library,
|
|
1482
|
+
which is a Python binding to libsecp256k1. This implementation produces
|
|
1483
|
+
deterministic signatures (RFC 6979) using coincurve's sign_recoverable method.
|
|
1484
|
+
Args:
|
|
1485
|
+
request: The message to sign
|
|
1486
|
+
secret: The private key (hex string or PEM format)
|
|
1487
|
+
hash: The hash function to use
|
|
1488
|
+
fixed_length: Not used in coincurve implementation (signatures are always fixed length)
|
|
1489
|
+
Returns:
|
|
1490
|
+
dict: {'r': r_value, 's': s_value, 'v': v_value}
|
|
1491
|
+
Raises:
|
|
1492
|
+
ArgumentsRequired: If coincurve library is not available
|
|
1493
|
+
"""
|
|
1494
|
+
encoded_request = Exchange.encode(request)
|
|
1495
|
+
if hash is not None:
|
|
1496
|
+
digest = Exchange.hash(encoded_request, hash, 'binary')
|
|
1497
|
+
else:
|
|
1498
|
+
digest = base64.b16decode(encoded_request, casefold=True)
|
|
1499
|
+
if isinstance(secret, str):
|
|
1500
|
+
secret = Exchange.encode(secret)
|
|
1501
|
+
# Handle PEM format
|
|
1502
|
+
if secret.find(b'-----BEGIN EC PRIVATE KEY-----') > -1:
|
|
1503
|
+
secret = base64.b16decode(secret.replace(b'-----BEGIN EC PRIVATE KEY-----', b'').replace(b'-----END EC PRIVATE KEY-----', b'').replace(b'\n', b'').replace(b'\r', b''), casefold=True)
|
|
1504
|
+
else:
|
|
1505
|
+
# Assume hex format
|
|
1506
|
+
secret = base64.b16decode(secret, casefold=True)
|
|
1507
|
+
# Create coincurve PrivateKey
|
|
1508
|
+
private_key = coincurve.PrivateKey(secret)
|
|
1509
|
+
# Sign the digest using sign_recoverable which produces deterministic signatures (RFC 6979)
|
|
1510
|
+
# The signature format is: 32 bytes r + 32 bytes s + 1 byte recovery_id (v)
|
|
1511
|
+
signature = private_key.sign_recoverable(digest, hasher=None)
|
|
1512
|
+
# Extract r, s, and v from the recoverable signature (65 bytes total)
|
|
1513
|
+
r_binary = signature[:32]
|
|
1514
|
+
s_binary = signature[32:64]
|
|
1515
|
+
v = signature[64]
|
|
1516
|
+
# Convert to hex strings
|
|
1517
|
+
r = Exchange.decode(base64.b16encode(r_binary)).lower()
|
|
1518
|
+
s = Exchange.decode(base64.b16encode(s_binary)).lower()
|
|
1519
|
+
return {
|
|
1520
|
+
'r': r,
|
|
1521
|
+
's': s,
|
|
1522
|
+
'v': v,
|
|
1523
|
+
}
|
|
1524
|
+
|
|
1525
|
+
def binary_to_urlencoded_base64(data: bytes) -> str:
|
|
1526
|
+
encoded = base64.urlsafe_b64encode(data).decode("utf-8")
|
|
1527
|
+
return encoded.rstrip("=")
|
|
1528
|
+
|
|
1529
|
+
@staticmethod
|
|
1530
|
+
def eddsa(request, secret, curve='ed25519', url_encode=False):
|
|
1434
1531
|
if isinstance(secret, str):
|
|
1435
1532
|
secret = Exchange.encode(secret)
|
|
1436
1533
|
private_key = ed25519.Ed25519PrivateKey.from_private_bytes(secret) if len(secret) == 32 else load_pem_private_key(secret, None)
|
|
1437
|
-
|
|
1534
|
+
signature = private_key.sign(request)
|
|
1535
|
+
if url_encode:
|
|
1536
|
+
return Exchange.binary_to_urlencoded_base64(signature)
|
|
1537
|
+
|
|
1538
|
+
return Exchange.binary_to_base64(signature)
|
|
1438
1539
|
|
|
1439
1540
|
@staticmethod
|
|
1440
1541
|
def axolotl(request, secret, curve='ed25519'):
|
|
@@ -1505,6 +1606,12 @@ class Exchange(object):
|
|
|
1505
1606
|
parts = re.sub(r'0+$', '', str).split('.')
|
|
1506
1607
|
return len(parts[1]) if len(parts) > 1 else 0
|
|
1507
1608
|
|
|
1609
|
+
def map_to_safe_map(self, dictionary):
|
|
1610
|
+
return dictionary # wrapper for go
|
|
1611
|
+
|
|
1612
|
+
def safe_map_to_map(self, dictionary):
|
|
1613
|
+
return dictionary # wrapper for go
|
|
1614
|
+
|
|
1508
1615
|
def load_markets(self, reload=False, params={}):
|
|
1509
1616
|
"""
|
|
1510
1617
|
Loads and prepares the markets for trading.
|
|
@@ -1533,7 +1640,10 @@ class Exchange(object):
|
|
|
1533
1640
|
currencies = None
|
|
1534
1641
|
if self.has['fetchCurrencies'] is True:
|
|
1535
1642
|
currencies = self.fetch_currencies()
|
|
1643
|
+
self.options['cachedCurrencies'] = currencies
|
|
1536
1644
|
markets = self.fetch_markets(params)
|
|
1645
|
+
if 'cachedCurrencies' in self.options:
|
|
1646
|
+
del self.options['cachedCurrencies']
|
|
1537
1647
|
return self.set_markets(markets, currencies)
|
|
1538
1648
|
|
|
1539
1649
|
def fetch_markets(self, params={}):
|
|
@@ -1840,6 +1950,8 @@ class Exchange(object):
|
|
|
1840
1950
|
signature = auth_data.signature
|
|
1841
1951
|
return signature
|
|
1842
1952
|
|
|
1953
|
+
def is_binary_message(self, message):
|
|
1954
|
+
return isinstance(message, bytes) or isinstance(message, bytearray)
|
|
1843
1955
|
|
|
1844
1956
|
# ########################################################################
|
|
1845
1957
|
# ########################################################################
|
|
@@ -1878,7 +1990,7 @@ class Exchange(object):
|
|
|
1878
1990
|
# ########################################################################
|
|
1879
1991
|
# ########################################################################
|
|
1880
1992
|
|
|
1881
|
-
# METHODS BELOW THIS LINE ARE TRANSPILED FROM
|
|
1993
|
+
# METHODS BELOW THIS LINE ARE TRANSPILED FROM TYPESCRIPT
|
|
1882
1994
|
|
|
1883
1995
|
def describe(self) -> Any:
|
|
1884
1996
|
return {
|
|
@@ -2111,6 +2223,17 @@ class Exchange(object):
|
|
|
2111
2223
|
'watchLiquidations': None,
|
|
2112
2224
|
'watchLiquidationsForSymbols': None,
|
|
2113
2225
|
'watchMyLiquidations': None,
|
|
2226
|
+
'unWatchOrders': None,
|
|
2227
|
+
'unWatchTrades': None,
|
|
2228
|
+
'unWatchTradesForSymbols': None,
|
|
2229
|
+
'unWatchOHLCVForSymbols': None,
|
|
2230
|
+
'unWatchOrderBookForSymbols': None,
|
|
2231
|
+
'unWatchPositions': None,
|
|
2232
|
+
'unWatchOrderBook': None,
|
|
2233
|
+
'unWatchTickers': None,
|
|
2234
|
+
'unWatchMyTrades': None,
|
|
2235
|
+
'unWatchTicker': None,
|
|
2236
|
+
'unWatchOHLCV': None,
|
|
2114
2237
|
'watchMyLiquidationsForSymbols': None,
|
|
2115
2238
|
'withdraw': None,
|
|
2116
2239
|
'ws': None,
|
|
@@ -2302,6 +2425,12 @@ class Exchange(object):
|
|
|
2302
2425
|
# return the first index of the cache that can be applied to the orderbook or -1 if not possible
|
|
2303
2426
|
return -1
|
|
2304
2427
|
|
|
2428
|
+
def arrays_concat(self, arraysOfArrays: List[Any]):
|
|
2429
|
+
result = []
|
|
2430
|
+
for i in range(0, len(arraysOfArrays)):
|
|
2431
|
+
result = self.array_concat(result, arraysOfArrays[i])
|
|
2432
|
+
return result
|
|
2433
|
+
|
|
2305
2434
|
def find_timeframe(self, timeframe, timeframes=None):
|
|
2306
2435
|
if timeframes is None:
|
|
2307
2436
|
timeframes = self.timeframes
|
|
@@ -2536,6 +2665,22 @@ class Exchange(object):
|
|
|
2536
2665
|
# set flag
|
|
2537
2666
|
self.isSandboxModeEnabled = False
|
|
2538
2667
|
|
|
2668
|
+
def enable_demo_trading(self, enable: bool):
|
|
2669
|
+
"""
|
|
2670
|
+
enables or disables demo trading mode
|
|
2671
|
+
:param boolean [enable]: True if demo trading should be enabled, False otherwise
|
|
2672
|
+
"""
|
|
2673
|
+
if self.isSandboxModeEnabled:
|
|
2674
|
+
raise NotSupported(self.id + ' demo trading does not support in sandbox environment. Please check https://www.binance.com/en/support/faq/detail/9be58f73e5e14338809e3b705b9687dd to see the differences')
|
|
2675
|
+
if enable:
|
|
2676
|
+
self.urls['apiBackupDemoTrading'] = self.urls['api']
|
|
2677
|
+
self.urls['api'] = self.urls['demo']
|
|
2678
|
+
elif 'apiBackupDemoTrading' in self.urls:
|
|
2679
|
+
self.urls['api'] = self.urls['apiBackupDemoTrading']
|
|
2680
|
+
newUrls = self.omit(self.urls, 'apiBackupDemoTrading')
|
|
2681
|
+
self.urls = newUrls
|
|
2682
|
+
self.options['enableDemoTrading'] = enable
|
|
2683
|
+
|
|
2539
2684
|
def sign(self, path, api: Any = 'public', method='GET', params={}, headers: Any = None, body: Any = None):
|
|
2540
2685
|
return {}
|
|
2541
2686
|
|
|
@@ -2567,6 +2712,9 @@ class Exchange(object):
|
|
|
2567
2712
|
def watch_trades(self, symbol: str, since: Int = None, limit: Int = None, params={}):
|
|
2568
2713
|
raise NotSupported(self.id + ' watchTrades() is not supported yet')
|
|
2569
2714
|
|
|
2715
|
+
def un_watch_orders(self, symbol: Str = None, params={}):
|
|
2716
|
+
raise NotSupported(self.id + ' unWatchOrders() is not supported yet')
|
|
2717
|
+
|
|
2570
2718
|
def un_watch_trades(self, symbol: str, params={}):
|
|
2571
2719
|
raise NotSupported(self.id + ' unWatchTrades() is not supported yet')
|
|
2572
2720
|
|
|
@@ -2594,6 +2742,18 @@ class Exchange(object):
|
|
|
2594
2742
|
def un_watch_order_book_for_symbols(self, symbols: List[str], params={}):
|
|
2595
2743
|
raise NotSupported(self.id + ' unWatchOrderBookForSymbols() is not supported yet')
|
|
2596
2744
|
|
|
2745
|
+
def un_watch_positions(self, symbols: Strings = None, params={}):
|
|
2746
|
+
raise NotSupported(self.id + ' unWatchPositions() is not supported yet')
|
|
2747
|
+
|
|
2748
|
+
def un_watch_ticker(self, symbol: str, params={}):
|
|
2749
|
+
raise NotSupported(self.id + ' unWatchTicker() is not supported yet')
|
|
2750
|
+
|
|
2751
|
+
def un_watch_mark_price(self, symbol: str, params={}):
|
|
2752
|
+
raise NotSupported(self.id + ' unWatchMarkPrice() is not supported yet')
|
|
2753
|
+
|
|
2754
|
+
def un_watch_mark_prices(self, symbols: Strings = None, params={}):
|
|
2755
|
+
raise NotSupported(self.id + ' unWatchMarkPrices() is not supported yet')
|
|
2756
|
+
|
|
2597
2757
|
def fetch_deposit_addresses(self, codes: Strings = None, params={}):
|
|
2598
2758
|
raise NotSupported(self.id + ' fetchDepositAddresses() is not supported yet')
|
|
2599
2759
|
|
|
@@ -2735,13 +2895,13 @@ class Exchange(object):
|
|
|
2735
2895
|
def transfer(self, code: str, amount: float, fromAccount: str, toAccount: str, params={}):
|
|
2736
2896
|
raise NotSupported(self.id + ' transfer() is not supported yet')
|
|
2737
2897
|
|
|
2738
|
-
def withdraw(self, code: str, amount: float, address: str, tag=None, params={}):
|
|
2898
|
+
def withdraw(self, code: str, amount: float, address: str, tag: Str = None, params={}):
|
|
2739
2899
|
raise NotSupported(self.id + ' withdraw() is not supported yet')
|
|
2740
2900
|
|
|
2741
2901
|
def create_deposit_address(self, code: str, params={}):
|
|
2742
2902
|
raise NotSupported(self.id + ' createDepositAddress() is not supported yet')
|
|
2743
2903
|
|
|
2744
|
-
def set_leverage(self, leverage:
|
|
2904
|
+
def set_leverage(self, leverage: int, symbol: Str = None, params={}):
|
|
2745
2905
|
raise NotSupported(self.id + ' setLeverage() is not supported yet')
|
|
2746
2906
|
|
|
2747
2907
|
def fetch_leverage(self, symbol: str, params={}):
|
|
@@ -2790,7 +2950,7 @@ class Exchange(object):
|
|
|
2790
2950
|
def fetch_deposit_addresses_by_network(self, code: str, params={}):
|
|
2791
2951
|
raise NotSupported(self.id + ' fetchDepositAddressesByNetwork() is not supported yet')
|
|
2792
2952
|
|
|
2793
|
-
def fetch_open_interest_history(self, symbol: str, timeframe='1h', since: Int = None, limit: Int = None, params={}):
|
|
2953
|
+
def fetch_open_interest_history(self, symbol: str, timeframe: str = '1h', since: Int = None, limit: Int = None, params={}):
|
|
2794
2954
|
raise NotSupported(self.id + ' fetchOpenInterestHistory() is not supported yet')
|
|
2795
2955
|
|
|
2796
2956
|
def fetch_open_interest(self, symbol: str, params={}):
|
|
@@ -2815,9 +2975,9 @@ class Exchange(object):
|
|
|
2815
2975
|
def parse_to_numeric(self, number):
|
|
2816
2976
|
stringVersion = self.number_to_string(number) # self will convert 1.0 and 1 to "1" and 1.1 to "1.1"
|
|
2817
2977
|
# keep self in mind:
|
|
2818
|
-
# in JS:
|
|
2978
|
+
# in JS: 1 == 1.0 is True
|
|
2819
2979
|
# in Python: 1 == 1.0 is True
|
|
2820
|
-
# in PHP
|
|
2980
|
+
# in PHP: 1 == 1.0 is True, but 1 == 1.0 is False.
|
|
2821
2981
|
if stringVersion.find('.') >= 0:
|
|
2822
2982
|
return float(stringVersion)
|
|
2823
2983
|
return int(stringVersion)
|
|
@@ -2863,7 +3023,7 @@ class Exchange(object):
|
|
|
2863
3023
|
'delay': 0.001,
|
|
2864
3024
|
'capacity': 1,
|
|
2865
3025
|
'cost': 1,
|
|
2866
|
-
'maxCapacity': 1000,
|
|
3026
|
+
'maxCapacity': self.safe_integer(self.options, 'maxRequestsQueue', 1000),
|
|
2867
3027
|
'refillRate': refillRate,
|
|
2868
3028
|
}
|
|
2869
3029
|
existingBucket = {} if (self.tokenBucket is None) else self.tokenBucket
|
|
@@ -2945,6 +3105,79 @@ class Exchange(object):
|
|
|
2945
3105
|
featureBlock['symbolRequired'] = self.in_array(key, ['createOrder', 'createOrders', 'fetchOHLCV'])
|
|
2946
3106
|
return featuresObj
|
|
2947
3107
|
|
|
3108
|
+
def feature_value(self, symbol: str, methodName: Str = None, paramName: Str = None, defaultValue: Any = None):
|
|
3109
|
+
"""
|
|
3110
|
+
self method is a very deterministic to help users to know what feature is supported by the exchange
|
|
3111
|
+
:param str [symbol]: unified symbol
|
|
3112
|
+
:param str [methodName]: view currently supported methods: https://docs.ccxt.com/#/README?id=features
|
|
3113
|
+
:param str [paramName]: unified param value, like: `triggerPrice`, `stopLoss.triggerPrice`(check docs for supported param names)
|
|
3114
|
+
:param dict [defaultValue]: return default value if no result found
|
|
3115
|
+
:returns dict: returns feature value
|
|
3116
|
+
"""
|
|
3117
|
+
market = self.market(symbol)
|
|
3118
|
+
return self.feature_value_by_type(market['type'], market['subType'], methodName, paramName, defaultValue)
|
|
3119
|
+
|
|
3120
|
+
def feature_value_by_type(self, marketType: str, subType: Str, methodName: Str = None, paramName: Str = None, defaultValue: Any = None):
|
|
3121
|
+
"""
|
|
3122
|
+
self method is a very deterministic to help users to know what feature is supported by the exchange
|
|
3123
|
+
:param str [marketType]: supported only: "spot", "swap", "future"
|
|
3124
|
+
:param str [subType]: supported only: "linear", "inverse"
|
|
3125
|
+
:param str [methodName]: view currently supported methods: https://docs.ccxt.com/#/README?id=features
|
|
3126
|
+
:param str [paramName]: unified param value(check docs for supported param names)
|
|
3127
|
+
:param dict [defaultValue]: return default value if no result found
|
|
3128
|
+
:returns dict: returns feature value
|
|
3129
|
+
"""
|
|
3130
|
+
# if exchange does not yet have features manually implemented
|
|
3131
|
+
if self.features is None:
|
|
3132
|
+
return defaultValue
|
|
3133
|
+
if marketType is None:
|
|
3134
|
+
return defaultValue # marketType is required
|
|
3135
|
+
# if marketType(e.g. 'option') does not exist in features
|
|
3136
|
+
if not (marketType in self.features):
|
|
3137
|
+
return defaultValue # unsupported marketType, check "exchange.features" for details
|
|
3138
|
+
# if marketType dict None
|
|
3139
|
+
if self.features[marketType] is None:
|
|
3140
|
+
return defaultValue
|
|
3141
|
+
methodsContainer = self.features[marketType]
|
|
3142
|
+
if subType is None:
|
|
3143
|
+
if marketType != 'spot':
|
|
3144
|
+
return defaultValue # subType is required for non-spot markets
|
|
3145
|
+
else:
|
|
3146
|
+
if not (subType in self.features[marketType]):
|
|
3147
|
+
return defaultValue # unsupported subType, check "exchange.features" for details
|
|
3148
|
+
# if subType dict None
|
|
3149
|
+
if self.features[marketType][subType] is None:
|
|
3150
|
+
return defaultValue
|
|
3151
|
+
methodsContainer = self.features[marketType][subType]
|
|
3152
|
+
# if user wanted only marketType and didn't provide methodName, eg: featureIsSupported('spot')
|
|
3153
|
+
if methodName is None:
|
|
3154
|
+
return defaultValue if (defaultValue is not None) else methodsContainer
|
|
3155
|
+
if not (methodName in methodsContainer):
|
|
3156
|
+
return defaultValue # unsupported method, check "exchange.features" for details')
|
|
3157
|
+
methodDict = methodsContainer[methodName]
|
|
3158
|
+
if methodDict is None:
|
|
3159
|
+
return defaultValue
|
|
3160
|
+
# if user wanted only method and didn't provide `paramName`, eg: featureIsSupported('swap', 'linear', 'createOrder')
|
|
3161
|
+
if paramName is None:
|
|
3162
|
+
return defaultValue if (defaultValue is not None) else methodDict
|
|
3163
|
+
splited = paramName.split('.') # can be only parent key(`stopLoss`) or with child(`stopLoss.triggerPrice`)
|
|
3164
|
+
parentKey = splited[0]
|
|
3165
|
+
subKey = self.safe_string(splited, 1)
|
|
3166
|
+
if not (parentKey in methodDict):
|
|
3167
|
+
return defaultValue # unsupported paramName, check "exchange.features" for details')
|
|
3168
|
+
dictionary = self.safe_dict(methodDict, parentKey)
|
|
3169
|
+
if dictionary is None:
|
|
3170
|
+
# if the value is not dictionary but a scalar value(or None), return
|
|
3171
|
+
return methodDict[parentKey]
|
|
3172
|
+
else:
|
|
3173
|
+
# return, when calling without subKey eg: featureValueByType('spot', None, 'createOrder', 'stopLoss')
|
|
3174
|
+
if subKey is None:
|
|
3175
|
+
return methodDict[parentKey]
|
|
3176
|
+
# raise an exception for unsupported subKey
|
|
3177
|
+
if not (subKey in methodDict[parentKey]):
|
|
3178
|
+
return defaultValue # unsupported subKey, check "exchange.features" for details
|
|
3179
|
+
return methodDict[parentKey][subKey]
|
|
3180
|
+
|
|
2948
3181
|
def orderbook_checksum_message(self, symbol: Str):
|
|
2949
3182
|
return symbol + ' = False'
|
|
2950
3183
|
|
|
@@ -3183,7 +3416,7 @@ class Exchange(object):
|
|
|
3183
3416
|
|
|
3184
3417
|
def set_markets(self, markets, currencies=None):
|
|
3185
3418
|
values = []
|
|
3186
|
-
self.markets_by_id =
|
|
3419
|
+
self.markets_by_id = self.create_safe_dictionary()
|
|
3187
3420
|
# handle marketId conflicts
|
|
3188
3421
|
# we insert spot markets first
|
|
3189
3422
|
marketValues = self.sort_by(self.to_array(markets), 'spot', True, True)
|
|
@@ -3206,14 +3439,18 @@ class Exchange(object):
|
|
|
3206
3439
|
else:
|
|
3207
3440
|
market['subType'] = None
|
|
3208
3441
|
values.append(market)
|
|
3209
|
-
self.markets = self.index_by(values, 'symbol')
|
|
3442
|
+
self.markets = self.map_to_safe_map(self.index_by(values, 'symbol'))
|
|
3210
3443
|
marketsSortedBySymbol = self.keysort(self.markets)
|
|
3211
3444
|
marketsSortedById = self.keysort(self.markets_by_id)
|
|
3212
3445
|
self.symbols = list(marketsSortedBySymbol.keys())
|
|
3213
3446
|
self.ids = list(marketsSortedById.keys())
|
|
3447
|
+
numCurrencies = 0
|
|
3214
3448
|
if currencies is not None:
|
|
3449
|
+
keys = list(currencies.keys())
|
|
3450
|
+
numCurrencies = len(keys)
|
|
3451
|
+
if numCurrencies > 0:
|
|
3215
3452
|
# currencies is always None when called in constructor but not when called from loadMarkets
|
|
3216
|
-
self.currencies = self.deep_extend(self.currencies, currencies)
|
|
3453
|
+
self.currencies = self.map_to_safe_map(self.deep_extend(self.currencies, currencies))
|
|
3217
3454
|
else:
|
|
3218
3455
|
baseCurrencies = []
|
|
3219
3456
|
quoteCurrencies = []
|
|
@@ -3239,8 +3476,8 @@ class Exchange(object):
|
|
|
3239
3476
|
quoteCurrencies.append(currency)
|
|
3240
3477
|
baseCurrencies = self.sort_by(baseCurrencies, 'code', False, '')
|
|
3241
3478
|
quoteCurrencies = self.sort_by(quoteCurrencies, 'code', False, '')
|
|
3242
|
-
self.baseCurrencies = self.index_by(baseCurrencies, 'code')
|
|
3243
|
-
self.quoteCurrencies = self.index_by(quoteCurrencies, 'code')
|
|
3479
|
+
self.baseCurrencies = self.map_to_safe_map(self.index_by(baseCurrencies, 'code'))
|
|
3480
|
+
self.quoteCurrencies = self.map_to_safe_map(self.index_by(quoteCurrencies, 'code'))
|
|
3244
3481
|
allCurrencies = self.array_concat(baseCurrencies, quoteCurrencies)
|
|
3245
3482
|
groupedCurrencies = self.group_by(allCurrencies, 'code')
|
|
3246
3483
|
codes = list(groupedCurrencies.keys())
|
|
@@ -3257,12 +3494,36 @@ class Exchange(object):
|
|
|
3257
3494
|
highestPrecisionCurrency = currentCurrency if (currentCurrency['precision'] > highestPrecisionCurrency['precision']) else highestPrecisionCurrency
|
|
3258
3495
|
resultingCurrencies.append(highestPrecisionCurrency)
|
|
3259
3496
|
sortedCurrencies = self.sort_by(resultingCurrencies, 'code')
|
|
3260
|
-
self.currencies = self.deep_extend(self.currencies, self.index_by(sortedCurrencies, 'code'))
|
|
3261
|
-
self.currencies_by_id = self.
|
|
3497
|
+
self.currencies = self.map_to_safe_map(self.deep_extend(self.currencies, self.index_by(sortedCurrencies, 'code')))
|
|
3498
|
+
self.currencies_by_id = self.index_by_safe(self.currencies, 'id')
|
|
3262
3499
|
currenciesSortedByCode = self.keysort(self.currencies)
|
|
3263
3500
|
self.codes = list(currenciesSortedByCode.keys())
|
|
3264
3501
|
return self.markets
|
|
3265
3502
|
|
|
3503
|
+
def set_markets_from_exchange(self, sourceExchange):
|
|
3504
|
+
# Validate that both exchanges are of the same type
|
|
3505
|
+
if self.id != sourceExchange.id:
|
|
3506
|
+
raise ArgumentsRequired(self.id + ' shareMarkets() can only share markets with exchanges of the same type(got ' + sourceExchange['id'] + ')')
|
|
3507
|
+
# Validate that source exchange has loaded markets
|
|
3508
|
+
if not sourceExchange.markets:
|
|
3509
|
+
raise ExchangeError('setMarketsFromExchange() source exchange must have loaded markets first. Can call by using loadMarkets function')
|
|
3510
|
+
# Set all market-related data
|
|
3511
|
+
self.markets = sourceExchange.markets
|
|
3512
|
+
self.markets_by_id = sourceExchange.markets_by_id
|
|
3513
|
+
self.symbols = sourceExchange.symbols
|
|
3514
|
+
self.ids = sourceExchange.ids
|
|
3515
|
+
self.currencies = sourceExchange.currencies
|
|
3516
|
+
self.baseCurrencies = sourceExchange.baseCurrencies
|
|
3517
|
+
self.quoteCurrencies = sourceExchange.quoteCurrencies
|
|
3518
|
+
self.codes = sourceExchange.codes
|
|
3519
|
+
# check marketHelperProps
|
|
3520
|
+
sourceExchangeHelpers = self.safe_list(sourceExchange.options, 'marketHelperProps', [])
|
|
3521
|
+
for i in range(0, len(sourceExchangeHelpers)):
|
|
3522
|
+
helper = sourceExchangeHelpers[i]
|
|
3523
|
+
if sourceExchange.options[helper] is not None:
|
|
3524
|
+
self.options[helper] = sourceExchange.options[helper]
|
|
3525
|
+
return self
|
|
3526
|
+
|
|
3266
3527
|
def get_describe_for_extended_ws_exchange(self, currentRestInstance: Any, parentRestInstance: Any, wsBaseDescribe: dict):
|
|
3267
3528
|
extendedRestDescribe = self.deep_extend(parentRestInstance.describe(), currentRestInstance.describe())
|
|
3268
3529
|
superWithRestDescribe = self.deep_extend(extendedRestDescribe, wsBaseDescribe)
|
|
@@ -3562,18 +3823,7 @@ class Exchange(object):
|
|
|
3562
3823
|
symbol = market['symbol'] if (market is not None) else None
|
|
3563
3824
|
return self.filter_by_symbol_since_limit(results, symbol, since, limit)
|
|
3564
3825
|
|
|
3565
|
-
def
|
|
3566
|
-
"""
|
|
3567
|
-
calculates the presumptive fee that would be charged for an order
|
|
3568
|
-
:param str symbol: unified market symbol
|
|
3569
|
-
:param str type: 'market' or 'limit'
|
|
3570
|
-
:param str side: 'buy' or 'sell'
|
|
3571
|
-
:param float amount: how much you want to trade, in units of the base currency on most exchanges, or number of contracts
|
|
3572
|
-
:param float price: the price for the order to be filled at, in units of the quote currency
|
|
3573
|
-
:param str takerOrMaker: 'taker' or 'maker'
|
|
3574
|
-
:param dict params:
|
|
3575
|
-
:returns dict: contains the rate, the percentage multiplied to the order amount to obtain the fee amount, and cost, the total value of the fee in units of the quote currency, for the order
|
|
3576
|
-
"""
|
|
3826
|
+
def calculate_fee_with_rate(self, symbol: str, type: str, side: str, amount: float, price: float, takerOrMaker='taker', feeRate: Num = None, params={}):
|
|
3577
3827
|
if type == 'market' and takerOrMaker == 'maker':
|
|
3578
3828
|
raise ArgumentsRequired(self.id + ' calculateFee() - you have provided incompatible arguments - "market" type order can not be "maker". Change either the "type" or the "takerOrMaker" argument to calculate the fee.')
|
|
3579
3829
|
market = self.markets[symbol]
|
|
@@ -3602,7 +3852,7 @@ class Exchange(object):
|
|
|
3602
3852
|
# even if `takerOrMaker` argument was set to 'maker', for 'market' orders we should forcefully override it to 'taker'
|
|
3603
3853
|
if type == 'market':
|
|
3604
3854
|
takerOrMaker = 'taker'
|
|
3605
|
-
rate = self.safe_string(market, takerOrMaker)
|
|
3855
|
+
rate = self.number_to_string(feeRate) if (feeRate is not None) else self.safe_string(market, takerOrMaker)
|
|
3606
3856
|
cost = Precise.string_mul(cost, rate)
|
|
3607
3857
|
return {
|
|
3608
3858
|
'type': takerOrMaker,
|
|
@@ -3611,6 +3861,20 @@ class Exchange(object):
|
|
|
3611
3861
|
'cost': self.parse_number(cost),
|
|
3612
3862
|
}
|
|
3613
3863
|
|
|
3864
|
+
def calculate_fee(self, symbol: str, type: str, side: str, amount: float, price: float, takerOrMaker='taker', params={}):
|
|
3865
|
+
"""
|
|
3866
|
+
calculates the presumptive fee that would be charged for an order
|
|
3867
|
+
:param str symbol: unified market symbol
|
|
3868
|
+
:param str type: 'market' or 'limit'
|
|
3869
|
+
:param str side: 'buy' or 'sell'
|
|
3870
|
+
:param float amount: how much you want to trade, in units of the base currency on most exchanges, or number of contracts
|
|
3871
|
+
:param float price: the price for the order to be filled at, in units of the quote currency
|
|
3872
|
+
:param str takerOrMaker: 'taker' or 'maker'
|
|
3873
|
+
:param dict params:
|
|
3874
|
+
:returns dict: contains the rate, the percentage multiplied to the order amount to obtain the fee amount, and cost, the total value of the fee in units of the quote currency, for the order
|
|
3875
|
+
"""
|
|
3876
|
+
return self.calculate_fee_with_rate(symbol, type, side, amount, price, takerOrMaker, None, params)
|
|
3877
|
+
|
|
3614
3878
|
def safe_liquidation(self, liquidation: dict, market: Market = None):
|
|
3615
3879
|
contracts = self.safe_string(liquidation, 'contracts')
|
|
3616
3880
|
contractSize = self.safe_string(market, 'contractSize')
|
|
@@ -3650,6 +3914,21 @@ class Exchange(object):
|
|
|
3650
3914
|
trade['cost'] = self.parse_number(cost)
|
|
3651
3915
|
return trade
|
|
3652
3916
|
|
|
3917
|
+
def create_ccxt_trade_id(self, timestamp=None, side=None, amount=None, price=None, takerOrMaker=None):
|
|
3918
|
+
# self approach is being used by multiple exchanges(mexc, woo, coinsbit, dydx, ...)
|
|
3919
|
+
id = None
|
|
3920
|
+
if timestamp is not None:
|
|
3921
|
+
id = self.number_to_string(timestamp)
|
|
3922
|
+
if side is not None:
|
|
3923
|
+
id += '-' + side
|
|
3924
|
+
if amount is not None:
|
|
3925
|
+
id += '-' + self.number_to_string(amount)
|
|
3926
|
+
if price is not None:
|
|
3927
|
+
id += '-' + self.number_to_string(price)
|
|
3928
|
+
if takerOrMaker is not None:
|
|
3929
|
+
id += '-' + takerOrMaker
|
|
3930
|
+
return id
|
|
3931
|
+
|
|
3653
3932
|
def parsed_fee_and_fees(self, container: Any):
|
|
3654
3933
|
fee = self.safe_dict(container, 'fee')
|
|
3655
3934
|
fees = self.safe_list(container, 'fees')
|
|
@@ -3758,7 +4037,7 @@ class Exchange(object):
|
|
|
3758
4037
|
for i in range(0, len(fees)):
|
|
3759
4038
|
fee = fees[i]
|
|
3760
4039
|
code = self.safe_string(fee, 'currency')
|
|
3761
|
-
feeCurrencyCode = code is not
|
|
4040
|
+
feeCurrencyCode = code if (code is not None) else str(i)
|
|
3762
4041
|
if feeCurrencyCode is not None:
|
|
3763
4042
|
rate = self.safe_string(fee, 'rate')
|
|
3764
4043
|
cost = self.safe_string(fee, 'cost')
|
|
@@ -3786,8 +4065,7 @@ class Exchange(object):
|
|
|
3786
4065
|
|
|
3787
4066
|
def safe_ticker(self, ticker: dict, market: Market = None):
|
|
3788
4067
|
open = self.omit_zero(self.safe_string(ticker, 'open'))
|
|
3789
|
-
close = self.omit_zero(self.
|
|
3790
|
-
last = self.omit_zero(self.safe_string(ticker, 'last'))
|
|
4068
|
+
close = self.omit_zero(self.safe_string_2(ticker, 'close', 'last'))
|
|
3791
4069
|
change = self.omit_zero(self.safe_string(ticker, 'change'))
|
|
3792
4070
|
percentage = self.omit_zero(self.safe_string(ticker, 'percentage'))
|
|
3793
4071
|
average = self.omit_zero(self.safe_string(ticker, 'average'))
|
|
@@ -3796,29 +4074,52 @@ class Exchange(object):
|
|
|
3796
4074
|
quoteVolume = self.safe_string(ticker, 'quoteVolume')
|
|
3797
4075
|
if vwap is None:
|
|
3798
4076
|
vwap = Precise.string_div(self.omit_zero(quoteVolume), baseVolume)
|
|
3799
|
-
|
|
3800
|
-
|
|
3801
|
-
|
|
3802
|
-
|
|
3803
|
-
|
|
3804
|
-
|
|
3805
|
-
|
|
3806
|
-
if average is None:
|
|
4077
|
+
# calculate open
|
|
4078
|
+
if change is not None:
|
|
4079
|
+
if close is None and average is not None:
|
|
4080
|
+
close = Precise.string_add(average, Precise.string_div(change, '2'))
|
|
4081
|
+
if open is None and close is not None:
|
|
4082
|
+
open = Precise.string_sub(close, change)
|
|
4083
|
+
elif percentage is not None:
|
|
4084
|
+
if close is None and average is not None:
|
|
4085
|
+
openAddClose = Precise.string_mul(average, '2')
|
|
4086
|
+
# openAddClose = open * (1 + (100 + percentage)/100)
|
|
4087
|
+
denominator = Precise.string_add('2', Precise.string_div(percentage, '100'))
|
|
4088
|
+
calcOpen = open if (open is not None) else Precise.string_div(openAddClose, denominator)
|
|
4089
|
+
close = Precise.string_mul(calcOpen, Precise.string_add('1', Precise.string_div(percentage, '100')))
|
|
4090
|
+
if open is None and close is not None:
|
|
4091
|
+
open = Precise.string_div(close, Precise.string_add('1', Precise.string_div(percentage, '100')))
|
|
4092
|
+
# change
|
|
4093
|
+
if change is None:
|
|
4094
|
+
if close is not None and open is not None:
|
|
4095
|
+
change = Precise.string_sub(close, open)
|
|
4096
|
+
elif close is not None and percentage is not None:
|
|
4097
|
+
change = Precise.string_mul(Precise.string_div(percentage, '100'), Precise.string_div(close, '100'))
|
|
4098
|
+
elif open is not None and percentage is not None:
|
|
4099
|
+
change = Precise.string_mul(open, Precise.string_div(percentage, '100'))
|
|
4100
|
+
# calculate things according to "open"(similar can be done with "close")
|
|
4101
|
+
if open is not None:
|
|
4102
|
+
# percentage(using change)
|
|
4103
|
+
if percentage is None and change is not None:
|
|
4104
|
+
percentage = Precise.string_mul(Precise.string_div(change, open), '100')
|
|
4105
|
+
# close(using change)
|
|
4106
|
+
if close is None and change is not None:
|
|
4107
|
+
close = Precise.string_add(open, change)
|
|
4108
|
+
# close(using average)
|
|
4109
|
+
if close is None and average is not None:
|
|
4110
|
+
close = Precise.string_mul(average, '2')
|
|
4111
|
+
# average
|
|
4112
|
+
if average is None and close is not None:
|
|
3807
4113
|
precision = 18
|
|
3808
4114
|
if market is not None and self.is_tick_precision():
|
|
3809
4115
|
marketPrecision = self.safe_dict(market, 'precision')
|
|
3810
4116
|
precisionPrice = self.safe_string(marketPrecision, 'price')
|
|
3811
4117
|
if precisionPrice is not None:
|
|
3812
4118
|
precision = self.precision_from_string(precisionPrice)
|
|
3813
|
-
average = Precise.string_div(Precise.string_add(
|
|
3814
|
-
if (percentage is None) and (change is not None) and (open is not None) and Precise.string_gt(open, '0'):
|
|
3815
|
-
percentage = Precise.string_mul(Precise.string_div(change, open), '100')
|
|
3816
|
-
if (change is None) and (percentage is not None) and (open is not None):
|
|
3817
|
-
change = Precise.string_div(Precise.string_mul(percentage, open), '100')
|
|
3818
|
-
if (open is None) and (last is not None) and (change is not None):
|
|
3819
|
-
open = Precise.string_sub(last, change)
|
|
4119
|
+
average = Precise.string_div(Precise.string_add(open, close), '2', precision)
|
|
3820
4120
|
# timestamp and symbol operations don't belong in safeTicker
|
|
3821
4121
|
# they should be done in the derived classes
|
|
4122
|
+
closeParsed = self.parse_number(self.omit_zero(close))
|
|
3822
4123
|
return self.extend(ticker, {
|
|
3823
4124
|
'bid': self.parse_number(self.omit_zero(self.safe_string(ticker, 'bid'))),
|
|
3824
4125
|
'bidVolume': self.safe_number(ticker, 'bidVolume'),
|
|
@@ -3827,8 +4128,8 @@ class Exchange(object):
|
|
|
3827
4128
|
'high': self.parse_number(self.omit_zero(self.safe_string(ticker, 'high'))),
|
|
3828
4129
|
'low': self.parse_number(self.omit_zero(self.safe_string(ticker, 'low'))),
|
|
3829
4130
|
'open': self.parse_number(self.omit_zero(open)),
|
|
3830
|
-
'close':
|
|
3831
|
-
'last':
|
|
4131
|
+
'close': closeParsed,
|
|
4132
|
+
'last': closeParsed,
|
|
3832
4133
|
'change': self.parse_number(change),
|
|
3833
4134
|
'percentage': self.parse_number(percentage),
|
|
3834
4135
|
'average': self.parse_number(average),
|
|
@@ -3861,19 +4162,19 @@ class Exchange(object):
|
|
|
3861
4162
|
def repay_margin(self, code: str, amount: float, symbol: Str = None, params={}):
|
|
3862
4163
|
raise NotSupported(self.id + ' repayMargin is deprecated, please use repayCrossMargin or repayIsolatedMargin instead')
|
|
3863
4164
|
|
|
3864
|
-
def fetch_ohlcv(self, symbol: str, timeframe='1m', since: Int = None, limit: Int = None, params={}):
|
|
4165
|
+
def fetch_ohlcv(self, symbol: str, timeframe: str = '1m', since: Int = None, limit: Int = None, params={}):
|
|
3865
4166
|
message = ''
|
|
3866
4167
|
if self.has['fetchTrades']:
|
|
3867
4168
|
message = '. If you want to build OHLCV candles from trade executions data, visit https://github.com/ccxt/ccxt/tree/master/examples/ and see "build-ohlcv-bars" file'
|
|
3868
4169
|
raise NotSupported(self.id + ' fetchOHLCV() is not supported yet' + message)
|
|
3869
4170
|
|
|
3870
|
-
def fetch_ohlcv_ws(self, symbol: str, timeframe='1m', since: Int = None, limit: Int = None, params={}):
|
|
4171
|
+
def fetch_ohlcv_ws(self, symbol: str, timeframe: str = '1m', since: Int = None, limit: Int = None, params={}):
|
|
3871
4172
|
message = ''
|
|
3872
4173
|
if self.has['fetchTradesWs']:
|
|
3873
4174
|
message = '. If you want to build OHLCV candles from trade executions data, visit https://github.com/ccxt/ccxt/tree/master/examples/ and see "build-ohlcv-bars" file'
|
|
3874
4175
|
raise NotSupported(self.id + ' fetchOHLCVWs() is not supported yet. Try using fetchOHLCV instead.' + message)
|
|
3875
4176
|
|
|
3876
|
-
def watch_ohlcv(self, symbol: str, timeframe='1m', since: Int = None, limit: Int = None, params={}):
|
|
4177
|
+
def watch_ohlcv(self, symbol: str, timeframe: str = '1m', since: Int = None, limit: Int = None, params={}):
|
|
3877
4178
|
raise NotSupported(self.id + ' watchOHLCV() is not supported yet')
|
|
3878
4179
|
|
|
3879
4180
|
def convert_trading_view_to_ohlcv(self, ohlcvs: List[List[float]], timestamp='t', open='o', high='h', low='l', close='c', volume='v', ms=False):
|
|
@@ -4166,11 +4467,11 @@ class Exchange(object):
|
|
|
4166
4467
|
raise NotSupported(self.id + ' - ' + networkCode + ' network did not return any result for ' + currencyCode)
|
|
4167
4468
|
else:
|
|
4168
4469
|
# if networkCode was provided by user, we should check it after response, referenced exchange doesn't support network-code during request
|
|
4169
|
-
|
|
4170
|
-
if
|
|
4171
|
-
chosenNetworkId =
|
|
4470
|
+
networkIdOrCode = networkCode if isIndexedByUnifiedNetworkCode else self.network_code_to_id(networkCode, currencyCode)
|
|
4471
|
+
if networkIdOrCode in indexedNetworkEntries:
|
|
4472
|
+
chosenNetworkId = networkIdOrCode
|
|
4172
4473
|
else:
|
|
4173
|
-
raise NotSupported(self.id + ' - ' +
|
|
4474
|
+
raise NotSupported(self.id + ' - ' + networkIdOrCode + ' network was not found for ' + currencyCode + ', use one of ' + ', '.join(availableNetworkIds))
|
|
4174
4475
|
else:
|
|
4175
4476
|
if responseNetworksLength == 0:
|
|
4176
4477
|
raise NotSupported(self.id + ' - no networks were returned for ' + currencyCode)
|
|
@@ -4207,7 +4508,7 @@ class Exchange(object):
|
|
|
4207
4508
|
return self.filter_by_since_limit(sorted, since, limit, 0, tail)
|
|
4208
4509
|
|
|
4209
4510
|
def parse_leverage_tiers(self, response: Any, symbols: List[str] = None, marketIdKey=None):
|
|
4210
|
-
# marketIdKey should only be None when response is a dictionary
|
|
4511
|
+
# marketIdKey should only be None when response is a dictionary.
|
|
4211
4512
|
symbols = self.market_symbols(symbols)
|
|
4212
4513
|
tiers = {}
|
|
4213
4514
|
symbolsLength = 0
|
|
@@ -4285,16 +4586,27 @@ class Exchange(object):
|
|
|
4285
4586
|
result.append(account)
|
|
4286
4587
|
return result
|
|
4287
4588
|
|
|
4288
|
-
def
|
|
4589
|
+
def parse_trades_helper(self, isWs: bool, trades: List[Any], market: Market = None, since: Int = None, limit: Int = None, params={}):
|
|
4289
4590
|
trades = self.to_array(trades)
|
|
4290
4591
|
result = []
|
|
4291
4592
|
for i in range(0, len(trades)):
|
|
4292
|
-
|
|
4593
|
+
parsed = None
|
|
4594
|
+
if isWs:
|
|
4595
|
+
parsed = self.parse_ws_trade(trades[i], market)
|
|
4596
|
+
else:
|
|
4597
|
+
parsed = self.parse_trade(trades[i], market)
|
|
4598
|
+
trade = self.extend(parsed, params)
|
|
4293
4599
|
result.append(trade)
|
|
4294
4600
|
result = self.sort_by_2(result, 'timestamp', 'id')
|
|
4295
4601
|
symbol = market['symbol'] if (market is not None) else None
|
|
4296
4602
|
return self.filter_by_symbol_since_limit(result, symbol, since, limit)
|
|
4297
4603
|
|
|
4604
|
+
def parse_trades(self, trades: List[Any], market: Market = None, since: Int = None, limit: Int = None, params={}):
|
|
4605
|
+
return self.parse_trades_helper(False, trades, market, since, limit, params)
|
|
4606
|
+
|
|
4607
|
+
def parse_ws_trades(self, trades: List[Any], market: Market = None, since: Int = None, limit: Int = None, params={}):
|
|
4608
|
+
return self.parse_trades_helper(True, trades, market, since, limit, params)
|
|
4609
|
+
|
|
4298
4610
|
def parse_transactions(self, transactions: List[Any], currency: Currency = None, since: Int = None, limit: Int = None, params={}):
|
|
4299
4611
|
transactions = self.to_array(transactions)
|
|
4300
4612
|
result = []
|
|
@@ -4458,15 +4770,15 @@ class Exchange(object):
|
|
|
4458
4770
|
if self.enableRateLimit:
|
|
4459
4771
|
cost = self.calculate_rate_limiter_cost(api, method, path, params, config)
|
|
4460
4772
|
self.throttle(cost)
|
|
4773
|
+
retries = None
|
|
4774
|
+
retries, params = self.handle_option_and_params(params, path, 'maxRetriesOnFailure', 0)
|
|
4775
|
+
retryDelay = None
|
|
4776
|
+
retryDelay, params = self.handle_option_and_params(params, path, 'maxRetriesOnFailureDelay', 0)
|
|
4461
4777
|
self.lastRestRequestTimestamp = self.milliseconds()
|
|
4462
4778
|
request = self.sign(path, api, method, params, headers, body)
|
|
4463
4779
|
self.last_request_headers = request['headers']
|
|
4464
4780
|
self.last_request_body = request['body']
|
|
4465
4781
|
self.last_request_url = request['url']
|
|
4466
|
-
retries = None
|
|
4467
|
-
retries, params = self.handle_option_and_params(params, path, 'maxRetriesOnFailure', 0)
|
|
4468
|
-
retryDelay = None
|
|
4469
|
-
retryDelay, params = self.handle_option_and_params(params, path, 'maxRetriesOnFailureDelay', 0)
|
|
4470
4782
|
for i in range(0, retries + 1):
|
|
4471
4783
|
try:
|
|
4472
4784
|
return self.fetch(request['url'], request['method'], request['headers'], request['body'])
|
|
@@ -4511,9 +4823,12 @@ class Exchange(object):
|
|
|
4511
4823
|
i_count = 6
|
|
4512
4824
|
tradesLength = len(trades)
|
|
4513
4825
|
oldest = min(tradesLength, limit)
|
|
4826
|
+
options = self.safe_dict(self.options, 'buildOHLCVC', {})
|
|
4827
|
+
skipZeroPrices = self.safe_bool(options, 'skipZeroPrices', True)
|
|
4514
4828
|
for i in range(0, oldest):
|
|
4515
4829
|
trade = trades[i]
|
|
4516
4830
|
ts = trade['timestamp']
|
|
4831
|
+
price = trade['price']
|
|
4517
4832
|
if ts < since:
|
|
4518
4833
|
continue
|
|
4519
4834
|
openingTime = int(math.floor(ts / ms)) * ms # shift to the edge of m/h/d(but not M)
|
|
@@ -4521,22 +4836,25 @@ class Exchange(object):
|
|
|
4521
4836
|
continue
|
|
4522
4837
|
ohlcv_length = len(ohlcvs)
|
|
4523
4838
|
candle = ohlcv_length - 1
|
|
4524
|
-
if (
|
|
4839
|
+
if skipZeroPrices and not (price > 0) and not (price < 0):
|
|
4840
|
+
continue
|
|
4841
|
+
isFirstCandle = candle == -1
|
|
4842
|
+
if isFirstCandle or openingTime >= self.sum(ohlcvs[candle][i_timestamp], ms):
|
|
4525
4843
|
# moved to a new timeframe -> create a new candle from opening trade
|
|
4526
4844
|
ohlcvs.append([
|
|
4527
4845
|
openingTime, # timestamp
|
|
4528
|
-
|
|
4529
|
-
|
|
4530
|
-
|
|
4531
|
-
|
|
4846
|
+
price, # O
|
|
4847
|
+
price, # H
|
|
4848
|
+
price, # L
|
|
4849
|
+
price, # C
|
|
4532
4850
|
trade['amount'], # V
|
|
4533
4851
|
1, # count
|
|
4534
4852
|
])
|
|
4535
4853
|
else:
|
|
4536
4854
|
# still processing the same timeframe -> update opening trade
|
|
4537
|
-
ohlcvs[candle][i_high] = max(ohlcvs[candle][i_high],
|
|
4538
|
-
ohlcvs[candle][i_low] = min(ohlcvs[candle][i_low],
|
|
4539
|
-
ohlcvs[candle][i_close] =
|
|
4855
|
+
ohlcvs[candle][i_high] = max(ohlcvs[candle][i_high], price)
|
|
4856
|
+
ohlcvs[candle][i_low] = min(ohlcvs[candle][i_low], price)
|
|
4857
|
+
ohlcvs[candle][i_close] = price
|
|
4540
4858
|
ohlcvs[candle][i_volume] = self.sum(ohlcvs[candle][i_volume], trade['amount'])
|
|
4541
4859
|
ohlcvs[candle][i_count] = self.sum(ohlcvs[candle][i_count], 1)
|
|
4542
4860
|
return ohlcvs
|
|
@@ -4676,6 +4994,11 @@ class Exchange(object):
|
|
|
4676
4994
|
return market
|
|
4677
4995
|
return result
|
|
4678
4996
|
|
|
4997
|
+
def market_or_null(self, symbol: str):
|
|
4998
|
+
if symbol is None:
|
|
4999
|
+
return None
|
|
5000
|
+
return self.market(symbol)
|
|
5001
|
+
|
|
4679
5002
|
def check_required_credentials(self, error=True):
|
|
4680
5003
|
"""
|
|
4681
5004
|
@ignore
|
|
@@ -4994,7 +5317,7 @@ class Exchange(object):
|
|
|
4994
5317
|
def fetch_position_mode(self, symbol: Str = None, params={}):
|
|
4995
5318
|
raise NotSupported(self.id + ' fetchPositionMode() is not supported yet')
|
|
4996
5319
|
|
|
4997
|
-
def create_trailing_amount_order(self, symbol: str, type: OrderType, side: OrderSide, amount: float, price: Num = None, trailingAmount=None, trailingTriggerPrice=None, params={}):
|
|
5320
|
+
def create_trailing_amount_order(self, symbol: str, type: OrderType, side: OrderSide, amount: float, price: Num = None, trailingAmount: Num = None, trailingTriggerPrice: Num = None, params={}):
|
|
4998
5321
|
"""
|
|
4999
5322
|
create a trailing order by providing the symbol, type, side, amount, price and trailingAmount
|
|
5000
5323
|
:param str symbol: unified symbol of the market to create an order in
|
|
@@ -5016,7 +5339,7 @@ class Exchange(object):
|
|
|
5016
5339
|
return self.create_order(symbol, type, side, amount, price, params)
|
|
5017
5340
|
raise NotSupported(self.id + ' createTrailingAmountOrder() is not supported yet')
|
|
5018
5341
|
|
|
5019
|
-
def create_trailing_amount_order_ws(self, symbol: str, type: OrderType, side: OrderSide, amount: float, price: Num = None, trailingAmount=None, trailingTriggerPrice=None, params={}):
|
|
5342
|
+
def create_trailing_amount_order_ws(self, symbol: str, type: OrderType, side: OrderSide, amount: float, price: Num = None, trailingAmount: Num = None, trailingTriggerPrice: Num = None, params={}):
|
|
5020
5343
|
"""
|
|
5021
5344
|
create a trailing order by providing the symbol, type, side, amount, price and trailingAmount
|
|
5022
5345
|
:param str symbol: unified symbol of the market to create an order in
|
|
@@ -5038,7 +5361,7 @@ class Exchange(object):
|
|
|
5038
5361
|
return self.create_order_ws(symbol, type, side, amount, price, params)
|
|
5039
5362
|
raise NotSupported(self.id + ' createTrailingAmountOrderWs() is not supported yet')
|
|
5040
5363
|
|
|
5041
|
-
def create_trailing_percent_order(self, symbol: str, type: OrderType, side: OrderSide, amount: float, price: Num = None, trailingPercent=None, trailingTriggerPrice=None, params={}):
|
|
5364
|
+
def create_trailing_percent_order(self, symbol: str, type: OrderType, side: OrderSide, amount: float, price: Num = None, trailingPercent: Num = None, trailingTriggerPrice: Num = None, params={}):
|
|
5042
5365
|
"""
|
|
5043
5366
|
create a trailing order by providing the symbol, type, side, amount, price and trailingPercent
|
|
5044
5367
|
:param str symbol: unified symbol of the market to create an order in
|
|
@@ -5060,7 +5383,7 @@ class Exchange(object):
|
|
|
5060
5383
|
return self.create_order(symbol, type, side, amount, price, params)
|
|
5061
5384
|
raise NotSupported(self.id + ' createTrailingPercentOrder() is not supported yet')
|
|
5062
5385
|
|
|
5063
|
-
def create_trailing_percent_order_ws(self, symbol: str, type: OrderType, side: OrderSide, amount: float, price: Num = None, trailingPercent=None, trailingTriggerPrice=None, params={}):
|
|
5386
|
+
def create_trailing_percent_order_ws(self, symbol: str, type: OrderType, side: OrderSide, amount: float, price: Num = None, trailingPercent: Num = None, trailingTriggerPrice: Num = None, params={}):
|
|
5064
5387
|
"""
|
|
5065
5388
|
create a trailing order by providing the symbol, type, side, amount, price and trailingPercent
|
|
5066
5389
|
:param str symbol: unified symbol of the market to create an order in
|
|
@@ -5351,6 +5674,9 @@ class Exchange(object):
|
|
|
5351
5674
|
def cancel_order_ws(self, id: str, symbol: Str = None, params={}):
|
|
5352
5675
|
raise NotSupported(self.id + ' cancelOrderWs() is not supported yet')
|
|
5353
5676
|
|
|
5677
|
+
def cancel_orders(self, ids: List[str], symbol: Str = None, params={}):
|
|
5678
|
+
raise NotSupported(self.id + ' cancelOrders() is not supported yet')
|
|
5679
|
+
|
|
5354
5680
|
def cancel_orders_ws(self, ids: List[str], symbol: Str = None, params={}):
|
|
5355
5681
|
raise NotSupported(self.id + ' cancelOrdersWs() is not supported yet')
|
|
5356
5682
|
|
|
@@ -5366,7 +5692,7 @@ class Exchange(object):
|
|
|
5366
5692
|
def cancel_all_orders_ws(self, symbol: Str = None, params={}):
|
|
5367
5693
|
raise NotSupported(self.id + ' cancelAllOrdersWs() is not supported yet')
|
|
5368
5694
|
|
|
5369
|
-
def cancel_unified_order(self, order, params={}):
|
|
5695
|
+
def cancel_unified_order(self, order: Order, params={}):
|
|
5370
5696
|
return self.cancel_order(self.safe_string(order, 'id'), self.safe_string(order, 'symbol'), params)
|
|
5371
5697
|
|
|
5372
5698
|
def fetch_orders(self, symbol: Str = None, since: Int = None, limit: Int = None, params={}):
|
|
@@ -5428,6 +5754,9 @@ class Exchange(object):
|
|
|
5428
5754
|
def fetch_greeks(self, symbol: str, params={}):
|
|
5429
5755
|
raise NotSupported(self.id + ' fetchGreeks() is not supported yet')
|
|
5430
5756
|
|
|
5757
|
+
def fetch_all_greeks(self, symbols: Strings = None, params={}):
|
|
5758
|
+
raise NotSupported(self.id + ' fetchAllGreeks() is not supported yet')
|
|
5759
|
+
|
|
5431
5760
|
def fetch_option_chain(self, code: str, params={}):
|
|
5432
5761
|
raise NotSupported(self.id + ' fetchOptionChain() is not supported yet')
|
|
5433
5762
|
|
|
@@ -5448,10 +5777,10 @@ class Exchange(object):
|
|
|
5448
5777
|
"""
|
|
5449
5778
|
raise NotSupported(self.id + ' fetchDepositsWithdrawals() is not supported yet')
|
|
5450
5779
|
|
|
5451
|
-
def fetch_deposits(self,
|
|
5780
|
+
def fetch_deposits(self, code: Str = None, since: Int = None, limit: Int = None, params={}):
|
|
5452
5781
|
raise NotSupported(self.id + ' fetchDeposits() is not supported yet')
|
|
5453
5782
|
|
|
5454
|
-
def fetch_withdrawals(self,
|
|
5783
|
+
def fetch_withdrawals(self, code: Str = None, since: Int = None, limit: Int = None, params={}):
|
|
5455
5784
|
raise NotSupported(self.id + ' fetchWithdrawals() is not supported yet')
|
|
5456
5785
|
|
|
5457
5786
|
def fetch_deposits_ws(self, code: Str = None, since: Int = None, limit: Int = None, params={}):
|
|
@@ -5512,7 +5841,9 @@ class Exchange(object):
|
|
|
5512
5841
|
return self.safe_string(self.commonCurrencies, code, code)
|
|
5513
5842
|
|
|
5514
5843
|
def currency(self, code: str):
|
|
5515
|
-
|
|
5844
|
+
keys = list(self.currencies.keys())
|
|
5845
|
+
numCurrencies = len(keys)
|
|
5846
|
+
if numCurrencies == 0:
|
|
5516
5847
|
raise ExchangeError(self.id + ' currencies not loaded')
|
|
5517
5848
|
if isinstance(code, str):
|
|
5518
5849
|
if code in self.currencies:
|
|
@@ -5681,10 +6012,16 @@ class Exchange(object):
|
|
|
5681
6012
|
precisionNumber = int(precision)
|
|
5682
6013
|
if precisionNumber == 0:
|
|
5683
6014
|
return '1'
|
|
5684
|
-
|
|
5685
|
-
|
|
5686
|
-
|
|
5687
|
-
|
|
6015
|
+
if precisionNumber > 0:
|
|
6016
|
+
parsedPrecision = '0.'
|
|
6017
|
+
for i in range(0, precisionNumber - 1):
|
|
6018
|
+
parsedPrecision = parsedPrecision + '0'
|
|
6019
|
+
return parsedPrecision + '1'
|
|
6020
|
+
else:
|
|
6021
|
+
parsedPrecision = '1'
|
|
6022
|
+
for i in range(0, precisionNumber * -1 - 1):
|
|
6023
|
+
parsedPrecision = parsedPrecision + '0'
|
|
6024
|
+
return parsedPrecision + '0'
|
|
5688
6025
|
|
|
5689
6026
|
def integer_precision_to_amount(self, precision: Str):
|
|
5690
6027
|
"""
|
|
@@ -5952,6 +6289,29 @@ class Exchange(object):
|
|
|
5952
6289
|
symbol = None if (market is None) else market['symbol']
|
|
5953
6290
|
return self.filter_by_symbol_since_limit(sorted, symbol, since, limit)
|
|
5954
6291
|
|
|
6292
|
+
def handle_trigger_prices_and_params(self, symbol, params, omitParams=True):
|
|
6293
|
+
#
|
|
6294
|
+
triggerPrice = self.safe_string_2(params, 'triggerPrice', 'stopPrice')
|
|
6295
|
+
triggerPriceStr: Str = None
|
|
6296
|
+
stopLossPrice = self.safe_string(params, 'stopLossPrice')
|
|
6297
|
+
stopLossPriceStr: Str = None
|
|
6298
|
+
takeProfitPrice = self.safe_string(params, 'takeProfitPrice')
|
|
6299
|
+
takeProfitPriceStr: Str = None
|
|
6300
|
+
#
|
|
6301
|
+
if triggerPrice is not None:
|
|
6302
|
+
if omitParams:
|
|
6303
|
+
params = self.omit(params, ['triggerPrice', 'stopPrice'])
|
|
6304
|
+
triggerPriceStr = self.price_to_precision(symbol, float(triggerPrice))
|
|
6305
|
+
if stopLossPrice is not None:
|
|
6306
|
+
if omitParams:
|
|
6307
|
+
params = self.omit(params, 'stopLossPrice')
|
|
6308
|
+
stopLossPriceStr = self.price_to_precision(symbol, float(stopLossPrice))
|
|
6309
|
+
if takeProfitPrice is not None:
|
|
6310
|
+
if omitParams:
|
|
6311
|
+
params = self.omit(params, 'takeProfitPrice')
|
|
6312
|
+
takeProfitPriceStr = self.price_to_precision(symbol, float(takeProfitPrice))
|
|
6313
|
+
return [triggerPriceStr, stopLossPriceStr, takeProfitPriceStr, params]
|
|
6314
|
+
|
|
5955
6315
|
def handle_trigger_direction_and_params(self, params, exchangeSpecificKey: Str = None, allowEmpty: Bool = False):
|
|
5956
6316
|
"""
|
|
5957
6317
|
@ignore
|
|
@@ -6105,7 +6465,7 @@ class Exchange(object):
|
|
|
6105
6465
|
else:
|
|
6106
6466
|
raise NotSupported(self.id + ' fetchFundingInterval() is not supported yet')
|
|
6107
6467
|
|
|
6108
|
-
def fetch_mark_ohlcv(self, symbol, timeframe='1m', since: Int = None, limit: Int = None, params={}):
|
|
6468
|
+
def fetch_mark_ohlcv(self, symbol: str, timeframe: str = '1m', since: Int = None, limit: Int = None, params={}):
|
|
6109
6469
|
"""
|
|
6110
6470
|
fetches historical mark price candlestick data containing the open, high, low, and close price of a market
|
|
6111
6471
|
:param str symbol: unified symbol of the market to fetch OHLCV data for
|
|
@@ -6123,7 +6483,7 @@ class Exchange(object):
|
|
|
6123
6483
|
else:
|
|
6124
6484
|
raise NotSupported(self.id + ' fetchMarkOHLCV() is not supported yet')
|
|
6125
6485
|
|
|
6126
|
-
def fetch_index_ohlcv(self, symbol: str, timeframe='1m', since: Int = None, limit: Int = None, params={}):
|
|
6486
|
+
def fetch_index_ohlcv(self, symbol: str, timeframe: str = '1m', since: Int = None, limit: Int = None, params={}):
|
|
6127
6487
|
"""
|
|
6128
6488
|
fetches historical index price candlestick data containing the open, high, low, and close price of a market
|
|
6129
6489
|
:param str symbol: unified symbol of the market to fetch OHLCV data for
|
|
@@ -6141,7 +6501,7 @@ class Exchange(object):
|
|
|
6141
6501
|
else:
|
|
6142
6502
|
raise NotSupported(self.id + ' fetchIndexOHLCV() is not supported yet')
|
|
6143
6503
|
|
|
6144
|
-
def fetch_premium_index_ohlcv(self, symbol: str, timeframe='1m', since: Int = None, limit: Int = None, params={}):
|
|
6504
|
+
def fetch_premium_index_ohlcv(self, symbol: str, timeframe: str = '1m', since: Int = None, limit: Int = None, params={}):
|
|
6145
6505
|
"""
|
|
6146
6506
|
fetches historical premium index price candlestick data containing the open, high, low, and close price of a market
|
|
6147
6507
|
:param str symbol: unified symbol of the market to fetch OHLCV data for
|
|
@@ -6373,7 +6733,7 @@ class Exchange(object):
|
|
|
6373
6733
|
calls = 0
|
|
6374
6734
|
result = []
|
|
6375
6735
|
errors = 0
|
|
6376
|
-
until = self.
|
|
6736
|
+
until = self.safe_integer_n(params, ['until', 'untill', 'till']) # do not omit it from params here
|
|
6377
6737
|
maxEntriesPerRequest, params = self.handle_max_entries_per_request_and_params(method, maxEntriesPerRequest, params)
|
|
6378
6738
|
if (paginationDirection == 'forward'):
|
|
6379
6739
|
if since is None:
|
|
@@ -6613,6 +6973,15 @@ class Exchange(object):
|
|
|
6613
6973
|
values = list(uniqueResult.values())
|
|
6614
6974
|
return values
|
|
6615
6975
|
|
|
6976
|
+
def remove_keys_from_dict(self, dict: dict, removeKeys: List[str]):
|
|
6977
|
+
keys = list(dict.keys())
|
|
6978
|
+
newDict = {}
|
|
6979
|
+
for i in range(0, len(keys)):
|
|
6980
|
+
key = keys[i]
|
|
6981
|
+
if not self.in_array(key, removeKeys):
|
|
6982
|
+
newDict[key] = dict[key]
|
|
6983
|
+
return newDict
|
|
6984
|
+
|
|
6616
6985
|
def handle_until_option(self, key: str, request, params, multiplier=1):
|
|
6617
6986
|
until = self.safe_integer_2(params, 'until', 'till')
|
|
6618
6987
|
if until is not None:
|
|
@@ -6660,6 +7029,27 @@ class Exchange(object):
|
|
|
6660
7029
|
def parse_greeks(self, greeks: dict, market: Market = None):
|
|
6661
7030
|
raise NotSupported(self.id + ' parseGreeks() is not supported yet')
|
|
6662
7031
|
|
|
7032
|
+
def parse_all_greeks(self, greeks, symbols: Strings = None, params={}):
|
|
7033
|
+
#
|
|
7034
|
+
# the value of greeks is either a dict or a list
|
|
7035
|
+
#
|
|
7036
|
+
results = []
|
|
7037
|
+
if isinstance(greeks, list):
|
|
7038
|
+
for i in range(0, len(greeks)):
|
|
7039
|
+
parsedTicker = self.parse_greeks(greeks[i])
|
|
7040
|
+
greek = self.extend(parsedTicker, params)
|
|
7041
|
+
results.append(greek)
|
|
7042
|
+
else:
|
|
7043
|
+
marketIds = list(greeks.keys())
|
|
7044
|
+
for i in range(0, len(marketIds)):
|
|
7045
|
+
marketId = marketIds[i]
|
|
7046
|
+
market = self.safe_market(marketId)
|
|
7047
|
+
parsed = self.parse_greeks(greeks[marketId], market)
|
|
7048
|
+
greek = self.extend(parsed, params)
|
|
7049
|
+
results.append(greek)
|
|
7050
|
+
symbols = self.market_symbols(symbols)
|
|
7051
|
+
return self.filter_by_array(results, 'symbol', symbols)
|
|
7052
|
+
|
|
6663
7053
|
def parse_option(self, chain: dict, currency: Currency = None, market: Market = None):
|
|
6664
7054
|
raise NotSupported(self.id + ' parseOption() is not supported yet')
|
|
6665
7055
|
|
|
@@ -6776,7 +7166,7 @@ class Exchange(object):
|
|
|
6776
7166
|
return reconstructedDate
|
|
6777
7167
|
|
|
6778
7168
|
def convert_market_id_expire_date(self, date: str):
|
|
6779
|
-
# parse 03JAN24 to 240103
|
|
7169
|
+
# parse 03JAN24 to 240103.
|
|
6780
7170
|
monthMappping = {
|
|
6781
7171
|
'JAN': '01',
|
|
6782
7172
|
'FEB': '02',
|
|
@@ -6861,14 +7251,104 @@ class Exchange(object):
|
|
|
6861
7251
|
"""
|
|
6862
7252
|
raise NotSupported(self.id + ' fetchTransfers() is not supported yet')
|
|
6863
7253
|
|
|
6864
|
-
def
|
|
7254
|
+
def un_watch_ohlcv(self, symbol: str, timeframe: str = '1m', params={}):
|
|
7255
|
+
"""
|
|
7256
|
+
watches historical candlestick data containing the open, high, low, and close price, and the volume of a market
|
|
7257
|
+
:param str symbol: unified symbol of the market to fetch OHLCV data for
|
|
7258
|
+
:param str timeframe: the length of time each candle represents
|
|
7259
|
+
:param dict [params]: extra parameters specific to the exchange API endpoint
|
|
7260
|
+
:returns int[][]: A list of candles ordered, open, high, low, close, volume
|
|
7261
|
+
"""
|
|
7262
|
+
raise NotSupported(self.id + ' unWatchOHLCV() is not supported yet')
|
|
7263
|
+
|
|
7264
|
+
def watch_mark_price(self, symbol: str, params={}):
|
|
7265
|
+
"""
|
|
7266
|
+
watches a mark price for a specific market
|
|
7267
|
+
:param str symbol: unified symbol of the market to fetch the ticker for
|
|
7268
|
+
:param dict [params]: extra parameters specific to the exchange API endpoint
|
|
7269
|
+
:returns dict: a `ticker structure <https://docs.ccxt.com/#/?id=ticker-structure>`
|
|
7270
|
+
"""
|
|
7271
|
+
raise NotSupported(self.id + ' watchMarkPrice() is not supported yet')
|
|
7272
|
+
|
|
7273
|
+
def watch_mark_prices(self, symbols: Strings = None, params={}):
|
|
7274
|
+
"""
|
|
7275
|
+
watches the mark price for all markets
|
|
7276
|
+
:param str[] symbols: unified symbol of the market to fetch the ticker for
|
|
7277
|
+
:param dict [params]: extra parameters specific to the exchange API endpoint
|
|
7278
|
+
:returns dict: a `ticker structure <https://docs.ccxt.com/#/?id=ticker-structure>`
|
|
7279
|
+
"""
|
|
7280
|
+
raise NotSupported(self.id + ' watchMarkPrices() is not supported yet')
|
|
7281
|
+
|
|
7282
|
+
def withdraw_ws(self, code: str, amount: float, address: str, tag: Str = None, params={}):
|
|
7283
|
+
"""
|
|
7284
|
+
make a withdrawal
|
|
7285
|
+
:param str code: unified currency code
|
|
7286
|
+
:param float amount: the amount to withdraw
|
|
7287
|
+
:param str address: the address to withdraw to
|
|
7288
|
+
:param str tag:
|
|
7289
|
+
:param dict [params]: extra parameters specific to the bitvavo api endpoint
|
|
7290
|
+
:returns dict: a `transaction structure <https://docs.ccxt.com/#/?id=transaction-structure>`
|
|
7291
|
+
"""
|
|
7292
|
+
raise NotSupported(self.id + ' withdrawWs() is not supported yet')
|
|
7293
|
+
|
|
7294
|
+
def un_watch_my_trades(self, symbol: Str = None, params={}):
|
|
7295
|
+
"""
|
|
7296
|
+
unWatches information on multiple trades made by the user
|
|
7297
|
+
:param str symbol: unified market symbol of the market orders were made in
|
|
7298
|
+
:param dict [params]: extra parameters specific to the exchange API endpoint
|
|
7299
|
+
:returns dict[]: a list of `order structures <https://docs.ccxt.com/#/?id=order-structure>`
|
|
7300
|
+
"""
|
|
7301
|
+
raise NotSupported(self.id + ' unWatchMyTrades() is not supported yet')
|
|
7302
|
+
|
|
7303
|
+
def create_orders_ws(self, orders: List[OrderRequest], params={}):
|
|
7304
|
+
"""
|
|
7305
|
+
create a list of trade orders
|
|
7306
|
+
:param Array orders: list of orders to create, each object should contain the parameters required by createOrder, namely symbol, type, side, amount, price and params
|
|
7307
|
+
:param dict [params]: extra parameters specific to the exchange API endpoint
|
|
7308
|
+
:returns dict: an `order structure <https://docs.ccxt.com/#/?id=order-structure>`
|
|
7309
|
+
"""
|
|
7310
|
+
raise NotSupported(self.id + ' createOrdersWs() is not supported yet')
|
|
7311
|
+
|
|
7312
|
+
def fetch_orders_by_status_ws(self, status: str, symbol: Str = None, since: Int = None, limit: Int = None, params={}):
|
|
7313
|
+
"""
|
|
7314
|
+
watches information on open orders with bid(buy) and ask(sell) prices, volumes and other data
|
|
7315
|
+
:param str symbol: unified symbol of the market to fetch the order book for
|
|
7316
|
+
:param int [limit]: the maximum amount of order book entries to return
|
|
7317
|
+
:param dict [params]: extra parameters specific to the exchange API endpoint
|
|
7318
|
+
:returns dict: A dictionary of `order book structures <https://docs.ccxt.com/#/?id=order-book-structure>` indexed by market symbols
|
|
7319
|
+
"""
|
|
7320
|
+
raise NotSupported(self.id + ' fetchOrdersByStatusWs() is not supported yet')
|
|
7321
|
+
|
|
7322
|
+
def un_watch_bids_asks(self, symbols: Strings = None, params={}):
|
|
7323
|
+
"""
|
|
7324
|
+
unWatches best bid & ask for symbols
|
|
7325
|
+
:param str[] symbols: unified symbol of the market to fetch the ticker for
|
|
7326
|
+
:param dict [params]: extra parameters specific to the exchange API endpoint
|
|
7327
|
+
:returns dict: a `ticker structure <https://docs.ccxt.com/#/?id=ticker-structure>`
|
|
7328
|
+
"""
|
|
7329
|
+
raise NotSupported(self.id + ' unWatchBidsAsks() is not supported yet')
|
|
7330
|
+
|
|
7331
|
+
def clean_unsubscription(self, client, subHash: str, unsubHash: str, subHashIsPrefix=False):
|
|
6865
7332
|
if unsubHash in client.subscriptions:
|
|
6866
7333
|
del client.subscriptions[unsubHash]
|
|
6867
|
-
if
|
|
6868
|
-
|
|
6869
|
-
|
|
6870
|
-
|
|
6871
|
-
|
|
7334
|
+
if not subHashIsPrefix:
|
|
7335
|
+
if subHash in client.subscriptions:
|
|
7336
|
+
del client.subscriptions[subHash]
|
|
7337
|
+
if subHash in client.futures:
|
|
7338
|
+
error = UnsubscribeError(self.id + ' ' + subHash)
|
|
7339
|
+
client.reject(error, subHash)
|
|
7340
|
+
else:
|
|
7341
|
+
clientSubscriptions = list(client.subscriptions.keys())
|
|
7342
|
+
for i in range(0, len(clientSubscriptions)):
|
|
7343
|
+
sub = clientSubscriptions[i]
|
|
7344
|
+
if sub.startswith(subHash):
|
|
7345
|
+
del client.subscriptions[sub]
|
|
7346
|
+
clientFutures = list(client.futures.keys())
|
|
7347
|
+
for i in range(0, len(clientFutures)):
|
|
7348
|
+
future = clientFutures[i]
|
|
7349
|
+
if future.startswith(subHash):
|
|
7350
|
+
error = UnsubscribeError(self.id + ' ' + future)
|
|
7351
|
+
client.reject(error, future)
|
|
6872
7352
|
client.resolve(True, unsubHash)
|
|
6873
7353
|
|
|
6874
7354
|
def clean_cache(self, subscription: dict):
|
|
@@ -6876,12 +7356,12 @@ class Exchange(object):
|
|
|
6876
7356
|
symbols = self.safe_list(subscription, 'symbols', [])
|
|
6877
7357
|
symbolsLength = len(symbols)
|
|
6878
7358
|
if topic == 'ohlcv':
|
|
6879
|
-
|
|
6880
|
-
for i in range(0, len(
|
|
6881
|
-
symbolAndTimeFrame =
|
|
7359
|
+
symbolsAndTimeframes = self.safe_list(subscription, 'symbolsAndTimeframes', [])
|
|
7360
|
+
for i in range(0, len(symbolsAndTimeframes)):
|
|
7361
|
+
symbolAndTimeFrame = symbolsAndTimeframes[i]
|
|
6882
7362
|
symbol = self.safe_string(symbolAndTimeFrame, 0)
|
|
6883
7363
|
timeframe = self.safe_string(symbolAndTimeFrame, 1)
|
|
6884
|
-
if symbol in self.ohlcvs:
|
|
7364
|
+
if (self.ohlcvs is not None) and (symbol in self.ohlcvs):
|
|
6885
7365
|
if timeframe in self.ohlcvs[symbol]:
|
|
6886
7366
|
del self.ohlcvs[symbol][timeframe]
|
|
6887
7367
|
elif symbolsLength > 0:
|
|
@@ -6896,24 +7376,31 @@ class Exchange(object):
|
|
|
6896
7376
|
elif topic == 'ticker':
|
|
6897
7377
|
if symbol in self.tickers:
|
|
6898
7378
|
del self.tickers[symbol]
|
|
7379
|
+
elif topic == 'bidsasks':
|
|
7380
|
+
if symbol in self.bidsasks:
|
|
7381
|
+
del self.bidsasks[symbol]
|
|
6899
7382
|
else:
|
|
6900
|
-
if topic == 'myTrades':
|
|
6901
|
-
|
|
6902
|
-
|
|
6903
|
-
|
|
6904
|
-
|
|
6905
|
-
|
|
6906
|
-
|
|
6907
|
-
|
|
6908
|
-
|
|
6909
|
-
|
|
6910
|
-
|
|
6911
|
-
|
|
6912
|
-
|
|
6913
|
-
del self.orders[orderSymbol]
|
|
6914
|
-
elif topic == 'ticker':
|
|
7383
|
+
if topic == 'myTrades' and (self.myTrades is not None):
|
|
7384
|
+
self.myTrades = None
|
|
7385
|
+
elif topic == 'orders' and (self.orders is not None):
|
|
7386
|
+
self.orders = None
|
|
7387
|
+
elif topic == 'positions' and (self.positions is not None):
|
|
7388
|
+
self.positions = None
|
|
7389
|
+
clients = list(self.clients.values())
|
|
7390
|
+
for i in range(0, len(clients)):
|
|
7391
|
+
client = clients[i]
|
|
7392
|
+
futures = client.futures
|
|
7393
|
+
if (futures is not None) and ('fetchPositionsSnapshot' in futures):
|
|
7394
|
+
del futures['fetchPositionsSnapshot']
|
|
7395
|
+
elif topic == 'ticker' and (self.tickers is not None):
|
|
6915
7396
|
tickerSymbols = list(self.tickers.keys())
|
|
6916
7397
|
for i in range(0, len(tickerSymbols)):
|
|
6917
7398
|
tickerSymbol = tickerSymbols[i]
|
|
6918
7399
|
if tickerSymbol in self.tickers:
|
|
6919
7400
|
del self.tickers[tickerSymbol]
|
|
7401
|
+
elif topic == 'bidsasks' and (self.bidsasks is not None):
|
|
7402
|
+
bidsaskSymbols = list(self.bidsasks.keys())
|
|
7403
|
+
for i in range(0, len(bidsaskSymbols)):
|
|
7404
|
+
bidsaskSymbol = bidsaskSymbols[i]
|
|
7405
|
+
if bidsaskSymbol in self.bidsasks:
|
|
7406
|
+
del self.bidsasks[bidsaskSymbol]
|