diff --git a/freqtrade/rpc/rpc.py b/freqtrade/rpc/rpc.py index cd2599fa4..13838502b 100644 --- a/freqtrade/rpc/rpc.py +++ b/freqtrade/rpc/rpc.py @@ -4,12 +4,12 @@ This module contains class to define a RPC communications import logging from abc import abstractmethod from datetime import date, datetime, timedelta, timezone -from dateutil.relativedelta import relativedelta from math import isnan from typing import Any, Dict, List, Optional, Tuple, Union import arrow import psutil +from dateutil.relativedelta import relativedelta from numpy import NAN, inf, int64, mean from pandas import DataFrame @@ -294,13 +294,14 @@ class RPC: self, timescale: int, stake_currency: str, fiat_display_currency: str) -> Dict[str, Any]: today = datetime.utcnow().date() + first_iso_day_of_week = today - timedelta(days=today.weekday()) # Monday profit_weeks: Dict[date, Dict] = {} if not (isinstance(timescale, int) and timescale > 0): raise RPCException('timescale must be an integer greater than 0') for week in range(0, timescale): - profitweek = today - timedelta(weeks=week) + profitweek = first_iso_day_of_week - timedelta(weeks=week) trades = Trade.get_trades(trade_filter=[ Trade.is_open.is_(False), Trade.close_date >= profitweek, @@ -335,14 +336,14 @@ class RPC: def _rpc_monthly_profit( self, timescale: int, stake_currency: str, fiat_display_currency: str) -> Dict[str, Any]: - today = datetime.utcnow().date() + first_day_of_month = datetime.utcnow().date().replace(day=1) profit_months: Dict[date, Dict] = {} if not (isinstance(timescale, int) and timescale > 0): raise RPCException('timescale must be an integer greater than 0') for month in range(0, timescale): - profitmonth = today - relativedelta(months=month) + profitmonth = first_day_of_month - relativedelta(months=month) trades = Trade.get_trades(trade_filter=[ Trade.is_open.is_(False), Trade.close_date >= profitmonth, @@ -357,7 +358,7 @@ class RPC: data = [ { - 'date': key, + 'date': f"{key.year}-{key.month}", 'abs_profit': value["amount"], 'fiat_value': self._fiat_converter.convert_amount( value['amount'], diff --git a/freqtrade/rpc/telegram.py b/freqtrade/rpc/telegram.py index 6ceb3cbdb..ae45d609f 100644 --- a/freqtrade/rpc/telegram.py +++ b/freqtrade/rpc/telegram.py @@ -481,7 +481,7 @@ class Telegram(RPCHandler): f"{day['fiat_value']:.3f} {stats['fiat_display_currency']}", f"{day['trade_count']} trades"] for day in stats['data']], headers=[ - 'Month', + 'Day', f'Profit {stake_cur}', f'Profit {fiat_disp_cur}', 'Trades', @@ -520,13 +520,14 @@ class Telegram(RPCHandler): f"{week['fiat_value']:.3f} {stats['fiat_display_currency']}", f"{week['trade_count']} trades"] for week in stats['data']], headers=[ - 'Week', + 'Monday', f'Profit {stake_cur}', f'Profit {fiat_disp_cur}', 'Trades', ], tablefmt='simple') - message = f'Weekly Profit over the last {timescale} weeks:\n
{stats_tab}'
+ message = f'Weekly Profit over the last {timescale} weeks ' \
+ f'(starting from Monday):\n{stats_tab} '
self._send_msg(message, parse_mode=ParseMode.HTML, reload_able=True,
callback_path="update_weekly", query=update.callback_query)
except RPCException as e:
@@ -565,7 +566,8 @@ class Telegram(RPCHandler):
'Trades',
],
tablefmt='simple')
- message = f'Monthly Profit over the last {timescale} months:\n{stats_tab}'
+ message = f'Monthly Profit over the last {timescale} months' \
+ f':\n{stats_tab} '
self._send_msg(message, parse_mode=ParseMode.HTML, reload_able=True,
callback_path="update_monthly", query=update.callback_query)
except RPCException as e:
diff --git a/requirements.txt b/requirements.txt
index 53f73557a..5cb464d35 100644
--- a/requirements.txt
+++ b/requirements.txt
@@ -44,4 +44,6 @@ colorama==0.4.4
questionary==1.10.0
prompt-toolkit==3.0.20
+# Extensions to datetime library
+types-python-dateutil==2.8.2
python-dateutil==2.8.2
\ No newline at end of file
diff --git a/tests/rpc/test_rpc_telegram.py b/tests/rpc/test_rpc_telegram.py
index ae9bbe892..0e00a52ae 100644
--- a/tests/rpc/test_rpc_telegram.py
+++ b/tests/rpc/test_rpc_telegram.py
@@ -5,7 +5,6 @@
import logging
import re
from datetime import datetime, timedelta
-from dateutil.relativedelta import relativedelta
from functools import reduce
from random import choice, randint
from string import ascii_uppercase
@@ -13,6 +12,7 @@ from unittest.mock import ANY, MagicMock
import arrow
import pytest
+from dateutil.relativedelta import relativedelta
from telegram import Chat, Message, ReplyKeyboardMarkup, Update
from telegram.error import BadRequest, NetworkError, TelegramError
@@ -354,7 +354,8 @@ def test_daily_handle(default_conf, update, ticker, limit_buy_order, fee,
context.args = ["2"]
telegram._daily(update=update, context=context)
assert msg_mock.call_count == 1
- assert 'Daily' in msg_mock.call_args_list[0][0][0]
+ assert "Daily Profit over the last 2 days:" in msg_mock.call_args_list[0][0][0]
+ assert 'Day ' in msg_mock.call_args_list[0][0][0]
assert str(datetime.utcnow().date()) in msg_mock.call_args_list[0][0][0]
assert str(' 0.00006217 BTC') in msg_mock.call_args_list[0][0][0]
assert str(' 0.933 USD') in msg_mock.call_args_list[0][0][0]
@@ -366,7 +367,7 @@ def test_daily_handle(default_conf, update, ticker, limit_buy_order, fee,
context.args = []
telegram._daily(update=update, context=context)
assert msg_mock.call_count == 1
- assert 'Daily' in msg_mock.call_args_list[0][0][0]
+ assert "Daily Profit over the last 7 days:" in msg_mock.call_args_list[0][0][0]
assert str(datetime.utcnow().date()) in msg_mock.call_args_list[0][0][0]
assert str(' 0.00006217 BTC') in msg_mock.call_args_list[0][0][0]
assert str(' 0.933 USD') in msg_mock.call_args_list[0][0][0]
@@ -422,7 +423,7 @@ def test_daily_wrong_input(default_conf, update, ticker, mocker) -> None:
context = MagicMock()
context.args = ["today"]
telegram._daily(update=update, context=context)
- assert str('Daily Profit over the last 7 days') in msg_mock.call_args_list[0][0][0]
+ assert str('Daily Profit over the last 7 days:') in msg_mock.call_args_list[0][0][0]
def test_weekly_handle(default_conf, update, ticker, limit_buy_order, fee,
@@ -462,8 +463,12 @@ def test_weekly_handle(default_conf, update, ticker, limit_buy_order, fee,
context.args = ["2"]
telegram._weekly(update=update, context=context)
assert msg_mock.call_count == 1
- assert 'Weekly' in msg_mock.call_args_list[0][0][0]
- assert str(datetime.utcnow().date()) in msg_mock.call_args_list[0][0][0]
+ assert "Weekly Profit over the last 2 weeks (starting from Monday):" \
+ in msg_mock.call_args_list[0][0][0]
+ assert 'Monday ' in msg_mock.call_args_list[0][0][0]
+ today = datetime.utcnow().date()
+ first_iso_day_of_current_week = today - timedelta(days=today.weekday())
+ assert str(first_iso_day_of_current_week) in msg_mock.call_args_list[0][0][0]
assert str(' 0.00006217 BTC') in msg_mock.call_args_list[0][0][0]
assert str(' 0.933 USD') in msg_mock.call_args_list[0][0][0]
assert str(' 1 trade') in msg_mock.call_args_list[0][0][0]
@@ -474,8 +479,9 @@ def test_weekly_handle(default_conf, update, ticker, limit_buy_order, fee,
context.args = []
telegram._weekly(update=update, context=context)
assert msg_mock.call_count == 1
+ assert "Weekly Profit over the last 8 weeks (starting from Monday):" \
+ in msg_mock.call_args_list[0][0][0]
assert 'Weekly' in msg_mock.call_args_list[0][0][0]
- assert str(datetime.utcnow().date()) in msg_mock.call_args_list[0][0][0]
assert str(' 0.00006217 BTC') in msg_mock.call_args_list[0][0][0]
assert str(' 0.933 USD') in msg_mock.call_args_list[0][0][0]
assert str(' 1 trade') in msg_mock.call_args_list[0][0][0]
@@ -519,8 +525,8 @@ def test_weekly_handle(default_conf, update, ticker, limit_buy_order, fee,
context.args = ["10"]
telegram._weekly(update=update, context=context)
assert msg_mock.call_count == 1
-
- a = msg_mock.call_args_list[0][0][0]
+ assert "Weekly Profit over the last 10 weeks (starting from Monday):" \
+ in msg_mock.call_args_list[0][0][0]
# Now, the time-shifted trade should be included
assert str(' 0.00006217 BTC') in msg_mock.call_args_list[0][0][0]
assert str(' 0.933 USD') in msg_mock.call_args_list[0][0][0]
@@ -549,11 +555,12 @@ def test_weekly_wrong_input(default_conf, update, ticker, mocker) -> None:
# Try invalid data
msg_mock.reset_mock()
freqtradebot.state = State.RUNNING
- # /daily today
+ # /weekly this week
context = MagicMock()
context.args = ["this week"]
telegram._weekly(update=update, context=context)
- assert str('Weekly Profit over the last 8 weeks') in msg_mock.call_args_list[0][0][0]
+ assert str('Weekly Profit over the last 8 weeks (starting from Monday):') \
+ in msg_mock.call_args_list[0][0][0]
def test_monthly_handle(default_conf, update, ticker, limit_buy_order, fee,
@@ -593,8 +600,11 @@ def test_monthly_handle(default_conf, update, ticker, limit_buy_order, fee,
context.args = ["2"]
telegram._monthly(update=update, context=context)
assert msg_mock.call_count == 1
- assert 'Monthly' in msg_mock.call_args_list[0][0][0]
- assert str(datetime.utcnow().date()) in msg_mock.call_args_list[0][0][0]
+ assert 'Monthly Profit over the last 2 months:' in msg_mock.call_args_list[0][0][0]
+ assert 'Month ' in msg_mock.call_args_list[0][0][0]
+ today = datetime.utcnow().date()
+ current_month = f"{today.year}-{today.month} "
+ assert current_month in msg_mock.call_args_list[0][0][0]
assert str(' 0.00006217 BTC') in msg_mock.call_args_list[0][0][0]
assert str(' 0.933 USD') in msg_mock.call_args_list[0][0][0]
assert str(' 1 trade') in msg_mock.call_args_list[0][0][0]
@@ -605,8 +615,9 @@ def test_monthly_handle(default_conf, update, ticker, limit_buy_order, fee,
context.args = []
telegram._monthly(update=update, context=context)
assert msg_mock.call_count == 1
- assert 'Monthly' in msg_mock.call_args_list[0][0][0]
- assert str(datetime.utcnow().date()) in msg_mock.call_args_list[0][0][0]
+ assert 'Monthly Profit over the last 6 months:' in msg_mock.call_args_list[0][0][0]
+ assert 'Month ' in msg_mock.call_args_list[0][0][0]
+ assert current_month in msg_mock.call_args_list[0][0][0]
assert str(' 0.00006217 BTC') in msg_mock.call_args_list[0][0][0]
assert str(' 0.933 USD') in msg_mock.call_args_list[0][0][0]
assert str(' 1 trade') in msg_mock.call_args_list[0][0][0]
@@ -630,7 +641,7 @@ def test_monthly_handle(default_conf, update, ticker, limit_buy_order, fee,
trades[0].open_date = datetime.utcnow() - relativedelta(months=6, days=5)
trades[0].close_date = datetime.utcnow() - relativedelta(months=6, days=3)
- # /weekly
+ # /monthly
# By default, the 6 previous months are shown
# So the previous modified trade should be excluded from the stats
context = MagicMock()
@@ -651,13 +662,41 @@ def test_monthly_handle(default_conf, update, ticker, limit_buy_order, fee,
telegram._monthly(update=update, context=context)
assert msg_mock.call_count == 1
- a = msg_mock.call_args_list[0][0][0]
# Now, the time-shifted trade should be included
assert str(' 0.00006217 BTC') in msg_mock.call_args_list[0][0][0]
assert str(' 0.933 USD') in msg_mock.call_args_list[0][0][0]
assert str(' 1 trades') in msg_mock.call_args_list[0][0][0]
+def test_monthly_wrong_input(default_conf, update, ticker, mocker) -> None:
+ mocker.patch.multiple(
+ 'freqtrade.exchange.Exchange',
+ fetch_ticker=ticker
+ )
+
+ telegram, freqtradebot, msg_mock = get_telegram_testobject(mocker, default_conf)
+ patch_get_signal(freqtradebot)
+
+ # Try invalid data
+ msg_mock.reset_mock()
+ freqtradebot.state = State.RUNNING
+ # /daily -2
+ context = MagicMock()
+ context.args = ["-3"]
+ telegram._monthly(update=update, context=context)
+ assert msg_mock.call_count == 1
+ assert 'must be an integer greater than 0' in msg_mock.call_args_list[0][0][0]
+
+ # Try invalid data
+ msg_mock.reset_mock()
+ freqtradebot.state = State.RUNNING
+ # /monthly february
+ context = MagicMock()
+ context.args = ["february"]
+ telegram._monthly(update=update, context=context)
+ assert str('Monthly Profit over the last 6 months:') in msg_mock.call_args_list[0][0][0]
+
+
def test_profit_handle(default_conf, update, ticker, ticker_sell_up, fee,
limit_buy_order, limit_sell_order, mocker) -> None:
mocker.patch('freqtrade.rpc.rpc.CryptoToFiatConverter._find_price', return_value=15000.0)