diff --git a/freqtrade/rpc/rpc.py b/freqtrade/rpc/rpc.py index b9d7cedd4..989cd9aca 100644 --- a/freqtrade/rpc/rpc.py +++ b/freqtrade/rpc/rpc.py @@ -731,7 +731,7 @@ class RPC: raise RPCException('trader is not running') if order_side == SignalDirection.SHORT and self._freqtrade.trading_mode == TradingMode.SPOT: - raise RPCException("Can't go short on Spot markets") + raise RPCException("Can't go short on Spot markets.") # Check if pair quote currency equals to the stake currency. stake_currency = self._freqtrade.config.get('stake_currency') diff --git a/freqtrade/rpc/telegram.py b/freqtrade/rpc/telegram.py index 1eee4e98b..ea105e0f8 100644 --- a/freqtrade/rpc/telegram.py +++ b/freqtrade/rpc/telegram.py @@ -155,8 +155,10 @@ class Telegram(RPCHandler): CommandHandler('start', self._start), CommandHandler('stop', self._stop), CommandHandler(['forcesell', 'forceexit'], self._forceexit), - CommandHandler(['forcebuy', 'forcelong'], partial(self._forceenter, order_side=SignalDirection.LONG)), - CommandHandler('forceshort', partial(self._forceenter, order_side=SignalDirection.SHORT)), + CommandHandler(['forcebuy', 'forcelong'], partial( + self._forceenter, order_side=SignalDirection.LONG)), + CommandHandler('forceshort', partial( + self._forceenter, order_side=SignalDirection.SHORT)), CommandHandler('trades', self._trades), CommandHandler('delete', self._delete_trade), CommandHandler('performance', self._performance), @@ -195,7 +197,7 @@ class Telegram(RPCHandler): pattern='update_sell_reason_performance'), CallbackQueryHandler(self._mix_tag_performance, pattern='update_mix_tag_performance'), CallbackQueryHandler(self._count, pattern='update_count'), - CallbackQueryHandler(self._forcebuy_inline), + CallbackQueryHandler(self._forceenter_inline), ] for handle in handles: self._updater.dispatcher.add_handler(handle) @@ -877,14 +879,15 @@ class Telegram(RPCHandler): except RPCException as e: self._send_msg(str(e)) - def _forcebuy_inline(self, update: Update, _: CallbackContext) -> None: + def _forceenter_inline(self, update: Update, _: CallbackContext) -> None: if update.callback_query: query = update.callback_query - pair, side = query.data.split('_||_') - order_side = SignalDirection(side) - query.answer() - query.edit_message_text(text=f"Manually entering {order_side} for {pair}") - self._forceenter_action(pair, None, order_side) + if query.data and '_||_' in query.data: + pair, side = query.data.split('_||_') + order_side = SignalDirection(side) + query.answer() + query.edit_message_text(text=f"Manually entering {order_side} for {pair}") + self._forceenter_action(pair, None, order_side) @staticmethod def _layout_inline_keyboard(buttons: List[InlineKeyboardButton], @@ -1281,14 +1284,14 @@ class Telegram(RPCHandler): :param update: message update :return: None """ - forcebuy_text = ("*/forcelong []:* `Instantly buys the given pair. " - "Optionally takes a rate at which to buy " - "(only applies to limit orders).` \n" - ) + forceenter_text = ("*/forcelong []:* `Instantly buys the given pair. " + "Optionally takes a rate at which to buy " + "(only applies to limit orders).` \n" + ) if self._rpc._freqtrade.trading_mode != TradingMode.SPOT: - forcebuy_text += ("*/forceshort []:* `Instantly shorts the given pair. " - "Optionally takes a rate at which to sell " - "(only applies to limit orders).` \n") + forceenter_text += ("*/forceshort []:* `Instantly shorts the given pair. " + "Optionally takes a rate at which to sell " + "(only applies to limit orders).` \n") message = ( "_BotControl_\n" "------------\n" @@ -1297,7 +1300,7 @@ class Telegram(RPCHandler): "*/stopbuy:* `Stops buying, but handles open trades gracefully` \n" "*/forceexit |all:* `Instantly exits the given trade or all trades, " "regardless of profit`\n" - f"{forcebuy_text if self._config.get('forcebuy_enable', False) else ''}" + f"{forceenter_text if self._config.get('forcebuy_enable', False) else ''}" "*/delete :* `Instantly delete the given trade in the database`\n" "*/whitelist:* `Show current whitelist` \n" "*/blacklist [pair]:* `Show current blacklist, or adds one or more pairs " diff --git a/tests/rpc/test_rpc.py b/tests/rpc/test_rpc.py index 1afda4b3d..90587160c 100644 --- a/tests/rpc/test_rpc.py +++ b/tests/rpc/test_rpc.py @@ -9,6 +9,7 @@ from numpy import isnan from freqtrade.edge import PairInfo from freqtrade.enums import State, TradingMode +from freqtrade.enums.signaltype import SignalDirection from freqtrade.exceptions import ExchangeError, InvalidOrderException, TemporaryError from freqtrade.persistence import Trade from freqtrade.persistence.pairlock_middleware import PairLocks @@ -1165,6 +1166,18 @@ def test_rpc_forceentry_disabled(mocker, default_conf) -> None: rpc._rpc_force_entry(pair, None) +def test_rpc_forceentry_wrong_mode(mocker, default_conf) -> None: + default_conf['forcebuy_enable'] = True + mocker.patch('freqtrade.rpc.telegram.Telegram', MagicMock()) + + freqtradebot = get_patched_freqtradebot(mocker, default_conf) + patch_get_signal(freqtradebot) + rpc = RPC(freqtradebot) + pair = 'ETH/BTC' + with pytest.raises(RPCException, match="Can't go short on Spot markets."): + rpc._rpc_force_entry(pair, None, order_side=SignalDirection.SHORT) + + @pytest.mark.usefixtures("init_persistence") def test_rpc_delete_lock(mocker, default_conf): freqtradebot = get_patched_freqtradebot(mocker, default_conf) diff --git a/tests/rpc/test_rpc_telegram.py b/tests/rpc/test_rpc_telegram.py index 4ba81f930..82ee9d884 100644 --- a/tests/rpc/test_rpc_telegram.py +++ b/tests/rpc/test_rpc_telegram.py @@ -1145,7 +1145,7 @@ def test_forceenter_handle(default_conf, update, mocker) -> None: telegram, freqtradebot, _ = get_telegram_testobject(mocker, default_conf) patch_get_signal(freqtradebot) - # /forcebuy ETH/BTC + # /forcelong ETH/BTC context = MagicMock() context.args = ["ETH/BTC"] telegram._forceenter(update=update, context=context, order_side=SignalDirection.LONG) @@ -1153,11 +1153,12 @@ def test_forceenter_handle(default_conf, update, mocker) -> None: assert fbuy_mock.call_count == 1 assert fbuy_mock.call_args_list[0][0][0] == 'ETH/BTC' assert fbuy_mock.call_args_list[0][0][1] is None + assert fbuy_mock.call_args_list[0][1]['order_side'] == SignalDirection.LONG # Reset and retry with specified price fbuy_mock = MagicMock(return_value=None) mocker.patch('freqtrade.rpc.RPC._rpc_force_entry', fbuy_mock) - # /forcebuy ETH/BTC 0.055 + # /forcelong ETH/BTC 0.055 context = MagicMock() context.args = ["ETH/BTC", "0.055"] telegram._forceenter(update=update, context=context, order_side=SignalDirection.LONG) @@ -1204,7 +1205,7 @@ def test_forceenter_no_pair(default_conf, update, mocker) -> None: update = MagicMock() update.callback_query = MagicMock() update.callback_query.data = 'XRP/USDT_||_long' - telegram._forcebuy_inline(update, None) + telegram._forceenter_inline(update, None) assert fbuy_mock.call_count == 1