From 4320c7a3cc85d10f5bd04b23fada32affdcbdb76 Mon Sep 17 00:00:00 2001 From: Matthias Date: Sun, 21 Apr 2024 14:04:50 +0200 Subject: [PATCH 1/5] Add humanize as dependency --- requirements.txt | 1 + setup.py | 1 + 2 files changed, 2 insertions(+) diff --git a/requirements.txt b/requirements.txt index 65964d79a..e17d97417 100644 --- a/requirements.txt +++ b/requirements.txt @@ -10,6 +10,7 @@ python-telegram-bot==21.1 # can't be hard-pinned due to telegram-bot pinning httpx with ~ httpx>=0.24.1 arrow==1.3.0 +humanize==4.9.0 cachetools==5.3.3 requests==2.31.0 urllib3==2.2.1 diff --git a/setup.py b/setup.py index 3caf74694..f52e52418 100644 --- a/setup.py +++ b/setup.py @@ -74,6 +74,7 @@ setup( 'SQLAlchemy>=2.0.6', 'python-telegram-bot>=20.1', 'arrow>=1.0.0', + 'humanize>=4.0.0', 'cachetools', 'requests', 'httpx>=0.24.1', From cc534c50004233afb508f9671a71085c76041c63 Mon Sep 17 00:00:00 2001 From: Matthias Date: Sun, 21 Apr 2024 15:29:11 +0200 Subject: [PATCH 2/5] switch from arrow to humanize --- freqtrade/exchange/exchange.py | 4 ++-- freqtrade/rpc/rpc.py | 10 +++++----- freqtrade/util/datetime_helpers.py | 8 ++++++++ tests/rpc/test_rpc.py | 8 ++++---- 4 files changed, 19 insertions(+), 11 deletions(-) diff --git a/freqtrade/exchange/exchange.py b/freqtrade/exchange/exchange.py index 38c16ac22..eed852e7c 100644 --- a/freqtrade/exchange/exchange.py +++ b/freqtrade/exchange/exchange.py @@ -44,7 +44,7 @@ from freqtrade.misc import (chunks, deep_merge_dicts, file_dump_json, file_load_ safe_value_fallback2) from freqtrade.plugins.pairlist.pairlist_helpers import expand_pairlist from freqtrade.util import dt_from_ts, dt_now -from freqtrade.util.datetime_helpers import dt_humanize, dt_ts +from freqtrade.util.datetime_helpers import dt_humanize_delta, dt_ts from freqtrade.util.periodic_cache import PeriodicCache @@ -2008,7 +2008,7 @@ class Exchange: logger.debug( "one_call: %s msecs (%s)", one_call, - dt_humanize(dt_now() - timedelta(milliseconds=one_call), only_distance=True) + dt_humanize_delta(dt_now() - timedelta(milliseconds=one_call)) ) input_coroutines = [self._async_get_candle_history( pair, timeframe, candle_type, since) for since in diff --git a/freqtrade/rpc/rpc.py b/freqtrade/rpc/rpc.py index b8be473a7..43be0fd94 100644 --- a/freqtrade/rpc/rpc.py +++ b/freqtrade/rpc/rpc.py @@ -30,8 +30,8 @@ from freqtrade.persistence.models import PairLock from freqtrade.plugins.pairlist.pairlist_helpers import expand_pairlist from freqtrade.rpc.fiat_convert import CryptoToFiatConverter from freqtrade.rpc.rpc_types import RPCSendMsg -from freqtrade.util import (decimals_per_coin, dt_humanize, dt_now, dt_ts_def, format_date, - shorten_date) +from freqtrade.util import decimals_per_coin, dt_now, dt_ts_def, format_date, shorten_date +from freqtrade.util.datetime_helpers import dt_humanize_delta from freqtrade.wallets import PositionWallet, Wallet @@ -307,7 +307,7 @@ class RPC: detail_trade = [ f'{trade.id} {direction_str}', trade.pair + active_attempt_side_symbols_str, - shorten_date(dt_humanize(trade.open_date, only_distance=True)), + shorten_date(dt_humanize_delta(trade.open_date_utc)), profit_str ] @@ -599,10 +599,10 @@ class RPC: 'trade_count': len(trades), 'closed_trade_count': closed_trade_count, 'first_trade_date': format_date(first_date), - 'first_trade_humanized': dt_humanize(first_date) if first_date else '', + 'first_trade_humanized': dt_humanize_delta(first_date) if first_date else '', 'first_trade_timestamp': dt_ts_def(first_date, 0), 'latest_trade_date': format_date(last_date), - 'latest_trade_humanized': dt_humanize(last_date) if last_date else '', + 'latest_trade_humanized': dt_humanize_delta(last_date) if last_date else '', 'latest_trade_timestamp': dt_ts_def(last_date, 0), 'avg_duration': str(timedelta(seconds=sum(durations) / num)).split('.')[0], 'best_pair': best_pair[0] if best_pair else '', diff --git a/freqtrade/util/datetime_helpers.py b/freqtrade/util/datetime_helpers.py index 2b288424a..f93186d63 100644 --- a/freqtrade/util/datetime_helpers.py +++ b/freqtrade/util/datetime_helpers.py @@ -3,6 +3,7 @@ from datetime import datetime, timezone from typing import Optional, Union import arrow +import humanize from freqtrade.constants import DATETIME_PRINT_FORMAT @@ -76,6 +77,13 @@ def shorten_date(_date: str) -> str: return new_date +def dt_humanize_delta(dt: datetime): + """ + Return a humanized string for the given timedelta. + """ + return humanize.naturaltime(dt) + + def dt_humanize(dt: datetime, **kwargs) -> str: """ Return a humanized string for the given datetime. diff --git a/tests/rpc/test_rpc.py b/tests/rpc/test_rpc.py index 050c51fed..5c8602c2f 100644 --- a/tests/rpc/test_rpc.py +++ b/tests/rpc/test_rpc.py @@ -221,7 +221,7 @@ def test_rpc_status_table(default_conf, ticker, fee, mocker) -> None: result, headers, fiat_profit_sum = rpc._rpc_status_table(default_conf['stake_currency'], 'USD') assert "Since" in headers assert "Pair" in headers - assert 'instantly' == result[0][2] + assert 'now' == result[0][2] assert 'ETH/BTC' in result[0][1] assert '0.00 (0.00)' == result[0][3] assert '0.00' == f'{fiat_profit_sum:.2f}' @@ -232,7 +232,7 @@ def test_rpc_status_table(default_conf, ticker, fee, mocker) -> None: result, headers, fiat_profit_sum = rpc._rpc_status_table(default_conf['stake_currency'], 'USD') assert "Since" in headers assert "Pair" in headers - assert 'instantly' == result[0][2] + assert 'now' == result[0][2] assert 'ETH/BTC' in result[0][1] assert '-0.41% (-0.00)' == result[0][3] assert '-0.00' == f'{fiat_profit_sum:.2f}' @@ -243,7 +243,7 @@ def test_rpc_status_table(default_conf, ticker, fee, mocker) -> None: assert "Since" in headers assert "Pair" in headers assert len(result[0]) == 4 - assert 'instantly' == result[0][2] + assert 'now' == result[0][2] assert 'ETH/BTC' in result[0][1] assert '-0.41% (-0.06)' == result[0][3] assert '-0.06' == f'{fiat_profit_sum:.2f}' @@ -260,7 +260,7 @@ def test_rpc_status_table(default_conf, ticker, fee, mocker) -> None: mocker.patch(f'{EXMS}.get_rate', MagicMock(side_effect=ExchangeError("Pair 'ETH/BTC' not available"))) result, headers, fiat_profit_sum = rpc._rpc_status_table(default_conf['stake_currency'], 'USD') - assert 'instantly' == result[0][2] + assert 'now' == result[0][2] assert 'ETH/BTC' in result[0][1] assert 'nan%' == result[0][3] assert isnan(fiat_profit_sum) From d48cff3b9aedbb0e3304e72a4d8a28b8b2f0dd15 Mon Sep 17 00:00:00 2001 From: Matthias Date: Sun, 21 Apr 2024 15:49:50 +0200 Subject: [PATCH 3/5] Update to dt_humanize_delta for all usages --- freqtrade/rpc/telegram.py | 9 ++++----- freqtrade/util/__init__.py | 8 +++++--- tests/rpc/test_rpc_telegram.py | 4 ++-- tests/utils/test_datetime_helpers.py | 14 +++++++++----- 4 files changed, 20 insertions(+), 15 deletions(-) diff --git a/freqtrade/rpc/telegram.py b/freqtrade/rpc/telegram.py index 5c5a65e22..030075c64 100644 --- a/freqtrade/rpc/telegram.py +++ b/freqtrade/rpc/telegram.py @@ -33,7 +33,7 @@ from freqtrade.misc import chunks, plural from freqtrade.persistence import Trade from freqtrade.rpc import RPC, RPCException, RPCHandler from freqtrade.rpc.rpc_types import RPCEntryMsg, RPCExitMsg, RPCOrderMsg, RPCSendMsg -from freqtrade.util import dt_humanize, fmt_coin, format_date, round_value +from freqtrade.util import dt_from_ts, dt_humanize_delta, fmt_coin, format_date, round_value MAX_MESSAGE_LENGTH = MessageLimit.MAX_TEXT_LENGTH @@ -573,8 +573,7 @@ class Telegram(RPCHandler): # TODO: This calculation ignores fees. price_to_1st_entry = ((cur_entry_average - first_avg) / first_avg) if is_open: - lines.append("({})".format(dt_humanize(order["order_filled_date"], - granularity=["day", "hour", "minute"]))) + lines.append("({})".format(dt_humanize_delta(order["order_filled_date"]))) lines.append(f"*Amount:* {round_value(cur_entry_amount, 8)} " f"({fmt_coin(order['cost'], quote_currency)})") lines.append(f"*Average {wording} Price:* {round_value(cur_entry_average, 8)} " @@ -657,7 +656,7 @@ class Telegram(RPCHandler): position_adjust = self._config.get('position_adjustment_enable', False) max_entries = self._config.get('max_entry_position_adjustment', -1) for r in results: - r['open_date_hum'] = dt_humanize(r['open_date']) + r['open_date_hum'] = dt_humanize_delta(r['open_date']) r['num_entries'] = len([o for o in r['orders'] if o['ft_is_entry']]) r['num_exits'] = len([o for o in r['orders'] if not o['ft_is_entry'] and not o['ft_order_side'] == 'stoploss']) @@ -1289,7 +1288,7 @@ class Telegram(RPCHandler): nrecent ) trades_tab = tabulate( - [[dt_humanize(trade['close_date']), + [[dt_humanize_delta(dt_from_ts(trade['close_timestamp'])), trade['pair'] + " (#" + str(trade['trade_id']) + ")", f"{(trade['close_profit']):.2%} ({trade['close_profit_abs']})"] for trade in trades['trades']], diff --git a/freqtrade/util/__init__.py b/freqtrade/util/__init__.py index b85b36ffb..1eee3d20e 100644 --- a/freqtrade/util/__init__.py +++ b/freqtrade/util/__init__.py @@ -1,6 +1,7 @@ -from freqtrade.util.datetime_helpers import (dt_floor_day, dt_from_ts, dt_humanize, dt_now, dt_ts, - dt_ts_def, dt_ts_none, dt_utc, format_date, - format_ms_time, shorten_date) +from freqtrade.util.datetime_helpers import (dt_floor_day, dt_from_ts, dt_humanize, + dt_humanize_delta, dt_now, dt_ts, dt_ts_def, + dt_ts_none, dt_utc, format_date, format_ms_time, + shorten_date) from freqtrade.util.formatters import decimals_per_coin, fmt_coin, round_value from freqtrade.util.ft_precise import FtPrecise from freqtrade.util.measure_time import MeasureTime @@ -12,6 +13,7 @@ __all__ = [ 'dt_floor_day', 'dt_from_ts', 'dt_humanize', + 'dt_humanize_delta', 'dt_now', 'dt_ts', 'dt_ts_def', diff --git a/tests/rpc/test_rpc_telegram.py b/tests/rpc/test_rpc_telegram.py index 4d2a2dea1..9a3b713e2 100644 --- a/tests/rpc/test_rpc_telegram.py +++ b/tests/rpc/test_rpc_telegram.py @@ -1821,8 +1821,8 @@ async def test_edge_enabled(edge_conf, update, mocker) -> None: @pytest.mark.parametrize('is_short,regex_pattern', - [(True, r"just now[ ]*XRP\/BTC \(#3\) -1.00% \("), - (False, r"just now[ ]*XRP\/BTC \(#3\) 1.00% \(")]) + [(True, r"now[ ]*XRP\/BTC \(#3\) -1.00% \("), + (False, r"now[ ]*XRP\/BTC \(#3\) 1.00% \(")]) async def test_telegram_trades(mocker, update, default_conf, fee, is_short, regex_pattern): telegram, _, msg_mock = get_telegram_testobject(mocker, default_conf) diff --git a/tests/utils/test_datetime_helpers.py b/tests/utils/test_datetime_helpers.py index 6fbe75200..5b5c71100 100644 --- a/tests/utils/test_datetime_helpers.py +++ b/tests/utils/test_datetime_helpers.py @@ -3,8 +3,9 @@ from datetime import datetime, timedelta, timezone import pytest import time_machine -from freqtrade.util import (dt_floor_day, dt_from_ts, dt_humanize, dt_now, dt_ts, dt_ts_def, - dt_ts_none, dt_utc, format_date, format_ms_time, shorten_date) +from freqtrade.util import (dt_floor_day, dt_from_ts, dt_now, dt_ts, dt_ts_def, dt_ts_none, dt_utc, + format_date, format_ms_time, shorten_date) +from freqtrade.util.datetime_helpers import dt_humanize_delta def test_dt_now(): @@ -68,9 +69,12 @@ def test_shorten_date() -> None: def test_dt_humanize() -> None: - assert dt_humanize(dt_now()) == 'just now' - assert dt_humanize(dt_now(), only_distance=True) == 'instantly' - assert dt_humanize(dt_now() - timedelta(hours=16), only_distance=True) == '16 hours' + assert dt_humanize_delta(dt_now()) == 'now' + assert dt_humanize_delta(dt_now() - timedelta(minutes=50)) == '50 minutes ago' + assert dt_humanize_delta(dt_now() - timedelta(hours=16)) == '16 hours ago' + assert dt_humanize_delta(dt_now() - timedelta(hours=16, minutes=30)) == '16 hours ago' + assert dt_humanize_delta(dt_now() - timedelta(days=16, hours=10, minutes=25)) == '16 days ago' + assert dt_humanize_delta(dt_now() - timedelta(minutes=50)) == '50 minutes ago' def test_format_ms_time() -> None: From 54c2febe1c3ae8ee48d8b78792051ba79fb1b8e7 Mon Sep 17 00:00:00 2001 From: Matthias Date: Sun, 21 Apr 2024 15:50:29 +0200 Subject: [PATCH 4/5] Remove dt_humanize --- freqtrade/util/__init__.py | 8 +++----- freqtrade/util/datetime_helpers.py | 10 ---------- 2 files changed, 3 insertions(+), 15 deletions(-) diff --git a/freqtrade/util/__init__.py b/freqtrade/util/__init__.py index 1eee3d20e..6f523cd8e 100644 --- a/freqtrade/util/__init__.py +++ b/freqtrade/util/__init__.py @@ -1,7 +1,6 @@ -from freqtrade.util.datetime_helpers import (dt_floor_day, dt_from_ts, dt_humanize, - dt_humanize_delta, dt_now, dt_ts, dt_ts_def, - dt_ts_none, dt_utc, format_date, format_ms_time, - shorten_date) +from freqtrade.util.datetime_helpers import (dt_floor_day, dt_from_ts, dt_humanize_delta, dt_now, + dt_ts, dt_ts_def, dt_ts_none, dt_utc, format_date, + format_ms_time, shorten_date) from freqtrade.util.formatters import decimals_per_coin, fmt_coin, round_value from freqtrade.util.ft_precise import FtPrecise from freqtrade.util.measure_time import MeasureTime @@ -12,7 +11,6 @@ from freqtrade.util.template_renderer import render_template, render_template_wi __all__ = [ 'dt_floor_day', 'dt_from_ts', - 'dt_humanize', 'dt_humanize_delta', 'dt_now', 'dt_ts', diff --git a/freqtrade/util/datetime_helpers.py b/freqtrade/util/datetime_helpers.py index f93186d63..13581383f 100644 --- a/freqtrade/util/datetime_helpers.py +++ b/freqtrade/util/datetime_helpers.py @@ -2,7 +2,6 @@ import re from datetime import datetime, timezone from typing import Optional, Union -import arrow import humanize from freqtrade.constants import DATETIME_PRINT_FORMAT @@ -84,15 +83,6 @@ def dt_humanize_delta(dt: datetime): return humanize.naturaltime(dt) -def dt_humanize(dt: datetime, **kwargs) -> str: - """ - Return a humanized string for the given datetime. - :param dt: datetime to humanize - :param kwargs: kwargs to pass to arrow's humanize() - """ - return arrow.get(dt).humanize(**kwargs) - - def format_date(date: Optional[datetime]) -> str: """ Return a formatted date string. From 03835350c047fc3c134db2fb4259d4857ed7a7a7 Mon Sep 17 00:00:00 2001 From: Matthias Date: Sun, 21 Apr 2024 15:52:02 +0200 Subject: [PATCH 5/5] Remove arrow as dependency it seems to be no longer maintained --- requirements.txt | 1 - setup.py | 1 - 2 files changed, 2 deletions(-) diff --git a/requirements.txt b/requirements.txt index e17d97417..4fe1f497a 100644 --- a/requirements.txt +++ b/requirements.txt @@ -9,7 +9,6 @@ SQLAlchemy==2.0.29 python-telegram-bot==21.1 # can't be hard-pinned due to telegram-bot pinning httpx with ~ httpx>=0.24.1 -arrow==1.3.0 humanize==4.9.0 cachetools==5.3.3 requests==2.31.0 diff --git a/setup.py b/setup.py index f52e52418..504d3b2b7 100644 --- a/setup.py +++ b/setup.py @@ -73,7 +73,6 @@ setup( 'ccxt>=4.2.47', 'SQLAlchemy>=2.0.6', 'python-telegram-bot>=20.1', - 'arrow>=1.0.0', 'humanize>=4.0.0', 'cachetools', 'requests',