diff --git a/freqtrade/freqtradebot.py b/freqtrade/freqtradebot.py index fd5847a94..929066c18 100644 --- a/freqtrade/freqtradebot.py +++ b/freqtrade/freqtradebot.py @@ -242,7 +242,7 @@ class FreqtradeBot: else: fo = self.exchange.fetch_order(order.order_id, order.ft_pair) - self.update_trade_state(order.trade, fo, sl_order=order.ft_order_side == 'stoploss') + self.update_trade_state(order.trade, order.order_id, fo) except ExchangeError: logger.warning(f"Error updating {order.order_id}") @@ -272,7 +272,7 @@ class FreqtradeBot: # No action for buy orders ... continue if fo: - self.update_trade_state(trade, fo, sl_order=order.ft_order_side == 'stoploss') + self.update_trade_state(trade, order.order_id, fo) except ExchangeError: logger.warning(f"Error updating {order.order_id}") @@ -634,7 +634,7 @@ class FreqtradeBot: # Update fees if order is closed if order_status == 'closed': - self.update_trade_state(trade, order) + self.update_trade_state(trade, order_id, order) Trade.session.add(trade) Trade.session.flush() @@ -878,7 +878,7 @@ class FreqtradeBot: # We check if stoploss order is fulfilled if stoploss_order and stoploss_order['status'] in ('closed', 'triggered'): trade.sell_reason = SellType.STOPLOSS_ON_EXCHANGE.value - self.update_trade_state(trade, stoploss_order, sl_order=True) + self.update_trade_state(trade, trade.stoploss_order_id, stoploss_order) # Lock pair for one candle to prevent immediate rebuys self.strategy.lock_pair(trade.pair, timeframe_to_next_date(self.config['timeframe'])) @@ -989,7 +989,7 @@ class FreqtradeBot: logger.info('Cannot query order for %s due to %s', trade, traceback.format_exc()) continue - fully_cancelled = self.update_trade_state(trade, order) + fully_cancelled = self.update_trade_state(trade, trade.open_order_id, order) if (order['side'] == 'buy' and (order['status'] == 'open' or fully_cancelled) and ( fully_cancelled @@ -1070,7 +1070,7 @@ class FreqtradeBot: # we need to fall back to the values from order if corder does not contain these keys. trade.amount = filled_amount trade.stake_amount = trade.amount * trade.open_rate - self.update_trade_state(trade, corder, trade.amount) + self.update_trade_state(trade, trade.open_order_id, corder, trade.amount) trade.open_order_id = None logger.info('Partial buy order timeout for %s.', trade) @@ -1208,7 +1208,7 @@ class FreqtradeBot: trade.sell_reason = sell_reason.value # In case of market sell orders the order can be closed immediately if order.get('status', 'unknown') == 'closed': - self.update_trade_state(trade, order) + self.update_trade_state(trade, trade.open_order_id, order) Trade.session.flush() # Lock pair for one candle to prevent immediate rebuys @@ -1305,20 +1305,17 @@ class FreqtradeBot: # Common update trade state methods # - def update_trade_state(self, trade: Trade, action_order: dict = None, - order_amount: float = None, sl_order: bool = False) -> bool: + def update_trade_state(self, trade: Trade, order_id: str, action_order: dict = None, + order_amount: float = None) -> bool: """ Checks trades with open orders and updates the amount if necessary Handles closing both buy and sell orders. :return: True if order has been cancelled without being filled partially, False otherwise """ - # Get order details for actual price per unit - if trade.open_order_id: - order_id = trade.open_order_id - elif trade.stoploss_order_id and sl_order: - order_id = trade.stoploss_order_id - else: - return False + if not order_id: + logger.warning(f'Orderid for trade {trade} is empty.') + False + # Update trade with order values logger.info('Found open order for %s', trade) try: diff --git a/tests/test_freqtradebot.py b/tests/test_freqtradebot.py index 9a3dcbd78..93b71f176 100644 --- a/tests/test_freqtradebot.py +++ b/tests/test_freqtradebot.py @@ -1699,7 +1699,7 @@ def test_update_trade_state(mocker, default_conf, limit_buy_order, caplog) -> No amount=11, ) # Add datetime explicitly since sqlalchemy defaults apply only once written to database - freqtrade.update_trade_state(trade) + freqtrade.update_trade_state(trade, '123') # Test amount not modified by fee-logic assert not log_has_re(r'Applying fee to .*', caplog) assert trade.open_order_id is None @@ -1709,14 +1709,14 @@ def test_update_trade_state(mocker, default_conf, limit_buy_order, caplog) -> No mocker.patch('freqtrade.freqtradebot.FreqtradeBot.get_real_amount', return_value=90.81) assert trade.amount != 90.81 # test amount modified by fee-logic - freqtrade.update_trade_state(trade) + freqtrade.update_trade_state(trade, '123') assert trade.amount == 90.81 assert trade.open_order_id is None trade.is_open = True trade.open_order_id = None # Assert we call handle_trade() if trade is feasible for execution - freqtrade.update_trade_state(trade) + freqtrade.update_trade_state(trade, '123') assert log_has_re('Found open order for.*', caplog) @@ -1741,7 +1741,7 @@ def test_update_trade_state_withorderdict(default_conf, trades_for_order, limit_ open_order_id="123456", is_open=True, ) - freqtrade.update_trade_state(trade, limit_buy_order) + freqtrade.update_trade_state(trade, '123456', limit_buy_order) assert trade.amount != amount assert trade.amount == limit_buy_order['amount'] @@ -1763,11 +1763,11 @@ def test_update_trade_state_withorderdict_rounding_fee(default_conf, trades_for_ open_rate=0.245441, fee_open=fee.return_value, fee_close=fee.return_value, - open_order_id="123456", + open_order_id='123456', is_open=True, open_date=arrow.utcnow().datetime, ) - freqtrade.update_trade_state(trade, limit_buy_order) + freqtrade.update_trade_state(trade, '123456', limit_buy_order) assert trade.amount != amount assert trade.amount == limit_buy_order['amount'] assert log_has_re(r'Applying fee on amount for .*', caplog) @@ -1787,7 +1787,7 @@ def test_update_trade_state_exception(mocker, default_conf, 'freqtrade.freqtradebot.FreqtradeBot.get_real_amount', side_effect=DependencyException() ) - freqtrade.update_trade_state(trade) + freqtrade.update_trade_state(trade, trade.open_order_id) assert log_has('Could not update trade amount: ', caplog) @@ -1802,7 +1802,7 @@ def test_update_trade_state_orderexception(mocker, default_conf, caplog) -> None # Test raise of OperationalException exception grm_mock = mocker.patch("freqtrade.freqtradebot.FreqtradeBot.get_real_amount", MagicMock()) - freqtrade.update_trade_state(trade) + freqtrade.update_trade_state(trade, trade.open_order_id) assert grm_mock.call_count == 0 assert log_has(f'Unable to fetch order {trade.open_order_id}: ', caplog) @@ -1834,7 +1834,7 @@ def test_update_trade_state_sell(default_conf, trades_for_order, limit_sell_orde order = Order.parse_from_ccxt_object(limit_sell_order_open, 'LTC/ETH', 'sell') trade.orders.append(order) assert order.status == 'open' - freqtrade.update_trade_state(trade, limit_sell_order) + freqtrade.update_trade_state(trade, trade.open_order_id, limit_sell_order) assert trade.amount == limit_sell_order['amount'] # Wallet needs to be updated after closing a limit-sell order to reenable buying assert wallet_mock.call_count == 1