diff --git a/strategies/rotation/engine.py b/strategies/rotation/engine.py index fef294d..82144fb 100644 --- a/strategies/rotation/engine.py +++ b/strategies/rotation/engine.py @@ -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 列