diff --git a/freqtrade/freqtradebot.py b/freqtrade/freqtradebot.py index 1f9573122..8732533f7 100644 --- a/freqtrade/freqtradebot.py +++ b/freqtrade/freqtradebot.py @@ -752,7 +752,7 @@ class FreqtradeBot: # We check if stoploss order is fulfilled if stoploss_order and stoploss_order['status'] == 'closed': trade.sell_reason = SellType.STOPLOSS_ON_EXCHANGE.value - trade.update(stoploss_order) + self.update_trade_state(trade, stoploss_order, sl_order=True) # Lock pair for one candle to prevent immediate rebuys self.strategy.lock_pair(trade.pair, timeframe_to_next_date(self.config['ticker_interval'])) @@ -1123,7 +1123,7 @@ class FreqtradeBot: # def update_trade_state(self, trade: Trade, action_order: dict = None, - order_amount: float = None) -> bool: + order_amount: float = None, sl_order: bool = False) -> bool: """ Checks trades with open orders and updates the amount if necessary Handles closing both buy and sell orders. @@ -1131,34 +1131,37 @@ class FreqtradeBot: """ # Get order details for actual price per unit if trade.open_order_id: - # Update trade with order values - logger.info('Found open order for %s', trade) - try: - order = action_order or self.exchange.get_order(trade.open_order_id, trade.pair) - except InvalidOrderException as exception: - logger.warning('Unable to fetch order %s: %s', trade.open_order_id, exception) - return False - # Try update amount (binance-fix) - try: - new_amount = self.get_real_amount(trade, order, order_amount) - if not isclose(order['amount'], new_amount, abs_tol=constants.MATH_CLOSE_PREC): - order['amount'] = new_amount - order.pop('filled', None) - trade.recalc_open_trade_price() - except DependencyException as exception: - logger.warning("Could not update trade amount: %s", exception) + order_id = trade.open_order_id + elif trade.stoploss_order_id and sl_order: + order_id = trade.stoploss_order_id + else: + return False + # Update trade with order values + logger.info('Found open order for %s', trade) + try: + order = action_order or self.exchange.get_order(order_id, trade.pair) + except InvalidOrderException as exception: + logger.warning('Unable to fetch order %s: %s', order_id, exception) + return False + # Try update amount (binance-fix) + try: + new_amount = self.get_real_amount(trade, order, order_amount) + if not isclose(order['amount'], new_amount, abs_tol=constants.MATH_CLOSE_PREC): + order['amount'] = new_amount + order.pop('filled', None) + trade.recalc_open_trade_price() + except DependencyException as exception: + logger.warning("Could not update trade amount: %s", exception) - if self.exchange.check_order_canceled_empty(order): - # Trade has been cancelled on exchange - # Handling of this will happen in check_handle_timeout. - return True - trade.update(order) + if self.exchange.check_order_canceled_empty(order): + # Trade has been cancelled on exchange + # Handling of this will happen in check_handle_timeout. + return True + trade.update(order) - # Updating wallets when order is closed - if not trade.is_open: - self.wallets.update() - - return False + # Updating wallets when order is closed + if not trade.is_open: + self.wallets.update() def apply_fee_conditional(self, trade: Trade, trade_base_currency: str, amount: float, fee_abs: float) -> float: diff --git a/tests/test_freqtradebot.py b/tests/test_freqtradebot.py index b426368c9..2ff51dffd 100644 --- a/tests/test_freqtradebot.py +++ b/tests/test_freqtradebot.py @@ -1140,7 +1140,8 @@ def test_handle_stoploss_on_exchange(mocker, default_conf, fee, caplog, 'status': 'closed', 'type': 'stop_loss_limit', 'price': 3, - 'average': 2 + 'average': 2, + 'amount': limit_buy_order['amount'], }) mocker.patch('freqtrade.exchange.Exchange.get_order', stoploss_order_hit) assert freqtrade.handle_stoploss_on_exchange(trade) is True diff --git a/tests/test_integration.py b/tests/test_integration.py index 90cdde61f..1396e86f5 100644 --- a/tests/test_integration.py +++ b/tests/test_integration.py @@ -44,6 +44,8 @@ def test_may_execute_sell_stoploss_on_exchange_multi(default_conf, ticker, fee, } stoploss_order_closed = stoploss_order_open.copy() stoploss_order_closed['status'] = 'closed' + stoploss_order_closed['filled'] = stoploss_order_closed['amount'] + # Sell first trade based on stoploss, keep 2nd and 3rd trade open stoploss_order_mock = MagicMock( side_effect=[stoploss_order_closed, stoploss_order_open, stoploss_order_open]) @@ -98,7 +100,7 @@ def test_may_execute_sell_stoploss_on_exchange_multi(default_conf, ticker, fee, assert cancel_order_mock.call_count == 1 # Wallets must be updated between stoploss cancellation and selling, and will be updated again # during update_trade_state - assert wallets_mock.call_count == 3 + assert wallets_mock.call_count == 4 trade = trades[0] assert trade.sell_reason == SellType.STOPLOSS_ON_EXCHANGE.value