From be8ca023f70ed2fa8b66bc44b9f60032fab06b20 Mon Sep 17 00:00:00 2001 From: aszerW Date: Tue, 19 May 2026 00:38:04 +0800 Subject: [PATCH] =?UTF-8?q?fix:=20V3=E5=8A=A8=E6=80=81=E9=98=88=E5=80=BC?= =?UTF-8?q?=E4=B8=A4=E5=A4=84=E6=A0=B9=E5=9B=A0=E4=BF=AE=E5=A4=8D?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit 根因1: _check_rebalance重复代码计分Bug - 问题: "CL=F,931862.CSI,931862.CSI"中短债只计一次得分 - 修复: 直接遍历持仓列表计算得分,而非遍历factor_cols做in判断 - 影响: 278天应防御时继续持有风险资产 根因2: 短债填充依赖阈值过滤 - 问题: 短债动量为负时(钱荒等)不填充防御仓位 - 修复: 无条件填充短债(防御机制不应依赖动量阈值) - 影响: 17天仓位不满(1/3或2/3空置) 修复后结果: - 净值: 292.56 → 334.46 (+14.4%) - 夏普: 1.33 → 1.41 (+0.08) - 持仓3只: 92.3% → 99.4% (满仓率提升) - 短债填充更积极: 28.7%时间持有短债 --- strategies/shared/signals/selectors.py | 15 +++++++-------- 1 file changed, 7 insertions(+), 8 deletions(-) diff --git a/strategies/shared/signals/selectors.py b/strategies/shared/signals/selectors.py index 4797ffe..5f55cc6 100644 --- a/strategies/shared/signals/selectors.py +++ b/strategies/shared/signals/selectors.py @@ -205,8 +205,9 @@ class TopNSelector(SignalGenerator): return False # 组合完全相同,不调仓 # 计算新旧组合的总得分 - old_total = sum(float(row.get(col, 0)) for col in factor_cols if col in old_codes) - new_total = sum(float(row.get(col, 0)) for col in factor_cols if col in new_codes) + # 修复: 按实际份数计算得分(支持重复代码如"931862.CSI,931862.CSI") + old_total = sum(float(row.get(c, 0)) for c in old_codes) + new_total = sum(float(row.get(c, 0)) for c in new_codes) # 新组合得分需超过当前组合一定比例才调仓 # 即使 threshold=0,也要确保 new_total >= old_total @@ -247,15 +248,13 @@ class TopNSelector(SignalGenerator): selected = [code for code, score in sorted_champions[:self.select_num]] # V3: 空余仓位填充短债 + # 短债填充是防御机制,不应受阈值过滤影响 if cfg.get('fill_bond', False) and bond_code: n_bond_slots = self.select_num - len(selected) if n_bond_slots > 0: - # 检查短债是否满足阈值条件(需在原始scores中存在) - bond_score = scores.get(bond_code, None) - if bond_score is not None: - # 用短债代码填充空余仓位(可能重复填充多次) - for _ in range(n_bond_slots): - selected.append(bond_code) + # 修复: 无条件填充短债(防御仓位不应依赖动量阈值) + for _ in range(n_bond_slots): + selected.append(bond_code) return selected