Merge pull request #4982 from eschava/profit_day_week

day/week options for Telegram '/profit' command
This commit is contained in:
Matthias 2021-06-08 19:26:57 +01:00 committed by GitHub
commit e71d965e32
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
5 changed files with 47 additions and 27 deletions

View File

@ -123,7 +123,7 @@ Telegram is not mandatory. However, this is a great way to control your bot. Mor
- `/stop`: Stops the trader.
- `/stopbuy`: Stop entering new trades.
- `/status <trade_id>|[table]`: Lists all or specific open trades.
- `/profit`: Lists cumulative profit from all finished trades
- `/profit [<n>]`: Lists cumulative profit from all finished trades, over the last n days.
- `/forcesell <trade_id>|all`: Instantly sells the given trade (Ignoring `minimum_roi`).
- `/performance`: Show performance of each finished trade grouped by pair
- `/balance`: Show account balance per currency.

View File

@ -163,7 +163,7 @@ official commands. You can ask at any moment for help with `/help`.
| `/count` | Displays number of trades used and available
| `/locks` | Show currently locked pairs.
| `/unlock <pair or lock_id>` | Remove the lock for this pair (or for this lock id).
| `/profit` | Display a summary of your profit/loss from close trades and some stats about your performance
| `/profit [<n>]` | Display a summary of your profit/loss from close trades and some stats about your performance, over the last n days (all trades by default)
| `/forcesell <trade_id>` | Instantly sells the given trade (Ignoring `minimum_roi`).
| `/forcesell all` | Instantly sells all open trades (Ignoring `minimum_roi`).
| `/forcebuy <pair> [rate]` | Instantly buys the given pair. Rate is optional. (`forcebuy_enable` must be set to True)

View File

@ -355,9 +355,10 @@ class RPC:
return {'sell_reasons': sell_reasons, 'durations': durations}
def _rpc_trade_statistics(
self, stake_currency: str, fiat_display_currency: str) -> Dict[str, Any]:
self, stake_currency: str, fiat_display_currency: str,
start_date: datetime = datetime.fromtimestamp(0)) -> Dict[str, Any]:
""" Returns cumulative profit statistics """
trades = Trade.get_trades().order_by(Trade.id).all()
trades = Trade.get_trades([Trade.open_date >= start_date]).order_by(Trade.id).all()
profit_all_coin = []
profit_all_ratio = []

View File

@ -5,7 +5,8 @@ This module manage Telegram communication
"""
import json
import logging
from datetime import timedelta
import re
from datetime import date, datetime, timedelta
from html import escape
from itertools import chain
from math import isnan
@ -99,23 +100,27 @@ class Telegram(RPCHandler):
# TODO: DRY! - its not good to list all valid cmds here. But otherwise
# this needs refacoring of the whole telegram module (same
# problem in _help()).
valid_keys: List[str] = ['/start', '/stop', '/status', '/status table',
'/trades', '/profit', '/performance', '/daily',
'/stats', '/count', '/locks', '/balance',
'/stopbuy', '/reload_config', '/show_config',
'/logs', '/whitelist', '/blacklist', '/edge',
'/help', '/version']
valid_keys: List[str] = [r'/start$', r'/stop$', r'/status$', r'/status table$',
r'/trades$', r'/performance$', r'/daily$', r'/daily \d+$',
r'/profit$', r'/profit \d+',
r'/stats$', r'/count$', r'/locks$', r'/balance$',
r'/stopbuy$', r'/reload_config$', r'/show_config$',
r'/logs$', r'/whitelist$', r'/blacklist$', r'/edge$',
r'/forcebuy$', r'/help$', r'/version$']
# Create keys for generation
valid_keys_print = [k.replace('$', '') for k in valid_keys]
# custom keyboard specified in config.json
cust_keyboard = self._config['telegram'].get('keyboard', [])
if cust_keyboard:
combined = "(" + ")|(".join(valid_keys) + ")"
# check for valid shortcuts
invalid_keys = [b for b in chain.from_iterable(cust_keyboard)
if b not in valid_keys]
if not re.match(combined, b)]
if len(invalid_keys):
err_msg = ('config.telegram.keyboard: Invalid commands for '
f'custom Telegram keyboard: {invalid_keys}'
f'\nvalid commands are: {valid_keys}')
f'\nvalid commands are: {valid_keys_print}')
raise OperationalException(err_msg)
else:
self._keyboard = cust_keyboard
@ -457,9 +462,20 @@ class Telegram(RPCHandler):
stake_cur = self._config['stake_currency']
fiat_disp_cur = self._config.get('fiat_display_currency', '')
start_date = datetime.fromtimestamp(0)
timescale = None
try:
if context.args:
timescale = int(context.args[0])
today_start = datetime.combine(date.today(), datetime.min.time())
start_date = today_start - timedelta(days=timescale)
except (TypeError, ValueError, IndexError):
pass
stats = self._rpc._rpc_trade_statistics(
stake_cur,
fiat_disp_cur)
fiat_disp_cur,
start_date)
profit_closed_coin = stats['profit_closed_coin']
profit_closed_percent_mean = stats['profit_closed_percent_mean']
profit_closed_percent_sum = stats['profit_closed_percent_sum']
@ -487,16 +503,18 @@ class Telegram(RPCHandler):
else:
markdown_msg = "`No closed trade` \n"
markdown_msg += (f"*ROI:* All trades\n"
f"∙ `{round_coin_value(profit_all_coin, stake_cur)} "
f"({profit_all_percent_mean:.2f}%) "
f"({profit_all_percent_sum} \N{GREEK CAPITAL LETTER SIGMA}%)`\n"
f"∙ `{round_coin_value(profit_all_fiat, fiat_disp_cur)}`\n"
f"*Total Trade Count:* `{trade_count}`\n"
f"*First Trade opened:* `{first_trade_date}`\n"
f"*Latest Trade opened:* `{latest_trade_date}\n`"
f"*Win / Loss:* `{stats['winning_trades']} / {stats['losing_trades']}`"
)
markdown_msg += (
f"*ROI:* All trades\n"
f"∙ `{round_coin_value(profit_all_coin, stake_cur)} "
f"({profit_all_percent_mean:.2f}%) "
f"({profit_all_percent_sum} \N{GREEK CAPITAL LETTER SIGMA}%)`\n"
f"∙ `{round_coin_value(profit_all_fiat, fiat_disp_cur)}`\n"
f"*Total Trade Count:* `{trade_count}`\n"
f"*{'First Trade opened' if not timescale else 'Showing Profit since'}:* "
f"`{first_trade_date}`\n"
f"*Latest Trade opened:* `{latest_trade_date}\n`"
f"*Win / Loss:* `{stats['winning_trades']} / {stats['losing_trades']}`"
)
if stats['closed_trade_count'] > 0:
markdown_msg += (f"\n*Avg. Duration:* `{avg_duration}`\n"
f"*Best Performing:* `{best_pair}: {best_rate:.2f}%`")
@ -959,7 +977,8 @@ class Telegram(RPCHandler):
" `pending buy orders are marked with an asterisk (*)`\n"
" `pending sell orders are marked with a double asterisk (**)`\n"
"*/trades [limit]:* `Lists last closed trades (limited to 10 by default)`\n"
"*/profit:* `Lists cumulative profit from all finished trades`\n"
"*/profit [<n>]:* `Lists cumulative profit from all finished trades, "
"over the last n days`\n"
"*/forcesell <trade_id>|all:* `Instantly sells the given trade or all trades, "
"regardless of profit`\n"
f"{forcebuy_text if self._config.get('forcebuy_enable', False) else ''}"

View File

@ -1607,7 +1607,7 @@ def test__send_msg_keyboard(default_conf, mocker, caplog) -> None:
['/count', '/start', '/stop', '/help']]
default_keyboard = ReplyKeyboardMarkup(default_keys_list)
custom_keys_list = [['/daily', '/stats', '/balance', '/profit'],
custom_keys_list = [['/daily', '/stats', '/balance', '/profit', '/profit 5'],
['/count', '/start', '/reload_config', '/help']]
custom_keyboard = ReplyKeyboardMarkup(custom_keys_list)
@ -1641,5 +1641,5 @@ def test__send_msg_keyboard(default_conf, mocker, caplog) -> None:
used_keyboard = bot.send_message.call_args[1]['reply_markup']
assert used_keyboard == custom_keyboard
assert log_has("using custom keyboard from config.json: "
"[['/daily', '/stats', '/balance', '/profit'], ['/count', "
"[['/daily', '/stats', '/balance', '/profit', '/profit 5'], ['/count', "
"'/start', '/reload_config', '/help']]", caplog)