From db5e038b0fd8d8bbc44a7a26111e91f49dd01b3f Mon Sep 17 00:00:00 2001 From: werkkrew Date: Sat, 13 Mar 2021 12:15:29 -0500 Subject: [PATCH] moved dynamic ROI to its own function in the strategy interface --- freqtrade/strategy/interface.py | 95 ++++++++++++++++++--------------- 1 file changed, 51 insertions(+), 44 deletions(-) diff --git a/freqtrade/strategy/interface.py b/freqtrade/strategy/interface.py index 627000195..ad7ebd6b8 100644 --- a/freqtrade/strategy/interface.py +++ b/freqtrade/strategy/interface.py @@ -628,55 +628,59 @@ class IStrategy(ABC): return SellCheckTuple(sell_flag=False, sell_type=SellType.NONE) + def min_roi_reached_dynamic(self, trade_dur: int) -> Tuple[Optional[int], Optional[float]]: + """ + Based on trade duration returns an ROI value based on various functions. + :param trade_dur: trade duration in minutes + :return: minimal ROI value or None if no proper ROI type was found. + """ + dynamic_roi = self.dynamic_roi + minimal_roi = self.minimal_roi + start, end = dynamic_roi['dynamic_roi_start'], dynamic_roi['dynamic_roi_end'] + + # linear decay: f(t) = start - (rate * t) + if dynamic_roi['dynamic_roi_type'] == 'linear': + rate = (start - end) / dynamic_roi['dynamic_roi_time'] + min_roi = max(end, start - (rate * trade_dur)) + # exponential decay: f(t) = start * e^(-rate*t) + elif dynamic_roi['dynamic_roi_type'] == 'exponential': + min_roi = max(end, start * np.exp(-dynamic_roi['dynamic_roi_rate']*trade_dur)) + elif dynamic_roi['dynamic_roi_type'] == 'connect': + # connect the points in the defined table with lines + past_roi = list(filter(lambda x: x <= trade_dur, minimal_roi.keys())) + next_roi = list(filter(lambda x: x > trade_dur, minimal_roi.keys())) + if not past_roi: + return None, None + current_entry = max(past_roi) + # if we are past the final point in the table, use that key/vaule pair + if not next_roi: + return current_entry, minimal_roi[current_entry] + # use the slope-intercept formula between the two points in the roi table we are between + else: + next_entry = min(next_roi) + # y = mx + b + x1, y1 = current_entry, minimal_roi[current_entry] + x2, y2 = next_entry, minimal_roi[next_entry] + m = (y1-y2)/(x1-x2) + b = (x1*y2 - x2*y1)/(x1-x2) + min_roi = (m * trade_dur) + b + else: + return None, None + + return trade_dur, min_roi + 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 :return: minimal ROI entry value or None if none proper ROI entry was found. """ - dynamic_roi = self.dynamic_roi - minimal_roi = self.minimal_roi - start, end = dynamic_roi['dynamic_roi_start'], dynamic_roi['dynamic_roi_end'] - - # if the dynamic_roi dict is defined and enabled, use it, otherwise fallback to default functionality - if dynamic_roi and dynamic_roi['dynamic_roi_enabled']: - # linear decay: f(t) = start - (rate * t) - if dynamic_roi['dynamic_roi_type'] == 'linear': - rate = (start - end) / dynamic_roi['dynamic_roi_time'] - min_roi = max(end, start - (rate * trade_dur)) - # exponential decay: f(t) = start * e^(-rate*t) - elif dynamic_roi['dynamic_roi_type'] == 'exponential': - min_roi = max(end, start * np.exp(-dynamic_roi['dynamic_roi_rate']*trade_dur)) - elif dynamic_roi['dynamic_roi_type'] == 'connect': - # connect the points in the defined table with lines - past_roi = list(filter(lambda x: x <= trade_dur, minimal_roi.keys())) - next_roi = list(filter(lambda x: x > trade_dur, minimal_roi.keys())) - if not past_roi: - return None, None - current_entry = max(past_roi) - if not next_roi: - return current_entry, minimal_roi[current_entry] - # use the slope-intercept formula between the two points in the roi table we are between - else: - next_entry = min(next_roi) - # y = mx + b - x1, y1 = current_entry, minimal_roi[current_entry] - x2, y2 = next_entry, minimal_roi[next_entry] - m = (y1-y2)/(x1-x2) - b = (x1*y2 - x2*y1)/(x1-x2) - min_roi = (m * trade_dur) + b - else: - min_roi = 0 - - return trade_dur, min_roi - # Default ROI table functionality - else: - # 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, None - roi_entry = max(roi_list) - return roi_entry, self.minimal_roi[roi_entry] + # 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, None + roi_entry = max(roi_list) + return roi_entry, self.minimal_roi[roi_entry] def min_roi_reached(self, trade: Trade, current_profit: float, current_time: datetime) -> bool: """ @@ -687,7 +691,10 @@ class IStrategy(ABC): """ # Check if time matches and current rate is above threshold trade_dur = int((current_time.timestamp() - trade.open_date_utc.timestamp()) // 60) - _, roi = self.min_roi_reached_entry(trade_dur) + if self.dynamic_roi and self.dynamic_roi['dynamic_roi_enabled']: + _, roi = self.min_roi_reached_dynamic(trade_dur) + else: + _, roi = self.min_roi_reached_entry(trade_dur) if roi is None: return False else: