From 0310d1cdf289a94fab11ffe9c65bf2c146c346ce Mon Sep 17 00:00:00 2001 From: Sam Germain Date: Sun, 3 Apr 2022 02:41:29 -0600 Subject: [PATCH] enter_tag can be used to determine roi --- freqtrade/constants.py | 2 +- freqtrade/freqtradebot.py | 25 ++++++++++++++--- freqtrade/resolvers/strategy_resolver.py | 10 +++++-- freqtrade/rpc/api_server/api_schemas.py | 2 +- freqtrade/strategy/interface.py | 35 ++++++++++++++++++------ 5 files changed, 57 insertions(+), 17 deletions(-) diff --git a/freqtrade/constants.py b/freqtrade/constants.py index 1a21ec77f..9d0cffbd0 100644 --- a/freqtrade/constants.py +++ b/freqtrade/constants.py @@ -138,7 +138,7 @@ CONF_SCHEMA = { 'minimal_roi': { 'type': 'object', 'patternProperties': { - '^[0-9.]+$': {'type': 'number'} + '^[0-9a-zA-Z.]+$': {'type': ['number', 'dict']} }, 'minProperties': 1 }, diff --git a/freqtrade/freqtradebot.py b/freqtrade/freqtradebot.py index 7c20a7f60..5fb4d92be 100644 --- a/freqtrade/freqtradebot.py +++ b/freqtrade/freqtradebot.py @@ -932,7 +932,16 @@ class FreqtradeBot(LoggingMixin): logger.debug('checking exit') exit_rate = self.exchange.get_rate( trade.pair, side='exit', is_short=trade.is_short, refresh=True) - if self._check_and_execute_exit(trade, exit_rate, enter, exit_, exit_tag): + + enter_tag = # TODO + if self._check_and_execute_exit( + trade=trade, + exit_rate=exit_rate, + enter=enter, + exit_=exit_, + enter_tag=enter_tag, + exit_tag=exit_tag, + ): return True logger.debug(f'Found no {exit_signal_type} signal for %s.', trade) @@ -1089,8 +1098,15 @@ class FreqtradeBot(LoggingMixin): logger.warning(f"Could not create trailing stoploss order " f"for pair {trade.pair}.") - def _check_and_execute_exit(self, trade: Trade, exit_rate: float, - enter: bool, exit_: bool, exit_tag: Optional[str]) -> bool: + def _check_and_execute_exit( + self, + trade: Trade, + exit_rate: float, + enter: bool, + exit_: bool, + enter_tag: Optional[str], + exit_tag: Optional[str], + ) -> bool: """ Check and execute trade exit """ @@ -1100,7 +1116,8 @@ class FreqtradeBot(LoggingMixin): datetime.now(timezone.utc), enter=enter, exit_=exit_, - force_stoploss=self.edge.stoploss(trade.pair) if self.edge else 0 + force_stoploss=self.edge.stoploss(trade.pair) if self.edge else 0, + enter_tag=enter_tag, ) if should_exit.exit_flag: diff --git a/freqtrade/resolvers/strategy_resolver.py b/freqtrade/resolvers/strategy_resolver.py index 44d590b67..b6f082def 100644 --- a/freqtrade/resolvers/strategy_resolver.py +++ b/freqtrade/resolvers/strategy_resolver.py @@ -143,8 +143,14 @@ class StrategyResolver(IResolver): # Sort and apply type conversions if hasattr(strategy, 'minimal_roi'): strategy.minimal_roi = dict(sorted( - {int(key): value for (key, value) in strategy.minimal_roi.items()}.items(), - key=lambda t: t[0])) + { + {int(k): v for (k, v) in value.items()} + if type(value is dict) + else int(key): value + for (key, value) in strategy.minimal_roi.items() + }.items(), + key=lambda t: t[0] + )) if hasattr(strategy, 'stoploss'): strategy.stoploss = float(strategy.stoploss) return strategy diff --git a/freqtrade/rpc/api_server/api_schemas.py b/freqtrade/rpc/api_server/api_schemas.py index a9135cce2..230013282 100644 --- a/freqtrade/rpc/api_server/api_schemas.py +++ b/freqtrade/rpc/api_server/api_schemas.py @@ -160,7 +160,7 @@ class ShowConfig(BaseModel): available_capital: Optional[float] stake_currency_decimals: int max_open_trades: int - minimal_roi: Dict[str, Any] + minimal_roi: Dict[str, Union[Dict[str, float], float]] stoploss: Optional[float] trailing_stop: Optional[bool] trailing_stop_positive: Optional[float] diff --git a/freqtrade/strategy/interface.py b/freqtrade/strategy/interface.py index 4f9e91b56..838fe8177 100644 --- a/freqtrade/strategy/interface.py +++ b/freqtrade/strategy/interface.py @@ -843,7 +843,7 @@ class IStrategy(ABC, HyperStrategyMixin): def should_exit(self, trade: Trade, rate: float, current_time: datetime, *, enter: bool, exit_: bool, low: float = None, high: float = None, - force_stoploss: float = 0) -> ExitCheckTuple: + force_stoploss: float = 0, enter_tag: Optional[str] = None) -> ExitCheckTuple: """ This function evaluates if one of the conditions required to trigger an exit order has been reached, which can either be a stop-loss, ROI or exit-signal. @@ -868,9 +868,15 @@ class IStrategy(ABC, HyperStrategyMixin): current_profit = trade.calc_profit_ratio(current_rate) # if enter signal and ignore_roi is set, we don't need to evaluate min_roi. - roi_reached = (not (enter and self.ignore_roi_if_entry_signal) - and self.min_roi_reached(trade=trade, current_profit=current_profit, - current_time=current_time)) + roi_reached = ( + not (enter and self.ignore_roi_if_entry_signal) and + self.min_roi_reached( + trade=trade, + current_profit=current_profit, + current_time=current_time, + enter_tag=enter_tag, + ) + ) exit_signal = ExitType.NONE custom_reason = '' @@ -1007,20 +1013,31 @@ class IStrategy(ABC, HyperStrategyMixin): return ExitCheckTuple(exit_type=ExitType.NONE) - def min_roi_reached_entry(self, trade_dur: int) -> Tuple[Optional[int], Optional[float]]: + def min_roi_reached_entry( + self, + trade_dur: int, + enter_tag: Optional[str] = None, + ) -> 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. """ + minimal_roi = self.minimal_roi[enter_tag] if enter_tag else self.minimal_roi # Get highest entry in ROI dict where key <= trade-duration - roi_list = list(filter(lambda x: x <= trade_dur, self.minimal_roi.keys())) + roi_list = list(filter(lambda x: x <= trade_dur, minimal_roi.keys())) if not roi_list: return None, None roi_entry = max(roi_list) - return roi_entry, self.minimal_roi[roi_entry] + return roi_entry, minimal_roi[roi_entry] - def min_roi_reached(self, trade: Trade, current_profit: float, current_time: datetime) -> bool: + def min_roi_reached( + self, + trade: Trade, + current_profit: float, + current_time: datetime, + enter_tag: Optional[str] = None, + ) -> bool: """ Based on trade duration, current profit of the trade and ROI configuration, decides whether bot should exit. @@ -1029,7 +1046,7 @@ class IStrategy(ABC, HyperStrategyMixin): """ # 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) + _, roi = self.min_roi_reached_entry(trade_dur, enter_tag) if roi is None: return False else: