diff --git a/freqtrade/exchange/exchange.py b/freqtrade/exchange/exchange.py index 8c584e177..b677e7bd1 100644 --- a/freqtrade/exchange/exchange.py +++ b/freqtrade/exchange/exchange.py @@ -2247,11 +2247,16 @@ class Exchange: raise OperationalException( "Freqtrade only supports isolated futures for leverage trading") + @retrier def get_leverage_tiers_for_pair(self, pair: str): # When exchanges can load all their leverage tiers at once in the constructor # then this method does nothing, it should only be implemented when the leverage # tiers requires per symbol fetching to avoid excess api calls - if not self._ft_has['can_fetch_multiple_tiers']: + if ( + self._api.has['fetchLeverageTiers'] and + not self._ft_has['can_fetch_multiple_tiers'] and + self.trading_mode == TradingMode.FUTURES + ): try: return self._api.fetch_leverage_tiers(pair) except ccxt.BadRequest: diff --git a/tests/exchange/test_binance.py b/tests/exchange/test_binance.py index 98e19616d..d4c98fd63 100644 --- a/tests/exchange/test_binance.py +++ b/tests/exchange/test_binance.py @@ -628,12 +628,3 @@ def test_get_maintenance_ratio_and_amt_binance( exchange._leverage_tiers = leverage_tiers (result_ratio, result_amt) = exchange.get_maintenance_ratio_and_amt(pair, nominal_value) assert (round(result_ratio, 8), round(result_amt, 8)) == (mm_ratio, amt) - - -def test_load_leverage_tiers_binance(mocker, default_conf, leverage_tiers): - # TODO-lev - api_mock = MagicMock() - default_conf['trading_mode'] = 'futures' - default_conf['margin_mode'] = 'isolated' - exchange = get_patched_exchange(mocker, default_conf, api_mock) - assert exchange diff --git a/tests/exchange/test_exchange.py b/tests/exchange/test_exchange.py index ab792e675..2fb1c85ad 100644 --- a/tests/exchange/test_exchange.py +++ b/tests/exchange/test_exchange.py @@ -3560,8 +3560,7 @@ def test__ccxt_config( ("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): - # TODO-lev: Branch coverage +def test_get_max_leverage_from_markets(default_conf, mocker, pair, nominal_value, max_lev): default_conf['trading_mode'] = 'futures' default_conf['margin_mode'] = 'isolated' api_mock = MagicMock() @@ -3570,6 +3569,11 @@ def test_get_max_leverage(default_conf, mocker, pair, nominal_value, max_lev): assert exchange.get_max_leverage(pair, nominal_value) == max_lev +def test_get_max_leverage_from_tiers(default_conf, mocker): + # TODO-lev: + return + + @pytest.mark.parametrize( 'size,funding_rate,mark_price,time_in_ratio,funding_fee,kraken_fee', [ (10, 0.0001, 2.0, 1.0, 0.002, 0.002), @@ -4227,45 +4231,123 @@ def test_get_max_pair_stake_amount( def test_load_leverage_tiers(mocker, default_conf, leverage_tiers): - # TODO-lev api_mock = MagicMock() - default_conf['trading_mode'] = 'futures' - default_conf['margin_mode'] = 'isolated' - exchange = get_patched_exchange(mocker, default_conf, api_mock) - - api_mock = MagicMock() - api_mock.set_margin_mode = MagicMock() + api_mock.fetch_leverage_tiers = MagicMock() type(api_mock).has = PropertyMock(return_value={'fetchLeverageTiers': True}) default_conf['dry_run'] = False + default_conf['trading_mode'] = 'futures' + default_conf['margin_mode'] = 'isolated' ccxt_exceptionhandlers( mocker, default_conf, api_mock, "binance", - "set_margin_mode", - "set_margin_mode", - pair="XRP/USDT", - margin_mode=margin_mode + "load_leverage_tiers", + "fetch_leverage_tiers", ) -def test_parse_leverage_tier(mocker, default_conf, leverage_tiers): - # TODO-lev - api_mock = MagicMock() - default_conf['trading_mode'] = 'futures' - default_conf['margin_mode'] = 'isolated' - exchange = get_patched_exchange(mocker, default_conf, api_mock) - assert exchange +def test_parse_leverage_tier(mocker, default_conf): + exchange = get_patched_exchange(mocker, default_conf) + + tier = { + "tier": 1, + "notionalFloor": 0, + "notionalCap": 100000, + "maintenanceMarginRatio": 0.025, + "maxLeverage": 20, + "info": { + "bracket": "1", + "initialLeverage": "20", + "notionalCap": "100000", + "notionalFloor": "0", + "maintMarginRatio": "0.025", + "cum": "0.0" + } + } + + assert exchange.parse_leverage_tier(tier) == { + "min": 0, + "max": 100000, + "mmr": 0.025, + "lev": 20, + "maintAmt": 0.0, + } + + tier2 = { + 'tier': 1, + 'notionalFloor': 0, + 'notionalCap': 2000, + 'maintenanceMarginRatio': 0.01, + 'maxLeverage': 75, + 'info': { + 'baseMaxLoan': '', + 'imr': '0.013', + 'instId': '', + 'maxLever': '75', + 'maxSz': '2000', + 'minSz': '0', + 'mmr': '0.01', + 'optMgnFactor': '0', + 'quoteMaxLoan': '', + 'tier': '1', + 'uly': 'SHIB-USDT' + } + } + + assert exchange.parse_leverage_tier(tier2) == { + 'min': 0, + 'max': 2000, + 'mmr': 0.01, + 'lev': 75, + "maintAmt": None, + } def test_get_leverage_tiers_for_pair(mocker, default_conf, leverage_tiers): - # TODO-lev + api_mock = MagicMock() + api_mock.fetch_leverage_tiers = MagicMock() + # Spot + type(api_mock)._ft_has = PropertyMock(return_value={'fetchLeverageTiers': True}) + exchange = get_patched_exchange(mocker, default_conf, api_mock) + exchange._ft_has['can_fetch_multiple_tiers'] = False + assert exchange.get_leverage_tiers_for_pair('ADA/USDT') is None + + # 'can_fetch_multiple_tiers': True default_conf['trading_mode'] = 'futures' default_conf['margin_mode'] = 'isolated' + type(api_mock).has = PropertyMock(return_value={'fetchLeverageTiers': True}) exchange = get_patched_exchange(mocker, default_conf, api_mock) - assert exchange + exchange._ft_has['can_fetch_multiple_tiers'] = True + assert exchange.get_leverage_tiers_for_pair('ADA/USDT:USDT') is None + + # 'fetchLeverageTiers': False + type(api_mock).has = PropertyMock(return_value={'fetchLeverageTiers': False}) + exchange = get_patched_exchange(mocker, default_conf, api_mock) + exchange._ft_has['can_fetch_multiple_tiers'] = False + assert exchange.get_leverage_tiers_for_pair('ADA/USDT:USDT') is None + + # 'fetchLeverageTiers': False + type(api_mock).has = PropertyMock(return_value={'fetchLeverageTiers': True}) + exchange = get_patched_exchange(mocker, default_conf, api_mock) + exchange._ft_has['can_fetch_multiple_tiers'] = False + assert exchange.get_leverage_tiers_for_pair('ADA/USDT:USDT') is not None + + type(api_mock).has = PropertyMock(return_value={'fetchLeverageTiers': True}) + default_conf['dry_run'] = False + exchange = get_patched_exchange(mocker, default_conf, api_mock) + + ccxt_exceptionhandlers( + mocker, + default_conf, + api_mock, + "binance", + "get_leverage_tiers_for_pair", + "fetch_leverage_tiers", + pair='ETH/USDT:USDT', + ) def test_get_maintenance_ratio_and_amt(mocker, default_conf, leverage_tiers):