diff --git a/freqtrade/freqtradebot.py b/freqtrade/freqtradebot.py index fb42a8924..a0b773a16 100644 --- a/freqtrade/freqtradebot.py +++ b/freqtrade/freqtradebot.py @@ -202,10 +202,10 @@ class FreqtradeBot(LoggingMixin): msg = { 'type': RPCMessageType.WARNING, 'status': f"{len(open_trades)} open trades active.\n\n" - f"Handle these trades manually on {self.exchange.name}, " - f"or '/start' the bot again and use '/stopbuy' " - f"to handle open trades gracefully. \n" - f"{'Trades are simulated.' if self.config['dry_run'] else ''}", + f"Handle these trades manually on {self.exchange.name}, " + f"or '/start' the bot again and use '/stopbuy' " + f"to handle open trades gracefully. \n" + f"{'Trades are simulated.' if self.config['dry_run'] else ''}", } self.rpc.send_msg(msg) diff --git a/freqtrade/persistence/models.py b/freqtrade/persistence/models.py index 8ccf8bbef..3da415e9b 100644 --- a/freqtrade/persistence/models.py +++ b/freqtrade/persistence/models.py @@ -850,18 +850,17 @@ class Trade(_DECL_BASE, LocalTrade): .group_by(Trade.pair) \ .order_by(desc('profit_sum_abs')) \ .all() - - response = [ + return [ { 'pair': pair, - 'profit': profit, + 'profit_ratio': profit, + 'profit': round(profit * 100, 2), # Compatibility mode + 'profit_pct': round(profit * 100, 2), 'profit_abs': profit_abs, 'count': count } for pair, profit, profit_abs, count in pair_rates ] - [x.update({'profit': round(x['profit'] * 100, 2)}) for x in response] - return response @staticmethod def get_buy_tag_performance(pair: Optional[str]) -> List[Dict[str, Any]]: @@ -885,17 +884,16 @@ class Trade(_DECL_BASE, LocalTrade): .order_by(desc('profit_sum_abs')) \ .all() - response = [ + return [ { 'buy_tag': buy_tag if buy_tag is not None else "Other", - 'profit': profit, + 'profit_ratio': profit, + 'profit_pct': round(profit * 100, 2), 'profit_abs': profit_abs, 'count': count } for buy_tag, profit, profit_abs, count in buy_tag_perf ] - [x.update({'profit': round(x['profit'] * 100, 2)}) for x in response] - return response @staticmethod def get_sell_reason_performance(pair: Optional[str]) -> List[Dict[str, Any]]: @@ -919,17 +917,16 @@ class Trade(_DECL_BASE, LocalTrade): .order_by(desc('profit_sum_abs')) \ .all() - response = [ + return [ { 'sell_reason': sell_reason if sell_reason is not None else "Other", - 'profit': profit, + 'profit_ratio': profit, + 'profit_pct': round(profit * 100, 2), 'profit_abs': profit_abs, 'count': count } for sell_reason, profit, profit_abs, count in sell_tag_perf ] - [x.update({'profit': round(x['profit'] * 100, 2)}) for x in response] - return response @staticmethod def get_mix_tag_performance(pair: Optional[str]) -> List[Dict[str, Any]]: diff --git a/freqtrade/rpc/api_server/api_schemas.py b/freqtrade/rpc/api_server/api_schemas.py index e9985c3c6..ff1915fca 100644 --- a/freqtrade/rpc/api_server/api_schemas.py +++ b/freqtrade/rpc/api_server/api_schemas.py @@ -63,6 +63,8 @@ class Count(BaseModel): class PerformanceEntry(BaseModel): pair: str profit: float + profit_ratio: float + profit_pct: float profit_abs: float count: int diff --git a/freqtrade/rpc/rpc.py b/freqtrade/rpc/rpc.py index 42d502cd8..da8d23b7a 100644 --- a/freqtrade/rpc/rpc.py +++ b/freqtrade/rpc/rpc.py @@ -161,8 +161,6 @@ class RPC: current_rate = NAN else: current_rate = trade.close_rate - - buy_tag = trade.buy_tag current_profit = trade.calc_profit_ratio(current_rate) current_profit_abs = trade.calc_profit(current_rate) current_profit_fiat: Optional[float] = None @@ -193,7 +191,6 @@ class RPC: profit_pct=round(current_profit * 100, 2), profit_abs=current_profit_abs, profit_fiat=current_profit_fiat, - buy_tag=buy_tag, stoploss_current_dist=stoploss_current_dist, stoploss_current_dist_ratio=round(stoploss_current_dist_ratio, 8), diff --git a/freqtrade/rpc/telegram.py b/freqtrade/rpc/telegram.py index 23938c686..e07734fda 100644 --- a/freqtrade/rpc/telegram.py +++ b/freqtrade/rpc/telegram.py @@ -861,7 +861,7 @@ class Telegram(RPCHandler): stat_line = ( f"{i+1}.\t {trade['pair']}\t" f"{round_coin_value(trade['profit_abs'], self._config['stake_currency'])} " - f"({trade['profit']:.2f}%) " + f"({trade['profit_pct']:.2f}%) " f"({trade['count']})\n") if len(output + stat_line) >= MAX_TELEGRAM_MESSAGE_LENGTH: @@ -896,7 +896,7 @@ class Telegram(RPCHandler): stat_line = ( f"{i+1}.\t {trade['buy_tag']}\t" f"{round_coin_value(trade['profit_abs'], self._config['stake_currency'])} " - f"({trade['profit']:.2f}%) " + f"({trade['profit_pct']:.2f}%) " f"({trade['count']})\n") if len(output + stat_line) >= MAX_TELEGRAM_MESSAGE_LENGTH: @@ -931,7 +931,7 @@ class Telegram(RPCHandler): stat_line = ( f"{i+1}.\t {trade['sell_reason']}\t" f"{round_coin_value(trade['profit_abs'], self._config['stake_currency'])} " - f"({trade['profit']:.2f}%) " + f"({trade['profit_pct']:.2f}%) " f"({trade['count']})\n") if len(output + stat_line) >= MAX_TELEGRAM_MESSAGE_LENGTH: @@ -1158,7 +1158,7 @@ class Telegram(RPCHandler): " `pending sell orders are marked with a double asterisk (**)`\n" "*/buys :* `Shows the buy_tag performance`\n" "*/sells :* `Shows the sell reason performance`\n" - "*/mix_tag :* `Shows combined buy tag + sell reason performance`\n" + "*/mix_tags :* `Shows combined buy tag + sell reason performance`\n" "*/trades [limit]:* `Lists last closed trades (limited to 10 by default)`\n" "*/profit []:* `Lists cumulative profit from all finished trades, " "over the last n days`\n" diff --git a/tests/rpc/test_rpc.py b/tests/rpc/test_rpc.py index aeb0483de..945217b8a 100644 --- a/tests/rpc/test_rpc.py +++ b/tests/rpc/test_rpc.py @@ -822,11 +822,10 @@ def test_performance_handle(default_conf, ticker, limit_buy_order, fee, trade.close_date = datetime.utcnow() trade.is_open = False res = rpc._rpc_performance() - print(str(res)) assert len(res) == 1 assert res[0]['pair'] == 'ETH/BTC' assert res[0]['count'] == 1 - assert prec_satoshi(res[0]['profit'], 6.2) + assert prec_satoshi(res[0]['profit_pct'], 6.2) def test_buy_tag_performance_handle(default_conf, ticker, limit_buy_order, fee, @@ -861,7 +860,7 @@ def test_buy_tag_performance_handle(default_conf, ticker, limit_buy_order, fee, assert len(res) == 1 assert res[0]['buy_tag'] == 'Other' assert res[0]['count'] == 1 - assert prec_satoshi(res[0]['profit'], 6.2) + assert prec_satoshi(res[0]['profit_pct'], 6.2) trade.buy_tag = "TEST_TAG" res = rpc._rpc_buy_tag_performance(None) @@ -869,7 +868,7 @@ def test_buy_tag_performance_handle(default_conf, ticker, limit_buy_order, fee, assert len(res) == 1 assert res[0]['buy_tag'] == 'TEST_TAG' assert res[0]['count'] == 1 - assert prec_satoshi(res[0]['profit'], 6.2) + assert prec_satoshi(res[0]['profit_pct'], 6.2) def test_buy_tag_performance_handle_2(mocker, default_conf, markets, fee): @@ -888,17 +887,17 @@ def test_buy_tag_performance_handle_2(mocker, default_conf, markets, fee): assert len(res) == 2 assert res[0]['buy_tag'] == 'TEST1' assert res[0]['count'] == 1 - assert prec_satoshi(res[0]['profit'], 0.5) + assert prec_satoshi(res[0]['profit_pct'], 0.5) assert res[1]['buy_tag'] == 'Other' assert res[1]['count'] == 1 - assert prec_satoshi(res[1]['profit'], 1.0) + assert prec_satoshi(res[1]['profit_pct'], 1.0) # Test for a specific pair res = rpc._rpc_buy_tag_performance('ETC/BTC') assert len(res) == 1 assert res[0]['count'] == 1 assert res[0]['buy_tag'] == 'TEST1' - assert prec_satoshi(res[0]['profit'], 0.5) + assert prec_satoshi(res[0]['profit_pct'], 0.5) def test_sell_reason_performance_handle(default_conf, ticker, limit_buy_order, fee, @@ -933,7 +932,7 @@ def test_sell_reason_performance_handle(default_conf, ticker, limit_buy_order, f assert len(res) == 1 assert res[0]['sell_reason'] == 'Other' assert res[0]['count'] == 1 - assert prec_satoshi(res[0]['profit'], 6.2) + assert prec_satoshi(res[0]['profit_pct'], 6.2) trade.sell_reason = "TEST1" res = rpc._rpc_sell_reason_performance(None) @@ -941,7 +940,7 @@ def test_sell_reason_performance_handle(default_conf, ticker, limit_buy_order, f assert len(res) == 1 assert res[0]['sell_reason'] == 'TEST1' assert res[0]['count'] == 1 - assert prec_satoshi(res[0]['profit'], 6.2) + assert prec_satoshi(res[0]['profit_pct'], 6.2) def test_sell_reason_performance_handle_2(mocker, default_conf, markets, fee): @@ -960,17 +959,17 @@ def test_sell_reason_performance_handle_2(mocker, default_conf, markets, fee): assert len(res) == 2 assert res[0]['sell_reason'] == 'sell_signal' assert res[0]['count'] == 1 - assert prec_satoshi(res[0]['profit'], 0.5) + assert prec_satoshi(res[0]['profit_pct'], 0.5) assert res[1]['sell_reason'] == 'roi' assert res[1]['count'] == 1 - assert prec_satoshi(res[1]['profit'], 1.0) + assert prec_satoshi(res[1]['profit_pct'], 1.0) # Test for a specific pair res = rpc._rpc_sell_reason_performance('ETC/BTC') assert len(res) == 1 assert res[0]['count'] == 1 assert res[0]['sell_reason'] == 'sell_signal' - assert prec_satoshi(res[0]['profit'], 0.5) + assert prec_satoshi(res[0]['profit_pct'], 0.5) def test_mix_tag_performance_handle(default_conf, ticker, limit_buy_order, fee, diff --git a/tests/rpc/test_rpc_apiserver.py b/tests/rpc/test_rpc_apiserver.py index 02ed26459..e0bbee861 100644 --- a/tests/rpc/test_rpc_apiserver.py +++ b/tests/rpc/test_rpc_apiserver.py @@ -812,8 +812,10 @@ def test_api_performance(botclient, fee): rc = client_get(client, f"{BASE_URI}/performance") assert_response(rc) assert len(rc.json()) == 2 - assert rc.json() == [{'count': 1, 'pair': 'LTC/ETH', 'profit': 7.61, 'profit_abs': 0.01872279}, - {'count': 1, 'pair': 'XRP/ETH', 'profit': -5.57, 'profit_abs': -0.1150375}] + assert rc.json() == [{'count': 1, 'pair': 'LTC/ETH', 'profit': 7.61, 'profit_pct': 7.61, + 'profit_ratio': 0.07609203, 'profit_abs': 0.01872279}, + {'count': 1, 'pair': 'XRP/ETH', 'profit': -5.57, 'profit_pct': -5.57, + 'profit_ratio': -0.05570419, 'profit_abs': -0.1150375}] def test_api_status(botclient, mocker, ticker, fee, markets):