diff --git a/freqtrade/optimize/backtesting.py b/freqtrade/optimize/backtesting.py index e96a91856..80dc9a443 100644 --- a/freqtrade/optimize/backtesting.py +++ b/freqtrade/optimize/backtesting.py @@ -221,7 +221,7 @@ class Backtesting(object): elif sell.sell_type == (SellType.ROI): # get next entry in min_roi > to trade duration # Interface.py skips on trade_duration <= duration - roi_entry = max(list(filter(lambda x: trade_dur > x, + roi_entry = max(list(filter(lambda x: trade_dur >= x, self.strategy.minimal_roi.keys()))) roi = self.strategy.minimal_roi[roi_entry] diff --git a/freqtrade/strategy/interface.py b/freqtrade/strategy/interface.py index f3ff9ba7a..6dbdf72e8 100644 --- a/freqtrade/strategy/interface.py +++ b/freqtrade/strategy/interface.py @@ -320,17 +320,18 @@ class IStrategy(ABC): def min_roi_reached(self, trade: Trade, current_profit: float, current_time: datetime) -> bool: """ Based an earlier trade and current price and ROI configuration, decides whether bot should - sell + sell. Requires current_profit to be in percent!! :return True if bot should sell at current rate """ # Check if time matches and current rate is above threshold - time_diff = (current_time.timestamp() - trade.open_date.timestamp()) / 60 - for duration, threshold in self.minimal_roi.items(): - if time_diff <= duration: - continue - if current_profit > threshold: - return True + trade_dur = (current_time.timestamp() - trade.open_date.timestamp()) / 60 + + # Get highest entry in ROI dict where key >= trade-duration + roi_entry = max(list(filter(lambda x: trade_dur >= x, self.minimal_roi.keys()))) + threshold = self.minimal_roi[roi_entry] + if current_profit > threshold: + return True return False diff --git a/freqtrade/tests/optimize/test_backtesting.py b/freqtrade/tests/optimize/test_backtesting.py index 6e0ab24a4..5ab44baad 100644 --- a/freqtrade/tests/optimize/test_backtesting.py +++ b/freqtrade/tests/optimize/test_backtesting.py @@ -530,10 +530,10 @@ def test_backtest(default_conf, fee, mocker) -> None: 'open_time': [Arrow(2018, 1, 29, 18, 40, 0).datetime, Arrow(2018, 1, 30, 3, 30, 0).datetime], 'close_time': [Arrow(2018, 1, 29, 22, 35, 0).datetime, - Arrow(2018, 1, 30, 4, 15, 0).datetime], + Arrow(2018, 1, 30, 4, 10, 0).datetime], 'open_index': [78, 184], - 'close_index': [125, 193], - 'trade_duration': [235, 45], + 'close_index': [125, 192], + 'trade_duration': [235, 40], 'open_at_end': [False, False], 'open_rate': [0.104445, 0.10302485], 'close_rate': [0.104969, 0.103541], diff --git a/freqtrade/tests/strategy/test_interface.py b/freqtrade/tests/strategy/test_interface.py index 08634073d..f2e8c577f 100644 --- a/freqtrade/tests/strategy/test_interface.py +++ b/freqtrade/tests/strategy/test_interface.py @@ -118,6 +118,7 @@ def test_tickerdata_to_dataframe(default_conf) -> None: def test_min_roi_reached(default_conf, fee) -> None: + # Use list to confirm sequence does not matter min_roi_list = [{20: 0.05, 55: 0.01, 0: 0.1}, {0: 0.1, 20: 0.05, 55: 0.01}] for roi in min_roi_list: @@ -143,6 +144,47 @@ def test_min_roi_reached(default_conf, fee) -> None: assert strategy.min_roi_reached(trade, 0.02, arrow.utcnow().shift(minutes=-1).datetime) +def test_min_roi_reached2(default_conf, fee) -> None: + + # test with ROI raising after last interval + min_roi_list = [{20: 0.07, + 30: 0.05, + 55: 0.30, + 0: 0.1 + }, + {0: 0.1, + 20: 0.07, + 30: 0.05, + 55: 0.30 + }, + ] + for roi in min_roi_list: + strategy = DefaultStrategy(default_conf) + strategy.minimal_roi = roi + trade = Trade( + pair='ETH/BTC', + stake_amount=0.001, + open_date=arrow.utcnow().shift(hours=-1).datetime, + fee_open=fee.return_value, + fee_close=fee.return_value, + exchange='bittrex', + open_rate=1, + ) + + assert not strategy.min_roi_reached(trade, 0.02, arrow.utcnow().shift(minutes=-56).datetime) + assert strategy.min_roi_reached(trade, 0.12, arrow.utcnow().shift(minutes=-56).datetime) + + assert not strategy.min_roi_reached(trade, 0.04, arrow.utcnow().shift(minutes=-39).datetime) + assert strategy.min_roi_reached(trade, 0.071, arrow.utcnow().shift(minutes=-39).datetime) + + assert not strategy.min_roi_reached(trade, 0.04, arrow.utcnow().shift(minutes=-26).datetime) + assert strategy.min_roi_reached(trade, 0.06, arrow.utcnow().shift(minutes=-26).datetime) + + # Should not trigger with 20% profit since after 55 minutes only 30% is active. + assert not strategy.min_roi_reached(trade, 0.20, arrow.utcnow().shift(minutes=-2).datetime) + assert strategy.min_roi_reached(trade, 0.31, arrow.utcnow().shift(minutes=-2).datetime) + + def test_analyze_ticker_default(ticker_history, mocker, caplog) -> None: caplog.set_level(logging.DEBUG) ind_mock = MagicMock(side_effect=lambda x, meta: x)