From 55d91f018f9f2785d782ac1c47d48b3c96f097fc Mon Sep 17 00:00:00 2001 From: Sam Germain Date: Tue, 1 Feb 2022 20:24:06 -0600 Subject: [PATCH] exchange._get_stake_amount_limit (merged min_pair_stake_amount and get_max_tradeable amount) --- freqtrade/exchange/exchange.py | 88 +++++++++++++++------------------ tests/exchange/test_exchange.py | 16 +++--- 2 files changed, 48 insertions(+), 56 deletions(-) diff --git a/freqtrade/exchange/exchange.py b/freqtrade/exchange/exchange.py index 557c4c3f3..3adb174b1 100644 --- a/freqtrade/exchange/exchange.py +++ b/freqtrade/exchange/exchange.py @@ -9,7 +9,7 @@ import logging from copy import deepcopy from datetime import datetime, timedelta, timezone from math import ceil -from typing import Any, Dict, List, Optional, Tuple, Union +from typing import Any, Dict, List, Literal, Optional, Tuple, Union import arrow import ccxt @@ -677,33 +677,59 @@ class Exchange: else: return 1 / pow(10, precision) - def get_min_pair_stake_amount(self, pair: str, price: float, stoploss: float, - leverage: Optional[float] = 1.0) -> Optional[float]: + def get_min_pair_stake_amount( + self, + pair: str, + price: float, + stoploss: float, + leverage: Optional[float] = 1.0 + ) -> Optional[float]: + return self._get_stake_amount_limit(pair, price, stoploss, 'min', leverage) + + def get_max_pair_stake_amount( + self, + pair: str, + price: float, + stoploss: float + ) -> Optional[float]: + return self._get_stake_amount_limit(pair, price, stoploss, 'max') + + def _get_stake_amount_limit( + self, + pair: str, + price: float, + stoploss: float, + limit: Literal['min', 'max'], + leverage: Optional[float] = 1.0 + ) -> Optional[float]: + + isMin = limit == 'min' + try: market = self.markets[pair] except KeyError: raise ValueError(f"Can't get market information for symbol {pair}") - min_stake_amounts = [] + stake_limits = [] limits = market['limits'] - if (limits['cost']['min'] is not None): - min_stake_amounts.append( + if (limits['cost'][limit] is not None): + stake_limits.append( self._contracts_to_amount( pair, - limits['cost']['min'] + limits['cost'][limit] ) ) - if (limits['amount']['min'] is not None): - min_stake_amounts.append( + if (limits['amount'][limit] is not None): + stake_limits.append( self._contracts_to_amount( pair, - limits['amount']['min'] * price + limits['amount'][limit] * price ) ) - if not min_stake_amounts: - return None + if not stake_limits: + return None if isMin else float('inf') # reserve some percent defined in config (5% default) + stoploss amount_reserve_percent = 1.0 + self._config.get('amount_reserve_percent', @@ -718,9 +744,9 @@ class Exchange: # for cost (quote, stake currency), so max() is used here. # See also #2575 at github. return self._get_stake_amount_considering_leverage( - max(min_stake_amounts) * amount_reserve_percent, + max(stake_limits) * amount_reserve_percent, leverage or 1.0 - ) + ) if isMin else min(stake_limits) # TODO-lev: Account 4 max_reserve_percent in max limits? def _get_stake_amount_considering_leverage(self, stake_amount: float, leverage: float): """ @@ -2119,40 +2145,6 @@ class Exchange: raise OperationalException( "Freqtrade only supports isolated futures for leverage trading") - def get_max_amount_tradable(self, pair: str, price: float) -> float: - ''' - Gets the maximum amount of a currency that the exchange will let you trade - ''' - try: - market = self.markets[pair] - except KeyError: - raise ValueError(f"Can't get market information for symbol {pair}") - - market = self.markets[pair] - limits = market['limits'] - max_amounts = [] - - if (limits['cost']['max'] is not None): - max_amounts.append( - self._contracts_to_amount( - pair, - limits['cost']['max'] / price - ) - ) - - if (limits['amount']['max'] is not None): - max_amounts.append( - self._contracts_to_amount( - pair, - limits['amount']['max'] - ) - ) - - if not max_amounts: - return float('inf') - else: - return min(max_amounts) - def is_exchange_known_ccxt(exchange_name: str, ccxt_module: CcxtModuleType = None) -> bool: return exchange_name in ccxt_exchanges(ccxt_module) diff --git a/tests/exchange/test_exchange.py b/tests/exchange/test_exchange.py index b81e2a206..83d2fe98e 100644 --- a/tests/exchange/test_exchange.py +++ b/tests/exchange/test_exchange.py @@ -4044,7 +4044,7 @@ def test_liquidation_price( ), 2), expected) -def test_get_max_amount_tradable( +def test_get_max_pair_stake_amount( mocker, default_conf, ): @@ -4154,14 +4154,14 @@ def test_get_max_amount_tradable( } mocker.patch('freqtrade.exchange.Exchange.markets', markets) - assert exchange.get_max_amount_tradable('XRP/USDT:USDT', 2.0) == 10000 - assert exchange.get_max_amount_tradable('LTC/USDT:USDT', 2.0) == float('inf') - assert exchange.get_max_amount_tradable('ETH/USDT:USDT', 2.0) == 100 - assert exchange.get_max_amount_tradable('DOGE/USDT:USDT', 2.0) == 250 - assert exchange.get_max_amount_tradable('LUNA/USDT:USDT', 2.0) == 2.5 + assert exchange.get_max_pair_stake_amount('XRP/USDT:USDT', 2.0, 0.0) == 20000 + assert exchange.get_max_pair_stake_amount('LTC/USDT:USDT', 2.0, 0.0) == float('inf') + assert exchange.get_max_pair_stake_amount('ETH/USDT:USDT', 2.0, 0.0) == 200 + assert exchange.get_max_pair_stake_amount('DOGE/USDT:USDT', 2.0, 0.0) == 500 + assert exchange.get_max_pair_stake_amount('LUNA/USDT:USDT', 2.0, 0.0) == 5.0 default_conf['trading_mode'] = 'spot' exchange = get_patched_exchange(mocker, default_conf, api_mock) mocker.patch('freqtrade.exchange.Exchange.markets', markets) - assert exchange.get_max_amount_tradable('BTC/USDT', 2.0) == 10000 - assert exchange.get_max_amount_tradable('ADA/USDT', 2.0) == 250 + assert exchange.get_max_pair_stake_amount('BTC/USDT', 2.0, 0.0) == 20000 + assert exchange.get_max_pair_stake_amount('ADA/USDT', 2.0, 0.0) == 500