diff --git a/freqtrade/exchange/binance.py b/freqtrade/exchange/binance.py index d23f84e7b..231dc1a95 100644 --- a/freqtrade/exchange/binance.py +++ b/freqtrade/exchange/binance.py @@ -2,7 +2,7 @@ import json import logging from pathlib import Path -from typing import Dict, List, Optional, Tuple +from typing import Any, Dict, List, Optional, Tuple import arrow import ccxt @@ -38,6 +38,12 @@ class Binance(Exchange): # (TradingMode.FUTURES, Collateral.ISOLATED) # TODO-lev: Uncomment once supported ] + def __init__(self, config: Dict[str, Any], validate: bool = True) -> None: + super().__init__(config, validate) + self._leverage_brackets: Dict = {} + if self.trading_mode != TradingMode.SPOT: + self.fill_leverage_brackets() + @property def _ccxt_config(self) -> Dict: # Parameters to add directly to ccxt sync/async initialization. diff --git a/freqtrade/exchange/exchange.py b/freqtrade/exchange/exchange.py index f711bc258..ad74fa0c1 100644 --- a/freqtrade/exchange/exchange.py +++ b/freqtrade/exchange/exchange.py @@ -89,7 +89,6 @@ class Exchange: self._api: ccxt.Exchange = None self._api_async: ccxt_async.Exchange = None self._markets: Dict = {} - self._leverage_brackets: Dict = {} self._config.update(config) @@ -158,9 +157,6 @@ class Exchange: self._api_async = self._init_ccxt( exchange_config, ccxt_async, ccxt_kwargs=ccxt_async_config) - if self.trading_mode != TradingMode.SPOT: - self.fill_leverage_brackets() - logger.info('Using Exchange "%s"', self.name) if validate: @@ -1637,9 +1633,9 @@ class Exchange: def fill_leverage_brackets(self): """ - # TODO-lev: Should maybe be renamed, leverage_brackets might not be accurate for kraken Assigns property _leverage_brackets to a dictionary of information about the leverage allowed on each pair + Not used by most exchanges, only used by Binance at time of writing """ return @@ -1649,7 +1645,15 @@ class Exchange: :param pair: The base/quote currency pair being traded :nominal_value: The total value of the trade in quote currency (collateral + debt) """ - return 1.0 + market = self.markets[pair] + if ( + 'limits' in market and + 'leverage' in market['limits'] and + 'max' in market['limits']['leverage'] + ): + return market['limits']['leverage']['max'] + else: + return 1.0 @retrier def _set_leverage( diff --git a/freqtrade/exchange/ftx.py b/freqtrade/exchange/ftx.py index 5072d653e..2acf32ba3 100644 --- a/freqtrade/exchange/ftx.py +++ b/freqtrade/exchange/ftx.py @@ -1,6 +1,6 @@ """ FTX exchange subclass """ import logging -from typing import Any, Dict, List, Optional, Tuple +from typing import Any, Dict, List, Tuple import ccxt @@ -168,18 +168,3 @@ class Ftx(Exchange): if order['type'] == 'stop': return safe_value_fallback2(order, order, 'id_stop', 'id') return order['id'] - - def fill_leverage_brackets(self): - """ - FTX leverage is static across the account, and doesn't change from pair to pair, - so _leverage_brackets doesn't need to be set - """ - return - - def get_max_leverage(self, pair: Optional[str], nominal_value: Optional[float]) -> float: - """ - Returns the maximum leverage that a pair can be traded at, which is always 20 on ftx - :param pair: Here for super method, not used on FTX - :nominal_value: Here for super method, not used on FTX - """ - return 20.0 diff --git a/freqtrade/exchange/kraken.py b/freqtrade/exchange/kraken.py index 710260c76..d2cbcd347 100644 --- a/freqtrade/exchange/kraken.py +++ b/freqtrade/exchange/kraken.py @@ -139,40 +139,6 @@ class Kraken(Exchange): except ccxt.BaseError as e: raise OperationalException(e) from e - def fill_leverage_brackets(self): - """ - Assigns property _leverage_brackets to a dictionary of information about the leverage - allowed on each pair - """ - leverages = {} - - for pair, market in self.markets.items(): - leverages[pair] = [1] - info = market['info'] - leverage_buy = info.get('leverage_buy', []) - leverage_sell = info.get('leverage_sell', []) - if len(leverage_buy) > 0 or len(leverage_sell) > 0: - if leverage_buy != leverage_sell: - logger.warning( - f"The buy({leverage_buy}) and sell({leverage_sell}) leverage are not equal" - "for {pair}. Please notify freqtrade because this has never happened before" - ) - if max(leverage_buy) <= max(leverage_sell): - leverages[pair] += [int(lev) for lev in leverage_buy] - else: - leverages[pair] += [int(lev) for lev in leverage_sell] - else: - leverages[pair] += [int(lev) for lev in leverage_buy] - self._leverage_brackets = leverages - - def get_max_leverage(self, pair: Optional[str], nominal_value: Optional[float]) -> float: - """ - Returns the maximum leverage that a pair can be traded at - :param pair: The base/quote currency pair being traded - :nominal_value: Here for super class, not needed on Kraken - """ - return float(max(self._leverage_brackets[pair])) - def _set_leverage( self, leverage: float, diff --git a/tests/conftest.py b/tests/conftest.py index 1cb4c186e..6d424c246 100644 --- a/tests/conftest.py +++ b/tests/conftest.py @@ -590,10 +590,10 @@ def get_markets(): 'min': 0.0001, 'max': 500000, }, - }, - 'info': { - 'leverage_buy': ['2'], - 'leverage_sell': ['2'], + 'leverage': { + 'min': 1.0, + 'max': 2.0 + } }, }, 'TKN/BTC': { @@ -619,10 +619,10 @@ def get_markets(): 'min': 0.0001, 'max': 500000, }, - }, - 'info': { - 'leverage_buy': ['2', '3', '4', '5'], - 'leverage_sell': ['2', '3', '4', '5'], + 'leverage': { + 'min': 1.0, + 'max': 5.0 + } }, }, 'BLK/BTC': { @@ -647,10 +647,10 @@ def get_markets(): 'min': 0.0001, 'max': 500000, }, - }, - 'info': { - 'leverage_buy': ['2', '3'], - 'leverage_sell': ['2', '3'], + 'leverage': { + 'min': 1.0, + 'max': 3.0 + }, }, }, 'LTC/BTC': { @@ -676,10 +676,7 @@ def get_markets(): 'max': 500000, }, }, - 'info': { - 'leverage_buy': [], - 'leverage_sell': [], - }, + 'info': {}, }, 'XRP/BTC': { 'id': 'xrpbtc', @@ -757,10 +754,7 @@ def get_markets(): 'max': None } }, - 'info': { - 'leverage_buy': [], - 'leverage_sell': [], - }, + 'info': {}, }, 'ETH/USDT': { 'id': 'USDT-ETH', diff --git a/tests/exchange/test_exchange.py b/tests/exchange/test_exchange.py index 430c648d0..de1328f3e 100644 --- a/tests/exchange/test_exchange.py +++ b/tests/exchange/test_exchange.py @@ -3267,3 +3267,16 @@ def test__ccxt_config( default_conf['collateral'] = 'isolated' exchange = get_patched_exchange(mocker, default_conf, id=exchange_name) assert exchange._ccxt_config == ccxt_config + + +@pytest.mark.parametrize('pair,nominal_value,max_lev', [ + ("ETH/BTC", 0.0, 2.0), + ("TKN/BTC", 100.0, 5.0), + ("BLK/BTC", 173.31, 3.0), + ("LTC/BTC", 0.0, 1.0), + ("TKN/USDT", 210.30, 1.0), +]) +def test_get_max_leverage(default_conf, mocker, pair, nominal_value, max_lev): + # Binance has a different method of getting the max leverage + exchange = get_patched_exchange(mocker, default_conf, id="kraken") + assert exchange.get_max_leverage(pair, nominal_value) == max_lev diff --git a/tests/exchange/test_ftx.py b/tests/exchange/test_ftx.py index ca6b24d64..97093bdcb 100644 --- a/tests/exchange/test_ftx.py +++ b/tests/exchange/test_ftx.py @@ -250,20 +250,3 @@ def test_get_order_id(mocker, default_conf): } } assert exchange.get_order_id_conditional(order) == '1111' - - -@pytest.mark.parametrize('pair,nominal_value,max_lev', [ - ("ADA/BTC", 0.0, 20.0), - ("BTC/EUR", 100.0, 20.0), - ("ZEC/USD", 173.31, 20.0), -]) -def test_get_max_leverage_ftx(default_conf, mocker, pair, nominal_value, max_lev): - exchange = get_patched_exchange(mocker, default_conf, id="ftx") - assert exchange.get_max_leverage(pair, nominal_value) == max_lev - - -def test_fill_leverage_brackets_ftx(default_conf, mocker): - # FTX only has one account wide leverage, so there's no leverage brackets - exchange = get_patched_exchange(mocker, default_conf, id="ftx") - exchange.fill_leverage_brackets() - assert exchange._leverage_brackets == {} diff --git a/tests/exchange/test_kraken.py b/tests/exchange/test_kraken.py index 641d2f263..0e7233cb4 100644 --- a/tests/exchange/test_kraken.py +++ b/tests/exchange/test_kraken.py @@ -295,42 +295,3 @@ def test_stoploss_adjust_kraken(mocker, default_conf, sl1, sl2, sl3, side): # Test with invalid order case ... order['type'] = 'stop_loss_limit' assert not exchange.stoploss_adjust(sl3, order, side=side) - - -@pytest.mark.parametrize('pair,nominal_value,max_lev', [ - ("ADA/BTC", 0.0, 3.0), - ("BTC/EUR", 100.0, 5.0), - ("ZEC/USD", 173.31, 2.0), -]) -def test_get_max_leverage_kraken(default_conf, mocker, pair, nominal_value, max_lev): - exchange = get_patched_exchange(mocker, default_conf, id="kraken") - exchange._leverage_brackets = { - 'ADA/BTC': ['2', '3'], - 'BTC/EUR': ['2', '3', '4', '5'], - 'ZEC/USD': ['2'] - } - assert exchange.get_max_leverage(pair, nominal_value) == max_lev - - -def test_fill_leverage_brackets_kraken(default_conf, mocker): - api_mock = MagicMock() - exchange = get_patched_exchange(mocker, default_conf, api_mock, id="kraken") - exchange.fill_leverage_brackets() - - assert exchange._leverage_brackets == { - 'BLK/BTC': [1, 2, 3], - 'TKN/BTC': [1, 2, 3, 4, 5], - 'ETH/BTC': [1, 2], - 'LTC/BTC': [1], - 'XRP/BTC': [1], - 'NEO/BTC': [1], - 'BTT/BTC': [1], - 'ETH/USDT': [1], - 'LTC/USDT': [1], - 'LTC/USD': [1], - 'XLTCUSDT': [1], - 'LTC/ETH': [1], - 'NEO/USDT': [1], - 'TKN/USDT': [1], - 'XRP/USDT': [1] - }