diff --git a/freqtrade/rpc/telegram.py b/freqtrade/rpc/telegram.py index 03797bf93..5f1b1fabd 100644 --- a/freqtrade/rpc/telegram.py +++ b/freqtrade/rpc/telegram.py @@ -224,9 +224,9 @@ class Telegram(RPCHandler): is_fill = msg['type'] in [RPCMessageType.BUY_FILL, RPCMessageType.SHORT_FILL] emoji = '\N{CHECK MARK}' if is_fill else '\N{LARGE BLUE CIRCLE}' - enter_side = {'enter': 'Long', 'entered': 'Longed'} if msg['type'] \ - in [RPCMessageType.BUY_FILL, RPCMessageType.BUY] \ - else {'enter': 'Short', 'entered': 'Shorted'} + enter_side = ({'enter': 'Long', 'entered': 'Longed'} if msg['type'] + in [RPCMessageType.BUY_FILL, RPCMessageType.BUY] + else {'enter': 'Short', 'entered': 'Shorted'}) message = ( f"{emoji} *{msg['exchange']}:*" f" {enter_side['entered'] if is_fill else enter_side['enter']} {msg['pair']}" @@ -259,6 +259,8 @@ class Telegram(RPCHandler): msg['enter_tag'] = msg['enter_tag'] if "enter_tag" in msg.keys() else None msg['emoji'] = self._get_sell_emoji(msg) + msg['leverage_text'] = (f"*Leverage:* `{msg['leverage']:.1f}`\n" + if msg.get('leverage', None) is not None else "") # Check if all sell properties are available. # This might not be the case if the message origin is triggered by /forcesell @@ -272,20 +274,19 @@ class Telegram(RPCHandler): else: msg['profit_extra'] = '' is_fill = msg['type'] == RPCMessageType.SELL_FILL - message = "".join([ - f"{msg['emoji']} *{msg['exchange']}:* ", - f"{'Exited' if is_fill else 'Exiting'} {msg['pair']} (#{msg['trade_id']})\n", - f"*{'Profit' if is_fill else 'Unrealized Profit'}:* ", - f"`{msg['profit_ratio']:.2%}{msg['profit_extra']}`\n", - f"*Enter Tag:* `{msg['enter_tag']}`\n", - f"*Exit Reason:* `{msg['sell_reason']}`\n", - f"*Duration:* `{msg['duration']} ({msg['duration_min']:.1f} min)`\n", - f"*Direction:* `{msg['direction']}`\n", - f"*Leverage:* `{msg['leverage']:.1f}`\n" if - msg.get('leverage', None) is not None else "", - f"*Amount:* `{msg['amount']:.8f}`\n", + message = ( + f"{msg['emoji']} *{msg['exchange']}:* " + f"{'Exited' if is_fill else 'Exiting'} {msg['pair']} (#{msg['trade_id']})\n" + f"*{'Profit' if is_fill else 'Unrealized Profit'}:* " + f"`{msg['profit_ratio']:.2%}{msg['profit_extra']}`\n" + f"*Enter Tag:* `{msg['enter_tag']}`\n" + f"*Exit Reason:* `{msg['sell_reason']}`\n" + f"*Duration:* `{msg['duration']} ({msg['duration_min']:.1f} min)`\n" + f"*Direction:* `{msg['direction']}`\n" + f"{msg['leverage_text']}" + f"*Amount:* `{msg['amount']:.8f}`\n" f"*Open Rate:* `{msg['open_rate']:.8f}`\n" - ]) + ) if msg['type'] == RPCMessageType.SELL: message += (f"*Current Rate:* `{msg['current_rate']:.8f}`\n" f"*Close Rate:* `{msg['limit']:.8f}`") diff --git a/tests/rpc/test_rpc_telegram.py b/tests/rpc/test_rpc_telegram.py index 32b2a45a5..ac74f1e88 100644 --- a/tests/rpc/test_rpc_telegram.py +++ b/tests/rpc/test_rpc_telegram.py @@ -1651,10 +1651,9 @@ def test_show_config_handle(default_conf, update, mocker) -> None: assert '*Initial Stoploss:* `-0.1`' in msg_mock.call_args_list[0][0][0] -@pytest.mark.parametrize( - 'message_type,enter,enter_signal,leverage', - [(RPCMessageType.BUY, 'Long', 'long_signal_01', None), - (RPCMessageType.SHORT, 'Short', 'short_signal_01', 2.0)]) +@pytest.mark.parametrize('message_type,enter,enter_signal,leverage', [ + (RPCMessageType.BUY, 'Long', 'long_signal_01', None), + (RPCMessageType.SHORT, 'Short', 'short_signal_01', 2.0)]) def test_send_msg_buy_notification(default_conf, mocker, caplog, message_type, enter, enter_signal, leverage) -> None: @@ -1704,10 +1703,9 @@ def test_send_msg_buy_notification(default_conf, mocker, caplog, message_type, msg_mock.call_args_list[0][1]['disable_notification'] is True -@pytest.mark.parametrize( - 'message_type,enter,enter_signal', - [(RPCMessageType.BUY_CANCEL, 'Long', 'long_signal_01'), - (RPCMessageType.SHORT_CANCEL, 'Short', 'short_signal_01')]) +@pytest.mark.parametrize('message_type,enter,enter_signal', [ + (RPCMessageType.BUY_CANCEL, 'Long', 'long_signal_01'), + (RPCMessageType.SHORT_CANCEL, 'Short', 'short_signal_01')]) def test_send_msg_buy_cancel_notification(default_conf, mocker, message_type, enter, enter_signal) -> None: @@ -1754,10 +1752,9 @@ def test_send_msg_protection_notification(default_conf, mocker, time_machine) -> "*All pairs* will be locked until `2021-09-01 06:45:00`.") -@pytest.mark.parametrize( - 'message_type,entered,enter_signal,leverage', - [(RPCMessageType.BUY_FILL, 'Longed', 'long_signal_01', None), - (RPCMessageType.SHORT_FILL, 'Shorted', 'short_signal_01', 2.0)]) +@pytest.mark.parametrize('message_type,entered,enter_signal,leverage', [ + (RPCMessageType.BUY_FILL, 'Longed', 'long_signal_01', None), + (RPCMessageType.SHORT_FILL, 'Shorted', 'short_signal_01', 2.0)]) def test_send_msg_buy_fill_notification(default_conf, mocker, message_type, entered, enter_signal, leverage) -> None: @@ -1779,15 +1776,15 @@ def test_send_msg_buy_fill_notification(default_conf, mocker, message_type, ente 'amount': 1333.3333333333335, 'open_date': arrow.utcnow().shift(hours=-1) }) - message = [f'\N{CHECK MARK} *Binance:* {entered} ETH/BTC (#1)\n', - f'*Enter Tag:* `{enter_signal}`\n', - f'*Leverage:* `{leverage}`\n' if leverage else '', - '*Amount:* `1333.33333333`\n', - '*Open Rate:* `0.00001099`\n', - '*Total:* `(0.00100000 BTC, 12.345 USD)`'] - # raise ValueError(msg_mock.call_args[0][0]) - assert msg_mock.call_args[0][0] \ - == "".join(message) + leverage_text = f'*Leverage:* `{leverage}`\n' if leverage else '' + assert msg_mock.call_args[0][0] == ( + f'\N{CHECK MARK} *Binance:* {entered} ETH/BTC (#1)\n' + f'*Enter Tag:* `{enter_signal}`\n' + f"{leverage_text}" + '*Amount:* `1333.33333333`\n' + '*Open Rate:* `0.00001099`\n' + '*Total:* `(0.00100000 BTC, 12.345 USD)`' + ) def test_send_msg_sell_notification(default_conf, mocker) -> None: @@ -1818,19 +1815,19 @@ def test_send_msg_sell_notification(default_conf, mocker) -> None: 'open_date': arrow.utcnow().shift(hours=-1), 'close_date': arrow.utcnow(), }) - assert msg_mock.call_args[0][0] \ - == ('\N{WARNING SIGN} *Binance:* Exiting KEY/ETH (#1)\n' - '*Unrealized Profit:* `-57.41% (loss: -0.05746268 ETH / -24.812 USD)`\n' - '*Enter Tag:* `buy_signal1`\n' - '*Exit Reason:* `stop_loss`\n' - '*Duration:* `1:00:00 (60.0 min)`\n' - '*Direction:* `Long`\n' - '*Leverage:* `1.0`\n' - '*Amount:* `1333.33333333`\n' - '*Open Rate:* `0.00007500`\n' - '*Current Rate:* `0.00003201`\n' - '*Close Rate:* `0.00003201`' - ) + assert msg_mock.call_args[0][0] == ( + '\N{WARNING SIGN} *Binance:* Exiting KEY/ETH (#1)\n' + '*Unrealized Profit:* `-57.41% (loss: -0.05746268 ETH / -24.812 USD)`\n' + '*Enter Tag:* `buy_signal1`\n' + '*Exit Reason:* `stop_loss`\n' + '*Duration:* `1:00:00 (60.0 min)`\n' + '*Direction:* `Long`\n' + '*Leverage:* `1.0`\n' + '*Amount:* `1333.33333333`\n' + '*Open Rate:* `0.00007500`\n' + '*Current Rate:* `0.00003201`\n' + '*Close Rate:* `0.00003201`' + ) msg_mock.reset_mock() telegram.send_msg({ @@ -1853,18 +1850,18 @@ def test_send_msg_sell_notification(default_conf, mocker) -> None: 'open_date': arrow.utcnow().shift(days=-1, hours=-2, minutes=-30), 'close_date': arrow.utcnow(), }) - assert msg_mock.call_args[0][0] \ - == ('\N{WARNING SIGN} *Binance:* Exiting KEY/ETH (#1)\n' - '*Unrealized Profit:* `-57.41%`\n' - '*Enter Tag:* `buy_signal1`\n' - '*Exit Reason:* `stop_loss`\n' - '*Duration:* `1 day, 2:30:00 (1590.0 min)`\n' - '*Direction:* `Long`\n' - '*Amount:* `1333.33333333`\n' - '*Open Rate:* `0.00007500`\n' - '*Current Rate:* `0.00003201`\n' - '*Close Rate:* `0.00003201`' - ) + assert msg_mock.call_args[0][0] == ( + '\N{WARNING SIGN} *Binance:* Exiting KEY/ETH (#1)\n' + '*Unrealized Profit:* `-57.41%`\n' + '*Enter Tag:* `buy_signal1`\n' + '*Exit Reason:* `stop_loss`\n' + '*Duration:* `1 day, 2:30:00 (1590.0 min)`\n' + '*Direction:* `Long`\n' + '*Amount:* `1333.33333333`\n' + '*Open Rate:* `0.00007500`\n' + '*Current Rate:* `0.00003201`\n' + '*Close Rate:* `0.00003201`' + ) # Reset singleton function to avoid random breaks telegram._rpc._fiat_converter.convert_amount = old_convamount @@ -1882,9 +1879,9 @@ def test_send_msg_sell_cancel_notification(default_conf, mocker) -> None: 'pair': 'KEY/ETH', 'reason': 'Cancelled on exchange' }) - assert msg_mock.call_args[0][0] \ - == ('\N{WARNING SIGN} *Binance:* Cancelling exit Order for KEY/ETH (#1).' - ' Reason: Cancelled on exchange.') + assert msg_mock.call_args[0][0] == ( + '\N{WARNING SIGN} *Binance:* Cancelling exit Order for KEY/ETH (#1).' + ' Reason: Cancelled on exchange.') msg_mock.reset_mock() telegram.send_msg({ @@ -1894,17 +1891,15 @@ def test_send_msg_sell_cancel_notification(default_conf, mocker) -> None: 'pair': 'KEY/ETH', 'reason': 'timeout' }) - assert msg_mock.call_args[0][0] \ - == ('\N{WARNING SIGN} *Binance:* Cancelling exit Order for KEY/ETH (#1).' - ' Reason: timeout.') + assert msg_mock.call_args[0][0] == ( + '\N{WARNING SIGN} *Binance:* Cancelling exit Order for KEY/ETH (#1). Reason: timeout.') # Reset singleton function to avoid random breaks telegram._rpc._fiat_converter.convert_amount = old_convamount -@pytest.mark.parametrize( - 'direction,enter_signal,leverage', - [('Long', 'long_signal_01', None), - ('Short', 'short_signal_01', 2.0)]) +@pytest.mark.parametrize('direction,enter_signal,leverage', [ + ('Long', 'long_signal_01', None), + ('Short', 'short_signal_01', 2.0)]) def test_send_msg_sell_fill_notification(default_conf, mocker, direction, enter_signal, leverage) -> None: @@ -1932,18 +1927,20 @@ def test_send_msg_sell_fill_notification(default_conf, mocker, direction, 'open_date': arrow.utcnow().shift(days=-1, hours=-2, minutes=-30), 'close_date': arrow.utcnow(), }) - message = ['\N{WARNING SIGN} *Binance:* Exited KEY/ETH (#1)\n', - '*Profit:* `-57.41%`\n', - f'*Enter Tag:* `{enter_signal}`\n', - '*Exit Reason:* `stop_loss`\n', - '*Duration:* `1 day, 2:30:00 (1590.0 min)`\n', - f"*Direction:* `{direction}`\n", - f"*Leverage:* `{leverage:.1f}`\n" if leverage is not None else "", - '*Amount:* `1333.33333333`\n', - '*Open Rate:* `0.00007500`\n', - '*Close Rate:* `0.00003201`'] - assert msg_mock.call_args[0][0] \ - == "".join(message) + + leverage_text = f'*Leverage:* `{leverage}`\n' if leverage else '' + assert msg_mock.call_args[0][0] == ( + '\N{WARNING SIGN} *Binance:* Exited KEY/ETH (#1)\n' + '*Profit:* `-57.41%`\n' + f'*Enter Tag:* `{enter_signal}`\n' + '*Exit Reason:* `stop_loss`\n' + '*Duration:* `1 day, 2:30:00 (1590.0 min)`\n' + f"*Direction:* `{direction}`\n" + f"{leverage_text}" + '*Amount:* `1333.33333333`\n' + '*Open Rate:* `0.00007500`\n' + '*Close Rate:* `0.00003201`' + ) def test_send_msg_status_notification(default_conf, mocker) -> None: @@ -1982,12 +1979,11 @@ def test_send_msg_unknown_type(default_conf, mocker) -> None: }) -@pytest.mark.parametrize( - 'message_type,enter,enter_signal,leverage', - [(RPCMessageType.BUY, 'Long', 'long_signal_01', None), - (RPCMessageType.SHORT, 'Short', 'short_signal_01', 2.0)]) -def test_send_msg_buy_notification_no_fiat(default_conf, mocker, message_type, - enter, enter_signal, leverage) -> None: +@pytest.mark.parametrize('message_type,enter,enter_signal,leverage', [ + (RPCMessageType.BUY, 'Long', 'long_signal_01', None), + (RPCMessageType.SHORT, 'Short', 'short_signal_01', 2.0)]) +def test_send_msg_buy_notification_no_fiat( + default_conf, mocker, message_type, enter, enter_signal, leverage) -> None: del default_conf['fiat_display_currency'] telegram, _, msg_mock = get_telegram_testobject(mocker, default_conf) @@ -2009,22 +2005,23 @@ def test_send_msg_buy_notification_no_fiat(default_conf, mocker, message_type, 'open_date': arrow.utcnow().shift(hours=-1) }) - message = [f'\N{LARGE BLUE CIRCLE} *Binance:* {enter} ETH/BTC (#1)\n', - f'*Enter Tag:* `{enter_signal}`\n', - f'*Leverage:* `{leverage}`\n' if leverage else '', - '*Amount:* `1333.33333333`\n', - '*Open Rate:* `0.00001099`\n', - '*Current Rate:* `0.00001099`\n', - '*Total:* `(0.00100000 BTC)`'] - assert msg_mock.call_args[0][0] == "".join(message) + leverage_text = f'*Leverage:* `{leverage}`\n' if leverage else '' + assert msg_mock.call_args[0][0] == ( + f'\N{LARGE BLUE CIRCLE} *Binance:* {enter} ETH/BTC (#1)\n' + f'*Enter Tag:* `{enter_signal}`\n' + f'{leverage_text}' + '*Amount:* `1333.33333333`\n' + '*Open Rate:* `0.00001099`\n' + '*Current Rate:* `0.00001099`\n' + '*Total:* `(0.00100000 BTC)`' + ) -@pytest.mark.parametrize( - 'direction,enter_signal,leverage', - [('Long', 'long_signal_01', None), - ('Short', 'short_signal_01', 2.0)]) -def test_send_msg_sell_notification_no_fiat(default_conf, mocker, direction, - enter_signal, leverage) -> None: +@pytest.mark.parametrize('direction,enter_signal,leverage', [ + ('Long', 'long_signal_01', None), + ('Short', 'short_signal_01', 2.0)]) +def test_send_msg_sell_notification_no_fiat( + default_conf, mocker, direction, enter_signal, leverage) -> None: del default_conf['fiat_display_currency'] telegram, _, msg_mock = get_telegram_testobject(mocker, default_conf) @@ -2051,19 +2048,20 @@ def test_send_msg_sell_notification_no_fiat(default_conf, mocker, direction, 'close_date': arrow.utcnow(), }) - message = ['\N{WARNING SIGN} *Binance:* Exiting KEY/ETH (#1)\n', - '*Unrealized Profit:* `-57.41%`\n', - f'*Enter Tag:* `{enter_signal}`\n', - '*Exit Reason:* `stop_loss`\n', - '*Duration:* `2:35:03 (155.1 min)`\n', - f'*Direction:* `{direction}`\n', - f'*Leverage:* `{leverage}`\n' if leverage else '', - '*Amount:* `1333.33333333`\n', - '*Open Rate:* `0.00007500`\n', - '*Current Rate:* `0.00003201`\n', - '*Close Rate:* `0.00003201`', - ] - assert msg_mock.call_args[0][0] == "".join(message) + leverage_text = f'*Leverage:* `{leverage}`\n' if leverage else '' + assert msg_mock.call_args[0][0] == ( + '\N{WARNING SIGN} *Binance:* Exiting KEY/ETH (#1)\n' + '*Unrealized Profit:* `-57.41%`\n' + f'*Enter Tag:* `{enter_signal}`\n' + '*Exit Reason:* `stop_loss`\n' + '*Duration:* `2:35:03 (155.1 min)`\n' + f'*Direction:* `{direction}`\n' + f'{leverage_text}' + '*Amount:* `1333.33333333`\n' + '*Open Rate:* `0.00007500`\n' + '*Current Rate:* `0.00003201`\n' + '*Close Rate:* `0.00003201`' + ) @pytest.mark.parametrize('msg,expected', [ diff --git a/tests/test_freqtradebot.py b/tests/test_freqtradebot.py index a3ab44839..5dd11a642 100644 --- a/tests/test_freqtradebot.py +++ b/tests/test_freqtradebot.py @@ -2949,15 +2949,13 @@ def test_execute_trade_exit_down(default_conf_usdt, ticker_usdt, fee, ticker_usd @pytest.mark.parametrize( - "is_short,amount,open_rate,current_rate,limit,profit_amount," - "profit_ratio,profit_or_loss", [ + "is_short,amount,open_rate,current_rate,limit,profit_amount,profit_ratio,profit_or_loss", [ (False, 30, 2.0, 2.3, 2.25, 7.18125, 0.11938903, 'profit'), (True, 29.70297029, 2.02, 2.2, 2.25, -7.14876237, -0.11944465, 'loss'), # TODO-lev ]) def test_execute_trade_exit_custom_exit_price( default_conf_usdt, ticker_usdt, fee, ticker_usdt_sell_up, is_short, amount, open_rate, - current_rate, limit, profit_amount, profit_ratio, profit_or_loss, mocker - ) -> None: + current_rate, limit, profit_amount, profit_ratio, profit_or_loss, mocker) -> None: rpc_mock = patch_RPCManager(mocker) patch_exchange(mocker) mocker.patch.multiple(