From 756bada570cedd50f9ea1c610c2b8e3ec43ac846 Mon Sep 17 00:00:00 2001 From: Matthias Date: Thu, 20 Feb 2025 19:09:36 +0100 Subject: [PATCH] feat: Add "replace-exit_order" to backtesting --- freqtrade/optimize/backtesting.py | 45 +++++++++++++++++++++---------- 1 file changed, 31 insertions(+), 14 deletions(-) diff --git a/freqtrade/optimize/backtesting.py b/freqtrade/optimize/backtesting.py index d3f1bc63b..5304175d8 100644 --- a/freqtrade/optimize/backtesting.py +++ b/freqtrade/optimize/backtesting.py @@ -396,6 +396,8 @@ class Backtesting: self.canceled_trade_entries = 0 self.canceled_entry_orders = 0 self.replaced_entry_orders = 0 + self.canceled_exit_orders = 0 + self.replaced_exit_orders = 0 self.dataprovider.clear_cache() if enable_protections: self._load_protections(self.strategy) @@ -1234,8 +1236,8 @@ class Backtesting: for order in [o for o in trade.orders if o.ft_is_open]: if order.side == trade.entry_side: self.canceled_entry_orders += 1 - # elif order.side == trade.exit_side: - # self.canceled_exit_orders += 1 + elif order.side == trade.exit_side: + self.canceled_exit_orders += 1 # canceled orders are removed from the trade del trade.orders[trade.orders.index(order)] @@ -1300,8 +1302,9 @@ class Backtesting: """ # only check on new candles for open entry orders if order.side == trade.entry_side and current_time > order.order_date_utc: + is_entry = order.side == trade.entry_side requested_rate = strategy_safe_wrapper( - self.strategy.adjust_entry_price, default_retval=order.ft_price + self.strategy.adjust_order_price, default_retval=order.ft_price )( trade=trade, # type: ignore[arg-type] order=order, @@ -1311,6 +1314,7 @@ class Backtesting: current_order_rate=order.ft_price, entry_tag=trade.enter_tag, side=trade.trade_direction, + is_entry=is_entry, ) # default value is current order price # cancel existing order whenever a new rate is requested (or None) @@ -1319,22 +1323,35 @@ class Backtesting: return False else: del trade.orders[trade.orders.index(order)] - self.canceled_entry_orders += 1 + if is_entry: + self.canceled_entry_orders += 1 + else: + self.canceled_exit_orders += 1 # place new order if result was not None if requested_rate: - self._enter_trade( - pair=trade.pair, - row=row, - trade=trade, - requested_rate=requested_rate, - requested_stake=(order.safe_remaining * order.ft_price / trade.leverage), - direction="short" if trade.is_short else "long", - ) + if is_entry: + self._enter_trade( + pair=trade.pair, + row=row, + trade=trade, + requested_rate=requested_rate, + requested_stake=(order.safe_remaining * order.ft_price / trade.leverage), + direction="short" if trade.is_short else "long", + ) + self.replaced_entry_orders += 1 + else: + self._exit_trade( + trade=trade, + sell_row=row, + close_rate=requested_rate, + amount=order.safe_remaining, + exit_reason=order.ft_order_tag, + ) + self.replaced_exit_orders += 1 # Delete trade if no successful entries happened (if placing the new order failed) - if not trade.has_open_orders and trade.nr_of_successful_entries == 0: + if not trade.has_open_orders and is_entry and trade.nr_of_successful_entries == 0: return True - self.replaced_entry_orders += 1 else: # assumption: there can't be multiple open entry orders at any given time return trade.nr_of_successful_entries == 0