diff --git a/freqtrade/freqtradebot.py b/freqtrade/freqtradebot.py index c2b15d23f..09a5ea746 100644 --- a/freqtrade/freqtradebot.py +++ b/freqtrade/freqtradebot.py @@ -11,7 +11,6 @@ from typing import Any, Dict, List, Optional import arrow from cachetools import TTLCache -from pandas import DataFrame from freqtrade import __version__, constants from freqtrade.configuration import validate_config_consistency @@ -784,10 +783,10 @@ class FreqtradeBot(LoggingMixin): config_ask_strategy = self.config.get('ask_strategy', {}) - analyzed_df, _ = self.dataprovider.get_analyzed_dataframe(trade.pair, - self.strategy.timeframe) if (config_ask_strategy.get('use_sell_signal', True) or config_ask_strategy.get('ignore_roi_if_buy_signal', False)): + analyzed_df, _ = self.dataprovider.get_analyzed_dataframe(trade.pair, + self.strategy.timeframe) (buy, sell) = self.strategy.get_signal(trade.pair, self.strategy.timeframe, analyzed_df) @@ -814,13 +813,13 @@ class FreqtradeBot(LoggingMixin): # resulting in outdated RPC messages self._sell_rate_cache[trade.pair] = sell_rate - if self._check_and_execute_sell(analyzed_df, trade, sell_rate, buy, sell): + if self._check_and_execute_sell(trade, sell_rate, buy, sell): return True else: logger.debug('checking sell') sell_rate = self.get_sell_rate(trade.pair, True) - if self._check_and_execute_sell(analyzed_df, trade, sell_rate, buy, sell): + if self._check_and_execute_sell(trade, sell_rate, buy, sell): return True logger.debug('Found no sell signal for %s.', trade) @@ -951,13 +950,13 @@ class FreqtradeBot(LoggingMixin): logger.warning(f"Could not create trailing stoploss order " f"for pair {trade.pair}.") - def _check_and_execute_sell(self, dataframe: DataFrame, trade: Trade, sell_rate: float, + def _check_and_execute_sell(self, trade: Trade, sell_rate: float, buy: bool, sell: bool) -> bool: """ Check and execute sell """ should_sell = self.strategy.should_sell( - dataframe, trade, sell_rate, datetime.now(timezone.utc), buy, sell, + trade, sell_rate, datetime.now(timezone.utc), buy, sell, force_stoploss=self.edge.stoploss(trade.pair) if self.edge else 0 ) diff --git a/freqtrade/optimize/backtesting.py b/freqtrade/optimize/backtesting.py index 899da03e4..80d816985 100644 --- a/freqtrade/optimize/backtesting.py +++ b/freqtrade/optimize/backtesting.py @@ -247,10 +247,9 @@ class Backtesting: else: return sell_row[OPEN_IDX] - def _get_sell_trade_entry(self, dataframe: DataFrame, trade: LocalTrade, - sell_row: Tuple) -> Optional[LocalTrade]: + def _get_sell_trade_entry(self, trade: LocalTrade, sell_row: Tuple) -> Optional[LocalTrade]: - sell = self.strategy.should_sell(dataframe, trade, sell_row[OPEN_IDX], # type: ignore + sell = self.strategy.should_sell(trade, sell_row[OPEN_IDX], # type: ignore sell_row[DATE_IDX].to_pydatetime(), sell_row[BUY_IDX], sell_row[SELL_IDX], low=sell_row[LOW_IDX], high=sell_row[HIGH_IDX]) @@ -398,7 +397,7 @@ class Backtesting: for trade in open_trades[pair]: # also check the buying candle for sell conditions. - trade_entry = self._get_sell_trade_entry(processed[pair], trade, row) + trade_entry = self._get_sell_trade_entry(trade, row) # Sell occured if trade_entry: # logger.debug(f"{pair} - Backtesting sell {trade}") diff --git a/freqtrade/strategy/interface.py b/freqtrade/strategy/interface.py index 63dcc75d9..c483e6afb 100644 --- a/freqtrade/strategy/interface.py +++ b/freqtrade/strategy/interface.py @@ -277,7 +277,7 @@ class IStrategy(ABC, HyperStrategyMixin): return True def custom_stoploss(self, pair: str, trade: Trade, current_time: datetime, current_rate: float, - current_profit: float, dataframe: DataFrame, **kwargs) -> float: + current_profit: float, **kwargs) -> float: """ Custom stoploss logic, returning the new distance relative to current_rate (as ratio). e.g. returning -0.05 would create a stoploss 5% below current_rate. @@ -300,8 +300,7 @@ class IStrategy(ABC, HyperStrategyMixin): return self.stoploss def custom_sell(self, pair: str, trade: Trade, current_time: datetime, current_rate: float, - current_profit: float, dataframe: DataFrame, - **kwargs) -> Optional[Union[str, bool]]: + current_profit: float, **kwargs) -> Optional[Union[str, bool]]: """ Custom sell signal logic indicating that specified position should be sold. Returning a string or True from this method is equal to setting sell signal on a candle at specified @@ -539,8 +538,8 @@ class IStrategy(ABC, HyperStrategyMixin): else: return False - def should_sell(self, dataframe: DataFrame, trade: Trade, rate: float, date: datetime, - buy: bool, sell: bool, low: float = None, high: float = None, + def should_sell(self, trade: Trade, rate: float, date: datetime, buy: bool, + sell: bool, low: float = None, high: float = None, force_stoploss: float = 0) -> SellCheckTuple: """ This function evaluates if one of the conditions required to trigger a sell @@ -556,9 +555,8 @@ class IStrategy(ABC, HyperStrategyMixin): trade.adjust_min_max_rates(high or current_rate) - stoplossflag = self.stop_loss_reached(dataframe=dataframe, current_rate=current_rate, - trade=trade, current_time=date, - current_profit=current_profit, + stoplossflag = self.stop_loss_reached(current_rate=current_rate, trade=trade, + current_time=date, current_profit=current_profit, force_stoploss=force_stoploss, high=high) # Set current rate to high for backtesting sell @@ -583,7 +581,7 @@ class IStrategy(ABC, HyperStrategyMixin): else: custom_reason = strategy_safe_wrapper(self.custom_sell, default_retval=False)( pair=trade.pair, trade=trade, current_time=date, current_rate=current_rate, - current_profit=current_profit, dataframe=dataframe) + current_profit=current_profit) if custom_reason: sell_signal = SellType.CUSTOM_SELL if isinstance(custom_reason, str): @@ -620,7 +618,7 @@ class IStrategy(ABC, HyperStrategyMixin): # logger.debug(f"{trade.pair} - No sell signal.") return SellCheckTuple(sell_type=SellType.NONE) - def stop_loss_reached(self, dataframe: DataFrame, current_rate: float, trade: Trade, + def stop_loss_reached(self, current_rate: float, trade: Trade, current_time: datetime, current_profit: float, force_stoploss: float, high: float = None) -> SellCheckTuple: """ @@ -638,8 +636,7 @@ class IStrategy(ABC, HyperStrategyMixin): )(pair=trade.pair, trade=trade, current_time=current_time, current_rate=current_rate, - current_profit=current_profit, - dataframe=dataframe) + current_profit=current_profit) # Sanity check - error cases will return None if stop_loss_value: # logger.info(f"{trade.pair} {stop_loss_value=} {current_profit=}") diff --git a/tests/strategy/test_default_strategy.py b/tests/strategy/test_default_strategy.py index a8862e9c9..ec7b3c33d 100644 --- a/tests/strategy/test_default_strategy.py +++ b/tests/strategy/test_default_strategy.py @@ -41,5 +41,4 @@ def test_default_strategy(result, fee): rate=20000, time_in_force='gtc', sell_reason='roi') is True assert strategy.custom_stoploss(pair='ETH/BTC', trade=trade, current_time=datetime.now(), - current_rate=20_000, current_profit=0.05, dataframe=None - ) == strategy.stoploss + current_rate=20_000, current_profit=0.05) == strategy.stoploss diff --git a/tests/strategy/test_interface.py b/tests/strategy/test_interface.py index a241d7f43..182dde335 100644 --- a/tests/strategy/test_interface.py +++ b/tests/strategy/test_interface.py @@ -360,7 +360,7 @@ def test_stop_loss_reached(default_conf, fee, profit, adjusted, expected, traili now = arrow.utcnow().datetime sl_flag = strategy.stop_loss_reached(current_rate=trade.open_rate * (1 + profit), trade=trade, current_time=now, current_profit=profit, - force_stoploss=0, high=None, dataframe=None) + force_stoploss=0, high=None) assert isinstance(sl_flag, SellCheckTuple) assert sl_flag.sell_type == expected if expected == SellType.NONE: @@ -371,7 +371,7 @@ def test_stop_loss_reached(default_conf, fee, profit, adjusted, expected, traili sl_flag = strategy.stop_loss_reached(current_rate=trade.open_rate * (1 + profit2), trade=trade, current_time=now, current_profit=profit2, - force_stoploss=0, high=None, dataframe=None) + force_stoploss=0, high=None) assert sl_flag.sell_type == expected2 if expected2 == SellType.NONE: assert sl_flag.sell_flag is False