diff --git a/freqtrade/freqtradebot.py b/freqtrade/freqtradebot.py index 572ceeabf..279bb6161 100644 --- a/freqtrade/freqtradebot.py +++ b/freqtrade/freqtradebot.py @@ -987,18 +987,20 @@ class FreqtradeBot(LoggingMixin): fully_cancelled = self.update_trade_state(trade, trade.open_order_id, order) + order_obj = trade.select_order_by_order_id(trade.open_order_id) + if (order['side'] == 'buy' and (order['status'] == 'open' or fully_cancelled) and ( fully_cancelled - or self.strategy.ft_check_timed_out( - 'buy', trade, order, datetime.now(timezone.utc)) - )): + or (order_obj and self.strategy.ft_check_timed_out( + 'buy', trade, order_obj, datetime.now(timezone.utc)) + ))): self.handle_cancel_enter(trade, order, constants.CANCEL_REASON['TIMEOUT']) elif (order['side'] == 'sell' and (order['status'] == 'open' or fully_cancelled) and ( fully_cancelled - or self.strategy.ft_check_timed_out( - 'sell', trade, order, datetime.now(timezone.utc))) - ): + or (order_obj and self.strategy.ft_check_timed_out( + 'sell', trade, order_obj, datetime.now(timezone.utc)) + ))): self.handle_cancel_exit(trade, order, constants.CANCEL_REASON['TIMEOUT']) canceled_count = trade.get_exit_order_count() max_timeouts = self.config.get('unfilledtimeout', {}).get('exit_timeout_count', 0) diff --git a/freqtrade/optimize/backtesting.py b/freqtrade/optimize/backtesting.py index 5793da02b..082fe0d5e 100644 --- a/freqtrade/optimize/backtesting.py +++ b/freqtrade/optimize/backtesting.py @@ -593,7 +593,7 @@ class Backtesting: if pos_adjust and self._get_order_filled(order.price, row): order.close_bt_order(current_time) else: - trade.open_order_id = self.order_id_counter + trade.open_order_id = str(self.order_id_counter) trade.orders.append(order) trade.recalc_trade_from_orders() @@ -642,7 +642,7 @@ class Backtesting: """ for order in [o for o in trade.orders if o.ft_is_open]: - timedout = self.strategy.ft_check_timed_out(order.side, trade, {}, current_time) + timedout = self.strategy.ft_check_timed_out(order.side, trade, order, current_time) if timedout: if order.side == 'buy': if trade.nr_of_successful_buys == 0: diff --git a/freqtrade/persistence/models.py b/freqtrade/persistence/models.py index 6dd4ceaad..2e0f0753a 100644 --- a/freqtrade/persistence/models.py +++ b/freqtrade/persistence/models.py @@ -132,6 +132,10 @@ class Order(_DECL_BASE): order_filled_date = Column(DateTime, nullable=True) order_update_date = Column(DateTime, nullable=True) + @property + def order_date_utc(self): + return self.order_date.replace(tzinfo=timezone.utc) + def __repr__(self): return (f'Order(id={self.id}, order_id={self.order_id}, trade_id={self.ft_trade_id}, ' @@ -641,6 +645,16 @@ class LocalTrade(): if self.stop_loss_pct is not None and self.open_rate is not None: self.adjust_stop_loss(self.open_rate, self.stop_loss_pct) + def select_order_by_order_id(self, order_id: str) -> Optional[Order]: + """ + Finds order object by Order id. + :param order_id: Exchange order id + """ + orders = [o for o in self.orders if o.order_id == order_id] + if orders: + return orders[0] + return None + def select_order( self, order_side: str = None, is_open: Optional[bool] = None) -> Optional[Order]: """ diff --git a/freqtrade/strategy/interface.py b/freqtrade/strategy/interface.py index 78dae6c5d..0bd7834e2 100644 --- a/freqtrade/strategy/interface.py +++ b/freqtrade/strategy/interface.py @@ -18,6 +18,7 @@ from freqtrade.exceptions import OperationalException, StrategyError from freqtrade.exchange import timeframe_to_minutes, timeframe_to_seconds from freqtrade.exchange.exchange import timeframe_to_next_date from freqtrade.persistence import PairLocks, Trade +from freqtrade.persistence.models import LocalTrade, Order from freqtrade.strategy.hyper import HyperStrategyMixin from freqtrade.strategy.informative_decorator import (InformativeData, PopulateIndicators, _create_and_merge_informative_pair, @@ -862,23 +863,22 @@ class IStrategy(ABC, HyperStrategyMixin): else: return current_profit > roi - def ft_check_timed_out(self, side: str, trade: Trade, order: Dict, + def ft_check_timed_out(self, side: str, trade: LocalTrade, order: Order, current_time: datetime) -> bool: """ FT Internal method. Check if timeout is active, and if the order is still open and timed out """ timeout = self.config.get('unfilledtimeout', {}).get(side) - ordertime = arrow.get(order['datetime']).datetime if timeout is not None: timeout_unit = self.config.get('unfilledtimeout', {}).get('unit', 'minutes') timeout_kwargs = {timeout_unit: -timeout} timeout_threshold = current_time + timedelta(**timeout_kwargs) - timedout = (order['status'] == 'open' and order['side'] == side - and ordertime < timeout_threshold) + timedout = (order.status == 'open' and order.side == side + and order.order_date_utc < timeout_threshold) if timedout: return True - time_method = self.check_sell_timeout if order['side'] == 'sell' else self.check_buy_timeout + time_method = self.check_sell_timeout if order.side == 'sell' else self.check_buy_timeout return strategy_safe_wrapper(time_method, default_retval=False)(