diff --git a/freqtrade/freqtradebot.py b/freqtrade/freqtradebot.py index 93f5fdf3d..bf3b62e85 100644 --- a/freqtrade/freqtradebot.py +++ b/freqtrade/freqtradebot.py @@ -756,7 +756,7 @@ class FreqtradeBot(LoggingMixin): logger.error(f'Unable to place a stoploss order on exchange. {e}') logger.warning('Exiting the trade forcefully') self.execute_trade_exit(trade, trade.stop_loss, sell_reason=SellCheckTuple( - sell_type=SellType.EMERGENCY_SELL)) + sell_type=SellType.EMERGENCY_SELL), side=trade.exit_side) except ExchangeError: trade.stoploss_order_id = None @@ -876,7 +876,7 @@ class FreqtradeBot(LoggingMixin): if should_exit.sell_flag: logger.info(f'Exit for {trade.pair} detected. Reason: {should_exit.sell_type}') - self.execute_trade_exit(trade, exit_rate, should_exit) + self.execute_trade_exit(trade, exit_rate, should_exit, side=trade.exit_side) return True return False @@ -1081,21 +1081,28 @@ class FreqtradeBot(LoggingMixin): raise DependencyException( f"Not enough amount to exit trade. Trade-amount: {amount}, Wallet: {wallet_amount}") - def execute_trade_exit(self, trade: Trade, limit: float, sell_reason: SellCheckTuple) -> bool: + def execute_trade_exit( + self, + trade: Trade, + limit: float, + sell_reason: SellCheckTuple, # TODO-lev update to exit_reason + side: str + ) -> bool: """ Executes a trade exit for the given trade and limit :param trade: Trade instance :param limit: limit rate for the sell order :param sell_reason: Reason the sell was triggered + :param side: "buy" or "sell" :return: True if it succeeds (supported) False (not supported) """ - sell_type = 'sell' # TODO-lev: Update to exit + exit_type = 'sell' # TODO-lev: Update to exit if sell_reason.sell_type in (SellType.STOP_LOSS, SellType.TRAILING_STOP_LOSS): - sell_type = 'stoploss' + exit_type = 'stoploss' # if stoploss is on exchange and we are on dry_run mode, # we consider the sell price stop price - if self.config['dry_run'] and sell_type == 'stoploss' \ + if self.config['dry_run'] and exit_type == 'stoploss' \ and self.strategy.order_types['stoploss_on_exchange']: limit = trade.stop_loss @@ -1119,7 +1126,7 @@ class FreqtradeBot(LoggingMixin): except InvalidOrderException: logger.exception(f"Could not cancel stoploss order {trade.stoploss_order_id}") - order_type = self.strategy.order_types[sell_type] + order_type = self.strategy.order_types[exit_type] if sell_reason.sell_type == SellType.EMERGENCY_SELL: # Emergency sells (default to market!) order_type = self.strategy.order_types.get("emergencysell", "market") @@ -1143,10 +1150,10 @@ class FreqtradeBot(LoggingMixin): order = self.exchange.create_order( pair=trade.pair, ordertype=order_type, - side="sell", amount=amount, rate=limit, - time_in_force=time_in_force + time_in_force=time_in_force, + side=trade.exit_side ) except InsufficientFundsError as e: logger.warning(f"Unable to place order {e}.") @@ -1154,7 +1161,7 @@ class FreqtradeBot(LoggingMixin): self.handle_insufficient_funds(trade) return False - order_obj = Order.parse_from_ccxt_object(order, trade.pair, 'sell') + order_obj = Order.parse_from_ccxt_object(order, trade.pair, trade.exit_side) trade.orders.append(order_obj) trade.open_order_id = order['id'] diff --git a/freqtrade/rpc/rpc.py b/freqtrade/rpc/rpc.py index 7facacf97..8128313b7 100644 --- a/freqtrade/rpc/rpc.py +++ b/freqtrade/rpc/rpc.py @@ -561,7 +561,7 @@ class RPC: current_rate = self._freqtrade.exchange.get_rate( trade.pair, refresh=False, side="sell") sell_reason = SellCheckTuple(sell_type=SellType.FORCE_SELL) - self._freqtrade.execute_trade_exit(trade, current_rate, sell_reason) + self._freqtrade.execute_trade_exit(trade, current_rate, sell_reason, side="sell") # ---- EOF def _exec_forcesell ---- if self._freqtrade.state != State.RUNNING: diff --git a/tests/test_freqtradebot.py b/tests/test_freqtradebot.py index 0e92dbc84..37289888c 100644 --- a/tests/test_freqtradebot.py +++ b/tests/test_freqtradebot.py @@ -2651,6 +2651,7 @@ def test_handle_cancel_exit_cancel_exception(mocker, default_conf) -> None: assert freqtrade.handle_cancel_exit(trade, order, reason) == 'error cancelling order' +# TODO-lev: Add short tests def test_execute_trade_exit_up(default_conf, ticker, fee, ticker_sell_up, mocker) -> None: rpc_mock = patch_RPCManager(mocker) patch_exchange(mocker) @@ -2679,15 +2680,16 @@ def test_execute_trade_exit_up(default_conf, ticker, fee, ticker_sell_up, mocker fetch_ticker=ticker_sell_up ) # Prevented sell ... - freqtrade.execute_trade_exit(trade=trade, limit=ticker_sell_up()['bid'], + # TODO-lev: side="buy" + freqtrade.execute_trade_exit(trade=trade, limit=ticker_sell_up()['bid'], side="sell", sell_reason=SellCheckTuple(sell_type=SellType.ROI)) assert rpc_mock.call_count == 0 assert freqtrade.strategy.confirm_trade_exit.call_count == 1 # Repatch with true freqtrade.strategy.confirm_trade_exit = MagicMock(return_value=True) - - freqtrade.execute_trade_exit(trade=trade, limit=ticker_sell_up()['bid'], + # TODO-lev: side="buy" + freqtrade.execute_trade_exit(trade=trade, limit=ticker_sell_up()['bid'], side="sell", sell_reason=SellCheckTuple(sell_type=SellType.ROI)) assert freqtrade.strategy.confirm_trade_exit.call_count == 1 @@ -2739,8 +2741,8 @@ def test_execute_trade_exit_down(default_conf, ticker, fee, ticker_sell_down, mo 'freqtrade.exchange.Exchange', fetch_ticker=ticker_sell_down ) - - freqtrade.execute_trade_exit(trade=trade, limit=ticker_sell_down()['bid'], + # TODO-lev: side="buy" + freqtrade.execute_trade_exit(trade=trade, limit=ticker_sell_down()['bid'], side="sell", sell_reason=SellCheckTuple(sell_type=SellType.STOP_LOSS)) assert rpc_mock.call_count == 2 @@ -2800,8 +2802,8 @@ def test_execute_trade_exit_custom_exit_price(default_conf, ticker, fee, ticker_ # Set a custom exit price freqtrade.strategy.custom_exit_price = lambda **kwargs: 1.170e-05 - - freqtrade.execute_trade_exit(trade=trade, limit=ticker_sell_up()['bid'], + # TODO-lev: side="buy" + freqtrade.execute_trade_exit(trade=trade, limit=ticker_sell_up()['bid'], side="sell", sell_reason=SellCheckTuple(sell_type=SellType.SELL_SIGNAL)) # Sell price must be different to default bid price @@ -2863,7 +2865,8 @@ def test_execute_trade_exit_down_stoploss_on_exchange_dry_run(default_conf, tick # Setting trade stoploss to 0.01 trade.stop_loss = 0.00001099 * 0.99 - freqtrade.execute_trade_exit(trade=trade, limit=ticker_sell_down()['bid'], + # TODO-lev: side="buy" + freqtrade.execute_trade_exit(trade=trade, limit=ticker_sell_down()['bid'], side="sell", sell_reason=SellCheckTuple(sell_type=SellType.STOP_LOSS)) assert rpc_mock.call_count == 2 @@ -2919,7 +2922,8 @@ def test_execute_trade_exit_sloe_cancel_exception( freqtrade.config['dry_run'] = False trade.stoploss_order_id = "abcd" - freqtrade.execute_trade_exit(trade=trade, limit=1234, + # TODO-lev: side="buy" + freqtrade.execute_trade_exit(trade=trade, limit=1234, side="sell", sell_reason=SellCheckTuple(sell_type=SellType.STOP_LOSS)) assert create_order_mock.call_count == 2 assert log_has('Could not cancel stoploss order abcd', caplog) @@ -2970,7 +2974,8 @@ def test_execute_trade_exit_with_stoploss_on_exchange(default_conf, ticker, fee, fetch_ticker=ticker_sell_up ) - freqtrade.execute_trade_exit(trade=trade, limit=ticker_sell_up()['bid'], + # TODO-lev: side="buy" + freqtrade.execute_trade_exit(trade=trade, limit=ticker_sell_up()['bid'], side="sell", sell_reason=SellCheckTuple(sell_type=SellType.STOP_LOSS)) trade = Trade.query.first() @@ -3078,7 +3083,8 @@ def test_execute_trade_exit_market_order(default_conf, ticker, fee, ) freqtrade.config['order_types']['sell'] = 'market' - freqtrade.execute_trade_exit(trade=trade, limit=ticker_sell_up()['bid'], + # TODO-lev: side="buy" + freqtrade.execute_trade_exit(trade=trade, limit=ticker_sell_up()['bid'], side="sell", sell_reason=SellCheckTuple(sell_type=SellType.ROI)) assert not trade.is_open @@ -3137,8 +3143,9 @@ def test_execute_trade_exit_insufficient_funds_error(default_conf, ticker, fee, ) sell_reason = SellCheckTuple(sell_type=SellType.ROI) + # TODO-lev: side="buy" assert not freqtrade.execute_trade_exit(trade=trade, limit=ticker_sell_up()['bid'], - sell_reason=sell_reason) + sell_reason=sell_reason, side="sell") assert mock_insuf.call_count == 1 @@ -3394,7 +3401,8 @@ def test_locked_pairs(default_conf, ticker, fee, ticker_sell_down, mocker, caplo fetch_ticker=ticker_sell_down ) - freqtrade.execute_trade_exit(trade=trade, limit=ticker_sell_down()['bid'], + # TODO-lev: side="buy" + freqtrade.execute_trade_exit(trade=trade, limit=ticker_sell_down()['bid'], side="sell", sell_reason=SellCheckTuple(sell_type=SellType.STOP_LOSS)) trade.close(ticker_sell_down()['bid']) assert freqtrade.strategy.is_pair_locked(trade.pair)