Improve/simplify telegram exception handling
Move exceptionhandling to the decorator.
This commit is contained in:
parent
dac4a35be2
commit
95651fcd5a
@ -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:
|
||||||
|
Loading…
Reference in New Issue
Block a user