Improve/simplify telegram exception handling

Move exceptionhandling to the decorator.
This commit is contained in:
Matthias 2022-12-01 06:27:19 +01:00
parent dac4a35be2
commit 95651fcd5a

View File

@ -79,6 +79,8 @@ def authorized_only(command_handler: Callable[..., None]) -> Callable[..., Any]:
) )
try: try:
return command_handler(self, *args, **kwargs) return command_handler(self, *args, **kwargs)
except RPCException as e:
self._send_msg(str(e))
except BaseException: except BaseException:
logger.exception('Exception occurred within Telegram module') logger.exception('Exception occurred within Telegram module')
@ -538,72 +540,67 @@ class Telegram(RPCHandler):
handler for `/status` and `/status <id>`. handler for `/status` and `/status <id>`.
""" """
try: # Check if there's at least one numerical ID provided.
# If so, try to get only these trades.
trade_ids = []
if context.args and len(context.args) > 0:
trade_ids = [int(i) for i in context.args if i.isnumeric()]
# Check if there's at least one numerical ID provided. results = self._rpc._rpc_trade_status(trade_ids=trade_ids)
# If so, try to get only these trades. position_adjust = self._config.get('position_adjustment_enable', False)
trade_ids = [] max_entries = self._config.get('max_entry_position_adjustment', -1)
if context.args and len(context.args) > 0: for r in results:
trade_ids = [int(i) for i in context.args if i.isnumeric()] r['open_date_hum'] = arrow.get(r['open_date']).humanize()
r['num_entries'] = len([o for o in r['orders'] if o['ft_is_entry']])
r['exit_reason'] = r.get('exit_reason', "")
lines = [
"*Trade ID:* `{trade_id}`" +
(" `(since {open_date_hum})`" if r['is_open'] else ""),
"*Current Pair:* {pair}",
"*Direction:* " + ("`Short`" if r.get('is_short') else "`Long`"),
"*Leverage:* `{leverage}`" if r.get('leverage') else "",
"*Amount:* `{amount} ({stake_amount} {quote_currency})`",
"*Enter Tag:* `{enter_tag}`" if r['enter_tag'] else "",
"*Exit Reason:* `{exit_reason}`" if r['exit_reason'] else "",
]
results = self._rpc._rpc_trade_status(trade_ids=trade_ids) if position_adjust:
position_adjust = self._config.get('position_adjustment_enable', False) max_buy_str = (f"/{max_entries + 1}" if (max_entries > 0) else "")
max_entries = self._config.get('max_entry_position_adjustment', -1) lines.append("*Number of Entries:* `{num_entries}`" + max_buy_str)
for r in results:
r['open_date_hum'] = arrow.get(r['open_date']).humanize()
r['num_entries'] = len([o for o in r['orders'] if o['ft_is_entry']])
r['exit_reason'] = r.get('exit_reason', "")
lines = [
"*Trade ID:* `{trade_id}`" +
(" `(since {open_date_hum})`" if r['is_open'] else ""),
"*Current Pair:* {pair}",
"*Direction:* " + ("`Short`" if r.get('is_short') else "`Long`"),
"*Leverage:* `{leverage}`" if r.get('leverage') else "",
"*Amount:* `{amount} ({stake_amount} {quote_currency})`",
"*Enter Tag:* `{enter_tag}`" if r['enter_tag'] else "",
"*Exit Reason:* `{exit_reason}`" if r['exit_reason'] else "",
]
if position_adjust: lines.extend([
max_buy_str = (f"/{max_entries + 1}" if (max_entries > 0) else "") "*Open Rate:* `{open_rate:.8f}`",
lines.append("*Number of Entries:* `{num_entries}`" + max_buy_str) "*Close Rate:* `{close_rate:.8f}`" if r['close_rate'] else "",
"*Open Date:* `{open_date}`",
"*Close Date:* `{close_date}`" if r['close_date'] else "",
"*Current Rate:* `{current_rate:.8f}`" if r['is_open'] else "",
("*Current Profit:* " if r['is_open'] else "*Close Profit: *")
+ "`{profit_ratio:.2%}`",
])
lines.extend([ if r['is_open']:
"*Open Rate:* `{open_rate:.8f}`", if r.get('realized_profit'):
"*Close Rate:* `{close_rate:.8f}`" if r['close_rate'] else "", lines.append("*Realized Profit:* `{realized_profit:.8f}`")
"*Open Date:* `{open_date}`", if (r['stop_loss_abs'] != r['initial_stop_loss_abs']
"*Close Date:* `{close_date}`" if r['close_date'] else "", and r['initial_stop_loss_ratio'] is not None):
"*Current Rate:* `{current_rate:.8f}`" if r['is_open'] else "", # Adding initial stoploss only if it is different from stoploss
("*Current Profit:* " if r['is_open'] else "*Close Profit: *") lines.append("*Initial Stoploss:* `{initial_stop_loss_abs:.8f}` "
+ "`{profit_ratio:.2%}`", "`({initial_stop_loss_ratio:.2%})`")
])
if r['is_open']: # Adding stoploss and stoploss percentage only if it is not None
if r.get('realized_profit'): lines.append("*Stoploss:* `{stop_loss_abs:.8f}` " +
lines.append("*Realized Profit:* `{realized_profit:.8f}`") ("`({stop_loss_ratio:.2%})`" if r['stop_loss_ratio'] else ""))
if (r['stop_loss_abs'] != r['initial_stop_loss_abs'] lines.append("*Stoploss distance:* `{stoploss_current_dist:.8f}` "
and r['initial_stop_loss_ratio'] is not None): "`({stoploss_current_dist_ratio:.2%})`")
# Adding initial stoploss only if it is different from stoploss if r['open_order']:
lines.append("*Initial Stoploss:* `{initial_stop_loss_abs:.8f}` " lines.append(
"`({initial_stop_loss_ratio:.2%})`") "*Open Order:* `{open_order}`"
+ "- `{exit_order_status}`" if r['exit_order_status'] else "")
# Adding stoploss and stoploss percentage only if it is not None lines_detail = self._prepare_order_details(
lines.append("*Stoploss:* `{stop_loss_abs:.8f}` " + r['orders'], r['quote_currency'], r['is_open'])
("`({stop_loss_ratio:.2%})`" if r['stop_loss_ratio'] else "")) lines.extend(lines_detail if lines_detail else "")
lines.append("*Stoploss distance:* `{stoploss_current_dist:.8f}` " self.__send_status_msg(lines, r)
"`({stoploss_current_dist_ratio:.2%})`")
if r['open_order']:
lines.append(
"*Open Order:* `{open_order}`"
+ "- `{exit_order_status}`" if r['exit_order_status'] else "")
lines_detail = self._prepare_order_details(
r['orders'], r['quote_currency'], r['is_open'])
lines.extend(lines_detail if lines_detail else "")
self.__send_status_msg(lines, r)
except RPCException as e:
self._send_msg(str(e))
def __send_status_msg(self, lines: List[str], r: Dict[str, Any]) -> None: def __send_status_msg(self, lines: List[str], r: Dict[str, Any]) -> None:
""" """
@ -630,37 +627,34 @@ class Telegram(RPCHandler):
:param update: message update :param update: message update
:return: None :return: None
""" """
try: fiat_currency = self._config.get('fiat_display_currency', '')
fiat_currency = self._config.get('fiat_display_currency', '') statlist, head, fiat_profit_sum = self._rpc._rpc_status_table(
statlist, head, fiat_profit_sum = self._rpc._rpc_status_table( self._config['stake_currency'], fiat_currency)
self._config['stake_currency'], fiat_currency)
show_total = not isnan(fiat_profit_sum) and len(statlist) > 1 show_total = not isnan(fiat_profit_sum) and len(statlist) > 1
max_trades_per_msg = 50 max_trades_per_msg = 50
""" """
Calculate the number of messages of 50 trades per message Calculate the number of messages of 50 trades per message
0.99 is used to make sure that there are no extra (empty) messages 0.99 is used to make sure that there are no extra (empty) messages
As an example with 50 trades, there will be int(50/50 + 0.99) = 1 message As an example with 50 trades, there will be int(50/50 + 0.99) = 1 message
""" """
messages_count = max(int(len(statlist) / max_trades_per_msg + 0.99), 1) messages_count = max(int(len(statlist) / max_trades_per_msg + 0.99), 1)
for i in range(0, messages_count): for i in range(0, messages_count):
trades = statlist[i * max_trades_per_msg:(i + 1) * max_trades_per_msg] trades = statlist[i * max_trades_per_msg:(i + 1) * max_trades_per_msg]
if show_total and i == messages_count - 1: if show_total and i == messages_count - 1:
# append total line # append total line
trades.append(["Total", "", "", f"{fiat_profit_sum:.2f} {fiat_currency}"]) trades.append(["Total", "", "", f"{fiat_profit_sum:.2f} {fiat_currency}"])
message = tabulate(trades, message = tabulate(trades,
headers=head, headers=head,
tablefmt='simple') tablefmt='simple')
if show_total and i == messages_count - 1: if show_total and i == messages_count - 1:
# insert separators line between Total # insert separators line between Total
lines = message.split("\n") lines = message.split("\n")
message = "\n".join(lines[:-1] + [lines[1]] + [lines[-1]]) message = "\n".join(lines[:-1] + [lines[1]] + [lines[-1]])
self._send_msg(f"<pre>{message}</pre>", parse_mode=ParseMode.HTML, self._send_msg(f"<pre>{message}</pre>", parse_mode=ParseMode.HTML,
reload_able=True, callback_path="update_status_table", reload_able=True, callback_path="update_status_table",
query=update.callback_query) query=update.callback_query)
except RPCException as e:
self._send_msg(str(e))
@authorized_only @authorized_only
def _timeunit_stats(self, update: Update, context: CallbackContext, unit: str) -> None: def _timeunit_stats(self, update: Update, context: CallbackContext, unit: str) -> None:
@ -686,35 +680,32 @@ class Telegram(RPCHandler):
timescale = int(context.args[0]) if context.args else val.default timescale = int(context.args[0]) if context.args else val.default
except (TypeError, ValueError, IndexError): except (TypeError, ValueError, IndexError):
timescale = val.default timescale = val.default
try: stats = self._rpc._rpc_timeunit_profit(
stats = self._rpc._rpc_timeunit_profit( timescale,
timescale, stake_cur,
stake_cur, fiat_disp_cur,
fiat_disp_cur, unit
unit )
) stats_tab = tabulate(
stats_tab = tabulate( [[f"{period['date']} ({period['trade_count']})",
[[f"{period['date']} ({period['trade_count']})", f"{round_coin_value(period['abs_profit'], stats['stake_currency'])}",
f"{round_coin_value(period['abs_profit'], stats['stake_currency'])}", f"{period['fiat_value']:.2f} {stats['fiat_display_currency']}",
f"{period['fiat_value']:.2f} {stats['fiat_display_currency']}", f"{period['rel_profit']:.2%}",
f"{period['rel_profit']:.2%}", ] for period in stats['data']],
] for period in stats['data']], headers=[
headers=[ f"{val.header} (count)",
f"{val.header} (count)", f'{stake_cur}',
f'{stake_cur}', f'{fiat_disp_cur}',
f'{fiat_disp_cur}', 'Profit %',
'Profit %', 'Trades',
'Trades', ],
], tablefmt='simple')
tablefmt='simple') message = (
message = ( f'<b>{val.message} Profit over the last {timescale} {val.message2}</b>:\n'
f'<b>{val.message} Profit over the last {timescale} {val.message2}</b>:\n' f'<pre>{stats_tab}</pre>'
f'<pre>{stats_tab}</pre>' )
) self._send_msg(message, parse_mode=ParseMode.HTML, reload_able=True,
self._send_msg(message, parse_mode=ParseMode.HTML, reload_able=True, callback_path=val.callback, query=update.callback_query)
callback_path=val.callback, query=update.callback_query)
except RPCException as e:
self._send_msg(str(e))
@authorized_only @authorized_only
def _daily(self, update: Update, context: CallbackContext) -> None: def _daily(self, update: Update, context: CallbackContext) -> None:
@ -878,79 +869,76 @@ class Telegram(RPCHandler):
@authorized_only @authorized_only
def _balance(self, update: Update, context: CallbackContext) -> None: def _balance(self, update: Update, context: CallbackContext) -> None:
""" Handler for /balance """ """ Handler for /balance """
try: result = self._rpc._rpc_balance(self._config['stake_currency'],
result = self._rpc._rpc_balance(self._config['stake_currency'], self._config.get('fiat_display_currency', ''))
self._config.get('fiat_display_currency', ''))
balance_dust_level = self._config['telegram'].get('balance_dust_level', 0.0) balance_dust_level = self._config['telegram'].get('balance_dust_level', 0.0)
if not balance_dust_level: if not balance_dust_level:
balance_dust_level = DUST_PER_COIN.get(self._config['stake_currency'], 1.0) balance_dust_level = DUST_PER_COIN.get(self._config['stake_currency'], 1.0)
output = '' output = ''
if self._config['dry_run']: if self._config['dry_run']:
output += "*Warning:* Simulated balances in Dry Mode.\n" output += "*Warning:* Simulated balances in Dry Mode.\n"
starting_cap = round_coin_value( starting_cap = round_coin_value(
result['starting_capital'], self._config['stake_currency']) result['starting_capital'], self._config['stake_currency'])
output += f"Starting capital: `{starting_cap}`" output += f"Starting capital: `{starting_cap}`"
starting_cap_fiat = round_coin_value( starting_cap_fiat = round_coin_value(
result['starting_capital_fiat'], self._config['fiat_display_currency'] result['starting_capital_fiat'], self._config['fiat_display_currency']
) if result['starting_capital_fiat'] > 0 else '' ) if result['starting_capital_fiat'] > 0 else ''
output += (f" `, {starting_cap_fiat}`.\n" output += (f" `, {starting_cap_fiat}`.\n"
) if result['starting_capital_fiat'] > 0 else '.\n' ) if result['starting_capital_fiat'] > 0 else '.\n'
total_dust_balance = 0 total_dust_balance = 0
total_dust_currencies = 0 total_dust_currencies = 0
for curr in result['currencies']: for curr in result['currencies']:
curr_output = '' curr_output = ''
if curr['est_stake'] > balance_dust_level: if curr['est_stake'] > balance_dust_level:
if curr['is_position']: if curr['is_position']:
curr_output = ( curr_output = (
f"*{curr['currency']}:*\n" f"*{curr['currency']}:*\n"
f"\t`{curr['side']}: {curr['position']:.8f}`\n" f"\t`{curr['side']}: {curr['position']:.8f}`\n"
f"\t`Leverage: {curr['leverage']:.1f}`\n" f"\t`Leverage: {curr['leverage']:.1f}`\n"
f"\t`Est. {curr['stake']}: " f"\t`Est. {curr['stake']}: "
f"{round_coin_value(curr['est_stake'], curr['stake'], False)}`\n") f"{round_coin_value(curr['est_stake'], curr['stake'], False)}`\n")
else:
curr_output = (
f"*{curr['currency']}:*\n"
f"\t`Available: {curr['free']:.8f}`\n"
f"\t`Balance: {curr['balance']:.8f}`\n"
f"\t`Pending: {curr['used']:.8f}`\n"
f"\t`Est. {curr['stake']}: "
f"{round_coin_value(curr['est_stake'], curr['stake'], False)}`\n")
elif curr['est_stake'] <= balance_dust_level:
total_dust_balance += curr['est_stake']
total_dust_currencies += 1
# Handle overflowing message length
if len(output + curr_output) >= MAX_MESSAGE_LENGTH:
self._send_msg(output)
output = curr_output
else: else:
output += curr_output curr_output = (
f"*{curr['currency']}:*\n"
f"\t`Available: {curr['free']:.8f}`\n"
f"\t`Balance: {curr['balance']:.8f}`\n"
f"\t`Pending: {curr['used']:.8f}`\n"
f"\t`Est. {curr['stake']}: "
f"{round_coin_value(curr['est_stake'], curr['stake'], False)}`\n")
elif curr['est_stake'] <= balance_dust_level:
total_dust_balance += curr['est_stake']
total_dust_currencies += 1
if total_dust_balance > 0: # Handle overflowing message length
output += ( if len(output + curr_output) >= MAX_MESSAGE_LENGTH:
f"*{total_dust_currencies} Other " self._send_msg(output)
f"{plural(total_dust_currencies, 'Currency', 'Currencies')} " output = curr_output
f"(< {balance_dust_level} {result['stake']}):*\n" else:
f"\t`Est. {result['stake']}: " output += curr_output
f"{round_coin_value(total_dust_balance, result['stake'], False)}`\n")
tc = result['trade_count'] > 0
stake_improve = f" `({result['starting_capital_ratio']:.2%})`" if tc else ''
fiat_val = f" `({result['starting_capital_fiat_ratio']:.2%})`" if tc else ''
output += ("\n*Estimated Value*:\n" if total_dust_balance > 0:
f"\t`{result['stake']}: " output += (
f"{round_coin_value(result['total'], result['stake'], False)}`" f"*{total_dust_currencies} Other "
f"{stake_improve}\n" f"{plural(total_dust_currencies, 'Currency', 'Currencies')} "
f"\t`{result['symbol']}: " f"(< {balance_dust_level} {result['stake']}):*\n"
f"{round_coin_value(result['value'], result['symbol'], False)}`" f"\t`Est. {result['stake']}: "
f"{fiat_val}\n") f"{round_coin_value(total_dust_balance, result['stake'], False)}`\n")
self._send_msg(output, reload_able=True, callback_path="update_balance", tc = result['trade_count'] > 0
query=update.callback_query) stake_improve = f" `({result['starting_capital_ratio']:.2%})`" if tc else ''
except RPCException as e: fiat_val = f" `({result['starting_capital_fiat_ratio']:.2%})`" if tc else ''
self._send_msg(str(e))
output += ("\n*Estimated Value*:\n"
f"\t`{result['stake']}: "
f"{round_coin_value(result['total'], result['stake'], False)}`"
f"{stake_improve}\n"
f"\t`{result['symbol']}: "
f"{round_coin_value(result['value'], result['symbol'], False)}`"
f"{fiat_val}\n")
self._send_msg(output, reload_able=True, callback_path="update_balance",
query=update.callback_query)
@authorized_only @authorized_only
def _start(self, update: Update, context: CallbackContext) -> None: def _start(self, update: Update, context: CallbackContext) -> None:
@ -1125,26 +1113,23 @@ class Telegram(RPCHandler):
nrecent = int(context.args[0]) if context.args else 10 nrecent = int(context.args[0]) if context.args else 10
except (TypeError, ValueError, IndexError): except (TypeError, ValueError, IndexError):
nrecent = 10 nrecent = 10
try: trades = self._rpc._rpc_trade_history(
trades = self._rpc._rpc_trade_history( nrecent
nrecent )
) trades_tab = tabulate(
trades_tab = tabulate( [[arrow.get(trade['close_date']).humanize(),
[[arrow.get(trade['close_date']).humanize(), trade['pair'] + " (#" + str(trade['trade_id']) + ")",
trade['pair'] + " (#" + str(trade['trade_id']) + ")", f"{(trade['close_profit']):.2%} ({trade['close_profit_abs']})"]
f"{(trade['close_profit']):.2%} ({trade['close_profit_abs']})"] for trade in trades['trades']],
for trade in trades['trades']], headers=[
headers=[ 'Close Date',
'Close Date', 'Pair (ID)',
'Pair (ID)', f'Profit ({stake_cur})',
f'Profit ({stake_cur})', ],
], tablefmt='simple')
tablefmt='simple') message = (f"<b>{min(trades['trades_count'], nrecent)} recent trades</b>:\n"
message = (f"<b>{min(trades['trades_count'], nrecent)} recent trades</b>:\n" + (f"<pre>{trades_tab}</pre>" if trades['trades_count'] > 0 else ''))
+ (f"<pre>{trades_tab}</pre>" if trades['trades_count'] > 0 else '')) self._send_msg(message, parse_mode=ParseMode.HTML)
self._send_msg(message, parse_mode=ParseMode.HTML)
except RPCException as e:
self._send_msg(str(e))
@authorized_only @authorized_only
def _delete_trade(self, update: Update, context: CallbackContext) -> None: def _delete_trade(self, update: Update, context: CallbackContext) -> None:
@ -1155,18 +1140,14 @@ class Telegram(RPCHandler):
:param update: message update :param update: message update
:return: None :return: None
""" """
try: if not context.args or len(context.args) == 0:
if not context.args or len(context.args) == 0: raise RPCException("Trade-id not set.")
raise RPCException("Trade-id not set.") trade_id = int(context.args[0])
trade_id = int(context.args[0]) msg = self._rpc._rpc_delete(trade_id)
msg = self._rpc._rpc_delete(trade_id) self._send_msg((
self._send_msg(( f"`{msg['result_msg']}`\n"
f"`{msg['result_msg']}`\n" 'Please make sure to take care of this asset on the exchange manually.'
'Please make sure to take care of this asset on the exchange manually.' ))
))
except RPCException as e:
self._send_msg(str(e))
@authorized_only @authorized_only
def _performance(self, update: Update, context: CallbackContext) -> None: def _performance(self, update: Update, context: CallbackContext) -> None:
@ -1177,27 +1158,24 @@ class Telegram(RPCHandler):
:param update: message update :param update: message update
:return: None :return: None
""" """
try: trades = self._rpc._rpc_performance()
trades = self._rpc._rpc_performance() output = "<b>Performance:</b>\n"
output = "<b>Performance:</b>\n" for i, trade in enumerate(trades):
for i, trade in enumerate(trades): stat_line = (
stat_line = ( f"{i+1}.\t <code>{trade['pair']}\t"
f"{i+1}.\t <code>{trade['pair']}\t" f"{round_coin_value(trade['profit_abs'], self._config['stake_currency'])} "
f"{round_coin_value(trade['profit_abs'], self._config['stake_currency'])} " f"({trade['profit_ratio']:.2%}) "
f"({trade['profit_ratio']:.2%}) " f"({trade['count']})</code>\n")
f"({trade['count']})</code>\n")
if len(output + stat_line) >= MAX_MESSAGE_LENGTH: if len(output + stat_line) >= MAX_MESSAGE_LENGTH:
self._send_msg(output, parse_mode=ParseMode.HTML) self._send_msg(output, parse_mode=ParseMode.HTML)
output = stat_line output = stat_line
else: else:
output += stat_line output += stat_line
self._send_msg(output, parse_mode=ParseMode.HTML, self._send_msg(output, parse_mode=ParseMode.HTML,
reload_able=True, callback_path="update_performance", reload_able=True, callback_path="update_performance",
query=update.callback_query) query=update.callback_query)
except RPCException as e:
self._send_msg(str(e))
@authorized_only @authorized_only
def _enter_tag_performance(self, update: Update, context: CallbackContext) -> None: def _enter_tag_performance(self, update: Update, context: CallbackContext) -> None:
@ -1208,31 +1186,28 @@ class Telegram(RPCHandler):
:param update: message update :param update: message update
:return: None :return: None
""" """
try: pair = None
pair = None if context.args and isinstance(context.args[0], str):
if context.args and isinstance(context.args[0], str): pair = context.args[0]
pair = context.args[0]
trades = self._rpc._rpc_enter_tag_performance(pair) trades = self._rpc._rpc_enter_tag_performance(pair)
output = "<b>Entry Tag Performance:</b>\n" output = "<b>Entry Tag Performance:</b>\n"
for i, trade in enumerate(trades): for i, trade in enumerate(trades):
stat_line = ( stat_line = (
f"{i+1}.\t <code>{trade['enter_tag']}\t" f"{i+1}.\t <code>{trade['enter_tag']}\t"
f"{round_coin_value(trade['profit_abs'], self._config['stake_currency'])} " f"{round_coin_value(trade['profit_abs'], self._config['stake_currency'])} "
f"({trade['profit_ratio']:.2%}) " f"({trade['profit_ratio']:.2%}) "
f"({trade['count']})</code>\n") f"({trade['count']})</code>\n")
if len(output + stat_line) >= MAX_MESSAGE_LENGTH: if len(output + stat_line) >= MAX_MESSAGE_LENGTH:
self._send_msg(output, parse_mode=ParseMode.HTML) self._send_msg(output, parse_mode=ParseMode.HTML)
output = stat_line output = stat_line
else: else:
output += stat_line output += stat_line
self._send_msg(output, parse_mode=ParseMode.HTML, self._send_msg(output, parse_mode=ParseMode.HTML,
reload_able=True, callback_path="update_enter_tag_performance", reload_able=True, callback_path="update_enter_tag_performance",
query=update.callback_query) query=update.callback_query)
except RPCException as e:
self._send_msg(str(e))
@authorized_only @authorized_only
def _exit_reason_performance(self, update: Update, context: CallbackContext) -> None: def _exit_reason_performance(self, update: Update, context: CallbackContext) -> None:
@ -1243,31 +1218,28 @@ class Telegram(RPCHandler):
:param update: message update :param update: message update
:return: None :return: None
""" """
try: pair = None
pair = None if context.args and isinstance(context.args[0], str):
if context.args and isinstance(context.args[0], str): pair = context.args[0]
pair = context.args[0]
trades = self._rpc._rpc_exit_reason_performance(pair) trades = self._rpc._rpc_exit_reason_performance(pair)
output = "<b>Exit Reason Performance:</b>\n" output = "<b>Exit Reason Performance:</b>\n"
for i, trade in enumerate(trades): for i, trade in enumerate(trades):
stat_line = ( stat_line = (
f"{i+1}.\t <code>{trade['exit_reason']}\t" f"{i+1}.\t <code>{trade['exit_reason']}\t"
f"{round_coin_value(trade['profit_abs'], self._config['stake_currency'])} " f"{round_coin_value(trade['profit_abs'], self._config['stake_currency'])} "
f"({trade['profit_ratio']:.2%}) " f"({trade['profit_ratio']:.2%}) "
f"({trade['count']})</code>\n") f"({trade['count']})</code>\n")
if len(output + stat_line) >= MAX_MESSAGE_LENGTH: if len(output + stat_line) >= MAX_MESSAGE_LENGTH:
self._send_msg(output, parse_mode=ParseMode.HTML) self._send_msg(output, parse_mode=ParseMode.HTML)
output = stat_line output = stat_line
else: else:
output += stat_line output += stat_line
self._send_msg(output, parse_mode=ParseMode.HTML, self._send_msg(output, parse_mode=ParseMode.HTML,
reload_able=True, callback_path="update_exit_reason_performance", reload_able=True, callback_path="update_exit_reason_performance",
query=update.callback_query) query=update.callback_query)
except RPCException as e:
self._send_msg(str(e))
@authorized_only @authorized_only
def _mix_tag_performance(self, update: Update, context: CallbackContext) -> None: def _mix_tag_performance(self, update: Update, context: CallbackContext) -> None:
@ -1278,31 +1250,28 @@ class Telegram(RPCHandler):
:param update: message update :param update: message update
:return: None :return: None
""" """
try: pair = None
pair = None if context.args and isinstance(context.args[0], str):
if context.args and isinstance(context.args[0], str): pair = context.args[0]
pair = context.args[0]
trades = self._rpc._rpc_mix_tag_performance(pair) trades = self._rpc._rpc_mix_tag_performance(pair)
output = "<b>Mix Tag Performance:</b>\n" output = "<b>Mix Tag Performance:</b>\n"
for i, trade in enumerate(trades): for i, trade in enumerate(trades):
stat_line = ( stat_line = (
f"{i+1}.\t <code>{trade['mix_tag']}\t" f"{i+1}.\t <code>{trade['mix_tag']}\t"
f"{round_coin_value(trade['profit_abs'], self._config['stake_currency'])} " f"{round_coin_value(trade['profit_abs'], self._config['stake_currency'])} "
f"({trade['profit']:.2%}) " f"({trade['profit']:.2%}) "
f"({trade['count']})</code>\n") f"({trade['count']})</code>\n")
if len(output + stat_line) >= MAX_MESSAGE_LENGTH: if len(output + stat_line) >= MAX_MESSAGE_LENGTH:
self._send_msg(output, parse_mode=ParseMode.HTML) self._send_msg(output, parse_mode=ParseMode.HTML)
output = stat_line output = stat_line
else: else:
output += stat_line output += stat_line
self._send_msg(output, parse_mode=ParseMode.HTML, self._send_msg(output, parse_mode=ParseMode.HTML,
reload_able=True, callback_path="update_mix_tag_performance", reload_able=True, callback_path="update_mix_tag_performance",
query=update.callback_query) query=update.callback_query)
except RPCException as e:
self._send_msg(str(e))
@authorized_only @authorized_only
def _count(self, update: Update, context: CallbackContext) -> None: def _count(self, update: Update, context: CallbackContext) -> None:
@ -1313,18 +1282,15 @@ class Telegram(RPCHandler):
:param update: message update :param update: message update
:return: None :return: None
""" """
try: counts = self._rpc._rpc_count()
counts = self._rpc._rpc_count() message = tabulate({k: [v] for k, v in counts.items()},
message = tabulate({k: [v] for k, v in counts.items()}, headers=['current', 'max', 'total stake'],
headers=['current', 'max', 'total stake'], tablefmt='simple')
tablefmt='simple') message = "<pre>{}</pre>".format(message)
message = "<pre>{}</pre>".format(message) logger.debug(message)
logger.debug(message) self._send_msg(message, parse_mode=ParseMode.HTML,
self._send_msg(message, parse_mode=ParseMode.HTML, reload_able=True, callback_path="update_count",
reload_able=True, callback_path="update_count", query=update.callback_query)
query=update.callback_query)
except RPCException as e:
self._send_msg(str(e))
@authorized_only @authorized_only
def _locks(self, update: Update, context: CallbackContext) -> None: def _locks(self, update: Update, context: CallbackContext) -> None:
@ -1372,22 +1338,19 @@ class Telegram(RPCHandler):
Handler for /whitelist Handler for /whitelist
Shows the currently active whitelist Shows the currently active whitelist
""" """
try: whitelist = self._rpc._rpc_whitelist()
whitelist = self._rpc._rpc_whitelist()
if context.args: if context.args:
if "sorted" in context.args: if "sorted" in context.args:
whitelist['whitelist'] = sorted(whitelist['whitelist']) whitelist['whitelist'] = sorted(whitelist['whitelist'])
if "baseonly" in context.args: if "baseonly" in context.args:
whitelist['whitelist'] = [pair.split("/")[0] for pair in whitelist['whitelist']] whitelist['whitelist'] = [pair.split("/")[0] for pair in whitelist['whitelist']]
message = f"Using whitelist `{whitelist['method']}` with {whitelist['length']} pairs\n" message = f"Using whitelist `{whitelist['method']}` with {whitelist['length']} pairs\n"
message += f"`{', '.join(whitelist['whitelist'])}`" message += f"`{', '.join(whitelist['whitelist'])}`"
logger.debug(message) logger.debug(message)
self._send_msg(message) self._send_msg(message)
except RPCException as e:
self._send_msg(str(e))
@authorized_only @authorized_only
def _blacklist(self, update: Update, context: CallbackContext) -> None: def _blacklist(self, update: Update, context: CallbackContext) -> None:
@ -1425,30 +1388,27 @@ class Telegram(RPCHandler):
Shows the latest logs Shows the latest logs
""" """
try: try:
try: limit = int(context.args[0]) if context.args else 10
limit = int(context.args[0]) if context.args else 10 except (TypeError, ValueError, IndexError):
except (TypeError, ValueError, IndexError): limit = 10
limit = 10 logs = RPC._rpc_get_logs(limit)['logs']
logs = RPC._rpc_get_logs(limit)['logs'] msgs = ''
msgs = '' msg_template = "*{}* {}: {} \\- `{}`"
msg_template = "*{}* {}: {} \\- `{}`" for logrec in logs:
for logrec in logs: msg = msg_template.format(escape_markdown(logrec[0], version=2),
msg = msg_template.format(escape_markdown(logrec[0], version=2), escape_markdown(logrec[2], version=2),
escape_markdown(logrec[2], version=2), escape_markdown(logrec[3], version=2),
escape_markdown(logrec[3], version=2), escape_markdown(logrec[4], version=2))
escape_markdown(logrec[4], version=2)) if len(msgs + msg) + 10 >= MAX_MESSAGE_LENGTH:
if len(msgs + msg) + 10 >= MAX_MESSAGE_LENGTH: # Send message immediately if it would become too long
# Send message immediately if it would become too long
self._send_msg(msgs, parse_mode=ParseMode.MARKDOWN_V2)
msgs = msg + '\n'
else:
# Append message to messages to send
msgs += msg + '\n'
if msgs:
self._send_msg(msgs, parse_mode=ParseMode.MARKDOWN_V2) self._send_msg(msgs, parse_mode=ParseMode.MARKDOWN_V2)
except RPCException as e: msgs = msg + '\n'
self._send_msg(str(e)) else:
# Append message to messages to send
msgs += msg + '\n'
if msgs:
self._send_msg(msgs, parse_mode=ParseMode.MARKDOWN_V2)
@authorized_only @authorized_only
def _edge(self, update: Update, context: CallbackContext) -> None: def _edge(self, update: Update, context: CallbackContext) -> None:
@ -1456,21 +1416,17 @@ class Telegram(RPCHandler):
Handler for /edge Handler for /edge
Shows information related to Edge Shows information related to Edge
""" """
try: edge_pairs = self._rpc._rpc_edge()
edge_pairs = self._rpc._rpc_edge() if not edge_pairs:
if not edge_pairs: message = '<b>Edge only validated following pairs:</b>'
message = '<b>Edge only validated following pairs:</b>' self._send_msg(message, parse_mode=ParseMode.HTML)
self._send_msg(message, parse_mode=ParseMode.HTML)
for chunk in chunks(edge_pairs, 25): for chunk in chunks(edge_pairs, 25):
edge_pairs_tab = tabulate(chunk, headers='keys', tablefmt='simple') edge_pairs_tab = tabulate(chunk, headers='keys', tablefmt='simple')
message = (f'<b>Edge only validated following pairs:</b>\n' message = (f'<b>Edge only validated following pairs:</b>\n'
f'<pre>{edge_pairs_tab}</pre>') f'<pre>{edge_pairs_tab}</pre>')
self._send_msg(message, parse_mode=ParseMode.HTML) self._send_msg(message, parse_mode=ParseMode.HTML)
except RPCException as e:
self._send_msg(str(e))
@authorized_only @authorized_only
def _help(self, update: Update, context: CallbackContext) -> None: def _help(self, update: Update, context: CallbackContext) -> None:
@ -1551,12 +1507,9 @@ class Telegram(RPCHandler):
Handler for /health Handler for /health
Shows the last process timestamp Shows the last process timestamp
""" """
try: health = self._rpc._health()
health = self._rpc._health() message = f"Last process: `{health['last_process_loc']}`"
message = f"Last process: `{health['last_process_loc']}`" self._send_msg(message)
self._send_msg(message)
except RPCException as e:
self._send_msg(str(e))
@authorized_only @authorized_only
def _version(self, update: Update, context: CallbackContext) -> None: def _version(self, update: Update, context: CallbackContext) -> None: