diff --git a/freqtrade/optimize/backtesting.py b/freqtrade/optimize/backtesting.py index c7d25cb7b..6e7d36368 100644 --- a/freqtrade/optimize/backtesting.py +++ b/freqtrade/optimize/backtesting.py @@ -267,7 +267,7 @@ class Backtesting: # Set close_rate to stoploss closerate = trade.stop_loss elif sell.sell_type == (SellType.ROI): - roi = self.strategy.min_roi_reached_entry(trade_dur) + roi_entry, roi = self.strategy.min_roi_reached_entry(trade_dur) if roi is not None: # - (Expected abs profit + open_rate + open_fee) / (fee_close -1) closerate = - (trade.open_rate * roi + trade.open_rate * @@ -275,8 +275,14 @@ class Backtesting: # Use the maximum between closerate and low as we # cannot sell outside of a candle. - # Applies when using {"xx": -1} as roi to force sells after xx minutes + # Applies when a new ROI setting comes in place and the whole candle is above that. closerate = max(closerate, sell_row.low) + if roi == -1 and roi_entry % self.timeframe_mins == 0: + # When forceselling with ROI=-1, the roi-entry will always be "on the entry". + # If that entry is a multiple of the timeframe (so on open) + # - we'll use open instead of close + closerate = max(closerate, sell_row.open) + else: # This should not be reached... closerate = sell_row.open diff --git a/freqtrade/strategy/interface.py b/freqtrade/strategy/interface.py index e208138e7..2b3a6194f 100644 --- a/freqtrade/strategy/interface.py +++ b/freqtrade/strategy/interface.py @@ -394,7 +394,7 @@ class IStrategy(ABC): return SellCheckTuple(sell_flag=False, sell_type=SellType.NONE) - def min_roi_reached_entry(self, trade_dur: int) -> Optional[float]: + def min_roi_reached_entry(self, trade_dur: int) -> Tuple[Optional[int], Optional[float]]: """ Based on trade duration defines the ROI entry that may have been reached. :param trade_dur: trade duration in minutes @@ -403,9 +403,9 @@ class IStrategy(ABC): # Get highest entry in ROI dict where key <= trade-duration roi_list = list(filter(lambda x: x <= trade_dur, self.minimal_roi.keys())) if not roi_list: - return None + return None, None roi_entry = max(roi_list) - return self.minimal_roi[roi_entry] + return roi_entry, self.minimal_roi[roi_entry] def min_roi_reached(self, trade: Trade, current_profit: float, current_time: datetime) -> bool: """ @@ -415,7 +415,7 @@ class IStrategy(ABC): """ # Check if time matches and current rate is above threshold trade_dur = int((current_time.timestamp() - trade.open_date.timestamp()) // 60) - roi = self.min_roi_reached_entry(trade_dur) + _, roi = self.min_roi_reached_entry(trade_dur) if roi is None: return False else: