fix(report): 修复 generate_legacy_report 数据类型处理

- benchmark_data 可能是 Series 或 DataFrame,添加类型判断
- etf_nav_data 现在是字典格式 {etf_code: DataFrame},修正解析逻辑
- 正确从 DataFrame.attrs 中提取净值和溢价率数据
This commit is contained in:
2026-05-14 01:10:12 +08:00
parent 72e980e956
commit 7121e78e70

View File

@@ -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 已经是 Seriesclose 价格)
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
# 生成原引擎格式的报告