diff --git a/freqtrade/optimize/backtesting.py b/freqtrade/optimize/backtesting.py index e2ca39530..0c63c4c14 100644 --- a/freqtrade/optimize/backtesting.py +++ b/freqtrade/optimize/backtesting.py @@ -228,16 +228,18 @@ class Backtesting: # Special case: trailing triggers within same candle as trade opened. Assume most # pessimistic price movement, which is moving just enough to arm stoploss and # immediately going down to stop price. - if (sell.sell_type == SellType.TRAILING_STOP_LOSS and trade_dur == 0 - and self.strategy.trailing_stop_positive): - if self.strategy.trailing_only_offset_is_reached: + if sell.sell_type == SellType.TRAILING_STOP_LOSS and trade_dur == 0: + if not self.strategy.use_custom_stoploss and self.strategy.trailing_stop and \ + self.strategy.trailing_only_offset_is_reached and \ + self.strategy.trailing_stop_positive_offset is not None and \ + self.strategy.trailing_stop_positive: # Worst case: price reaches stop_positive_offset and dives down. stop_rate = (sell_row[OPEN_IDX] * (1 + abs(self.strategy.trailing_stop_positive_offset) - abs(self.strategy.trailing_stop_positive))) else: # Worst case: price ticks tiny bit above open and dives down. - stop_rate = sell_row[OPEN_IDX] * (1 - abs(self.strategy.trailing_stop_positive)) + stop_rate = sell_row[OPEN_IDX] * (1 - abs(trade.stop_loss_pct)) assert stop_rate < sell_row[HIGH_IDX] return stop_rate diff --git a/tests/optimize/__init__.py b/tests/optimize/__init__.py index 7cca2b1a8..ca91019e6 100644 --- a/tests/optimize/__init__.py +++ b/tests/optimize/__init__.py @@ -34,6 +34,7 @@ class BTContainer(NamedTuple): trailing_stop_positive: Optional[float] = None trailing_stop_positive_offset: float = 0.0 use_sell_signal: bool = False + use_custom_stoploss: bool = False def _get_frame_time_from_offset(offset): diff --git a/tests/optimize/test_backtest_detail.py b/tests/optimize/test_backtest_detail.py index 855e5b97d..0bf197739 100644 --- a/tests/optimize/test_backtest_detail.py +++ b/tests/optimize/test_backtest_detail.py @@ -501,6 +501,21 @@ tc31 = BTContainer(data=[ trades=[BTrade(sell_reason=SellType.TRAILING_STOP_LOSS, open_tick=1, close_tick=1)] ) +# Test 32: trailing_stop should be triggered immediately on trade open candle. +# stop-loss: 1%, ROI: 10% (should not apply) +tc32 = BTContainer(data=[ + # D O H L C V B S + [0, 5000, 5050, 4950, 5000, 6172, 1, 0], + [1, 5000, 5500, 5000, 4900, 6172, 0, 0], # enter trade (signal on last candle) and stop + [2, 4900, 5250, 4500, 5100, 6172, 0, 0], + [3, 5100, 5100, 4650, 4750, 6172, 0, 0], + [4, 4750, 4950, 4350, 4750, 6172, 0, 0]], + stop_loss=-0.01, roi={"0": 0.10}, profit_perc=-0.01, trailing_stop=True, + trailing_only_offset_is_reached=True, trailing_stop_positive_offset=0.02, + trailing_stop_positive=0.01, use_custom_stoploss=True, + trades=[BTrade(sell_reason=SellType.TRAILING_STOP_LOSS, open_tick=1, close_tick=1)] +) + TESTS = [ tc0, tc1, @@ -534,6 +549,7 @@ TESTS = [ tc29, tc30, tc31, + tc32, ] @@ -561,6 +577,7 @@ def test_backtest_results(default_conf, fee, mocker, caplog, data) -> None: backtesting._set_strategy(backtesting.strategylist[0]) backtesting.strategy.advise_buy = lambda a, m: frame backtesting.strategy.advise_sell = lambda a, m: frame + backtesting.strategy.use_custom_stoploss = data.use_custom_stoploss caplog.set_level(logging.DEBUG) pair = "UNITTEST/BTC"