mirror of
https://github.com/freqtrade/freqtrade.git
synced 2026-02-02 20:30:25 +00:00
Merge pull request #11396 from freqtrade/fix/constant_exit_cancels
Fix constant exit cancels and replacements
This commit is contained in:
@@ -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)
|
||||
|
||||
@@ -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
|
||||
|
||||
Reference in New Issue
Block a user