From d37e23f68e45993a21cf63fe144a2acdd4909780 Mon Sep 17 00:00:00 2001 From: Matthias Date: Sun, 22 Dec 2024 18:07:07 +0100 Subject: [PATCH] feat: implement order replacement for backtesting --- freqtrade/optimize/backtesting.py | 41 ++++++++++++++++++++++++++++++- 1 file changed, 40 insertions(+), 1 deletion(-) diff --git a/freqtrade/optimize/backtesting.py b/freqtrade/optimize/backtesting.py index bfb6af4e6..8cccae02d 100644 --- a/freqtrade/optimize/backtesting.py +++ b/freqtrade/optimize/backtesting.py @@ -831,6 +831,10 @@ class Backtesting: amount = amount_to_contract_precision( amount or trade.amount, trade.amount_precision, self.precision_mode, trade.contract_size ) + + if self.handle_similar_order(trade, close_rate, amount, trade.exit_side, exit_candle_time): + return None + order = Order( id=self.order_id_counter, ft_trade_id=trade.id, @@ -1116,6 +1120,10 @@ class Backtesting: orders=[], ) LocalTrade.add_bt_trade(trade) + elif self.handle_similar_order( + trade, propose_rate, amount, trade.entry_side, current_time + ): + return None trade.adjust_stop_loss(trade.open_rate, self.strategy.stoploss, initial=True) @@ -1214,6 +1222,37 @@ class Backtesting: # default maintain trade return False + def cancel_open_orders(self, trade: LocalTrade, current_time: datetime): + """ + Cancel all open orders for the given trade. + """ + 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 + # canceled orders are removed from the trade + del trade.orders[trade.orders.index(order)] + + def handle_similar_order( + self, trade: LocalTrade, price: float, amount: float, side: str, current_time: datetime + ) -> bool: + """ + Handle similar order for the given trade. + """ + if trade.has_open_orders: + oo = trade.select_order(side, True) + if oo: + 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=}" + ) + return True + self.cancel_open_orders(trade, current_time) + + return False + def check_order_cancel( self, trade: LocalTrade, order: Order, current_time: datetime ) -> bool | None: @@ -1399,7 +1438,7 @@ class Backtesting: self.wallets.update() # 4. Create exit orders (if any) - if trade.has_open_position or trade.has_open_orders: + if trade.has_open_position: self._check_trade_exit(trade, row, current_time) # Place exit order if necessary # 5. Process exit orders.