gate-io-api 0.0.74__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.

@@ -4,7 +4,7 @@
4
4
 
5
5
  # -----------------------------------------------------------------------------
6
6
 
7
- __version__ = '4.4.95'
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,8 +316,7 @@ class Exchange(object):
310
316
  bidsasks = None
311
317
  base_currencies = None
312
318
  quote_currencies = None
313
- currencies = None
314
-
319
+ currencies = {}
315
320
  options = None # Python does not allow to define properties in run-time with setattr
316
321
  isSandboxModeEnabled = False
317
322
  accounts = None
@@ -579,6 +584,8 @@ class Exchange(object):
579
584
  if self.verbose:
580
585
  self.log("\nfetch Response:", self.id, method, url, http_status_code, "ResponseHeaders:", headers, "ResponseBody:", http_response)
581
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
582
589
  response.raise_for_status()
583
590
 
584
591
  except Timeout as e:
@@ -1306,6 +1313,10 @@ class Exchange(object):
1306
1313
  elif algoType == 'ES':
1307
1314
  rawSignature = Exchange.ecdsa(token, secret, 'p256', algorithm)
1308
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
1309
1320
  else:
1310
1321
  signature = Exchange.hmac(Exchange.encode(token), secret, algos[algorithm], 'binary')
1311
1322
  return token + '.' + Exchange.urlencode_base64(signature)
@@ -1376,9 +1387,9 @@ class Exchange(object):
1376
1387
  return msgHash
1377
1388
 
1378
1389
  @staticmethod
1379
- def starknet_sign (hash, pri):
1390
+ def starknet_sign (msg_hash, pri):
1380
1391
  # // TODO: unify to ecdsa
1381
- r, s = message_signature(hash, pri)
1392
+ r, s = message_signature(msg_hash, pri)
1382
1393
  return Exchange.json([hex(r), hex(s)])
1383
1394
 
1384
1395
  @staticmethod
@@ -1395,6 +1406,21 @@ class Exchange(object):
1395
1406
 
1396
1407
  @staticmethod
1397
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
+ """
1398
1424
  # your welcome - frosty00
1399
1425
  algorithms = {
1400
1426
  'p192': [ecdsa.NIST192p, 'sha256'],
@@ -1406,6 +1432,14 @@ class Exchange(object):
1406
1432
  }
1407
1433
  if algorithm not in algorithms:
1408
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
1409
1443
  curve_info = algorithms[algorithm]
1410
1444
  hash_function = getattr(hashlib, curve_info[1])
1411
1445
  encoded_request = Exchange.encode(request)
@@ -1439,12 +1473,69 @@ class Exchange(object):
1439
1473
  'v': v,
1440
1474
  }
1441
1475
 
1476
+
1442
1477
  @staticmethod
1443
- def eddsa(request, secret, curve='ed25519'):
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):
1444
1531
  if isinstance(secret, str):
1445
1532
  secret = Exchange.encode(secret)
1446
1533
  private_key = ed25519.Ed25519PrivateKey.from_private_bytes(secret) if len(secret) == 32 else load_pem_private_key(secret, None)
1447
- return Exchange.binary_to_base64(private_key.sign(request))
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)
1448
1539
 
1449
1540
  @staticmethod
1450
1541
  def axolotl(request, secret, curve='ed25519'):
@@ -1859,6 +1950,8 @@ class Exchange(object):
1859
1950
  signature = auth_data.signature
1860
1951
  return signature
1861
1952
 
1953
+ def is_binary_message(self, message):
1954
+ return isinstance(message, bytes) or isinstance(message, bytearray)
1862
1955
 
1863
1956
  # ########################################################################
1864
1957
  # ########################################################################
@@ -1897,7 +1990,7 @@ class Exchange(object):
1897
1990
  # ########################################################################
1898
1991
  # ########################################################################
1899
1992
 
1900
- # METHODS BELOW THIS LINE ARE TRANSPILED FROM JAVASCRIPT TO PYTHON AND PHP
1993
+ # METHODS BELOW THIS LINE ARE TRANSPILED FROM TYPESCRIPT
1901
1994
 
1902
1995
  def describe(self) -> Any:
1903
1996
  return {
@@ -2130,6 +2223,17 @@ class Exchange(object):
2130
2223
  'watchLiquidations': None,
2131
2224
  'watchLiquidationsForSymbols': None,
2132
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,
2133
2237
  'watchMyLiquidationsForSymbols': None,
2134
2238
  'withdraw': None,
2135
2239
  'ws': None,
@@ -2321,6 +2425,12 @@ class Exchange(object):
2321
2425
  # return the first index of the cache that can be applied to the orderbook or -1 if not possible
2322
2426
  return -1
2323
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
+
2324
2434
  def find_timeframe(self, timeframe, timeframes=None):
2325
2435
  if timeframes is None:
2326
2436
  timeframes = self.timeframes
@@ -2555,6 +2665,22 @@ class Exchange(object):
2555
2665
  # set flag
2556
2666
  self.isSandboxModeEnabled = False
2557
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
+
2558
2684
  def sign(self, path, api: Any = 'public', method='GET', params={}, headers: Any = None, body: Any = None):
2559
2685
  return {}
2560
2686
 
@@ -2616,6 +2742,18 @@ class Exchange(object):
2616
2742
  def un_watch_order_book_for_symbols(self, symbols: List[str], params={}):
2617
2743
  raise NotSupported(self.id + ' unWatchOrderBookForSymbols() is not supported yet')
2618
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
+
2619
2757
  def fetch_deposit_addresses(self, codes: Strings = None, params={}):
2620
2758
  raise NotSupported(self.id + ' fetchDepositAddresses() is not supported yet')
2621
2759
 
@@ -2757,13 +2895,13 @@ class Exchange(object):
2757
2895
  def transfer(self, code: str, amount: float, fromAccount: str, toAccount: str, params={}):
2758
2896
  raise NotSupported(self.id + ' transfer() is not supported yet')
2759
2897
 
2760
- 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={}):
2761
2899
  raise NotSupported(self.id + ' withdraw() is not supported yet')
2762
2900
 
2763
2901
  def create_deposit_address(self, code: str, params={}):
2764
2902
  raise NotSupported(self.id + ' createDepositAddress() is not supported yet')
2765
2903
 
2766
- def set_leverage(self, leverage: Int, symbol: Str = None, params={}):
2904
+ def set_leverage(self, leverage: int, symbol: Str = None, params={}):
2767
2905
  raise NotSupported(self.id + ' setLeverage() is not supported yet')
2768
2906
 
2769
2907
  def fetch_leverage(self, symbol: str, params={}):
@@ -2812,7 +2950,7 @@ class Exchange(object):
2812
2950
  def fetch_deposit_addresses_by_network(self, code: str, params={}):
2813
2951
  raise NotSupported(self.id + ' fetchDepositAddressesByNetwork() is not supported yet')
2814
2952
 
2815
- 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={}):
2816
2954
  raise NotSupported(self.id + ' fetchOpenInterestHistory() is not supported yet')
2817
2955
 
2818
2956
  def fetch_open_interest(self, symbol: str, params={}):
@@ -2837,9 +2975,9 @@ class Exchange(object):
2837
2975
  def parse_to_numeric(self, number):
2838
2976
  stringVersion = self.number_to_string(number) # self will convert 1.0 and 1 to "1" and 1.1 to "1.1"
2839
2977
  # keep self in mind:
2840
- # in JS: 1 == 1.0 is True; 1 == 1.0 is True
2978
+ # in JS: 1 == 1.0 is True
2841
2979
  # in Python: 1 == 1.0 is True
2842
- # in PHP 1 == 1.0 is True, but 1 == 1.0 is False.
2980
+ # in PHP: 1 == 1.0 is True, but 1 == 1.0 is False.
2843
2981
  if stringVersion.find('.') >= 0:
2844
2982
  return float(stringVersion)
2845
2983
  return int(stringVersion)
@@ -2885,7 +3023,7 @@ class Exchange(object):
2885
3023
  'delay': 0.001,
2886
3024
  'capacity': 1,
2887
3025
  'cost': 1,
2888
- 'maxCapacity': 1000,
3026
+ 'maxCapacity': self.safe_integer(self.options, 'maxRequestsQueue', 1000),
2889
3027
  'refillRate': refillRate,
2890
3028
  }
2891
3029
  existingBucket = {} if (self.tokenBucket is None) else self.tokenBucket
@@ -2967,6 +3105,79 @@ class Exchange(object):
2967
3105
  featureBlock['symbolRequired'] = self.in_array(key, ['createOrder', 'createOrders', 'fetchOHLCV'])
2968
3106
  return featuresObj
2969
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
+
2970
3181
  def orderbook_checksum_message(self, symbol: Str):
2971
3182
  return symbol + ' = False'
2972
3183
 
@@ -3233,7 +3444,11 @@ class Exchange(object):
3233
3444
  marketsSortedById = self.keysort(self.markets_by_id)
3234
3445
  self.symbols = list(marketsSortedBySymbol.keys())
3235
3446
  self.ids = list(marketsSortedById.keys())
3447
+ numCurrencies = 0
3236
3448
  if currencies is not None:
3449
+ keys = list(currencies.keys())
3450
+ numCurrencies = len(keys)
3451
+ if numCurrencies > 0:
3237
3452
  # currencies is always None when called in constructor but not when called from loadMarkets
3238
3453
  self.currencies = self.map_to_safe_map(self.deep_extend(self.currencies, currencies))
3239
3454
  else:
@@ -3285,6 +3500,30 @@ class Exchange(object):
3285
3500
  self.codes = list(currenciesSortedByCode.keys())
3286
3501
  return self.markets
3287
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
+
3288
3527
  def get_describe_for_extended_ws_exchange(self, currentRestInstance: Any, parentRestInstance: Any, wsBaseDescribe: dict):
3289
3528
  extendedRestDescribe = self.deep_extend(parentRestInstance.describe(), currentRestInstance.describe())
3290
3529
  superWithRestDescribe = self.deep_extend(extendedRestDescribe, wsBaseDescribe)
@@ -3584,18 +3823,7 @@ class Exchange(object):
3584
3823
  symbol = market['symbol'] if (market is not None) else None
3585
3824
  return self.filter_by_symbol_since_limit(results, symbol, since, limit)
3586
3825
 
3587
- def calculate_fee(self, symbol: str, type: str, side: str, amount: float, price: float, takerOrMaker='taker', params={}):
3588
- """
3589
- calculates the presumptive fee that would be charged for an order
3590
- :param str symbol: unified market symbol
3591
- :param str type: 'market' or 'limit'
3592
- :param str side: 'buy' or 'sell'
3593
- :param float amount: how much you want to trade, in units of the base currency on most exchanges, or number of contracts
3594
- :param float price: the price for the order to be filled at, in units of the quote currency
3595
- :param str takerOrMaker: 'taker' or 'maker'
3596
- :param dict params:
3597
- :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
3598
- """
3826
+ def calculate_fee_with_rate(self, symbol: str, type: str, side: str, amount: float, price: float, takerOrMaker='taker', feeRate: Num = None, params={}):
3599
3827
  if type == 'market' and takerOrMaker == 'maker':
3600
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.')
3601
3829
  market = self.markets[symbol]
@@ -3624,7 +3852,7 @@ class Exchange(object):
3624
3852
  # even if `takerOrMaker` argument was set to 'maker', for 'market' orders we should forcefully override it to 'taker'
3625
3853
  if type == 'market':
3626
3854
  takerOrMaker = 'taker'
3627
- rate = self.safe_string(market, takerOrMaker)
3855
+ rate = self.number_to_string(feeRate) if (feeRate is not None) else self.safe_string(market, takerOrMaker)
3628
3856
  cost = Precise.string_mul(cost, rate)
3629
3857
  return {
3630
3858
  'type': takerOrMaker,
@@ -3633,6 +3861,20 @@ class Exchange(object):
3633
3861
  'cost': self.parse_number(cost),
3634
3862
  }
3635
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
+
3636
3878
  def safe_liquidation(self, liquidation: dict, market: Market = None):
3637
3879
  contracts = self.safe_string(liquidation, 'contracts')
3638
3880
  contractSize = self.safe_string(market, 'contractSize')
@@ -3672,6 +3914,21 @@ class Exchange(object):
3672
3914
  trade['cost'] = self.parse_number(cost)
3673
3915
  return trade
3674
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
+
3675
3932
  def parsed_fee_and_fees(self, container: Any):
3676
3933
  fee = self.safe_dict(container, 'fee')
3677
3934
  fees = self.safe_list(container, 'fees')
@@ -3905,19 +4162,19 @@ class Exchange(object):
3905
4162
  def repay_margin(self, code: str, amount: float, symbol: Str = None, params={}):
3906
4163
  raise NotSupported(self.id + ' repayMargin is deprecated, please use repayCrossMargin or repayIsolatedMargin instead')
3907
4164
 
3908
- 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={}):
3909
4166
  message = ''
3910
4167
  if self.has['fetchTrades']:
3911
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'
3912
4169
  raise NotSupported(self.id + ' fetchOHLCV() is not supported yet' + message)
3913
4170
 
3914
- 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={}):
3915
4172
  message = ''
3916
4173
  if self.has['fetchTradesWs']:
3917
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'
3918
4175
  raise NotSupported(self.id + ' fetchOHLCVWs() is not supported yet. Try using fetchOHLCV instead.' + message)
3919
4176
 
3920
- 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={}):
3921
4178
  raise NotSupported(self.id + ' watchOHLCV() is not supported yet')
3922
4179
 
3923
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):
@@ -4329,16 +4586,27 @@ class Exchange(object):
4329
4586
  result.append(account)
4330
4587
  return result
4331
4588
 
4332
- def parse_trades(self, trades: List[Any], market: Market = None, since: Int = None, limit: Int = None, params={}):
4589
+ def parse_trades_helper(self, isWs: bool, trades: List[Any], market: Market = None, since: Int = None, limit: Int = None, params={}):
4333
4590
  trades = self.to_array(trades)
4334
4591
  result = []
4335
4592
  for i in range(0, len(trades)):
4336
- trade = self.extend(self.parse_trade(trades[i], market), params)
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)
4337
4599
  result.append(trade)
4338
4600
  result = self.sort_by_2(result, 'timestamp', 'id')
4339
4601
  symbol = market['symbol'] if (market is not None) else None
4340
4602
  return self.filter_by_symbol_since_limit(result, symbol, since, limit)
4341
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
+
4342
4610
  def parse_transactions(self, transactions: List[Any], currency: Currency = None, since: Int = None, limit: Int = None, params={}):
4343
4611
  transactions = self.to_array(transactions)
4344
4612
  result = []
@@ -4555,9 +4823,12 @@ class Exchange(object):
4555
4823
  i_count = 6
4556
4824
  tradesLength = len(trades)
4557
4825
  oldest = min(tradesLength, limit)
4826
+ options = self.safe_dict(self.options, 'buildOHLCVC', {})
4827
+ skipZeroPrices = self.safe_bool(options, 'skipZeroPrices', True)
4558
4828
  for i in range(0, oldest):
4559
4829
  trade = trades[i]
4560
4830
  ts = trade['timestamp']
4831
+ price = trade['price']
4561
4832
  if ts < since:
4562
4833
  continue
4563
4834
  openingTime = int(math.floor(ts / ms)) * ms # shift to the edge of m/h/d(but not M)
@@ -4565,22 +4836,25 @@ class Exchange(object):
4565
4836
  continue
4566
4837
  ohlcv_length = len(ohlcvs)
4567
4838
  candle = ohlcv_length - 1
4568
- if (candle == -1) or (openingTime >= self.sum(ohlcvs[candle][i_timestamp], ms)):
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):
4569
4843
  # moved to a new timeframe -> create a new candle from opening trade
4570
4844
  ohlcvs.append([
4571
4845
  openingTime, # timestamp
4572
- trade['price'], # O
4573
- trade['price'], # H
4574
- trade['price'], # L
4575
- trade['price'], # C
4846
+ price, # O
4847
+ price, # H
4848
+ price, # L
4849
+ price, # C
4576
4850
  trade['amount'], # V
4577
4851
  1, # count
4578
4852
  ])
4579
4853
  else:
4580
4854
  # still processing the same timeframe -> update opening trade
4581
- ohlcvs[candle][i_high] = max(ohlcvs[candle][i_high], trade['price'])
4582
- ohlcvs[candle][i_low] = min(ohlcvs[candle][i_low], trade['price'])
4583
- ohlcvs[candle][i_close] = trade['price']
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
4584
4858
  ohlcvs[candle][i_volume] = self.sum(ohlcvs[candle][i_volume], trade['amount'])
4585
4859
  ohlcvs[candle][i_count] = self.sum(ohlcvs[candle][i_count], 1)
4586
4860
  return ohlcvs
@@ -4720,6 +4994,11 @@ class Exchange(object):
4720
4994
  return market
4721
4995
  return result
4722
4996
 
4997
+ def market_or_null(self, symbol: str):
4998
+ if symbol is None:
4999
+ return None
5000
+ return self.market(symbol)
5001
+
4723
5002
  def check_required_credentials(self, error=True):
4724
5003
  """
4725
5004
  @ignore
@@ -5038,7 +5317,7 @@ class Exchange(object):
5038
5317
  def fetch_position_mode(self, symbol: Str = None, params={}):
5039
5318
  raise NotSupported(self.id + ' fetchPositionMode() is not supported yet')
5040
5319
 
5041
- 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={}):
5042
5321
  """
5043
5322
  create a trailing order by providing the symbol, type, side, amount, price and trailingAmount
5044
5323
  :param str symbol: unified symbol of the market to create an order in
@@ -5060,7 +5339,7 @@ class Exchange(object):
5060
5339
  return self.create_order(symbol, type, side, amount, price, params)
5061
5340
  raise NotSupported(self.id + ' createTrailingAmountOrder() is not supported yet')
5062
5341
 
5063
- 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={}):
5064
5343
  """
5065
5344
  create a trailing order by providing the symbol, type, side, amount, price and trailingAmount
5066
5345
  :param str symbol: unified symbol of the market to create an order in
@@ -5082,7 +5361,7 @@ class Exchange(object):
5082
5361
  return self.create_order_ws(symbol, type, side, amount, price, params)
5083
5362
  raise NotSupported(self.id + ' createTrailingAmountOrderWs() is not supported yet')
5084
5363
 
5085
- 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={}):
5086
5365
  """
5087
5366
  create a trailing order by providing the symbol, type, side, amount, price and trailingPercent
5088
5367
  :param str symbol: unified symbol of the market to create an order in
@@ -5104,7 +5383,7 @@ class Exchange(object):
5104
5383
  return self.create_order(symbol, type, side, amount, price, params)
5105
5384
  raise NotSupported(self.id + ' createTrailingPercentOrder() is not supported yet')
5106
5385
 
5107
- 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={}):
5108
5387
  """
5109
5388
  create a trailing order by providing the symbol, type, side, amount, price and trailingPercent
5110
5389
  :param str symbol: unified symbol of the market to create an order in
@@ -5395,6 +5674,9 @@ class Exchange(object):
5395
5674
  def cancel_order_ws(self, id: str, symbol: Str = None, params={}):
5396
5675
  raise NotSupported(self.id + ' cancelOrderWs() is not supported yet')
5397
5676
 
5677
+ def cancel_orders(self, ids: List[str], symbol: Str = None, params={}):
5678
+ raise NotSupported(self.id + ' cancelOrders() is not supported yet')
5679
+
5398
5680
  def cancel_orders_ws(self, ids: List[str], symbol: Str = None, params={}):
5399
5681
  raise NotSupported(self.id + ' cancelOrdersWs() is not supported yet')
5400
5682
 
@@ -5410,7 +5692,7 @@ class Exchange(object):
5410
5692
  def cancel_all_orders_ws(self, symbol: Str = None, params={}):
5411
5693
  raise NotSupported(self.id + ' cancelAllOrdersWs() is not supported yet')
5412
5694
 
5413
- def cancel_unified_order(self, order, params={}):
5695
+ def cancel_unified_order(self, order: Order, params={}):
5414
5696
  return self.cancel_order(self.safe_string(order, 'id'), self.safe_string(order, 'symbol'), params)
5415
5697
 
5416
5698
  def fetch_orders(self, symbol: Str = None, since: Int = None, limit: Int = None, params={}):
@@ -5495,10 +5777,10 @@ class Exchange(object):
5495
5777
  """
5496
5778
  raise NotSupported(self.id + ' fetchDepositsWithdrawals() is not supported yet')
5497
5779
 
5498
- def fetch_deposits(self, symbol: Str = None, since: Int = None, limit: Int = None, params={}):
5780
+ def fetch_deposits(self, code: Str = None, since: Int = None, limit: Int = None, params={}):
5499
5781
  raise NotSupported(self.id + ' fetchDeposits() is not supported yet')
5500
5782
 
5501
- def fetch_withdrawals(self, symbol: Str = None, since: Int = None, limit: Int = None, params={}):
5783
+ def fetch_withdrawals(self, code: Str = None, since: Int = None, limit: Int = None, params={}):
5502
5784
  raise NotSupported(self.id + ' fetchWithdrawals() is not supported yet')
5503
5785
 
5504
5786
  def fetch_deposits_ws(self, code: Str = None, since: Int = None, limit: Int = None, params={}):
@@ -5559,7 +5841,9 @@ class Exchange(object):
5559
5841
  return self.safe_string(self.commonCurrencies, code, code)
5560
5842
 
5561
5843
  def currency(self, code: str):
5562
- if self.currencies is None:
5844
+ keys = list(self.currencies.keys())
5845
+ numCurrencies = len(keys)
5846
+ if numCurrencies == 0:
5563
5847
  raise ExchangeError(self.id + ' currencies not loaded')
5564
5848
  if isinstance(code, str):
5565
5849
  if code in self.currencies:
@@ -6005,6 +6289,29 @@ class Exchange(object):
6005
6289
  symbol = None if (market is None) else market['symbol']
6006
6290
  return self.filter_by_symbol_since_limit(sorted, symbol, since, limit)
6007
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
+
6008
6315
  def handle_trigger_direction_and_params(self, params, exchangeSpecificKey: Str = None, allowEmpty: Bool = False):
6009
6316
  """
6010
6317
  @ignore
@@ -6158,7 +6465,7 @@ class Exchange(object):
6158
6465
  else:
6159
6466
  raise NotSupported(self.id + ' fetchFundingInterval() is not supported yet')
6160
6467
 
6161
- 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={}):
6162
6469
  """
6163
6470
  fetches historical mark price candlestick data containing the open, high, low, and close price of a market
6164
6471
  :param str symbol: unified symbol of the market to fetch OHLCV data for
@@ -6176,7 +6483,7 @@ class Exchange(object):
6176
6483
  else:
6177
6484
  raise NotSupported(self.id + ' fetchMarkOHLCV() is not supported yet')
6178
6485
 
6179
- 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={}):
6180
6487
  """
6181
6488
  fetches historical index price candlestick data containing the open, high, low, and close price of a market
6182
6489
  :param str symbol: unified symbol of the market to fetch OHLCV data for
@@ -6194,7 +6501,7 @@ class Exchange(object):
6194
6501
  else:
6195
6502
  raise NotSupported(self.id + ' fetchIndexOHLCV() is not supported yet')
6196
6503
 
6197
- 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={}):
6198
6505
  """
6199
6506
  fetches historical premium index price candlestick data containing the open, high, low, and close price of a market
6200
6507
  :param str symbol: unified symbol of the market to fetch OHLCV data for
@@ -6426,7 +6733,7 @@ class Exchange(object):
6426
6733
  calls = 0
6427
6734
  result = []
6428
6735
  errors = 0
6429
- until = self.safe_integer_2(params, 'untill', 'till') # do not omit it from params here
6736
+ until = self.safe_integer_n(params, ['until', 'untill', 'till']) # do not omit it from params here
6430
6737
  maxEntriesPerRequest, params = self.handle_max_entries_per_request_and_params(method, maxEntriesPerRequest, params)
6431
6738
  if (paginationDirection == 'forward'):
6432
6739
  if since is None:
@@ -6666,6 +6973,15 @@ class Exchange(object):
6666
6973
  values = list(uniqueResult.values())
6667
6974
  return values
6668
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
+
6669
6985
  def handle_until_option(self, key: str, request, params, multiplier=1):
6670
6986
  until = self.safe_integer_2(params, 'until', 'till')
6671
6987
  if until is not None:
@@ -6935,6 +7251,83 @@ class Exchange(object):
6935
7251
  """
6936
7252
  raise NotSupported(self.id + ' fetchTransfers() is not supported yet')
6937
7253
 
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
+
6938
7331
  def clean_unsubscription(self, client, subHash: str, unsubHash: str, subHashIsPrefix=False):
6939
7332
  if unsubHash in client.subscriptions:
6940
7333
  del client.subscriptions[unsubHash]
@@ -6963,9 +7356,9 @@ class Exchange(object):
6963
7356
  symbols = self.safe_list(subscription, 'symbols', [])
6964
7357
  symbolsLength = len(symbols)
6965
7358
  if topic == 'ohlcv':
6966
- symbolsAndTimeFrames = self.safe_list(subscription, 'symbolsAndTimeframes', [])
6967
- for i in range(0, len(symbolsAndTimeFrames)):
6968
- symbolAndTimeFrame = symbolsAndTimeFrames[i]
7359
+ symbolsAndTimeframes = self.safe_list(subscription, 'symbolsAndTimeframes', [])
7360
+ for i in range(0, len(symbolsAndTimeframes)):
7361
+ symbolAndTimeFrame = symbolsAndTimeframes[i]
6969
7362
  symbol = self.safe_string(symbolAndTimeFrame, 0)
6970
7363
  timeframe = self.safe_string(symbolAndTimeFrame, 1)
6971
7364
  if (self.ohlcvs is not None) and (symbol in self.ohlcvs):
@@ -6983,14 +7376,31 @@ class Exchange(object):
6983
7376
  elif topic == 'ticker':
6984
7377
  if symbol in self.tickers:
6985
7378
  del self.tickers[symbol]
7379
+ elif topic == 'bidsasks':
7380
+ if symbol in self.bidsasks:
7381
+ del self.bidsasks[symbol]
6986
7382
  else:
6987
7383
  if topic == 'myTrades' and (self.myTrades is not None):
6988
7384
  self.myTrades = None
6989
7385
  elif topic == 'orders' and (self.orders is not None):
6990
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']
6991
7395
  elif topic == 'ticker' and (self.tickers is not None):
6992
7396
  tickerSymbols = list(self.tickers.keys())
6993
7397
  for i in range(0, len(tickerSymbols)):
6994
7398
  tickerSymbol = tickerSymbols[i]
6995
7399
  if tickerSymbol in self.tickers:
6996
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]