diff --git a/freqtrade/data/metrics.py b/freqtrade/data/metrics.py index cf59c330a..6d4e405cb 100644 --- a/freqtrade/data/metrics.py +++ b/freqtrade/data/metrics.py @@ -334,7 +334,10 @@ def calculate_expectancy(trades: pd.DataFrame) -> tuple[float, float]: def calculate_sortino( - trades: pd.DataFrame, min_date: datetime, max_date: datetime, starting_balance: float + trades: pd.DataFrame, + min_date: datetime | None, + max_date: datetime | None, + starting_balance: float, ) -> float: """ Calculate sortino @@ -362,7 +365,10 @@ def calculate_sortino( def calculate_sharpe( - trades: pd.DataFrame, min_date: datetime, max_date: datetime, starting_balance: float + trades: pd.DataFrame, + min_date: datetime | None, + max_date: datetime | None, + starting_balance: float, ) -> float: """ Calculate sharpe @@ -389,7 +395,10 @@ def calculate_sharpe( def calculate_calmar( - trades: pd.DataFrame, min_date: datetime, max_date: datetime, starting_balance: float + trades: pd.DataFrame, + min_date: datetime | None, + max_date: datetime | None, + starting_balance: float, ) -> float: """ Calculate calmar diff --git a/freqtrade/rpc/api_server/api_schemas.py b/freqtrade/rpc/api_server/api_schemas.py index 0dcd95edf..1783c9ebf 100644 --- a/freqtrade/rpc/api_server/api_schemas.py +++ b/freqtrade/rpc/api_server/api_schemas.py @@ -157,6 +157,11 @@ class Profit(BaseModel): winrate: float expectancy: float expectancy_ratio: float + sharpe: float + sortino: float + sqn: float + calmar: float + cagr: float max_drawdown: float max_drawdown_abs: float max_drawdown_start: str diff --git a/freqtrade/rpc/rpc.py b/freqtrade/rpc/rpc.py index 81bb52ce5..fc15cfda0 100644 --- a/freqtrade/rpc/rpc.py +++ b/freqtrade/rpc/rpc.py @@ -19,7 +19,16 @@ from freqtrade import __version__ from freqtrade.configuration.timerange import TimeRange from freqtrade.constants import CANCEL_REASON, DEFAULT_DATAFRAME_COLUMNS, Config from freqtrade.data.history import load_data -from freqtrade.data.metrics import DrawDownResult, calculate_expectancy, calculate_max_drawdown +from freqtrade.data.metrics import ( + DrawDownResult, + calculate_cagr, + calculate_calmar, + calculate_expectancy, + calculate_max_drawdown, + calculate_sharpe, + calculate_sortino, + calculate_sqn, +) from freqtrade.enums import ( CandleType, ExitCheckTuple, @@ -689,6 +698,34 @@ class RPC: last_date = trades[-1].open_date_utc if trades else None num = float(len(durations) or 1) bot_start = KeyValueStore.get_datetime_value("bot_start_time") + + sharpe = calculate_sharpe( + trades=trades_df, + min_date=first_date, + max_date=last_date, + starting_balance=starting_balance, + ) + sortino = calculate_sortino( + trades=trades_df, + min_date=first_date, + max_date=last_date, + starting_balance=starting_balance, + ) + sqn = calculate_sqn(trades=trades_df, starting_balance=starting_balance) + calmar = calculate_calmar( + trades=trades_df, + min_date=first_date, + max_date=last_date, + starting_balance=starting_balance, + ) + current_balance = self._freqtrade.wallets.get_total_stake_amount() + days_passed = max(1, (last_date - first_date).days) if first_date and last_date else 1 + cagr = calculate_cagr( + starting_balance=starting_balance, + final_balance=current_balance, + days_passed=days_passed, + ) + return { "profit_closed_coin": profit_closed_coin_sum, "profit_closed_percent_mean": round(profit_closed_ratio_mean * 100, 2), @@ -725,6 +762,11 @@ class RPC: "winrate": winrate, "expectancy": expectancy, "expectancy_ratio": expectancy_ratio, + "sharpe": sharpe, + "sortino": sortino, + "sqn": sqn, + "calmar": calmar, + "cagr": cagr, "max_drawdown": drawdown.relative_account_drawdown, "max_drawdown_abs": drawdown.drawdown_abs, "max_drawdown_start": format_date(drawdown.high_date), diff --git a/tests/rpc/test_rpc_apiserver.py b/tests/rpc/test_rpc_apiserver.py index e0944e872..6042a9f0d 100644 --- a/tests/rpc/test_rpc_apiserver.py +++ b/tests/rpc/test_rpc_apiserver.py @@ -1199,6 +1199,11 @@ def test_api_logs(botclient): "winrate": 0.0, "expectancy": -0.0033695635, "expectancy_ratio": -1.0, + "cagr": -0.0024567404889381805, + "calmar": -1910.497317469542, + "sharpe": -58.138247358830355, + "sortino": -58.138247358830355, + "sqn": -1.5215, "trading_volume": 75.945, }, ), @@ -1231,6 +1236,11 @@ def test_api_logs(botclient): "winrate": 1.0, "expectancy": 0.0003695635, "expectancy_ratio": 100, + "cagr": 0.0002698167695580622, + "calmar": -100.0, + "sharpe": 65.81269184917424, + "sortino": -100.0, + "sqn": 1.7224, "trading_volume": 75.945, }, ), @@ -1263,6 +1273,11 @@ def test_api_logs(botclient): "winrate": 0.5, "expectancy": -0.0027145635000000003, "expectancy_ratio": -0.48612137582114445, + "cagr": -0.0019796559404918757, + "calmar": -1857.4671689202785, + "sharpe": -36.14602907243071, + "sortino": -100.0, + "sqn": -0.946, "trading_volume": 75.945, }, ), @@ -1326,6 +1341,11 @@ def test_api_profit(botclient, mocker, ticker, fee, markets, is_short, expected) "winrate": expected["winrate"], "expectancy": expected["expectancy"], "expectancy_ratio": expected["expectancy_ratio"], + "sharpe": expected["sharpe"], + "sortino": expected["sortino"], + "sqn": expected["sqn"], + "calmar": expected["calmar"], + "cagr": expected["cagr"], "max_drawdown": ANY, "max_drawdown_abs": ANY, "max_drawdown_start": ANY,