fix(engine): 修复净值计算中NaN值导致的缺失问题
问题分析: 1. HSTECH.HK数据从2020-08-26才开始,早期数据缺失 2. 原净值计算使用iloc[0]作为基准,若首日价格是NaN则整列净值变成NaN 3. 原日收益率计算中,某品种日收益率NaN会导致mean()返回NaN 4. 导致策略净值62个缺失值(3.53%) 修复内容: 1. strategies/rotation/engine.py - 各品种净值计算 - 使用第一个有效价格作为基准(而非iloc[0]) - 若品种无有效数据则净值列全部为NaN 2. strategies/rotation/engine.py - 策略日收益率计算 - select_num=1时:若日收益率是NaN则返回0.0 - select_num>1时:忽略NaN值计算mean() - 若所有品种日收益率都缺失则返回0.0 修复效果: - 策略净值缺失:从62个(3.53%)降至0个(0%) - HSTECH.HK净值缺失:从100%降至21.49%(377/1754) - 其他品种净值:全部无缺失 说明: - HSTECH.HK净值377个缺失是正常的(数据从2020-08-26才开始) - 早期缺失日期(2019年)非HSTECH持仓期,缺失由其他原因导致
This commit is contained in:
@@ -241,21 +241,29 @@ class RotationStrategy(BacktestStrategy):
|
||||
select_num = self.config["select_num"]
|
||||
trade_cost = self.config["trade_cost"]
|
||||
|
||||
# 计算策略日收益率
|
||||
# 计算策略日收益率 - 处理NaN值
|
||||
if select_num == 1:
|
||||
def calc_return(row):
|
||||
signal = row['信号']
|
||||
if not signal or pd.isna(signal):
|
||||
return 0.0
|
||||
return row.get(f"日收益率_{signal}", 0.0)
|
||||
ret = row.get(f"日收益率_{signal}", 0.0)
|
||||
# 如果日收益率是NaN,返回0.0
|
||||
return ret if pd.notna(ret) else 0.0
|
||||
result["轮动策略日收益率"] = result.apply(calc_return, axis=1)
|
||||
else:
|
||||
def calc_multi_return(row):
|
||||
codes = [c for c in row["信号"].split(",") if c] # 过滤空字符串
|
||||
if not codes:
|
||||
return 0.0
|
||||
returns = [row.get(f"日收益率_{c}", 0.0) for c in codes]
|
||||
return np.mean(returns)
|
||||
# 获取各品种日收益率,忽略NaN值
|
||||
returns = []
|
||||
for c in codes:
|
||||
ret = row.get(f"日收益率_{c}", None)
|
||||
if ret is not None and pd.notna(ret):
|
||||
returns.append(ret)
|
||||
# 如果所有品种日收益率都缺失,返回0.0
|
||||
return np.mean(returns) if returns else 0.0
|
||||
result["轮动策略日收益率"] = result.apply(calc_multi_return, axis=1)
|
||||
|
||||
# 扣除交易成本
|
||||
@@ -281,10 +289,16 @@ class RotationStrategy(BacktestStrategy):
|
||||
# 计算净值
|
||||
result["轮动策略净值"] = (1 + result["轮动策略日收益率"]).cumprod()
|
||||
|
||||
# 各ETF单独净值
|
||||
# 各ETF单独净值 - 使用第一个有效价格作为基准
|
||||
for code in self.valid_codes:
|
||||
first_price = result[code].iloc[0]
|
||||
result[f"净值_{code}"] = result[code] / first_price
|
||||
# 获取第一个有效价格(非NaN)
|
||||
valid_prices = result[code][result[code].notna()]
|
||||
if len(valid_prices) > 0:
|
||||
first_valid_price = valid_prices.iloc[0]
|
||||
result[f"净值_{code}"] = result[code] / first_valid_price
|
||||
else:
|
||||
# 如果没有有效数据,净值列全部为NaN
|
||||
result[f"净值_{code}"] = np.nan
|
||||
|
||||
# 基准净值
|
||||
# benchmark_data 是 DataFrame,需要提取 close 列
|
||||
|
||||
Reference in New Issue
Block a user