Merge pull request #11396 from freqtrade/fix/constant_exit_cancels

Fix constant exit cancels and replacements
This commit is contained in:
Matthias
2025-02-18 19:43:54 +01:00
committed by GitHub
2 changed files with 22 additions and 3 deletions

View File

@@ -64,7 +64,7 @@ from freqtrade.rpc.rpc_types import (
)
from freqtrade.strategy.interface import IStrategy
from freqtrade.strategy.strategy_wrapper import strategy_safe_wrapper
from freqtrade.util import FtPrecise, MeasureTime, dt_from_ts
from freqtrade.util import FtPrecise, MeasureTime, PeriodicCache, dt_from_ts, dt_now
from freqtrade.util.migrations.binance_mig import migrate_binance_futures_names
from freqtrade.wallets import Wallets
@@ -154,6 +154,7 @@ class FreqtradeBot(LoggingMixin):
# Protect exit-logic from forcesell and vice versa
self._exit_lock = Lock()
timeframe_secs = timeframe_to_seconds(self.strategy.timeframe)
self._exit_reason_cache = PeriodicCache(100, ttl=timeframe_secs)
LoggingMixin.__init__(self, logger, timeframe_secs)
self._schedule = Scheduler()
@@ -1374,6 +1375,15 @@ class FreqtradeBot(LoggingMixin):
for should_exit in exits:
if should_exit.exit_flag:
exit_tag1 = exit_tag if should_exit.exit_type == ExitType.EXIT_SIGNAL else None
if trade.has_open_orders:
if prev_eval := self._exit_reason_cache.get(
f"{trade.pair}_{trade.id}_{exit_tag1 or should_exit.exit_reason}", None
):
logger.debug(
f"Exit reason already seen this candle, first seen at {prev_eval}"
)
continue
logger.info(
f"Exit for {trade.pair} detected. Reason: {should_exit.exit_type}"
f"{f' Tag: {exit_tag1}' if exit_tag1 is not None else ''}"
@@ -1774,7 +1784,7 @@ class FreqtradeBot(LoggingMixin):
if trade.has_open_orders:
oo = trade.select_order(side, True)
if oo is not None:
if (price == oo.price) and (side == oo.side) and (amount == oo.amount):
if price == oo.price and side == oo.side and amount == oo.amount:
logger.info(
f"A similar open order was found for {trade.pair}. "
f"Keeping existing {trade.exit_side} order. {price=}, {amount=}"
@@ -2092,6 +2102,7 @@ class FreqtradeBot(LoggingMixin):
self.handle_insufficient_funds(trade)
return False
self._exit_reason_cache[f"{trade.pair}_{trade.id}_{exit_reason}"] = dt_now()
order_obj = Order.parse_from_ccxt_object(order, trade.pair, trade.exit_side, amount, limit)
order_obj.ft_order_tag = exit_reason
trade.orders.append(order_obj)

View File

@@ -3743,8 +3743,9 @@ def test_trailing_stop_loss_positive(
@pytest.mark.parametrize("is_short", [False, True])
def test_disable_ignore_roi_if_entry_signal(
default_conf_usdt, limit_order, limit_order_open, is_short, fee, mocker
default_conf_usdt, limit_order, limit_order_open, is_short, fee, mocker, time_machine
) -> None:
time_machine.move_to("2025-01-10 08:00:16 +00:00")
patch_RPCManager(mocker)
patch_exchange(mocker)
eside = entry_side(is_short)
@@ -3773,6 +3774,13 @@ def test_disable_ignore_roi_if_entry_signal(
patch_get_signal(freqtrade, enter_long=not is_short, enter_short=is_short, exit_short=is_short)
assert freqtrade.handle_trade(trade) is True
# Test if entry-signal is absent
patch_get_signal(freqtrade)
# Signal was evaluated already - no action.
assert freqtrade.handle_trade(trade) is False
# Move to after the candle expired
time_machine.shift(timedelta(hours=5))
# Test if entry-signal is absent
patch_get_signal(freqtrade)
assert freqtrade.handle_trade(trade) is True