From 21269b8a8d48d74f231b4adc21a2a5f175700d99 Mon Sep 17 00:00:00 2001 From: Matthias Date: Sat, 26 Apr 2025 10:16:00 +0200 Subject: [PATCH] feat: add backtest/.../wallets endpoint --- freqtrade/data/btanalysis/bt_fileutils.py | 14 ++++++++++++ freqtrade/rpc/api_server/api_backtest.py | 28 +++++++++++++++++++++++ freqtrade/rpc/api_server/api_schemas.py | 6 +++++ freqtrade/rpc/api_server/api_v1.py | 3 ++- 4 files changed, 50 insertions(+), 1 deletion(-) diff --git a/freqtrade/data/btanalysis/bt_fileutils.py b/freqtrade/data/btanalysis/bt_fileutils.py index e1c0ea64c..7d29ab9a2 100644 --- a/freqtrade/data/btanalysis/bt_fileutils.py +++ b/freqtrade/data/btanalysis/bt_fileutils.py @@ -312,6 +312,20 @@ def get_backtest_market_change(filename: Path, include_ts: bool = True) -> pd.Da return df +def get_backtest_wallet_change(filename: Path, strategy_name: str) -> pd.DataFrame: + """ + Read backtest wallet change file. + :param filename: Path to the backtest result zip file + :param strategy_name: Name of the strategy to load + :return: DataFrame with wallet change data + """ + data = load_file_from_zip(filename, f"{filename.stem}_{strategy_name}_wallet.feather") + df = pd.read_feather(BytesIO(data)) + + df.loc[:, "__date_ts"] = df.loc[:, "date"].astype(np.int64) // 1000 // 1000 + return df + + def find_existing_backtest_stats( dirname: Path | str, run_ids: dict[str, str], min_backtest_date: datetime | None = None ) -> dict[str, Any]: diff --git a/freqtrade/rpc/api_server/api_backtest.py b/freqtrade/rpc/api_server/api_backtest.py index 428fea1c9..92339c7d2 100644 --- a/freqtrade/rpc/api_server/api_backtest.py +++ b/freqtrade/rpc/api_server/api_backtest.py @@ -16,6 +16,7 @@ from freqtrade.data.btanalysis import ( get_backtest_market_change, get_backtest_result, get_backtest_resultlist, + get_backtest_wallet_change, load_and_merge_backtest_result, update_backtest_metadata, ) @@ -29,6 +30,7 @@ from freqtrade.rpc.api_server.api_schemas import ( BacktestMetadataUpdate, BacktestRequest, BacktestResponse, + BacktestWalletsSummary, ) from freqtrade.rpc.api_server.deps import get_config, verify_strategy from freqtrade.rpc.api_server.webserver_bgwork import ApiBG @@ -354,3 +356,29 @@ def api_get_backtest_market_change(file: str, config=Depends(get_config)): "data": df.values.tolist(), "length": len(df), } + + +@router.get( + "/backtest/history/{file}/{strategy}/wallet", + response_model=BacktestWalletsSummary, + tags=["webserver", "backtest"], +) +def api_get_backtest_wallet(file: str, strategy: str, config=Depends(get_config)): + bt_results_base: Path = config["user_data_dir"] / "backtest_results" + file_abs = (bt_results_base / file).with_suffix(".zip") + # Ensure file is in backtest_results directory + if not is_file_in_dir(file_abs, bt_results_base): + raise HTTPException(status_code=404, detail="File not found.") + + results = get_backtest_wallet_change(file_abs, strategy) + if results is None: + raise HTTPException(status_code=404, detail="File not found.") + # Consolidate the wallet to the base currency + results.loc[:, "total"] = results["price"] * results["balance"] + results = results.groupby(["date", "__date_ts"]).agg({"total": "sum"}).reset_index() + + return { + "columns": results.columns.tolist(), + "data": results.values.tolist(), + "length": len(results), + } diff --git a/freqtrade/rpc/api_server/api_schemas.py b/freqtrade/rpc/api_server/api_schemas.py index 7952d5724..df86300e5 100644 --- a/freqtrade/rpc/api_server/api_schemas.py +++ b/freqtrade/rpc/api_server/api_schemas.py @@ -679,6 +679,12 @@ class BacktestMarketChange(BaseModel): data: list[list[Any]] +class BacktestWalletsSummary(BaseModel): + columns: list[str] + length: int + data: list[list[Any]] + + class MarketRequest(ExchangeModePayloadMixin, BaseModel): base: str | None = None quote: str | None = None diff --git a/freqtrade/rpc/api_server/api_v1.py b/freqtrade/rpc/api_server/api_v1.py index d25bda78e..64283898f 100644 --- a/freqtrade/rpc/api_server/api_v1.py +++ b/freqtrade/rpc/api_server/api_v1.py @@ -69,7 +69,8 @@ logger = logging.getLogger(__name__) # 2.45: Add price to forceexit endpoint # 2.46: Add prepend_data to download-data endpoint # 2.47: Add Strategy parameters -API_VERSION = 2.47 +# 2.48: add /backtest/history/wallets endpoint +API_VERSION = 2.48 # Public API, requires no auth. router_public = APIRouter()