fix(engine): 修复净值起点归一化问题,确保净值从1.0开始

问题分析:
1. 策略净值起点原为1.0051,不是标准的1.0
2. 根本原因:日收益率在factor_data中已计算好,第一行包含前一天价格变化
3. cumprod()从第一行开始,导致起点不是1.0
4. 累计收益计算错误:
   - 原计算方式:净值终点-1 = 1822.83%
   - 正确收益:净值终点/起点 = 1813.07%
   - 差异约10%

修复内容:
- strategies/rotation/engine.py
- 策略净值归一化:净值 = cumprod() / cumprod().iloc[0]
- 基准净值归一化:同样处理确保起点=1.0
- 消除第一行日收益率包含的前一天收益影响

修复效果:
- 策略净值起点:从1.0051 → 1.0000 
- 基准净值起点:从1.0072 → 1.0000 
- 策略累计收益:从1822.83% → 1813.07% 
- 基准累计收益:从48.21% → 47.15% 

影响:
- HTML报告中净值曲线起点标准化为1.0
- KPI指标中的累计收益计算正确
- 与其他回测系统的净值展示方式一致
This commit is contained in:
2026-05-08 23:07:08 +08:00
parent 9ecc796d36
commit baeeb13c34

View File

@@ -286,8 +286,10 @@ class RotationStrategy(BacktestStrategy):
result["换手率"] = turnover_list
result["轮动策略日收益率"] -= result["换手率"] * trade_cost
# 计算净值
# 计算净值 - 强制起点为1.0
result["轮动策略净值"] = (1 + result["轮动策略日收益率"]).cumprod()
# 归一化确保净值起点为1.0(消除第一行日收益率包含的前一天收益)
result["轮动策略净值"] = result["轮动策略净值"] / result["轮动策略净值"].iloc[0]
# 各ETF单独净值 - 使用第一个有效价格作为基准
for code in self.valid_codes:
@@ -317,6 +319,8 @@ class RotationStrategy(BacktestStrategy):
result["基准日收益率"] = bench_ret.reindex(result.index, fill_value=0)
result["基准净值"] = (1 + result["基准日收益率"]).cumprod()
# 归一化确保基准净值起点为1.0
result["基准净值"] = result["基准净值"] / result["基准净值"].iloc[0]
self.backtest_result = result