From 76827b31a9c59d0d7344ff379f5ef7f0fc1a56f4 Mon Sep 17 00:00:00 2001 From: Matthias Date: Sat, 11 Jun 2022 11:18:21 +0200 Subject: [PATCH 1/3] Add relative profit to daily/weekly commands --- freqtrade/rpc/rpc.py | 11 +++++++++-- freqtrade/rpc/telegram.py | 12 +++++++----- tests/rpc/test_rpc.py | 4 +++- 3 files changed, 19 insertions(+), 8 deletions(-) diff --git a/freqtrade/rpc/rpc.py b/freqtrade/rpc/rpc.py index 64584382a..da5144dab 100644 --- a/freqtrade/rpc/rpc.py +++ b/freqtrade/rpc/rpc.py @@ -302,11 +302,12 @@ class RPC: return relativedelta(months=step) return timedelta(**{timeunit: step}) - profit_units: Dict[date, Dict] = {} - if not (isinstance(timescale, int) and timescale > 0): raise RPCException('timescale must be an integer greater than 0') + profit_units: Dict[date, Dict] = {} + daily_stake = self._freqtrade.wallets.get_total_stake_amount() + for day in range(0, timescale): profitday = start_date - time_offset(day) # Only query for necessary columns for performance reasons. @@ -318,8 +319,12 @@ class RPC: curdayprofit = sum( trade.close_profit_abs for trade in trades if trade.close_profit_abs is not None) + # Calculate this periods starting balance + daily_stake = daily_stake - curdayprofit profit_units[profitday] = { 'amount': curdayprofit, + 'daily_stake': daily_stake, + 'rel_profit': round(curdayprofit / daily_stake, 8) if daily_stake > 0 else 0, 'trades': len(trades), } @@ -327,6 +332,8 @@ class RPC: { 'date': f"{key.year}-{key.month:02d}" if timeunit == 'months' else key, 'abs_profit': value["amount"], + 'starting_balance': value["daily_stake"], + 'rel_profit': value["rel_profit"], 'fiat_value': self._fiat_converter.convert_amount( value['amount'], stake_currency, diff --git a/freqtrade/rpc/telegram.py b/freqtrade/rpc/telegram.py index 61b73553f..c3e4c1152 100644 --- a/freqtrade/rpc/telegram.py +++ b/freqtrade/rpc/telegram.py @@ -605,14 +605,16 @@ class Telegram(RPCHandler): unit ) stats_tab = tabulate( - [[period['date'], + [[f"{period['date']} ({period['trade_count']})", f"{round_coin_value(period['abs_profit'], stats['stake_currency'])}", f"{period['fiat_value']:.2f} {stats['fiat_display_currency']}", - f"{period['trade_count']} trades"] for period in stats['data']], + f"{period['rel_profit']:.2%}", + ] for period in stats['data']], headers=[ - val.header, - f'Profit {stake_cur}', - f'Profit {fiat_disp_cur}', + f"{val.header} (trades)", + f'Prof {stake_cur}', + f'Prof {fiat_disp_cur}', + 'Profit %', 'Trades', ], tablefmt='simple') diff --git a/tests/rpc/test_rpc.py b/tests/rpc/test_rpc.py index 982ac65d7..0273b8237 100644 --- a/tests/rpc/test_rpc.py +++ b/tests/rpc/test_rpc.py @@ -311,10 +311,12 @@ def test__rpc_timeunit_profit(default_conf_usdt, ticker, fee, assert days['fiat_display_currency'] == default_conf_usdt['fiat_display_currency'] for day in days['data']: # {'date': datetime.date(2022, 6, 11), 'abs_profit': 13.8299999, + # 'starting_balance': 1055.37, 'rel_profit': 0.0131044, # 'fiat_value': 0.0, 'trade_count': 2} assert day['abs_profit'] in (0.0, pytest.approx(13.8299999), pytest.approx(-4.0)) + assert day['rel_profit'] in (0.0, pytest.approx(0.01310441), pytest.approx(-0.00377583)) assert day['trade_count'] in (0, 1, 2) - + assert day['starting_balance'] in (pytest.approx(1059.37), pytest.approx(1055.37)) assert day['fiat_value'] in (0.0, ) # ensure first day is current date assert str(days['data'][0]['date']) == str(datetime.utcnow().date()) From 9ba11f7bcc0481bbc6db6c3faf3a9e25b8a0edd3 Mon Sep 17 00:00:00 2001 From: Matthias Date: Sat, 11 Jun 2022 11:26:49 +0200 Subject: [PATCH 2/3] Update docs and tests for new daily command --- docs/telegram-usage.md | 30 +++++++++++++++--------------- freqtrade/rpc/telegram.py | 6 +++--- tests/rpc/test_rpc_telegram.py | 32 ++++++++++++++++---------------- 3 files changed, 34 insertions(+), 34 deletions(-) diff --git a/docs/telegram-usage.md b/docs/telegram-usage.md index 27f5f91b6..6e21d3689 100644 --- a/docs/telegram-usage.md +++ b/docs/telegram-usage.md @@ -328,11 +328,11 @@ Per default `/daily` will return the 7 last days. The example below if for `/dai > **Daily Profit over the last 3 days:** ``` -Day Profit BTC Profit USD ----------- -------------- ------------ -2018-01-03 0.00224175 BTC 29,142 USD -2018-01-02 0.00033131 BTC 4,307 USD -2018-01-01 0.00269130 BTC 34.986 USD +Day (count) USDT USD Profit % +-------------- ------------ ---------- ---------- +2022-06-11 (1) -0.746 USDT -0.75 USD -0.08% +2022-06-10 (0) 0 USDT 0.00 USD 0.00% +2022-06-09 (5) 20 USDT 20.10 USD 5.00% ``` ### /weekly @@ -342,11 +342,11 @@ from Monday. The example below if for `/weekly 3`: > **Weekly Profit over the last 3 weeks (starting from Monday):** ``` -Monday Profit BTC Profit USD ----------- -------------- ------------ -2018-01-03 0.00224175 BTC 29,142 USD -2017-12-27 0.00033131 BTC 4,307 USD -2017-12-20 0.00269130 BTC 34.986 USD +Monday (count) Profit BTC Profit USD Profit % +------------- -------------- ------------ ---------- +2018-01-03 (5) 0.00224175 BTC 29,142 USD 4.98% +2017-12-27 (1) 0.00033131 BTC 4,307 USD 0.00% +2017-12-20 (4) 0.00269130 BTC 34.986 USD 5.12% ``` ### /monthly @@ -356,11 +356,11 @@ if for `/monthly 3`: > **Monthly Profit over the last 3 months:** ``` -Month Profit BTC Profit USD ----------- -------------- ------------ -2018-01 0.00224175 BTC 29,142 USD -2017-12 0.00033131 BTC 4,307 USD -2017-11 0.00269130 BTC 34.986 USD +Month (count) Profit BTC Profit USD Profit % +------------- -------------- ------------ ---------- +2018-01 (20) 0.00224175 BTC 29,142 USD 4.98% +2017-12 (5) 0.00033131 BTC 4,307 USD 0.00% +2017-11 (10) 0.00269130 BTC 34.986 USD 5.10% ``` ### /whitelist diff --git a/freqtrade/rpc/telegram.py b/freqtrade/rpc/telegram.py index c3e4c1152..2e1d23621 100644 --- a/freqtrade/rpc/telegram.py +++ b/freqtrade/rpc/telegram.py @@ -611,9 +611,9 @@ class Telegram(RPCHandler): f"{period['rel_profit']:.2%}", ] for period in stats['data']], headers=[ - f"{val.header} (trades)", - f'Prof {stake_cur}', - f'Prof {fiat_disp_cur}', + f"{val.header} (count)", + f'{stake_cur}', + f'{fiat_disp_cur}', 'Profit %', 'Trades', ], diff --git a/tests/rpc/test_rpc_telegram.py b/tests/rpc/test_rpc_telegram.py index 404fdd2b0..11a783f3a 100644 --- a/tests/rpc/test_rpc_telegram.py +++ b/tests/rpc/test_rpc_telegram.py @@ -432,9 +432,9 @@ def test_daily_handle(default_conf_usdt, update, ticker, fee, mocker) -> None: assert str(datetime.utcnow().date()) in msg_mock.call_args_list[0][0][0] assert ' 13.83 USDT' in msg_mock.call_args_list[0][0][0] assert ' 15.21 USD' in msg_mock.call_args_list[0][0][0] - assert ' 2 trade' in msg_mock.call_args_list[0][0][0] - assert '13.83 USDT 15.21 USD 2 trades' in msg_mock.call_args_list[0][0][0] - assert ' 0 trade' in msg_mock.call_args_list[0][0][0] + assert '(2)' in msg_mock.call_args_list[0][0][0] + assert '(2) 13.83 USDT 15.21 USD 1.31%' in msg_mock.call_args_list[0][0][0] + assert '(0)' in msg_mock.call_args_list[0][0][0] # Reset msg_mock msg_mock.reset_mock() @@ -446,9 +446,9 @@ def test_daily_handle(default_conf_usdt, update, ticker, fee, mocker) -> None: assert str((datetime.utcnow() - timedelta(days=5)).date()) in msg_mock.call_args_list[0][0][0] assert ' 13.83 USDT' in msg_mock.call_args_list[0][0][0] assert ' 15.21 USD' in msg_mock.call_args_list[0][0][0] - assert ' 2 trade' in msg_mock.call_args_list[0][0][0] - assert ' 1 trade' in msg_mock.call_args_list[0][0][0] - assert ' 0 trade' in msg_mock.call_args_list[0][0][0] + assert '(2)' in msg_mock.call_args_list[0][0][0] + assert '(1)' in msg_mock.call_args_list[0][0][0] + assert '(0)' in msg_mock.call_args_list[0][0][0] # Reset msg_mock msg_mock.reset_mock() @@ -459,7 +459,7 @@ def test_daily_handle(default_conf_usdt, update, ticker, fee, mocker) -> None: telegram._daily(update=update, context=context) assert ' 13.83 USDT' in msg_mock.call_args_list[0][0][0] assert ' 15.21 USD' in msg_mock.call_args_list[0][0][0] - assert ' 2 trade' in msg_mock.call_args_list[0][0][0] + assert '(2)' in msg_mock.call_args_list[0][0][0] def test_daily_wrong_input(default_conf, update, ticker, mocker) -> None: @@ -521,8 +521,8 @@ def test_weekly_handle(default_conf_usdt, update, ticker, fee, mocker) -> None: assert str(first_iso_day_of_current_week) in msg_mock.call_args_list[0][0][0] assert ' 9.83 USDT' in msg_mock.call_args_list[0][0][0] assert ' 10.81 USD' in msg_mock.call_args_list[0][0][0] - assert ' 3 trade' in msg_mock.call_args_list[0][0][0] - assert ' 0 trade' in msg_mock.call_args_list[0][0][0] + assert '(3)' in msg_mock.call_args_list[0][0][0] + assert '(0)' in msg_mock.call_args_list[0][0][0] # Reset msg_mock msg_mock.reset_mock() @@ -534,8 +534,8 @@ def test_weekly_handle(default_conf_usdt, update, ticker, fee, mocker) -> None: assert 'Weekly' in msg_mock.call_args_list[0][0][0] assert ' 9.83 USDT' in msg_mock.call_args_list[0][0][0] assert ' 10.81 USD' in msg_mock.call_args_list[0][0][0] - assert ' 3 trade' in msg_mock.call_args_list[0][0][0] - assert ' 0 trade' in msg_mock.call_args_list[0][0][0] + assert '(3)' in msg_mock.call_args_list[0][0][0] + assert '(0)' in msg_mock.call_args_list[0][0][0] # Try invalid data msg_mock.reset_mock() @@ -589,8 +589,8 @@ def test_monthly_handle(default_conf_usdt, update, ticker, fee, mocker) -> None: assert current_month in msg_mock.call_args_list[0][0][0] assert ' 9.83 USDT' in msg_mock.call_args_list[0][0][0] assert ' 10.81 USD' in msg_mock.call_args_list[0][0][0] - assert ' 3 trade' in msg_mock.call_args_list[0][0][0] - assert ' 0 trade' in msg_mock.call_args_list[0][0][0] + assert '(3)' in msg_mock.call_args_list[0][0][0] + assert '(0)' in msg_mock.call_args_list[0][0][0] # Reset msg_mock msg_mock.reset_mock() @@ -603,8 +603,8 @@ def test_monthly_handle(default_conf_usdt, update, ticker, fee, mocker) -> None: assert current_month in msg_mock.call_args_list[0][0][0] assert ' 9.83 USDT' in msg_mock.call_args_list[0][0][0] assert ' 10.81 USD' in msg_mock.call_args_list[0][0][0] - assert ' 3 trade' in msg_mock.call_args_list[0][0][0] - assert ' 0 trade' in msg_mock.call_args_list[0][0][0] + assert '(3)' in msg_mock.call_args_list[0][0][0] + assert '(0)' in msg_mock.call_args_list[0][0][0] # Reset msg_mock msg_mock.reset_mock() @@ -617,7 +617,7 @@ def test_monthly_handle(default_conf_usdt, update, ticker, fee, mocker) -> None: assert 'Monthly Profit over the last 12 months:' in msg_mock.call_args_list[0][0][0] assert ' 9.83 USDT' in msg_mock.call_args_list[0][0][0] assert ' 10.81 USD' in msg_mock.call_args_list[0][0][0] - assert ' 3 trade' in msg_mock.call_args_list[0][0][0] + assert '(3)' in msg_mock.call_args_list[0][0][0] # The one-digit months should contain a zero, Eg: September 2021 = "2021-09" # Since we loaded the last 12 months, any month should appear From 3a06337601b1ff4ca0609010635fb95b7eee7aa7 Mon Sep 17 00:00:00 2001 From: Matthias Date: Sat, 11 Jun 2022 11:28:45 +0200 Subject: [PATCH 3/3] Update API to provide new values. --- freqtrade/rpc/api_server/api_schemas.py | 2 ++ freqtrade/rpc/api_server/api_v1.py | 3 ++- 2 files changed, 4 insertions(+), 1 deletion(-) diff --git a/freqtrade/rpc/api_server/api_schemas.py b/freqtrade/rpc/api_server/api_schemas.py index a31c74c2e..11fdc0121 100644 --- a/freqtrade/rpc/api_server/api_schemas.py +++ b/freqtrade/rpc/api_server/api_schemas.py @@ -120,6 +120,8 @@ class Stats(BaseModel): class DailyRecord(BaseModel): date: date abs_profit: float + rel_profit: float + starting_balance: float fiat_value: float trade_count: int diff --git a/freqtrade/rpc/api_server/api_v1.py b/freqtrade/rpc/api_server/api_v1.py index 271e3de1b..225fe66b9 100644 --- a/freqtrade/rpc/api_server/api_v1.py +++ b/freqtrade/rpc/api_server/api_v1.py @@ -36,7 +36,8 @@ logger = logging.getLogger(__name__) # versions 2.xx -> futures/short branch # 2.14: Add entry/exit orders to trade response # 2.15: Add backtest history endpoints -API_VERSION = 2.15 +# 2.16: Additional daily metrics +API_VERSION = 2.16 # Public API, requires no auth. router_public = APIRouter()