diff --git a/freqtrade/persistence/trade_model.py b/freqtrade/persistence/trade_model.py index 1c1d87d0a..68a3c584b 100644 --- a/freqtrade/persistence/trade_model.py +++ b/freqtrade/persistence/trade_model.py @@ -493,7 +493,11 @@ class LocalTrade(): @property def open_orders_ids(self) -> list: - return [open_order.order_id for open_order in self.open_orders] + open_orders_ids_wo_sl = [] + for oo in self.open_orders: + if (oo.ft_order_side not in ['stoploss']): + open_orders_ids_wo_sl.append(oo.order_id) + return open_orders_ids_wo_sl def __init__(self, **kwargs): for key in kwargs: @@ -1391,13 +1395,18 @@ class Trade(ModelBase, LocalTrade): @hybrid_property def open_orders_ids(self) -> list: - return [open_order.order_id for open_order in self.open_orders] + open_orders_ids_wo_sl = [] + for oo in self.open_orders: + if (oo.ft_order_side not in ['stoploss']): + open_orders_ids_wo_sl.append(oo.order_id) + return open_orders_ids_wo_sl @open_orders_ids.expression def open_orders_ids(cls): return ( select(Order.order_id) .where(Order.ft_is_open is True) + .where(Order.ft_order_side != "stoploss") .where(Order.ft_trade_id == cls.id) .subquery() ) diff --git a/freqtrade/rpc/rpc.py b/freqtrade/rpc/rpc.py index c3759e03a..fed71bc17 100644 --- a/freqtrade/rpc/rpc.py +++ b/freqtrade/rpc/rpc.py @@ -870,9 +870,9 @@ class RPC: is_short = trade.is_short if not self._freqtrade.strategy.position_adjustment_enable: raise RPCException(f'position for {pair} already open - id: {trade.id}') - if trade.open_order_id is not None: + if trade.has_open_orders: raise RPCException(f'position for {pair} already open - id: {trade.id} ' - f'and has open order {trade.open_order_id}') + f'and has open order {trade.open_orders_ids}') else: if Trade.get_open_trade_count() >= self._config['max_open_trades']: raise RPCException("Maximum number of trades is reached.") @@ -909,17 +909,18 @@ class RPC: if not trade: logger.warning('cancel_open_order: Invalid trade_id received.') raise RPCException('Invalid trade_id.') - if not trade.open_order_id: + if not trade.has_open_orders: logger.warning('cancel_open_order: No open order for trade_id.') raise RPCException('No open order for trade_id.') - try: - order = self._freqtrade.exchange.fetch_order(trade.open_order_id, trade.pair) - except ExchangeError as e: - logger.info(f"Cannot query order for {trade} due to {e}.", exc_info=True) - raise RPCException("Order not found.") - self._freqtrade.handle_cancel_order(order, trade, CANCEL_REASON['USER_CANCEL']) - Trade.commit() + for open_order in trade.open_orders: + try: + order = self._freqtrade.exchange.fetch_order(open_order.order_id, trade.pair) + except ExchangeError as e: + logger.info(f"Cannot query order for {trade} due to {e}.", exc_info=True) + raise RPCException("Order not found.") + self._freqtrade.handle_cancel_order(order, trade, CANCEL_REASON['USER_CANCEL']) + Trade.commit() def _rpc_delete(self, trade_id: int) -> Dict[str, Union[str, int]]: """ @@ -934,12 +935,13 @@ class RPC: raise RPCException('invalid argument') # Try cancelling regular order if that exists - if trade.open_order_id: - try: - self._freqtrade.exchange.cancel_order(trade.open_order_id, trade.pair) - c_count += 1 - except (ExchangeError): - pass + if trade.has_open_orders: + for open_order in trade.open_orders: + try: + self._freqtrade.exchange.cancel_order(open_order.order_id, trade.pair) + c_count += 1 + except (ExchangeError): + pass # cancel stoploss on exchange ... if (self._freqtrade.strategy.order_types.get('stoploss_on_exchange') diff --git a/tests/rpc/test_rpc.py b/tests/rpc/test_rpc.py index 31d7ae37b..d04c9d2cf 100644 --- a/tests/rpc/test_rpc.py +++ b/tests/rpc/test_rpc.py @@ -42,7 +42,6 @@ def test_rpc_trade_status(default_conf, ticker, fee, mocker) -> None: 'strategy': ANY, 'enter_tag': ANY, 'timeframe': 5, - 'open_order_id': ANY, 'close_date': None, 'close_timestamp': None, 'open_rate': 1.098e-05, @@ -1093,7 +1092,8 @@ def test_rpc_force_entry(mocker, default_conf, ticker, fee, limit_buy_order_open trade = rpc._rpc_force_entry(pair, 0.0001, order_type='limit', stake_amount=0.05) assert trade.stake_amount == 0.05 assert trade.buy_tag == 'force_entry' - assert trade.open_order_id == 'mocked_limit_buy' + # assert trade.open_order_id == 'mocked_limit_buy' + assert trade.open_orders_ids[-1] == 'mocked_limit_buy' freqtradebot.strategy.position_adjustment_enable = True with pytest.raises(RPCException, match=r'position for LTC/BTC already open.*open order.*'): diff --git a/tests/test_freqtradebot.py b/tests/test_freqtradebot.py index 3b37ba0cb..3d849c920 100644 --- a/tests/test_freqtradebot.py +++ b/tests/test_freqtradebot.py @@ -2339,15 +2339,15 @@ def test_update_trade_state_exception(mocker, default_conf_usdt, is_short, limit # TODO: should not be magicmock trade = MagicMock() - trade.open_order_id = '123' trade.amount = 123 + open_order_id = '123' # Test raise of OperationalException exception mocker.patch( 'freqtrade.freqtradebot.FreqtradeBot.get_real_amount', side_effect=DependencyException() ) - freqtrade.update_trade_state(trade, trade.open_order_id) + freqtrade.update_trade_state(trade, open_order_id) assert log_has('Could not update trade amount: ', caplog) @@ -2357,13 +2357,13 @@ def test_update_trade_state_orderexception(mocker, default_conf_usdt, caplog) -> # TODO: should not be magicmock trade = MagicMock() - trade.open_order_id = '123' + open_order_id = '123' # Test raise of OperationalException exception grm_mock = mocker.patch("freqtrade.freqtradebot.FreqtradeBot.get_real_amount", MagicMock()) - freqtrade.update_trade_state(trade, trade.open_order_id) + freqtrade.update_trade_state(trade, open_order_id) assert grm_mock.call_count == 0 - assert log_has(f'Unable to fetch order {trade.open_order_id}: ', caplog) + assert log_has(f'Unable to fetch order {open_order_id}: ', caplog) @pytest.mark.parametrize("is_short", [False, True]) @@ -2848,7 +2848,7 @@ def test_adjust_entry_cancel( assert freqtrade.strategy.adjust_entry_price.call_count == 1 -@pytest.mark.parametrize("is_short", [False]) +@pytest.mark.parametrize("is_short", [False, True]) def test_adjust_entry_maintain_replace( default_conf_usdt, ticker_usdt, limit_buy_order_old, open_trade, limit_sell_order_old, fee, mocker, caplog, is_short