diff --git a/freqtrade/rpc/telegram.py b/freqtrade/rpc/telegram.py index f53154dff..2a700348f 100644 --- a/freqtrade/rpc/telegram.py +++ b/freqtrade/rpc/telegram.py @@ -28,7 +28,6 @@ from freqtrade.misc import chunks, plural, round_coin_value from freqtrade.persistence import Trade from freqtrade.rpc import RPC, RPCException, RPCHandler - logger = logging.getLogger(__name__) logger.debug('Included module rpc.telegram ...') @@ -227,7 +226,7 @@ class Telegram(RPCHandler): ) 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"*Amount:* `{msg['amount']:.8f} {msg['pair'].split('/')[0]}`\n") content.append(f"*Open Rate:* `{msg['limit']:.8f}`\n") content.append(f"*Current Rate:* `{msg['current_rate']:.8f}`\n") content.append( @@ -242,7 +241,66 @@ class Telegram(RPCHandler): message += ")`" return message + def _format_buy_msg_fill(self, msg: Dict[str, Any]) -> str: + if self._rpc._fiat_converter: + msg['stake_amount_fiat'] = self._rpc._fiat_converter.convert_amount( + msg['stake_amount'], msg['stake_currency'], msg['fiat_currency']) + else: + msg['stake_amount_fiat'] = 0 + + content = [] + content.append( + f"\N{CHECK MARK} *{msg['exchange']}:* Bought {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} {msg['pair'].split('/')[0]}`\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 = ''.join(content) + message += ")`" + return message + def _format_sell_msg(self, msg: Dict[str, Any]) -> str: + msg['amount'] = round(msg['amount'], 8) + msg['duration'] = msg['close_date'].replace( + microsecond=0) - msg['open_date'].replace(microsecond=0) + msg['duration_min'] = msg['duration'].total_seconds() / 60 + msg['buy_tag'] = msg['buy_tag'] if "buy_tag" in msg.keys() else None + + # Check if all sell properties are available. + # This might not be the case if the message origin is triggered by /forcesell + if (all(prop in msg for prop in ['gain', 'fiat_currency', 'stake_currency']) + 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) + else: + msg['profit_extra'] = '' + + msg['currency'] = msg['pair'].split('/')[0] + message = ("\N{LARGE RED CIRCLE} *{exchange}:* Selling {pair} (#{trade_id})\n" + "*Unrealized 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}` {currency}\n" + "*Open Rate:* `{open_rate:.8f}`\n" + "*Current Rate:* `{current_rate:.8f}`\n" + "*Close Rate:* `{close_rate:.8f}`").format( + **msg) # TODO: Updated from `limit`, confirm this is correct variable? + + return message + + def _format_sell_msg_fill(self, msg: Dict[str, Any]) -> str: msg['amount'] = round(msg['amount'], 8) msg['profit_percent'] = round(msg['profit_ratio'] * 100, 2) msg['duration'] = msg['close_date'].replace( @@ -263,15 +321,17 @@ class Telegram(RPCHandler): else: msg['profit_extra'] = '' - message = ("{emoji} *{exchange}:* Selling {pair} (#{trade_id})\n" + msg['currency'] = msg['pair'].split('/')[0] + import pprint + pprint.pprint(msg) + message = ("{emoji} *{exchange}:* Sold {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) + "*Amount:* `{amount:.8f}` {currency}\n" + "*Close Rate:* `{close_rate:.8f}`").format( + **msg) # TODO: Updated from `limit`, confirm this is correct variable to use? Limit not in dict for _fill return message @@ -287,15 +347,14 @@ class Telegram(RPCHandler): "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)) + message = self._format_buy_msg_fill(msg) + elif msg_type == RPCMessageType.SELL_FILL: - message = ("\N{LARGE CIRCLE} *{exchange}:* " - "Sell order for {pair} (#{trade_id}) filled " - "for {close_rate}.".format(**msg)) + message = self._format_sell_msg_fill(msg) + elif msg_type == RPCMessageType.SELL: message = self._format_sell_msg(msg) + elif msg_type == RPCMessageType.PROTECTION_TRIGGER: message = ( "*Protection* triggered due to {reason}. " @@ -357,7 +416,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}" @@ -689,9 +748,9 @@ class Telegram(RPCHandler): duration_msg = tabulate( [ ['Wins', str(timedelta(seconds=durations['wins'])) - if durations['wins'] != 'N/A' else 'N/A'], + if durations['wins'] != 'N/A' else 'N/A'], ['Losses', str(timedelta(seconds=durations['losses'])) - if durations['losses'] != 'N/A' else 'N/A'] + if durations['losses'] != 'N/A' else 'N/A'] ], headers=['', 'Avg. Duration'] ) @@ -922,9 +981,9 @@ class Telegram(RPCHandler): trade_id = int(context.args[0]) msg = self._rpc._rpc_delete(trade_id) self._send_msg(( - '`{result_msg}`\n' - 'Please make sure to take care of this asset on the exchange manually.' - ).format(**msg)) + '`{result_msg}`\n' + 'Please make sure to take care of this asset on the exchange manually.' + ).format(**msg)) except RPCException as e: self._send_msg(str(e)) @@ -943,7 +1002,7 @@ class Telegram(RPCHandler): output = "Performance:\n" for i, trade in enumerate(trades): stat_line = ( - f"{i+1}.\t {trade['pair']}\t" + f"{i + 1}.\t {trade['pair']}\t" f"{round_coin_value(trade['profit_abs'], self._config['stake_currency'])} " f"({trade['profit_ratio']:.2%}) " f"({trade['count']})\n") @@ -978,7 +1037,7 @@ class Telegram(RPCHandler): output = "Buy Tag Performance:\n" for i, trade in enumerate(trades): stat_line = ( - f"{i+1}.\t {trade['buy_tag']}\t" + f"{i + 1}.\t {trade['buy_tag']}\t" f"{round_coin_value(trade['profit_abs'], self._config['stake_currency'])} " f"({trade['profit_ratio']:.2%}) " f"({trade['count']})\n") @@ -1013,7 +1072,7 @@ class Telegram(RPCHandler): output = "Sell Reason Performance:\n" for i, trade in enumerate(trades): stat_line = ( - f"{i+1}.\t {trade['sell_reason']}\t" + f"{i + 1}.\t {trade['sell_reason']}\t" f"{round_coin_value(trade['profit_abs'], self._config['stake_currency'])} " f"({trade['profit_ratio']:.2%}) " f"({trade['count']})\n") @@ -1048,7 +1107,7 @@ class Telegram(RPCHandler): output = "Mix Tag Performance:\n" for i, trade in enumerate(trades): stat_line = ( - f"{i+1}.\t {trade['mix_tag']}\t" + f"{i + 1}.\t {trade['mix_tag']}\t" f"{round_coin_value(trade['profit_abs'], self._config['stake_currency'])} " f"({trade['profit']:.2%}) " f"({trade['count']})\n")