diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 1fc0f0d95..94867b0a4 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -560,7 +560,7 @@ jobs: merge-multiple: true - name: Publish to PyPI (Test) - uses: pypa/gh-action-pypi-publish@v1.12.3 + uses: pypa/gh-action-pypi-publish@v1.12.4 with: repository-url: https://test.pypi.org/legacy/ @@ -587,7 +587,7 @@ jobs: merge-multiple: true - name: Publish to PyPI - uses: pypa/gh-action-pypi-publish@v1.12.3 + uses: pypa/gh-action-pypi-publish@v1.12.4 deploy-docker: diff --git a/docs/requirements-docs.txt b/docs/requirements-docs.txt index ee49045e1..333e4d08d 100644 --- a/docs/requirements-docs.txt +++ b/docs/requirements-docs.txt @@ -2,6 +2,6 @@ markdown==3.7 mkdocs==1.6.1 mkdocs-material==9.5.50 mdx_truly_sane_lists==1.3 -pymdown-extensions==10.14 +pymdown-extensions==10.14.1 jinja2==3.1.5 mike==2.1.3 diff --git a/freqtrade/data/history/history_utils.py b/freqtrade/data/history/history_utils.py index f1e312426..47c323152 100644 --- a/freqtrade/data/history/history_utils.py +++ b/freqtrade/data/history/history_utils.py @@ -444,7 +444,7 @@ def _download_trades_history( if not trades.empty and since < trades.iloc[-1]["timestamp"]: # Reset since to the last available point # - 5 seconds (to ensure we're getting all trades) - since = trades.iloc[-1]["timestamp"] - (5 * 1000) + since = int(trades.iloc[-1]["timestamp"] - (5 * 1000)) logger.info( f"Using last trade date -5s - Downloading trades for {pair} " f"since: {format_ms_time(since)}." diff --git a/freqtrade/exchange/binance.py b/freqtrade/exchange/binance.py index 45535fc58..99f1f4d5c 100644 --- a/freqtrade/exchange/binance.py +++ b/freqtrade/exchange/binance.py @@ -367,7 +367,7 @@ class Binance(Exchange): return {} async def _async_get_trade_history_id_startup( - self, pair: str, since: int | None + self, pair: str, since: int ) -> tuple[list[list], str]: """ override for initial call diff --git a/freqtrade/exchange/exchange.py b/freqtrade/exchange/exchange.py index eec631b68..24656638c 100644 --- a/freqtrade/exchange/exchange.py +++ b/freqtrade/exchange/exchange.py @@ -2960,7 +2960,7 @@ class Exchange: return trades[-1].get("timestamp") async def _async_get_trade_history_id_startup( - self, pair: str, since: int | None + self, pair: str, since: int ) -> tuple[list[list], str]: """ override for initial trade_history_id call @@ -2968,7 +2968,7 @@ class Exchange: return await self._async_fetch_trades(pair, since=since) async def _async_get_trade_history_id( - self, pair: str, until: int, since: int | None = None, from_id: str | None = None + self, pair: str, *, until: int, since: int, from_id: str | None = None ) -> tuple[str, list[list]]: """ Asynchronously gets trade history using fetch_trades @@ -3022,7 +3022,7 @@ class Exchange: return (pair, trades) async def _async_get_trade_history_time( - self, pair: str, until: int, since: int | None = None + self, pair: str, until: int, since: int ) -> tuple[str, list[list]]: """ Asynchronously gets trade history using fetch_trades, @@ -3063,7 +3063,7 @@ class Exchange: async def _async_get_trade_history( self, pair: str, - since: int | None = None, + since: int, until: int | None = None, from_id: str | None = None, ) -> tuple[str, list[list]]: @@ -3094,7 +3094,7 @@ class Exchange: def get_historic_trades( self, pair: str, - since: int | None = None, + since: int, until: int | None = None, from_id: str | None = None, ) -> tuple[str, list]: diff --git a/freqtrade/persistence/trade_model.py b/freqtrade/persistence/trade_model.py index c75edcf04..c08dcb13b 100644 --- a/freqtrade/persistence/trade_model.py +++ b/freqtrade/persistence/trade_model.py @@ -6,7 +6,7 @@ import logging from collections import defaultdict from collections.abc import Sequence from dataclasses import dataclass -from datetime import datetime, timedelta, timezone +from datetime import datetime, timezone from math import isclose from typing import Any, ClassVar, Optional, cast @@ -1914,19 +1914,20 @@ class Trade(ModelBase, LocalTrade): return total_open_stake_amount or 0 @staticmethod - def get_overall_performance(minutes=None) -> list[dict[str, Any]]: + def _generic_performance_query(columns: list, filters: list, fallback: str = "") -> Select: """ - Returns List of dicts containing all Trades, including profit and trade count + Retrieve a generic select object to calculate performance grouped on `columns`. + Returns the following columns: + - columns + - profit_ratio + - profit_sum_abs + - count NOTE: Not supported in Backtesting. """ - filters: list = [Trade.is_open.is_(False)] - if minutes: - start_date = datetime.now(timezone.utc) - timedelta(minutes=minutes) - filters.append(Trade.close_date >= start_date) - + columns_coal = [func.coalesce(c, fallback).label(c.name) for c in columns] pair_costs = ( select( - Trade.pair, + *columns_coal, func.sum( ( func.coalesce(Order.filled, Order.amount) @@ -1939,35 +1940,46 @@ class Trade(ModelBase, LocalTrade): .filter( *filters, Order.ft_order_side == case((Trade.is_short.is_(True), "sell"), else_="buy"), + Order.filled > 0, ) - # Order.filled.gt > 0 - .group_by(Trade.pair) + .group_by(*columns) .cte("pair_costs") ) trades_grouped = ( select( - Trade.pair, + *columns_coal, func.sum(Trade.close_profit_abs).label("profit_sum_abs"), - func.count(Trade.pair).label("count"), + func.count(*columns_coal).label("count"), ) .filter(*filters) - .group_by(Trade.pair) + .group_by(*columns_coal) .cte("trades_grouped") ) q = ( select( - trades_grouped.c.pair, + *[trades_grouped.c[x.name] for x in columns], (trades_grouped.c.profit_sum_abs / pair_costs.c.cost_per_pair).label( "profit_ratio" ), trades_grouped.c.profit_sum_abs, trades_grouped.c.count, ) - .join(pair_costs, trades_grouped.c.pair == pair_costs.c.pair) + .join(pair_costs, *[trades_grouped.c[x.name] == pair_costs.c[x.name] for x in columns]) .order_by(desc("profit_sum_abs")) ) - pair_rates = Trade.session.execute(q).all() + return q + @staticmethod + def get_overall_performance(start_date: datetime | None = None) -> list[dict[str, Any]]: + """ + Returns List of dicts containing all Trades, including profit and trade count + NOTE: Not supported in Backtesting. + """ + filters: list = [Trade.is_open.is_(False)] + if start_date: + filters.append(Trade.close_date >= start_date) + pair_rates_query = Trade._generic_performance_query([Trade.pair], filters) + pair_rates = Trade.session.execute(pair_rates_query).all() return [ { "pair": pair, @@ -1992,17 +2004,8 @@ class Trade(ModelBase, LocalTrade): if pair is not None: filters.append(Trade.pair == pair) - enter_tag_perf = Trade.session.execute( - select( - Trade.enter_tag, - func.sum(Trade.close_profit).label("profit_sum"), - func.sum(Trade.close_profit_abs).label("profit_sum_abs"), - func.count(Trade.pair).label("count"), - ) - .filter(*filters) - .group_by(Trade.enter_tag) - .order_by(desc("profit_sum_abs")) - ).all() + pair_rates_query = Trade._generic_performance_query([Trade.enter_tag], filters, "Other") + enter_tag_perf = Trade.session.execute(pair_rates_query).all() return [ { @@ -2026,17 +2029,9 @@ class Trade(ModelBase, LocalTrade): filters: list = [Trade.is_open.is_(False)] if pair is not None: filters.append(Trade.pair == pair) - sell_tag_perf = Trade.session.execute( - select( - Trade.exit_reason, - func.sum(Trade.close_profit).label("profit_sum"), - func.sum(Trade.close_profit_abs).label("profit_sum_abs"), - func.count(Trade.pair).label("count"), - ) - .filter(*filters) - .group_by(Trade.exit_reason) - .order_by(desc("profit_sum_abs")) - ).all() + + pair_rates_query = Trade._generic_performance_query([Trade.exit_reason], filters, "Other") + sell_tag_perf = Trade.session.execute(pair_rates_query).all() return [ { @@ -2117,13 +2112,9 @@ class Trade(ModelBase, LocalTrade): if start_date: filters.append(Trade.close_date >= start_date) - best_pair = Trade.session.execute( - select(Trade.pair, func.sum(Trade.close_profit).label("profit_sum")) - .filter(*filters) - .group_by(Trade.pair) - .order_by(desc("profit_sum")) - ).first() - + pair_rates_query = Trade._generic_performance_query([Trade.pair], filters) + best_pair = Trade.session.execute(pair_rates_query).first() + # returns pair, profit_ratio, abs_profit, count return best_pair @staticmethod diff --git a/freqtrade/plugins/pairlist/PerformanceFilter.py b/freqtrade/plugins/pairlist/PerformanceFilter.py index 06ab11c54..6e7dfe3c3 100644 --- a/freqtrade/plugins/pairlist/PerformanceFilter.py +++ b/freqtrade/plugins/pairlist/PerformanceFilter.py @@ -3,12 +3,14 @@ Performance pair list filter """ import logging +from datetime import timedelta import pandas as pd from freqtrade.exchange.exchange_types import Tickers from freqtrade.persistence import Trade from freqtrade.plugins.pairlist.IPairList import IPairList, PairlistParameter, SupportsBacktesting +from freqtrade.util.datetime_helpers import dt_now logger = logging.getLogger(__name__) @@ -69,7 +71,8 @@ class PerformanceFilter(IPairList): """ # Get the trading performance for pairs from database try: - performance = pd.DataFrame(Trade.get_overall_performance(self._minutes)) + start_date = dt_now() - timedelta(minutes=self._minutes) + performance = pd.DataFrame(Trade.get_overall_performance(start_date)) except AttributeError: # Performancefilter does not work in backtesting. self.log_once("PerformanceFilter is not available in this mode.", logger.warning) diff --git a/freqtrade/rpc/api_server/api_schemas.py b/freqtrade/rpc/api_server/api_schemas.py index 97770ba13..9d3fde7e2 100644 --- a/freqtrade/rpc/api_server/api_schemas.py +++ b/freqtrade/rpc/api_server/api_schemas.py @@ -150,6 +150,7 @@ class Profit(BaseModel): best_pair: str best_rate: float best_pair_profit_ratio: float + best_pair_profit_abs: float winning_trades: int losing_trades: int profit_factor: float diff --git a/freqtrade/rpc/rpc.py b/freqtrade/rpc/rpc.py index 11b1fd003..bf7e53836 100644 --- a/freqtrade/rpc/rpc.py +++ b/freqtrade/rpc/rpc.py @@ -665,6 +665,7 @@ class RPC: "best_pair": best_pair[0] if best_pair else "", "best_rate": round(best_pair[1] * 100, 2) if best_pair else 0, # Deprecated "best_pair_profit_ratio": best_pair[1] if best_pair else 0, + "best_pair_profit_abs": best_pair[2] if best_pair else 0, "winning_trades": winning_trades, "losing_trades": losing_trades, "profit_factor": profit_factor, diff --git a/freqtrade/rpc/telegram.py b/freqtrade/rpc/telegram.py index a2926a278..5b4982346 100644 --- a/freqtrade/rpc/telegram.py +++ b/freqtrade/rpc/telegram.py @@ -1028,6 +1028,7 @@ class Telegram(RPCHandler): avg_duration = stats["avg_duration"] best_pair = stats["best_pair"] best_pair_profit_ratio = stats["best_pair_profit_ratio"] + best_pair_profit_abs = fmt_coin(stats["best_pair_profit_abs"], stake_cur) winrate = stats["winrate"] expectancy = stats["expectancy"] expectancy_ratio = stats["expectancy_ratio"] @@ -1067,7 +1068,8 @@ class Telegram(RPCHandler): if stats["closed_trade_count"] > 0: markdown_msg += ( f"\n*Avg. Duration:* `{avg_duration}`\n" - f"*Best Performing:* `{best_pair}: {best_pair_profit_ratio:.2%}`\n" + f"*Best Performing:* `{best_pair}: {best_pair_profit_abs} " + f"({best_pair_profit_ratio:.2%})`\n" f"*Trading volume:* `{fmt_coin(stats['trading_volume'], stake_cur)}`\n" f"*Profit factor:* `{stats['profit_factor']:.2f}`\n" f"*Max Drawdown:* `{stats['max_drawdown']:.2%} " diff --git a/requirements-dev.txt b/requirements-dev.txt index 530afac4d..14006b683 100644 --- a/requirements-dev.txt +++ b/requirements-dev.txt @@ -7,9 +7,9 @@ -r docs/requirements-docs.txt coveralls==4.0.1 -ruff==0.9.2 +ruff==0.9.3 mypy==1.14.1 -pre-commit==4.0.1 +pre-commit==4.1.0 pytest==8.3.4 pytest-asyncio==0.25.2 pytest-cov==6.0.0 diff --git a/requirements-hyperopt.txt b/requirements-hyperopt.txt index 9374d1d5a..6d193bdc7 100644 --- a/requirements-hyperopt.txt +++ b/requirements-hyperopt.txt @@ -5,4 +5,4 @@ scipy==1.15.1 scikit-learn==1.6.1 ft-scikit-optimize==0.9.2 -filelock==3.16.1 +filelock==3.17.0 diff --git a/requirements.txt b/requirements.txt index 825f43355..fe7078f01 100644 --- a/requirements.txt +++ b/requirements.txt @@ -4,7 +4,7 @@ bottleneck==1.4.2 numexpr==2.10.2 pandas-ta==0.3.14b -ccxt==4.4.49 +ccxt==4.4.50 cryptography==42.0.8; platform_machine == 'armv7l' cryptography==44.0.0; platform_machine != 'armv7l' aiohttp==3.10.11 @@ -13,7 +13,7 @@ python-telegram-bot==21.10 # can't be hard-pinned due to telegram-bot pinning httpx with ~ httpx>=0.24.1 humanize==4.11.0 -cachetools==5.5.0 +cachetools==5.5.1 requests==2.32.3 urllib3==2.3.0 jsonschema==4.23.0 @@ -38,8 +38,8 @@ orjson==3.10.15 sdnotify==0.3.2 # API Server -fastapi==0.115.6 -pydantic==2.10.5 +fastapi==0.115.7 +pydantic==2.10.6 uvicorn==0.34.0 pyjwt==2.10.1 aiofiles==24.1.0 @@ -47,7 +47,7 @@ psutil==6.1.1 # Building config files interactively questionary==2.1.0 -prompt-toolkit==3.0.36 +prompt-toolkit==3.0.50 # Extensions to datetime library python-dateutil==2.9.0.post0 pytz==2024.2 diff --git a/tests/persistence/test_persistence.py b/tests/persistence/test_persistence.py index b9606ee47..c30c27244 100644 --- a/tests/persistence/test_persistence.py +++ b/tests/persistence/test_persistence.py @@ -1931,9 +1931,9 @@ def test_get_overall_performance(fee): @pytest.mark.parametrize( "is_short,pair,profit", [ - (True, "ETC/BTC", -0.005), - (False, "XRP/BTC", 0.01), - (None, "XRP/BTC", 0.01), + (True, "XRP/BTC", -0.00018780487), + (False, "ETC/BTC", 0.00003860975), + (None, "XRP/BTC", 0.000025203252), ], ) def test_get_best_pair(fee, is_short, pair, profit): @@ -1942,9 +1942,9 @@ def test_get_best_pair(fee, is_short, pair, profit): create_mock_trades(fee, is_short) res = Trade.get_best_pair() - assert len(res) == 2 + assert len(res) == 4 assert res[0] == pair - assert res[1] == profit + assert pytest.approx(res[1]) == profit @pytest.mark.usefixtures("init_persistence") @@ -1954,9 +1954,9 @@ def test_get_best_pair_lev(fee): create_mock_trades_with_leverage(fee) res = Trade.get_best_pair() - assert len(res) == 2 - assert res[0] == "DOGE/BTC" - assert res[1] == 0.1713156134055116 + assert len(res) == 4 + assert res[0] == "ETC/BTC" + assert pytest.approx(res[1]) == 0.00003860975 @pytest.mark.usefixtures("init_persistence") diff --git a/tests/rpc/test_rpc.py b/tests/rpc/test_rpc.py index 5de33900c..71eaa3bfd 100644 --- a/tests/rpc/test_rpc.py +++ b/tests/rpc/test_rpc.py @@ -480,8 +480,8 @@ def test_rpc_trade_statistics(default_conf_usdt, ticker, fee, mocker) -> None: assert stats["first_trade_humanized"] == "2 days ago" assert stats["latest_trade_humanized"] == "17 minutes ago" assert stats["avg_duration"] in ("0:17:40") - assert stats["best_pair"] == "XRP/USDT" - assert stats["best_rate"] == 10.0 + assert stats["best_pair"] == "NEO/USDT" + assert stats["best_rate"] == 1.99 # Test non-available pair mocker.patch( @@ -492,8 +492,8 @@ def test_rpc_trade_statistics(default_conf_usdt, ticker, fee, mocker) -> None: assert stats["first_trade_humanized"] == "2 days ago" assert stats["latest_trade_humanized"] == "17 minutes ago" assert stats["avg_duration"] in ("0:17:40") - assert stats["best_pair"] == "XRP/USDT" - assert stats["best_rate"] == 10.0 + assert stats["best_pair"] == "NEO/USDT" + assert stats["best_rate"] == 1.99 assert isnan(stats["profit_all_coin"]) @@ -1018,14 +1018,14 @@ def test_enter_tag_performance_handle(default_conf, ticker, fee, mocker) -> None assert len(res) == 3 assert res[0]["enter_tag"] == "TEST1" assert res[0]["count"] == 1 - assert res[0]["profit_pct"] == 5.0 + assert res[0]["profit_pct"] == 1.99 res = rpc._rpc_enter_tag_performance(None) assert len(res) == 3 assert res[0]["enter_tag"] == "TEST1" assert res[0]["count"] == 1 - assert res[0]["profit_pct"] == 5.0 + assert res[0]["profit_pct"] == 1.99 def test_enter_tag_performance_handle_2(mocker, default_conf, markets, fee): @@ -1041,17 +1041,20 @@ def test_enter_tag_performance_handle_2(mocker, default_conf, markets, fee): assert len(res) == 2 assert res[0]["enter_tag"] == "TEST1" assert res[0]["count"] == 1 - assert pytest.approx(res[0]["profit_pct"]) == 0.5 + assert pytest.approx(res[0]["profit_pct"]) == 0.0 + assert pytest.approx(res[0]["profit_ratio"]) == 0.00003860975 assert res[1]["enter_tag"] == "Other" assert res[1]["count"] == 1 - assert pytest.approx(res[1]["profit_pct"]) == 1.0 + assert pytest.approx(res[1]["profit_pct"]) == 0.0 + assert pytest.approx(res[1]["profit_ratio"]) == 0.00002520325 # Test for a specific pair res = rpc._rpc_enter_tag_performance("ETC/BTC") assert len(res) == 1 assert res[0]["count"] == 1 assert res[0]["enter_tag"] == "TEST1" - assert pytest.approx(res[0]["profit_pct"]) == 0.5 + assert pytest.approx(res[0]["profit_pct"]) == 0.0 + assert pytest.approx(res[0]["profit_ratio"]) == 0.00003860975 def test_exit_reason_performance_handle(default_conf_usdt, ticker, fee, mocker) -> None: @@ -1075,7 +1078,7 @@ def test_exit_reason_performance_handle(default_conf_usdt, ticker, fee, mocker) assert len(res) == 3 assert res[0]["exit_reason"] == "exit_signal" assert res[0]["count"] == 1 - assert res[0]["profit_pct"] == 5.0 + assert res[0]["profit_pct"] == 1.99 assert res[1]["exit_reason"] == "roi" assert res[2]["exit_reason"] == "Other" @@ -1094,17 +1097,20 @@ def test_exit_reason_performance_handle_2(mocker, default_conf, markets, fee): assert len(res) == 2 assert res[0]["exit_reason"] == "sell_signal" assert res[0]["count"] == 1 - assert pytest.approx(res[0]["profit_pct"]) == 0.5 + assert pytest.approx(res[0]["profit_pct"]) == 0.0 + assert pytest.approx(res[0]["profit_ratio"]) == 0.00003860975 assert res[1]["exit_reason"] == "roi" assert res[1]["count"] == 1 - assert pytest.approx(res[1]["profit_pct"]) == 1.0 + assert pytest.approx(res[1]["profit_pct"]) == 0.0 + assert pytest.approx(res[1]["profit_ratio"]) == 0.000025203252 # Test for a specific pair res = rpc._rpc_exit_reason_performance("ETC/BTC") assert len(res) == 1 assert res[0]["count"] == 1 assert res[0]["exit_reason"] == "sell_signal" - assert pytest.approx(res[0]["profit_pct"]) == 0.5 + assert pytest.approx(res[0]["profit_pct"]) == 0.0 + assert pytest.approx(res[0]["profit_ratio"]) == 0.00003860975 def test_mix_tag_performance_handle(default_conf, ticker, fee, mocker) -> None: diff --git a/tests/rpc/test_rpc_apiserver.py b/tests/rpc/test_rpc_apiserver.py index 762dede40..2c895c963 100644 --- a/tests/rpc/test_rpc_apiserver.py +++ b/tests/rpc/test_rpc_apiserver.py @@ -963,9 +963,10 @@ def test_api_edge_disabled(botclient, mocker, ticker, fee, markets): ( True, { - "best_pair": "ETC/BTC", - "best_rate": -0.5, - "best_pair_profit_ratio": -0.005, + "best_pair": "XRP/BTC", + "best_rate": -0.02, + "best_pair_profit_ratio": -0.00018780487, + "best_pair_profit_abs": -0.001155, "profit_all_coin": 15.382312, "profit_all_fiat": 189894.6470718, "profit_all_percent_mean": 49.62, @@ -994,9 +995,10 @@ def test_api_edge_disabled(botclient, mocker, ticker, fee, markets): ( False, { - "best_pair": "XRP/BTC", - "best_rate": 1.0, - "best_pair_profit_ratio": 0.01, + "best_pair": "ETC/BTC", + "best_rate": 0.0, + "best_pair_profit_ratio": 0.00003860975, + "best_pair_profit_abs": 0.000584127, "profit_all_coin": -15.46546305, "profit_all_fiat": -190921.14135225, "profit_all_percent_mean": -49.62, @@ -1026,8 +1028,9 @@ def test_api_edge_disabled(botclient, mocker, ticker, fee, markets): None, { "best_pair": "XRP/BTC", - "best_rate": 1.0, - "best_pair_profit_ratio": 0.01, + "best_rate": 0.0, + "best_pair_profit_ratio": 0.000025203252, + "best_pair_profit_abs": 0.000155, "profit_all_coin": -14.87167525, "profit_all_fiat": -183590.83096125, "profit_all_percent_mean": 0.13, @@ -1080,7 +1083,8 @@ def test_api_profit(botclient, mocker, ticker, fee, markets, is_short, expected) assert rc.json() == { "avg_duration": ANY, "best_pair": expected["best_pair"], - "best_pair_profit_ratio": expected["best_pair_profit_ratio"], + "best_pair_profit_ratio": pytest.approx(expected["best_pair_profit_ratio"]), + "best_pair_profit_abs": expected["best_pair_profit_abs"], "best_rate": expected["best_rate"], "first_trade_date": ANY, "first_trade_humanized": ANY, @@ -1206,7 +1210,8 @@ def test_api_entries(botclient, fee): resp = response[0] assert resp["enter_tag"] == "TEST1" assert resp["count"] == 1 - assert resp["profit_pct"] == 0.5 + assert resp["profit_pct"] == 0.0 + assert pytest.approx(resp["profit_ratio"]) == 0.000038609756 def test_api_exits(botclient, fee): @@ -1225,7 +1230,8 @@ def test_api_exits(botclient, fee): resp = response[0] assert resp["exit_reason"] == "sell_signal" assert resp["count"] == 1 - assert resp["profit_pct"] == 0.5 + assert resp["profit_pct"] == 0.0 + assert pytest.approx(resp["profit_ratio"]) == 0.000038609756 def test_api_mix_tag(botclient, fee): diff --git a/tests/rpc/test_rpc_telegram.py b/tests/rpc/test_rpc_telegram.py index b743cdd8d..1882e09c4 100644 --- a/tests/rpc/test_rpc_telegram.py +++ b/tests/rpc/test_rpc_telegram.py @@ -918,7 +918,7 @@ async def test_telegram_profit_handle( ) assert "∙ `6.253 USD`" in msg_mock.call_args_list[-1][0][0] - assert "*Best Performing:* `ETH/USDT: 9.45%`" in msg_mock.call_args_list[-1][0][0] + assert "*Best Performing:* `ETH/USDT: 5.685 USDT (9.47%)`" in msg_mock.call_args_list[-1][0][0] assert "*Max Drawdown:*" in msg_mock.call_args_list[-1][0][0] assert "*Profit factor:*" in msg_mock.call_args_list[-1][0][0] assert "*Winrate:*" in msg_mock.call_args_list[-1][0][0] @@ -1611,7 +1611,7 @@ async def test_telegram_entry_tag_performance_handle( await telegram._enter_tag_performance(update=update, context=context) assert msg_mock.call_count == 1 assert "Entry Tag Performance" in msg_mock.call_args_list[0][0][0] - assert "`TEST1\t3.987 USDT (5.00%) (1)`" in msg_mock.call_args_list[0][0][0] + assert "`TEST1\t3.987 USDT (1.99%) (1)`" in msg_mock.call_args_list[0][0][0] context.args = ["XRP/USDT"] await telegram._enter_tag_performance(update=update, context=context) @@ -1644,7 +1644,7 @@ async def test_telegram_exit_reason_performance_handle( await telegram._exit_reason_performance(update=update, context=context) assert msg_mock.call_count == 1 assert "Exit Reason Performance" in msg_mock.call_args_list[0][0][0] - assert "`roi\t2.842 USDT (10.00%) (1)`" in msg_mock.call_args_list[0][0][0] + assert "`roi\t2.842 USDT (9.47%) (1)`" in msg_mock.call_args_list[0][0][0] context.args = ["XRP/USDT"] await telegram._exit_reason_performance(update=update, context=context)