Merge pull request #4982 from eschava/profit_day_week
day/week options for Telegram '/profit' command
This commit is contained in:
		| @@ -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) | ||||||
|   | |||||||
		Reference in New Issue
	
	Block a user