From 6b6b35ac1c0f0250a002dc0c9ec98da43e586877 Mon Sep 17 00:00:00 2001 From: Sam Germain Date: Tue, 1 Feb 2022 20:39:22 -0600 Subject: [PATCH] check for max stake limit in freqtradebot and backtesting --- freqtrade/freqtradebot.py | 22 ++++++++++++++----- freqtrade/optimize/backtesting.py | 18 +++++++++++---- tests/conftest.py | 12 +++++----- tests/optimize/test_backtest_detail.py | 1 + tests/optimize/test_backtesting.py | 8 +++++++ .../test_backtesting_adjust_position.py | 1 + 6 files changed, 47 insertions(+), 15 deletions(-) diff --git a/freqtrade/freqtradebot.py b/freqtrade/freqtradebot.py index 6f39c23f7..44665ff61 100644 --- a/freqtrade/freqtradebot.py +++ b/freqtrade/freqtradebot.py @@ -543,12 +543,19 @@ class FreqtradeBot(LoggingMixin): min_stake_amount = self.exchange.get_min_pair_stake_amount(trade.pair, current_rate, self.strategy.stoploss) - max_stake_amount = self.wallets.get_available_stake_amount() + max_stake_amount = self.exchange.get_max_pair_stake_amount(trade.pair, + current_rate, + self.strategy.stoploss) + if max_stake_amount is None: + # * Should never be executed + raise OperationalException(f'max_stake_amount is None for {trade}') + stake_available = self.wallets.get_available_stake_amount() logger.debug(f"Calling adjust_trade_position for pair {trade.pair}") stake_amount = strategy_safe_wrapper(self.strategy.adjust_trade_position, default_retval=None)( trade=trade, current_time=datetime.now(timezone.utc), current_rate=current_rate, - current_profit=current_profit, min_stake=min_stake_amount, max_stake=max_stake_amount) + current_profit=current_profit, min_stake=min_stake_amount, + max_stake=min(max_stake_amount, stake_available)) if stake_amount is not None and stake_amount > 0.0: # We should increase our position @@ -845,15 +852,20 @@ class FreqtradeBot(LoggingMixin): # We do however also need min-stake to determine leverage, therefore this is ignored as # edge-case for now. min_stake_amount = self.exchange.get_min_pair_stake_amount( - pair, enter_limit_requested, self.strategy.stoploss,) + pair, enter_limit_requested, self.strategy.stoploss) + max_stake_amount = self.exchange.get_max_pair_stake_amount( + pair, enter_limit_requested, self.strategy.stoploss) if not self.edge and trade is None: - max_stake_amount = self.wallets.get_available_stake_amount() + stake_available = self.wallets.get_available_stake_amount() + if max_stake_amount is None: + # * Should never be executed + raise OperationalException(f'max_stake_amount is None for {trade}') stake_amount = strategy_safe_wrapper(self.strategy.custom_stake_amount, default_retval=stake_amount)( pair=pair, current_time=datetime.now(timezone.utc), current_rate=enter_limit_requested, proposed_stake=stake_amount, - min_stake=min_stake_amount, max_stake=max_stake_amount, + min_stake=min_stake_amount, max_stake=min(max_stake_amount, stake_available), entry_tag=entry_tag, side=trade_side ) diff --git a/freqtrade/optimize/backtesting.py b/freqtrade/optimize/backtesting.py index 6fac22cdf..e973b1fbe 100644 --- a/freqtrade/optimize/backtesting.py +++ b/freqtrade/optimize/backtesting.py @@ -413,11 +413,16 @@ class Backtesting: current_profit = trade.calc_profit_ratio(row[OPEN_IDX]) min_stake = self.exchange.get_min_pair_stake_amount(trade.pair, row[OPEN_IDX], -0.1) - max_stake = self.wallets.get_available_stake_amount() + max_stake = self.exchange.get_max_pair_stake_amount(trade.pair, row[OPEN_IDX], -0.1) + if max_stake is None: + # * Should never be executed + raise OperationalException(f'max_stake_amount is None for {trade}') + stake_available = self.wallets.get_available_stake_amount() stake_amount = strategy_safe_wrapper(self.strategy.adjust_trade_position, default_retval=None)( trade=trade, current_time=row[DATE_IDX].to_pydatetime(), current_rate=row[OPEN_IDX], - current_profit=current_profit, min_stake=min_stake, max_stake=max_stake) + current_profit=current_profit, min_stake=min_stake, + max_stake=min(max_stake, stake_available)) # Check if we should increase our position if stake_amount is not None and stake_amount > 0.0: @@ -551,7 +556,11 @@ class Backtesting: propose_rate = min(max(propose_rate, row[LOW_IDX]), row[HIGH_IDX]) min_stake_amount = self.exchange.get_min_pair_stake_amount(pair, propose_rate, -0.05) or 0 - max_stake_amount = self.wallets.get_available_stake_amount() + max_stake_amount = self.exchange.get_max_pair_stake_amount(pair, propose_rate, -0.05) or 0 + if max_stake_amount is None: + # * Should never be executed + raise OperationalException(f'max_stake_amount is None for {trade}') + stake_available = self.wallets.get_available_stake_amount() pos_adjust = trade is not None if not pos_adjust: @@ -563,7 +572,8 @@ class Backtesting: stake_amount = strategy_safe_wrapper(self.strategy.custom_stake_amount, default_retval=stake_amount)( pair=pair, current_time=current_time, current_rate=propose_rate, - proposed_stake=stake_amount, min_stake=min_stake_amount, max_stake=max_stake_amount, + proposed_stake=stake_amount, min_stake=min_stake_amount, + max_stake=min(stake_available, max_stake_amount), entry_tag=entry_tag, side=direction) stake_amount = self.wallets.validate_stake_amount(pair, stake_amount, min_stake_amount) diff --git a/tests/conftest.py b/tests/conftest.py index 15705e987..9e85260b2 100644 --- a/tests/conftest.py +++ b/tests/conftest.py @@ -587,7 +587,7 @@ def get_markets(): 'limits': { 'amount': { 'min': 0.01, - 'max': 1000, + 'max': 100000000, }, 'price': { 'min': None, @@ -622,7 +622,7 @@ def get_markets(): 'limits': { 'amount': { 'min': 0.01, - 'max': 1000, + 'max': 100000000, }, 'price': { 'min': None, @@ -690,7 +690,7 @@ def get_markets(): 'limits': { 'amount': { 'min': 0.01, - 'max': 1000, + 'max': 100000000, }, 'price': { 'min': None, @@ -725,7 +725,7 @@ def get_markets(): 'limits': { 'amount': { 'min': 0.01, - 'max': 1000, + 'max': 100000000, }, 'price': { 'min': None, @@ -760,7 +760,7 @@ def get_markets(): 'limits': { 'amount': { 'min': 0.01, - 'max': 1000, + 'max': 100000000, }, 'price': { 'min': None, @@ -988,7 +988,7 @@ def get_markets(): 'limits': { 'amount': { 'min': 0.01, - 'max': 1000, + 'max': 100000000000, }, 'price': { 'min': None, diff --git a/tests/optimize/test_backtest_detail.py b/tests/optimize/test_backtest_detail.py index 86a67a25e..b89fbe8aa 100644 --- a/tests/optimize/test_backtest_detail.py +++ b/tests/optimize/test_backtest_detail.py @@ -609,6 +609,7 @@ def test_backtest_results(default_conf, fee, mocker, caplog, data) -> None: mocker.patch("freqtrade.exchange.Exchange.get_fee", return_value=0.0) mocker.patch("freqtrade.exchange.Exchange.get_min_pair_stake_amount", return_value=0.00001) + mocker.patch("freqtrade.exchange.Exchange.get_max_pair_stake_amount", return_value=float('inf')) mocker.patch("freqtrade.exchange.Binance.get_max_leverage", return_value=100) patch_exchange(mocker) frame = _build_backtest_dataframe(data.data) diff --git a/tests/optimize/test_backtesting.py b/tests/optimize/test_backtesting.py index ce58cf72a..e48722626 100644 --- a/tests/optimize/test_backtesting.py +++ b/tests/optimize/test_backtesting.py @@ -500,6 +500,7 @@ def test_backtest__enter_trade(default_conf, fee, mocker) -> None: default_conf['use_sell_signal'] = False mocker.patch('freqtrade.exchange.Exchange.get_fee', fee) mocker.patch("freqtrade.exchange.Exchange.get_min_pair_stake_amount", return_value=0.00001) + mocker.patch("freqtrade.exchange.Exchange.get_max_pair_stake_amount", return_value=float('inf')) patch_exchange(mocker) default_conf['stake_amount'] = 'unlimited' default_conf['max_open_trades'] = 2 @@ -566,6 +567,7 @@ def test_backtest__get_sell_trade_entry(default_conf, fee, mocker) -> None: default_conf['use_sell_signal'] = False mocker.patch('freqtrade.exchange.Exchange.get_fee', fee) mocker.patch("freqtrade.exchange.Exchange.get_min_pair_stake_amount", return_value=0.00001) + mocker.patch("freqtrade.exchange.Exchange.get_max_pair_stake_amount", return_value=float('inf')) patch_exchange(mocker) default_conf['timeframe_detail'] = '1m' default_conf['max_open_trades'] = 2 @@ -659,6 +661,7 @@ def test_backtest_one(default_conf, fee, mocker, testdatadir) -> None: default_conf['use_sell_signal'] = False mocker.patch('freqtrade.exchange.Exchange.get_fee', fee) mocker.patch("freqtrade.exchange.Exchange.get_min_pair_stake_amount", return_value=0.00001) + mocker.patch("freqtrade.exchange.Exchange.get_max_pair_stake_amount", return_value=float('inf')) patch_exchange(mocker) backtesting = Backtesting(default_conf) backtesting._set_strategy(backtesting.strategylist[0]) @@ -724,6 +727,7 @@ def test_backtest_1min_timeframe(default_conf, fee, mocker, testdatadir) -> None default_conf['use_sell_signal'] = False mocker.patch('freqtrade.exchange.Exchange.get_fee', fee) mocker.patch("freqtrade.exchange.Exchange.get_min_pair_stake_amount", return_value=0.00001) + mocker.patch("freqtrade.exchange.Exchange.get_max_pair_stake_amount", return_value=float('inf')) patch_exchange(mocker) backtesting = Backtesting(default_conf) backtesting._set_strategy(backtesting.strategylist[0]) @@ -772,6 +776,7 @@ def test_backtest_pricecontours_protections(default_conf, fee, mocker, testdatad default_conf['enable_protections'] = True mocker.patch('freqtrade.exchange.Exchange.get_fee', fee) mocker.patch("freqtrade.exchange.Exchange.get_min_pair_stake_amount", return_value=0.00001) + mocker.patch("freqtrade.exchange.Exchange.get_max_pair_stake_amount", return_value=float('inf')) tests = [ ['sine', 9], ['raise', 10], @@ -806,6 +811,7 @@ def test_backtest_pricecontours(default_conf, fee, mocker, testdatadir, default_conf['enable_protections'] = True mocker.patch("freqtrade.exchange.Exchange.get_min_pair_stake_amount", return_value=0.00001) + mocker.patch("freqtrade.exchange.Exchange.get_max_pair_stake_amount", return_value=float('inf')) mocker.patch('freqtrade.exchange.Exchange.get_fee', fee) # While buy-signals are unrealistic, running backtesting # over and over again should not cause different results @@ -846,6 +852,7 @@ def test_backtest_only_sell(mocker, default_conf, testdatadir): def test_backtest_alternate_buy_sell(default_conf, fee, mocker, testdatadir): mocker.patch("freqtrade.exchange.Exchange.get_min_pair_stake_amount", return_value=0.00001) + mocker.patch("freqtrade.exchange.Exchange.get_max_pair_stake_amount", return_value=float('inf')) mocker.patch('freqtrade.exchange.Exchange.get_fee', fee) backtest_conf = _make_backtest_conf(mocker, conf=default_conf, pair='UNITTEST/BTC', datadir=testdatadir) @@ -892,6 +899,7 @@ def test_backtest_multi_pair(default_conf, fee, mocker, tres, pair, testdatadir) return dataframe mocker.patch("freqtrade.exchange.Exchange.get_min_pair_stake_amount", return_value=0.00001) + mocker.patch("freqtrade.exchange.Exchange.get_max_pair_stake_amount", return_value=float('inf')) mocker.patch('freqtrade.exchange.Exchange.get_fee', fee) patch_exchange(mocker) diff --git a/tests/optimize/test_backtesting_adjust_position.py b/tests/optimize/test_backtesting_adjust_position.py index 91b55cdc0..f8586ffb6 100644 --- a/tests/optimize/test_backtesting_adjust_position.py +++ b/tests/optimize/test_backtesting_adjust_position.py @@ -17,6 +17,7 @@ def test_backtest_position_adjustment(default_conf, fee, mocker, testdatadir) -> default_conf['use_sell_signal'] = False mocker.patch('freqtrade.exchange.Exchange.get_fee', fee) mocker.patch("freqtrade.exchange.Exchange.get_min_pair_stake_amount", return_value=0.00001) + mocker.patch("freqtrade.exchange.Exchange.get_max_pair_stake_amount", return_value=float('inf')) patch_exchange(mocker) default_conf.update({ "stake_amount": 100.0,