diff --git a/freqtrade/enums/exitchecktuple.py b/freqtrade/enums/exitchecktuple.py index 580b4e21c..cb6411caf 100644 --- a/freqtrade/enums/exitchecktuple.py +++ b/freqtrade/enums/exitchecktuple.py @@ -18,3 +18,6 @@ class ExitCheckTuple: def __eq__(self, other): return self.exit_type == other.exit_type and self.exit_reason == other.exit_reason + + def __repr__(self): + return f"ExitCheckTuple({self.exit_type}, {self.exit_reason})" diff --git a/freqtrade/strategy/interface.py b/freqtrade/strategy/interface.py index 15627722c..69a3f9742 100644 --- a/freqtrade/strategy/interface.py +++ b/freqtrade/strategy/interface.py @@ -942,15 +942,22 @@ class IStrategy(ABC, HyperStrategyMixin): # Sequence: # Exit-signal - # ROI (if not stoploss) # Stoploss - if roi_reached and stoplossflag.exit_type != ExitType.STOP_LOSS: + # ROI + # Trailing stoploss + + if stoplossflag.exit_type == ExitType.STOP_LOSS: + + logger.debug(f"{trade.pair} - Stoploss hit. exit_type={stoplossflag.exit_type}") + exits.append(stoplossflag) + + if roi_reached: logger.debug(f"{trade.pair} - Required profit reached. exit_type=ExitType.ROI") exits.append(ExitCheckTuple(exit_type=ExitType.ROI)) - if stoplossflag.exit_flag: + if stoplossflag.exit_type == ExitType.TRAILING_STOP_LOSS: - logger.debug(f"{trade.pair} - Stoploss hit. exit_type={stoplossflag.exit_type}") + logger.debug(f"{trade.pair} - Trailing stoploss hit.") exits.append(stoplossflag) return exits diff --git a/tests/strategy/test_interface.py b/tests/strategy/test_interface.py index 8bdea852a..2cedea962 100644 --- a/tests/strategy/test_interface.py +++ b/tests/strategy/test_interface.py @@ -525,7 +525,7 @@ def test_custom_exit(default_conf, fee, caplog) -> None: assert log_has_re('Custom exit reason returned from custom_exit is too long.*', caplog) -def test_should_sell(default_conf, fee, caplog) -> None: +def test_should_sell(default_conf, fee) -> None: strategy = StrategyResolver.load_strategy(default_conf) trade = Trade( @@ -561,22 +561,24 @@ def test_should_sell(default_conf, fee, caplog) -> None: low=None, high=None) assert len(res) == 2 assert res == [ - ExitCheckTuple(exit_type=ExitType.ROI), ExitCheckTuple(exit_type=ExitType.STOP_LOSS), + ExitCheckTuple(exit_type=ExitType.ROI), ] strategy.custom_exit = MagicMock(return_value='hello world') - + # custom-exit and exit-signal is first res = strategy.should_exit(trade, 1, now, enter=False, exit_=False, low=None, high=None) assert len(res) == 3 assert res == [ ExitCheckTuple(exit_type=ExitType.CUSTOM_EXIT, exit_reason='hello world'), - ExitCheckTuple(exit_type=ExitType.ROI), ExitCheckTuple(exit_type=ExitType.STOP_LOSS), + ExitCheckTuple(exit_type=ExitType.ROI), ] + strategy.stop_loss_reached = MagicMock( + return_value=ExitCheckTuple(exit_type=ExitType.TRAILING_STOP_LOSS)) # Regular exit signal res = strategy.should_exit(trade, 1, now, enter=False, exit_=True, @@ -585,7 +587,7 @@ def test_should_sell(default_conf, fee, caplog) -> None: assert res == [ ExitCheckTuple(exit_type=ExitType.EXIT_SIGNAL), ExitCheckTuple(exit_type=ExitType.ROI), - ExitCheckTuple(exit_type=ExitType.STOP_LOSS), + ExitCheckTuple(exit_type=ExitType.TRAILING_STOP_LOSS), ] # Regular exit signal, no ROI @@ -596,9 +598,10 @@ def test_should_sell(default_conf, fee, caplog) -> None: assert len(res) == 2 assert res == [ ExitCheckTuple(exit_type=ExitType.EXIT_SIGNAL), - ExitCheckTuple(exit_type=ExitType.STOP_LOSS), + ExitCheckTuple(exit_type=ExitType.TRAILING_STOP_LOSS), ] + @pytest.mark.parametrize('side', TRADE_SIDES) def test_leverage_callback(default_conf, side) -> None: default_conf['strategy'] = 'StrategyTestV2'