diff --git a/docs/telegram-usage.md b/docs/telegram-usage.md index 43ab66cbf01..946686b1cf4 100644 --- a/docs/telegram-usage.md +++ b/docs/telegram-usage.md @@ -360,6 +360,8 @@ Return the performance of each crypto-currency the bot has sold. > 5. `STORJ/BTC 0.0009 BTC (27.24%) (1)` > ... +The relative performance is calculated against the total investment in the currency, aggregating all filled entries for the currency. + ### /balance Return the balance of all crypto-currency your have on the exchange. diff --git a/freqtrade/persistence/trade_model.py b/freqtrade/persistence/trade_model.py index 5f64e7c4a0e..0d5e7d20058 100644 --- a/freqtrade/persistence/trade_model.py +++ b/freqtrade/persistence/trade_model.py @@ -19,6 +19,7 @@ Select, String, UniqueConstraint, + case, desc, func, select, @@ -1934,17 +1935,49 @@ def get_overall_performance(minutes=None) -> list[dict[str, Any]]: start_date = datetime.now(timezone.utc) - timedelta(minutes=minutes) filters.append(Trade.close_date >= start_date) - pair_rates = Trade.session.execute( + pair_costs = ( + select( + Trade.pair, + func.sum( + ( + func.coalesce(Order.filled, Order.amount) + * func.coalesce(Order.average, Order.price, Order.ft_price) + ) + / func.coalesce(Trade.leverage, 1) + ).label("cost_per_pair"), + ) + .join(Order, Trade.id == Order.ft_trade_id) + .filter( + *filters, + Order.ft_order_side == case((Trade.is_short.is_(True), "sell"), else_="buy"), + ) + # Order.filled.gt > 0 + .group_by(Trade.pair) + .cte("pair_costs") + ) + trades_grouped = ( select( Trade.pair, - 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.pair) + .cte("trades_grouped") + ) + q = ( + select( + trades_grouped.c.pair, + (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) .order_by(desc("profit_sum_abs")) - ).all() + ) + pair_rates = Trade.session.execute(q).all() return [ { diff --git a/tests/rpc/test_rpc.py b/tests/rpc/test_rpc.py index 2aab2245313..5de33900cf9 100644 --- a/tests/rpc/test_rpc.py +++ b/tests/rpc/test_rpc.py @@ -993,7 +993,7 @@ def test_performance_handle(default_conf_usdt, ticker, fee, mocker) -> None: assert res[0]["pair"] == "NEO/USDT" assert res[0]["count"] == 1 assert res[0]["profit_abs"] == 3.9875 - assert res[0]["profit_pct"] == 5.0 + assert res[0]["profit_pct"] == 1.99 def test_enter_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 737df389c4a..762dede40d8 100644 --- a/tests/rpc/test_rpc_apiserver.py +++ b/tests/rpc/test_rpc_apiserver.py @@ -1166,26 +1166,26 @@ def test_api_performance(botclient, fee): { "count": 1, "pair": "NEO/USDT", - "profit": 5.0, - "profit_pct": 5, - "profit_ratio": 0.05, + "profit": 1.99, + "profit_pct": 1.99, + "profit_ratio": 0.0199375, "profit_abs": 3.9875, }, { "count": 1, "pair": "XRP/USDT", - "profit": 10.0, + "profit": 9.47, "profit_abs": 2.8425, - "profit_pct": 10.0, - "profit_ratio": 0.1, + "profit_pct": 9.47, + "profit_ratio": pytest.approx(0.094749999), }, { "count": 1, "pair": "LTC/USDT", - "profit": -20.0, + "profit": -20.45, "profit_abs": -4.09, - "profit_pct": -20.0, - "profit_ratio": -0.2, + "profit_pct": -20.45, + "profit_ratio": -0.2045, }, ] diff --git a/tests/rpc/test_rpc_telegram.py b/tests/rpc/test_rpc_telegram.py index 26db9c0f251..b743cdd8dfa 100644 --- a/tests/rpc/test_rpc_telegram.py +++ b/tests/rpc/test_rpc_telegram.py @@ -1591,7 +1591,7 @@ async def test_telegram_performance_handle(default_conf_usdt, update, ticker, fe await telegram._performance(update=update, context=MagicMock()) assert msg_mock.call_count == 1 assert "Performance" in msg_mock.call_args_list[0][0][0] - assert "XRP/USDT\t2.842 USDT (10.00%) (1)" in msg_mock.call_args_list[0][0][0] + assert "XRP/USDT\t2.842 USDT (9.47%) (1)" in msg_mock.call_args_list[0][0][0] async def test_telegram_entry_tag_performance_handle(