Merge pull request #4982 from eschava/profit_day_week
day/week options for Telegram '/profit' command
This commit is contained in:
commit
e71d965e32
@ -123,7 +123,7 @@ Telegram is not mandatory. However, this is a great way to control your bot. Mor
|
|||||||
- `/stop`: Stops the trader.
|
- `/stop`: Stops the trader.
|
||||||
- `/stopbuy`: Stop entering new trades.
|
- `/stopbuy`: Stop entering new trades.
|
||||||
- `/status <trade_id>|[table]`: Lists all or specific open 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`).
|
- `/forcesell <trade_id>|all`: Instantly sells the given trade (Ignoring `minimum_roi`).
|
||||||
- `/performance`: Show performance of each finished trade grouped by pair
|
- `/performance`: Show performance of each finished trade grouped by pair
|
||||||
- `/balance`: Show account balance per currency.
|
- `/balance`: Show account balance per currency.
|
||||||
|
@ -163,7 +163,7 @@ official commands. You can ask at any moment for help with `/help`.
|
|||||||
| `/count` | Displays number of trades used and available
|
| `/count` | Displays number of trades used and available
|
||||||
| `/locks` | Show currently locked pairs.
|
| `/locks` | Show currently locked pairs.
|
||||||
| `/unlock <pair or lock_id>` | Remove the lock for this pair (or for this lock id).
|
| `/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 <trade_id>` | Instantly sells the given trade (Ignoring `minimum_roi`).
|
||||||
| `/forcesell all` | Instantly sells all open trades (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)
|
| `/forcebuy <pair> [rate]` | Instantly buys the given pair. Rate is optional. (`forcebuy_enable` must be set to True)
|
||||||
|
@ -355,9 +355,10 @@ class RPC:
|
|||||||
return {'sell_reasons': sell_reasons, 'durations': durations}
|
return {'sell_reasons': sell_reasons, 'durations': durations}
|
||||||
|
|
||||||
def _rpc_trade_statistics(
|
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 """
|
""" 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_coin = []
|
||||||
profit_all_ratio = []
|
profit_all_ratio = []
|
||||||
|
@ -5,7 +5,8 @@ This module manage Telegram communication
|
|||||||
"""
|
"""
|
||||||
import json
|
import json
|
||||||
import logging
|
import logging
|
||||||
from datetime import timedelta
|
import re
|
||||||
|
from datetime import date, datetime, timedelta
|
||||||
from html import escape
|
from html import escape
|
||||||
from itertools import chain
|
from itertools import chain
|
||||||
from math import isnan
|
from math import isnan
|
||||||
@ -99,23 +100,27 @@ class Telegram(RPCHandler):
|
|||||||
# TODO: DRY! - its not good to list all valid cmds here. But otherwise
|
# TODO: DRY! - its not good to list all valid cmds here. But otherwise
|
||||||
# this needs refacoring of the whole telegram module (same
|
# this needs refacoring of the whole telegram module (same
|
||||||
# problem in _help()).
|
# problem in _help()).
|
||||||
valid_keys: List[str] = ['/start', '/stop', '/status', '/status table',
|
valid_keys: List[str] = [r'/start$', r'/stop$', r'/status$', r'/status table$',
|
||||||
'/trades', '/profit', '/performance', '/daily',
|
r'/trades$', r'/performance$', r'/daily$', r'/daily \d+$',
|
||||||
'/stats', '/count', '/locks', '/balance',
|
r'/profit$', r'/profit \d+',
|
||||||
'/stopbuy', '/reload_config', '/show_config',
|
r'/stats$', r'/count$', r'/locks$', r'/balance$',
|
||||||
'/logs', '/whitelist', '/blacklist', '/edge',
|
r'/stopbuy$', r'/reload_config$', r'/show_config$',
|
||||||
'/help', '/version']
|
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
|
# custom keyboard specified in config.json
|
||||||
cust_keyboard = self._config['telegram'].get('keyboard', [])
|
cust_keyboard = self._config['telegram'].get('keyboard', [])
|
||||||
if cust_keyboard:
|
if cust_keyboard:
|
||||||
|
combined = "(" + ")|(".join(valid_keys) + ")"
|
||||||
# check for valid shortcuts
|
# check for valid shortcuts
|
||||||
invalid_keys = [b for b in chain.from_iterable(cust_keyboard)
|
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):
|
if len(invalid_keys):
|
||||||
err_msg = ('config.telegram.keyboard: Invalid commands for '
|
err_msg = ('config.telegram.keyboard: Invalid commands for '
|
||||||
f'custom Telegram keyboard: {invalid_keys}'
|
f'custom Telegram keyboard: {invalid_keys}'
|
||||||
f'\nvalid commands are: {valid_keys}')
|
f'\nvalid commands are: {valid_keys_print}')
|
||||||
raise OperationalException(err_msg)
|
raise OperationalException(err_msg)
|
||||||
else:
|
else:
|
||||||
self._keyboard = cust_keyboard
|
self._keyboard = cust_keyboard
|
||||||
@ -457,9 +462,20 @@ class Telegram(RPCHandler):
|
|||||||
stake_cur = self._config['stake_currency']
|
stake_cur = self._config['stake_currency']
|
||||||
fiat_disp_cur = self._config.get('fiat_display_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(
|
stats = self._rpc._rpc_trade_statistics(
|
||||||
stake_cur,
|
stake_cur,
|
||||||
fiat_disp_cur)
|
fiat_disp_cur,
|
||||||
|
start_date)
|
||||||
profit_closed_coin = stats['profit_closed_coin']
|
profit_closed_coin = stats['profit_closed_coin']
|
||||||
profit_closed_percent_mean = stats['profit_closed_percent_mean']
|
profit_closed_percent_mean = stats['profit_closed_percent_mean']
|
||||||
profit_closed_percent_sum = stats['profit_closed_percent_sum']
|
profit_closed_percent_sum = stats['profit_closed_percent_sum']
|
||||||
@ -487,16 +503,18 @@ class Telegram(RPCHandler):
|
|||||||
else:
|
else:
|
||||||
markdown_msg = "`No closed trade` \n"
|
markdown_msg = "`No closed trade` \n"
|
||||||
|
|
||||||
markdown_msg += (f"*ROI:* All trades\n"
|
markdown_msg += (
|
||||||
f"∙ `{round_coin_value(profit_all_coin, stake_cur)} "
|
f"*ROI:* All trades\n"
|
||||||
f"({profit_all_percent_mean:.2f}%) "
|
f"∙ `{round_coin_value(profit_all_coin, stake_cur)} "
|
||||||
f"({profit_all_percent_sum} \N{GREEK CAPITAL LETTER SIGMA}%)`\n"
|
f"({profit_all_percent_mean:.2f}%) "
|
||||||
f"∙ `{round_coin_value(profit_all_fiat, fiat_disp_cur)}`\n"
|
f"({profit_all_percent_sum} \N{GREEK CAPITAL LETTER SIGMA}%)`\n"
|
||||||
f"*Total Trade Count:* `{trade_count}`\n"
|
f"∙ `{round_coin_value(profit_all_fiat, fiat_disp_cur)}`\n"
|
||||||
f"*First Trade opened:* `{first_trade_date}`\n"
|
f"*Total Trade Count:* `{trade_count}`\n"
|
||||||
f"*Latest Trade opened:* `{latest_trade_date}\n`"
|
f"*{'First Trade opened' if not timescale else 'Showing Profit since'}:* "
|
||||||
f"*Win / Loss:* `{stats['winning_trades']} / {stats['losing_trades']}`"
|
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:
|
if stats['closed_trade_count'] > 0:
|
||||||
markdown_msg += (f"\n*Avg. Duration:* `{avg_duration}`\n"
|
markdown_msg += (f"\n*Avg. Duration:* `{avg_duration}`\n"
|
||||||
f"*Best Performing:* `{best_pair}: {best_rate:.2f}%`")
|
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 buy orders are marked with an asterisk (*)`\n"
|
||||||
" `pending sell orders are marked with a double asterisk (**)`\n"
|
" `pending sell orders are marked with a double asterisk (**)`\n"
|
||||||
"*/trades [limit]:* `Lists last closed trades (limited to 10 by default)`\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, "
|
"*/forcesell <trade_id>|all:* `Instantly sells the given trade or all trades, "
|
||||||
"regardless of profit`\n"
|
"regardless of profit`\n"
|
||||||
f"{forcebuy_text if self._config.get('forcebuy_enable', False) else ''}"
|
f"{forcebuy_text if self._config.get('forcebuy_enable', False) else ''}"
|
||||||
|
@ -1607,7 +1607,7 @@ def test__send_msg_keyboard(default_conf, mocker, caplog) -> None:
|
|||||||
['/count', '/start', '/stop', '/help']]
|
['/count', '/start', '/stop', '/help']]
|
||||||
default_keyboard = ReplyKeyboardMarkup(default_keys_list)
|
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']]
|
['/count', '/start', '/reload_config', '/help']]
|
||||||
custom_keyboard = ReplyKeyboardMarkup(custom_keys_list)
|
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']
|
used_keyboard = bot.send_message.call_args[1]['reply_markup']
|
||||||
assert used_keyboard == custom_keyboard
|
assert used_keyboard == custom_keyboard
|
||||||
assert log_has("using custom keyboard from config.json: "
|
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)
|
"'/start', '/reload_config', '/help']]", caplog)
|
||||||
|
Loading…
Reference in New Issue
Block a user