From 7121e78e70b0f1804755456ab7bcf0b44f378dd7 Mon Sep 17 00:00:00 2001 From: aszerW Date: Thu, 14 May 2026 01:10:12 +0800 Subject: [PATCH] =?UTF-8?q?fix(report):=20=E4=BF=AE=E5=A4=8D=20generate=5F?= =?UTF-8?q?legacy=5Freport=20=E6=95=B0=E6=8D=AE=E7=B1=BB=E5=9E=8B=E5=A4=84?= =?UTF-8?q?=E7=90=86?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - benchmark_data 可能是 Series 或 DataFrame,添加类型判断 - etf_nav_data 现在是字典格式 {etf_code: DataFrame},修正解析逻辑 - 正确从 DataFrame.attrs 中提取净值和溢价率数据 --- scripts/generate_legacy_report.py | 50 +++++++++++++++++++++---------- 1 file changed, 35 insertions(+), 15 deletions(-) diff --git a/scripts/generate_legacy_report.py b/scripts/generate_legacy_report.py index f377c4d..ac4ab67 100644 --- a/scripts/generate_legacy_report.py +++ b/scripts/generate_legacy_report.py @@ -71,17 +71,25 @@ def run_with_legacy_report(): # 1. 基准净值和基准日收益率 benchmark_data = data.get('benchmark_data') - if benchmark_data is not None and not benchmark_data.empty: - # 对齐基准数据到回测日期 - benchmark_close = benchmark_data['close'] if 'close' in benchmark_data.columns else benchmark_data.iloc[:, 0] - benchmark_close_aligned = benchmark_close.reindex(backtest_result.index, method='ffill') + if benchmark_data is not None: + # benchmark_data 已经是 Series(close 价格) + if isinstance(benchmark_data, pd.Series): + benchmark_close = benchmark_data + elif isinstance(benchmark_data, pd.DataFrame): + benchmark_close = benchmark_data['close'] if 'close' in benchmark_data.columns else benchmark_data.iloc[:, 0] + else: + benchmark_close = None - # 计算基准净值 - benchmark_nav = (1 + benchmark_close_aligned.pct_change()).cumprod() - benchmark_nav = benchmark_nav / benchmark_nav.dropna().iloc[0] # 归一化起点为1 - - backtest_result['基准净值'] = benchmark_nav.values - backtest_result['基准日收益率'] = benchmark_close_aligned.pct_change().values + if benchmark_close is not None and len(benchmark_close) > 0: + # 对齐基准数据到回测日期 + benchmark_close_aligned = benchmark_close.reindex(backtest_result.index, method='ffill') + + # 计算基准净值 + benchmark_nav = (1 + benchmark_close_aligned.pct_change()).cumprod() + benchmark_nav = benchmark_nav / benchmark_nav.dropna().iloc[0] # 归一化起点为1 + + backtest_result['基准净值'] = benchmark_nav.values + backtest_result['基准日收益率'] = benchmark_close_aligned.pct_change().values # 2. 各标的净值(指数价格)- 使用index_data而非index_close # index_close可能对齐有问题,直接从index_data获取 @@ -143,14 +151,26 @@ def run_with_legacy_report(): price_aligned = etf_data[etf_code].reindex(backtest_result.index, method='ffill') etf_price_data[idx_code] = price_aligned.values - if etf_nav_data is not None: - # ETF净值数据列名是ETF代码,需要用etf_code_map映射 - # 并对齐到回测日期 + # ETF净值数据现在是字典格式 {etf_code: DataFrame} + etf_nav_data_raw = None + + if etf_nav_data and len(etf_nav_data) > 0: + # etf_nav_data 是字典 {etf_code: DataFrame} etf_nav_data_raw = pd.DataFrame(index=backtest_result.index) for idx_code, etf_code in etf_code_map.items(): - if etf_code in etf_nav_data.columns: + if etf_code in etf_nav_data: + # 从字典中获取净值 DataFrame + nav_df = etf_nav_data[etf_code] + if isinstance(nav_df, pd.DataFrame) and 'nav' in nav_df.columns: + nav_series = nav_df['nav'] + elif isinstance(nav_df, pd.DataFrame): + nav_series = nav_df.iloc[:, 0] + elif isinstance(nav_df, pd.Series): + nav_series = nav_df + else: + continue # 对齐净值数据到回测日期(使用ffill处理日期差异) - nav_aligned = etf_nav_data[etf_code].reindex(backtest_result.index, method='ffill') + nav_aligned = nav_series.reindex(backtest_result.index, method='ffill') etf_nav_data_raw[idx_code] = nav_aligned.values # 生成原引擎格式的报告