fix: 报告生成器数据对齐修复
修复内容:
1. 指数价格获取改用index_data而非index_close
- 原问题:index_close对齐后N225最后几天的值为nan
- 修复:从原始OHLCV获取close,用ffill填充缺失值
2. ETF净值数据对齐到回测日期
- 原问题:etf_nav_data索引与backtest_result不对齐
- 修复:用reindex(backtest_result.index, method='ffill')
3. ETF价格数据同样对齐到回测日期
修复后报告显示:
- 日经225指数最新价: 62713.65(原为nan)
- 创业板指溢价率: +3.70%⚠️
- 日经225溢价率: +0.85%
This commit is contained in:
@@ -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,
|
||||
|
||||
Reference in New Issue
Block a user