diff --git a/freqtrade/rpc/telegram.py b/freqtrade/rpc/telegram.py index cb686a47c..1db28546e 100644 --- a/freqtrade/rpc/telegram.py +++ b/freqtrade/rpc/telegram.py @@ -219,26 +219,28 @@ class Telegram(RPCHandler): msg['stake_amount'], msg['stake_currency'], msg['fiat_currency']) else: msg['stake_amount_fiat'] = 0 + is_fill = msg['type'] == RPCMessageType.BUY_FILL + emoji = '\N{CHECK MARK}' if is_fill else '\N{LARGE BLUE CIRCLE}' - content = [] - content.append( - f"\N{LARGE BLUE CIRCLE} *{msg['exchange']}:* Buying {msg['pair']}" + message = ( + f"{emoji} *{msg['exchange']}:* {'Bought' if is_fill else 'Buying'} {msg['pair']}" f" (#{msg['trade_id']})\n" - ) - if msg.get('buy_tag', None): - content.append(f"*Buy Tag:* `{msg['buy_tag']}`\n") - content.append(f"*Amount:* `{msg['amount']:.8f}`\n") - content.append(f"*Open Rate:* `{msg['limit']:.8f}`\n") - content.append(f"*Current Rate:* `{msg['current_rate']:.8f}`\n") - content.append( - f"*Total:* `({round_coin_value(msg['stake_amount'], msg['stake_currency'])}" - ) - if msg.get('fiat_currency', None): - content.append( - f", {round_coin_value(msg['stake_amount_fiat'], msg['fiat_currency'])}" ) + message += f"*Buy Tag:* `{msg['buy_tag']}`\n" if msg.get('buy_tag', None) else "" + message += f"*Amount:* `{msg['amount']:.8f}`\n" + + if msg['type'] == RPCMessageType.BUY_FILL: + message += f"*Open Rate:* `{msg['open_rate']:.8f}`\n" + + elif msg['type'] == RPCMessageType.BUY: + message += f"*Open Rate:* `{msg['limit']:.8f}`\n"\ + f"*Current Rate:* `{msg['current_rate']:.8f}`\n" + + message += f"*Total:* `({round_coin_value(msg['stake_amount'], msg['stake_currency'])}" + + if msg.get('fiat_currency', None): + message += f", {round_coin_value(msg['stake_amount_fiat'], msg['fiat_currency'])}" - message = ''.join(content) message += ")`" return message @@ -258,54 +260,57 @@ class Telegram(RPCHandler): and self._rpc._fiat_converter): msg['profit_fiat'] = self._rpc._fiat_converter.convert_amount( msg['profit_amount'], msg['stake_currency'], msg['fiat_currency']) - msg['profit_extra'] = (' ({gain}: {profit_amount:.8f} {stake_currency}' - ' / {profit_fiat:.3f} {fiat_currency})').format(**msg) + msg['profit_extra'] = ( + f" ({msg['gain']}: {msg['profit_amount']:.8f} {msg['stake_currency']}" + f" / {msg['profit_fiat']:.3f} {msg['fiat_currency']})") else: msg['profit_extra'] = '' + is_fill = msg['type'] == RPCMessageType.SELL_FILL + message = ( + f"{msg['emoji']} *{msg['exchange']}:* " + f"{'Sold' if is_fill else 'Selling'} {msg['pair']} (#{msg['trade_id']})\n" + f"*{'Profit' if is_fill else 'Unrealized Profit'}:* " + f"`{msg['profit_ratio']:.2%}{msg['profit_extra']}`\n" + f"*Buy Tag:* `{msg['buy_tag']}`\n" + f"*Sell Reason:* `{msg['sell_reason']}`\n" + f"*Duration:* `{msg['duration']} ({msg['duration_min']:.1f} min)`\n" + f"*Amount:* `{msg['amount']:.8f}`\n") - message = ("{emoji} *{exchange}:* Selling {pair} (#{trade_id})\n" - "*Profit:* `{profit_ratio:.2%}{profit_extra}`\n" - "*Buy Tag:* `{buy_tag}`\n" - "*Sell Reason:* `{sell_reason}`\n" - "*Duration:* `{duration} ({duration_min:.1f} min)`\n" - "*Amount:* `{amount:.8f}`\n" - "*Open Rate:* `{open_rate:.8f}`\n" - "*Current Rate:* `{current_rate:.8f}`\n" - "*Close Rate:* `{limit:.8f}`").format(**msg) + if msg['type'] == RPCMessageType.SELL: + message += (f"*Open Rate:* `{msg['open_rate']:.8f}`\n" + f"*Current Rate:* `{msg['current_rate']:.8f}`\n" + f"*Close Rate:* `{msg['limit']:.8f}`") + + elif msg['type'] == RPCMessageType.SELL_FILL: + message += f"*Close Rate:* `{msg['close_rate']:.8f}`" return message def compose_message(self, msg: Dict[str, Any], msg_type: RPCMessageType) -> str: - - if msg_type == RPCMessageType.BUY: + if msg_type in [RPCMessageType.BUY, RPCMessageType.BUY_FILL]: message = self._format_buy_msg(msg) + elif msg_type in [RPCMessageType.SELL, RPCMessageType.SELL_FILL]: + message = self._format_sell_msg(msg) + elif msg_type in (RPCMessageType.BUY_CANCEL, RPCMessageType.SELL_CANCEL): msg['message_side'] = 'buy' if msg_type == RPCMessageType.BUY_CANCEL else 'sell' message = ("\N{WARNING SIGN} *{exchange}:* " "Cancelling open {message_side} Order for {pair} (#{trade_id}). " "Reason: {reason}.".format(**msg)) - elif msg_type == RPCMessageType.BUY_FILL: - message = ("\N{LARGE CIRCLE} *{exchange}:* " - "Buy order for {pair} (#{trade_id}) filled " - "for {open_rate}.".format(**msg)) - elif msg_type == RPCMessageType.SELL_FILL: - message = ("\N{LARGE CIRCLE} *{exchange}:* " - "Sell order for {pair} (#{trade_id}) filled " - "for {close_rate}.".format(**msg)) - elif msg_type == RPCMessageType.SELL: - message = self._format_sell_msg(msg) elif msg_type == RPCMessageType.PROTECTION_TRIGGER: message = ( "*Protection* triggered due to {reason}. " "`{pair}` will be locked until `{lock_end_time}`." ).format(**msg) + elif msg_type == RPCMessageType.PROTECTION_TRIGGER_GLOBAL: message = ( "*Protection* triggered due to {reason}. " "*All pairs* will be locked until `{lock_end_time}`." ).format(**msg) + elif msg_type == RPCMessageType.STATUS: message = '*Status:* `{status}`'.format(**msg) @@ -357,7 +362,7 @@ class Telegram(RPCHandler): elif float(msg['profit_percent']) >= 0.0: return "\N{EIGHT SPOKED ASTERISK}" elif msg['sell_reason'] == "stop_loss": - return"\N{WARNING SIGN}" + return "\N{WARNING SIGN}" else: return "\N{CROSS MARK}" diff --git a/tests/rpc/test_rpc_telegram.py b/tests/rpc/test_rpc_telegram.py index 68f7457d5..95a2f4549 100644 --- a/tests/rpc/test_rpc_telegram.py +++ b/tests/rpc/test_rpc_telegram.py @@ -1686,17 +1686,25 @@ def test_send_msg_buy_fill_notification(default_conf, mocker) -> None: telegram.send_msg({ 'type': RPCMessageType.BUY_FILL, - 'buy_tag': 'buy_signal_01', 'trade_id': 1, + 'buy_tag': 'buy_signal_01', 'exchange': 'Binance', - 'pair': 'ETH/USDT', - 'open_rate': 200, - 'stake_amount': 100, - 'amount': 0.5, - 'open_date': arrow.utcnow().datetime + 'pair': 'ETH/BTC', + 'stake_amount': 0.001, + # 'stake_amount_fiat': 0.0, + 'stake_currency': 'BTC', + 'fiat_currency': 'USD', + 'open_rate': 1.099e-05, + 'amount': 1333.3333333333335, + 'open_date': arrow.utcnow().shift(hours=-1) }) - assert (msg_mock.call_args[0][0] == '\N{LARGE CIRCLE} *Binance:* ' - 'Buy order for ETH/USDT (#1) filled for 200.') + + assert msg_mock.call_args[0][0] \ + == '\N{CHECK MARK} *Binance:* Bought ETH/BTC (#1)\n' \ + '*Buy Tag:* `buy_signal_01`\n' \ + '*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: @@ -1727,7 +1735,7 @@ def test_send_msg_sell_notification(default_conf, mocker) -> None: }) assert msg_mock.call_args[0][0] \ == ('\N{WARNING SIGN} *Binance:* Selling KEY/ETH (#1)\n' - '*Profit:* `-57.41% (loss: -0.05746268 ETH / -24.812 USD)`\n' + '*Unrealized Profit:* `-57.41% (loss: -0.05746268 ETH / -24.812 USD)`\n' '*Buy Tag:* `buy_signal1`\n' '*Sell Reason:* `stop_loss`\n' '*Duration:* `1:00:00 (60.0 min)`\n' @@ -1759,7 +1767,7 @@ def test_send_msg_sell_notification(default_conf, mocker) -> None: }) assert msg_mock.call_args[0][0] \ == ('\N{WARNING SIGN} *Binance:* Selling KEY/ETH (#1)\n' - '*Profit:* `-57.41%`\n' + '*Unrealized Profit:* `-57.41%`\n' '*Buy Tag:* `buy_signal1`\n' '*Sell Reason:* `stop_loss`\n' '*Duration:* `1 day, 2:30:00 (1590.0 min)`\n' @@ -1813,25 +1821,30 @@ def test_send_msg_sell_fill_notification(default_conf, mocker) -> None: 'type': RPCMessageType.SELL_FILL, 'trade_id': 1, 'exchange': 'Binance', - 'pair': 'ETH/USDT', + 'pair': 'KEY/ETH', 'gain': 'loss', 'limit': 3.201e-05, - 'amount': 0.1, + 'amount': 1333.3333333333335, 'order_type': 'market', - 'open_rate': 500, - 'close_rate': 550, - 'current_rate': 3.201e-05, + 'open_rate': 7.5e-05, + 'close_rate': 3.201e-05, 'profit_amount': -0.05746268, 'profit_ratio': -0.57405275, 'stake_currency': 'ETH', - 'fiat_currency': 'USD', 'buy_tag': 'buy_signal1', 'sell_reason': SellType.STOP_LOSS.value, - 'open_date': arrow.utcnow().shift(hours=-1), + 'open_date': arrow.utcnow().shift(days=-1, hours=-2, minutes=-30), 'close_date': arrow.utcnow(), }) assert msg_mock.call_args[0][0] \ - == ('\N{LARGE CIRCLE} *Binance:* Sell order for ETH/USDT (#1) filled for 550.') + == ('\N{WARNING SIGN} *Binance:* Sold KEY/ETH (#1)\n' + '*Profit:* `-57.41%`\n' + '*Buy Tag:* `buy_signal1`\n' + '*Sell Reason:* `stop_loss`\n' + '*Duration:* `1 day, 2:30:00 (1590.0 min)`\n' + '*Amount:* `1333.33333333`\n' + '*Close Rate:* `0.00003201`' + ) def test_send_msg_status_notification(default_conf, mocker) -> None: @@ -1923,7 +1936,7 @@ def test_send_msg_sell_notification_no_fiat(default_conf, mocker) -> None: 'close_date': arrow.utcnow(), }) assert msg_mock.call_args[0][0] == ('\N{WARNING SIGN} *Binance:* Selling KEY/ETH (#1)\n' - '*Profit:* `-57.41%`\n' + '*Unrealized Profit:* `-57.41%`\n' '*Buy Tag:* `buy_signal1`\n' '*Sell Reason:* `stop_loss`\n' '*Duration:* `2:35:03 (155.1 min)`\n'