diff --git a/scripts/generate_legacy_report.py b/scripts/generate_legacy_report.py index 2df9704..1a5ace8 100644 --- a/scripts/generate_legacy_report.py +++ b/scripts/generate_legacy_report.py @@ -83,18 +83,33 @@ def run_with_legacy_report(): backtest_result['基准净值'] = benchmark_nav.values backtest_result['基准日收益率'] = benchmark_close_aligned.pct_change().values - # 2. 各标的净值(指数价格) - index_close = data.get('index_close') + # 2. 各标的净值(指数价格)- 使用index_data而非index_close + # index_close可能对齐有问题,直接从index_data获取 + index_data = data.get('index_data') valid_codes = data['valid_codes'] for code in valid_codes: - if index_close is not None and code in index_close.columns: + if index_data is not None and code in index_data: + # 从原始OHLCV数据获取close价格 + price_df = index_data[code] + if 'close' in price_df.columns: + price_series = price_df['close'] + else: + price_series = price_df.iloc[:, 0] # 取第一列 + + # 对齐到回测日期 + price_aligned = price_series.reindex(backtest_result.index, method='ffill') + + # 处理最后几天的NaN(用最后一个有效值填充) + price_aligned = price_aligned.ffill() # 前向填充剩余NaN + # 计算该标的的净值曲线 - price_series = index_close[code].loc[backtest_result.index] - nav_series = (1 + price_series.pct_change()).cumprod() - nav_series = nav_series / nav_series.iloc[0] if nav_series.iloc[0] > 0 else nav_series + nav_series = (1 + price_aligned.pct_change()).cumprod() + first_valid = nav_series.dropna().iloc[0] if len(nav_series.dropna()) > 0 else 1 + nav_series = nav_series / first_valid # 归一化起点为1 + backtest_result[f'净值_{code}'] = nav_series.values - backtest_result[code] = price_series.values # 当前价格 + backtest_result[code] = price_aligned.values # 当前价格 # 3. 得分列(从factor_df获取) for code in valid_codes: @@ -119,17 +134,24 @@ def run_with_legacy_report(): if etf_data is not None: # 转换列名:指数代码 -> ETF代码(通过etf_code_map) + # 并对齐到回测日期 etf_code_map = data.get('etf_code_map', {}) - etf_price_data = pd.DataFrame(index=etf_data.index) + etf_price_data = pd.DataFrame(index=backtest_result.index) for idx_code, etf_code in etf_code_map.items(): if etf_code in etf_data.columns: - etf_price_data[idx_code] = etf_data[etf_code] + # 对齐ETF价格数据到回测日期 + 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_nav_data_raw = pd.DataFrame(index=etf_nav_data.index) + # ETF净值数据列名是ETF代码,需要用etf_code_map映射 + # 并对齐到回测日期 + 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: - etf_nav_data_raw[idx_code] = etf_nav_data[etf_code] + # 对齐净值数据到回测日期(使用ffill处理日期差异) + nav_aligned = etf_nav_data[etf_code].reindex(backtest_result.index, method='ffill') + etf_nav_data_raw[idx_code] = nav_aligned.values # 生成原引擎格式的报告 print("\n" + "=" * 60) @@ -139,6 +161,9 @@ def run_with_legacy_report(): save_path = 'results/rotation_legacy' os.makedirs('results', exist_ok=True) + # 获取index_close用于报告图表绘制 + index_close = data.get('index_close') + metrics = generate_performance_report( backtest_result=backtest_result, code_list=valid_codes,