From b8d433d519aa1cb3abbfa67c227cbf9b242b637e Mon Sep 17 00:00:00 2001 From: aszerW Date: Mon, 25 May 2026 08:53:42 +0800 Subject: [PATCH] =?UTF-8?q?fix:=20=E4=BF=AE=E5=A4=8D=E5=9B=A0=E5=AD=90?= =?UTF-8?q?=E5=89=8D=E5=90=91=E5=A1=AB=E5=85=85=E4=B8=8D=E7=94=9F=E6=95=88?= =?UTF-8?q?=E7=9A=84=20bug?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit 问题根因: - reindex(method='ffill') 不会填充已存在的 NaN 值 - 当 factor_df 中已有 NaN(境外市场放假),reindex 无法填充 修复方案: - 改为两步操作:reindex() 然后 ffill() - ffill() 会填充所有 NaN,包括已存在的 影响范围: - rotation.py: positions 对齐到 A 股日历 - export_backtest_detail.py: 因子对齐到展示日历 验证结果: - 2026-04-30 HSI: nan → 0.2388 ✅ - 2026-05-08 HSI: nan → 0.1144 ✅ --- framework_v2/scripts/export_backtest_detail.py | 11 ++++++++++- framework_v2/strategies/rotation/rotation.py | 17 ++++++++++++++++- 2 files changed, 26 insertions(+), 2 deletions(-) diff --git a/framework_v2/scripts/export_backtest_detail.py b/framework_v2/scripts/export_backtest_detail.py index dbce3a2..70e0db3 100644 --- a/framework_v2/scripts/export_backtest_detail.py +++ b/framework_v2/scripts/export_backtest_detail.py @@ -155,12 +155,21 @@ def main(): # 将因子对齐到实际展示日历(前向填充) # 因子已经在原始数据上计算完成,这里只是将结果对齐到展示日历 - factor_df_aligned = factor_df.reindex(common_dates, method='ffill') + # 注意:必须先 reindex 再 ffill,因为 reindex(method='ffill') 不会填充已有的 NaN + factor_df_aligned = factor_df.reindex(common_dates) + factor_df_aligned = factor_df_aligned.ffill() # 调试:检查 2026-04-30 的值 if '2026-04-30' in common_dates: hsi_val = factor_df_aligned.loc['2026-04-30', 'HSI'] if 'HSI' in factor_df_aligned.columns else 'NO COLUMN' print(f"[DEBUG] factor_df_aligned['2026-04-30', 'HSI']: {hsi_val}") + + # 检查 factor_df 中 HSI 的最后几个有效值 + if 'HSI' in factor_df.columns: + hsi_last_valid = factor_df['HSI'].dropna() + if len(hsi_last_valid) > 0: + print(f"[DEBUG] factor_df['HSI'] 最后3个有效值:") + print(hsi_last_valid.tail(3)) # 持仓状态跟踪 holdings_state = {} # {code: {'entry_date': str, 'entry_price': float}} diff --git a/framework_v2/strategies/rotation/rotation.py b/framework_v2/strategies/rotation/rotation.py index 9b9240b..e244114 100644 --- a/framework_v2/strategies/rotation/rotation.py +++ b/framework_v2/strategies/rotation/rotation.py @@ -177,6 +177,19 @@ class GlobalRotationStrategy(StrategyBase): # 对齐所有因子的日期 factor_df = pd.DataFrame(factors) + # 调试输出 + print(f"\n [DEBUG] generate_signals - factor_df 信息:") + print(f" 索引类型: {type(factor_df.index)}") + print(f" 总行数: {len(factor_df)}") + print(f" 日期范围: {factor_df.index[0]} ~ {factor_df.index[-1]}") + + # 检查 HSI 的有效数据 + if 'HSI' in factor_df.columns: + hsi_valid = factor_df['HSI'].dropna() + print(f" HSI 有效数据: {len(hsi_valid)} 天") + print(f" HSI 最后3个有效值:") + print(f" {hsi_valid.tail(3).to_dict()}") + # 获取动态短债阈值(如果使用) bond_threshold = None if self.use_dynamic_threshold and self.bond_code and self.bond_code in factors: @@ -326,7 +339,9 @@ class GlobalRotationStrategy(StrategyBase): print(f" [对齐] 收益率数据: {len(returns_df)} 天, {len(returns_df.columns)} 个标的") # 对齐 positions 到 A 股日历 - positions = positions.reindex(trading_calendar, method='ffill') + # 注意:必须先 reindex 再 ffill,因为 reindex(method='ffill') 不会填充已有的 NaN + positions = positions.reindex(trading_calendar) + positions = positions.ffill() # 计算策略收益(仓位加权,T+1 执行) positions_delayed = positions.shift(1).fillna(0)