diff --git a/freqtrade/rpc/__init__.py b/freqtrade/rpc/__init__.py index e3922a161..163e0a8aa 100644 --- a/freqtrade/rpc/__init__.py +++ b/freqtrade/rpc/__init__.py @@ -272,38 +272,6 @@ def rpc_trade_statistics(stake_currency, fiat_display_currency) -> None: 'best_rate': round(bp_rate * 100, 2) }) - # Message to display - markdown_msg = """ -*ROI:* Close trades - ∙ `{profit_closed_coin:.8f} {coin} ({profit_closed_percent:.2f}%)` - ∙ `{profit_closed_fiat:.3f} {fiat}` -*ROI:* All trades - ∙ `{profit_all_coin:.8f} {coin} ({profit_all_percent:.2f}%)` - ∙ `{profit_all_fiat:.3f} {fiat}` - -*Total Trade Count:* `{trade_count}` -*First Trade opened:* `{first_trade_date}` -*Latest Trade opened:* `{latest_trade_date}` -*Avg. Duration:* `{avg_duration}` -*Best Performing:* `{best_pair}: {best_rate:.2f}%` - """.format( - coin=stake_currency, - fiat=fiat_display_currency, - profit_closed_coin=profit_closed_coin, - profit_closed_percent=profit_closed_percent, - profit_closed_fiat=profit_closed_fiat, - profit_all_coin=profit_all_coin, - profit_all_percent=profit_all_percent, - profit_all_fiat=profit_all_fiat, - trade_count=len(trades), - first_trade_date=arrow.get(trades[0].open_date).humanize(), - latest_trade_date=arrow.get(trades[-1].open_date).humanize(), - avg_duration=str(timedelta(seconds=sum(durations) / float(len(durations)))).split('.')[0], - best_pair=bp_pair, - best_rate=round(bp_rate * 100, 2), - ) - return markdown_msg - def rpc_balance(fiat_display_currency): """ @@ -369,7 +337,7 @@ def rpc_forcesell(trade_id) -> None: Sells the given trade at current price :return: error or None """ - def _exec_forcesell(trade: Trade) -> None: + def _exec_forcesell(trade: Trade) -> str: # Check if there is there is an open order if trade.open_order_id: order = exchange.get_order(trade.open_order_id) @@ -389,6 +357,7 @@ def rpc_forcesell(trade_id) -> None: current_rate = exchange.get_ticker(trade.pair, False)['bid'] from freqtrade.main import execute_sell execute_sell(trade, current_rate) + # ---- EOF def _exec_forcesell ---- if get_state() != State.RUNNING: return (True, '`trader is not running`') @@ -409,6 +378,7 @@ def rpc_forcesell(trade_id) -> None: return (True, 'Invalid argument.') _exec_forcesell(trade) + return (False, '') def rpc_performance() -> None: diff --git a/freqtrade/rpc/telegram.py b/freqtrade/rpc/telegram.py index c0a4c96af..ea170baa1 100644 --- a/freqtrade/rpc/telegram.py +++ b/freqtrade/rpc/telegram.py @@ -206,6 +206,7 @@ def _profit(bot: Bot, update: Update) -> None: send_msg(stats, bot=bot) return + # Message to display markdown_msg = """ *ROI:* Close trades ∙ `{profit_closed_coin:.8f} {coin} ({profit_closed_percent:.2f}%)` diff --git a/freqtrade/tests/rpc/test_rpc.py b/freqtrade/tests/rpc/test_rpc.py index 3c4ca9f23..63ceefc39 100644 --- a/freqtrade/tests/rpc/test_rpc.py +++ b/freqtrade/tests/rpc/test_rpc.py @@ -70,6 +70,86 @@ def test_send_msg_telegram_disabled(mocker): assert telegram_mock.call_count == 0 +def test_rpc_forcesell(default_conf, update, ticker, mocker): + mocker.patch.dict('freqtrade.main._CONF', default_conf) + mocker.patch('freqtrade.main.get_signal', side_effect=lambda s, t: (True, False)) + mocker.patch('freqtrade.main.rpc.send_msg', MagicMock()) + mocker.patch.multiple('freqtrade.rpc.telegram', + _CONF=default_conf, + init=MagicMock()) + cancel_order_mock = MagicMock() + mocker.patch.multiple('freqtrade.main.exchange', + validate_pairs=MagicMock(), + get_ticker=ticker, + cancel_order=cancel_order_mock, + get_order=MagicMock(return_value={ + 'closed': True, + 'type': 'LIMIT_BUY', + })) + main.init(default_conf, create_engine('sqlite://')) + + misc.update_state(misc.State.STOPPED) + (error, res) = rpc.rpc_forcesell(None) + assert error + assert res == '`trader is not running`' + misc.update_state(misc.State.RUNNING) + (error, res) = rpc.rpc_forcesell(None) + assert error + assert res == 'Invalid argument.' + + (error, res) = rpc.rpc_forcesell('all') + assert not error + assert res == '' + + main.create_trade(0.001, 5) + (error, res) = rpc.rpc_forcesell('all') + assert not error + assert res == '' + + (error, res) = rpc.rpc_forcesell('1') + assert not error + assert res == '' + + misc.update_state(misc.State.STOPPED) + + (error, res) = rpc.rpc_forcesell(None) + assert error + assert res == '`trader is not running`' + + (error, res) = rpc.rpc_forcesell('all') + assert error + assert res == '`trader is not running`' + + misc.update_state(misc.State.RUNNING) + + assert cancel_order_mock.call_count == 0 + # make an limit-buy open trade + mocker.patch.multiple('freqtrade.exchange', + get_order=MagicMock(return_value={ + 'closed': None, + 'type': 'LIMIT_BUY' + })) + # check that the trade is called, which is done + # by ensuring exchange.cancel_order is called + (error, res) = rpc.rpc_forcesell('1') + assert not error + assert res == '' + assert cancel_order_mock.call_count == 1 + + main.create_trade(0.001, 5) + # make an limit-sell open trade + mocker.patch.multiple('freqtrade.exchange', + get_order=MagicMock(return_value={ + 'closed': None, + 'type': 'LIMIT_SELL' + })) + (error, res) = rpc.rpc_forcesell('2') + assert not error + assert res == '' + # status quo, no exchange calls + assert cancel_order_mock.call_count == 1 + + def test_rpc_trade_status(default_conf, update, ticker, mocker): mocker.patch.dict('freqtrade.main._CONF', default_conf) mocker.patch('freqtrade.main.get_signal', side_effect=lambda s, t: (True, False))