From 57d6082f914e9def60bab185e91800e5beebff40 Mon Sep 17 00:00:00 2001 From: Simon Date: Thu, 1 Oct 2026 18:15:26 +0800 Subject: [PATCH] =?UTF-8?q?fix:=20=E5=A6=82=E5=AE=9E=E8=AF=B4=E6=98=8E?= =?UTF-8?q?=E4=B9=B0=E5=8D=96=E7=90=86=E7=94=B1=E7=9A=84=E8=A6=86=E7=9B=96?= =?UTF-8?q?=E8=BE=B9=E7=95=8C=EF=BC=88=E5=93=AA=E4=BA=9B=E4=B8=8D=E7=AE=97?= =?UTF-8?q?=E4=B9=B0=E5=8D=96=E7=82=B9=EF=BC=89?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit `unimplemented` 与「买卖说明」里都没写清一件事:理由覆盖的是 **signal_history 里的 每个买卖点(含涨停/停牌/跌停/现金不足等未成交情形)**,而「当日排名在 TopN 之外、 策略本来就无意买入」的候选根本不算买卖点 —— 用户看不到某只票的买入理由时, 应该能立刻分清「是漏了记录」还是「策略本来就没打算买」。 - 组合引擎与单策略引擎的 `_unimplemented` 各加一条说明,指向 `selection_history` 可查完整候选与名次(AGENT.md §24:没实现/有边界的要显式写出)。 - 「买卖说明」卡片底部同步写出这条边界。 --- backend/app/quant/combo_engine.py | 5 +++++ backend/app/quant/local_engine.py | 7 +++++++ frontend/web/components/TradeReasons.tsx | 3 +++ 3 files changed, 15 insertions(+) diff --git a/backend/app/quant/combo_engine.py b/backend/app/quant/combo_engine.py index 6f02555..10eeb4d 100644 --- a/backend/app/quant/combo_engine.py +++ b/backend/app/quant/combo_engine.py @@ -173,6 +173,11 @@ _UNIMPLEMENTED_BASE = [ "多策略打分采用 Borda 秩和(各策略 1/名次 求和):不假设不同策略的因子分值可比," "但极端情况下某策略覆盖极少股票会使其秩和贡献偏大" ), + ( + "买卖说明覆盖 signal_history 里的每个买卖点(含涨停/停牌/现金不足等未成交情形);" + "但「当日排名在 TopN 之外、策略本来就无意买入」的候选不计为买卖点," + "要看完整候选与名次请查选股明细(selection_history)" + ), ] diff --git a/backend/app/quant/local_engine.py b/backend/app/quant/local_engine.py index 76a5bd0..29d8636 100644 --- a/backend/app/quant/local_engine.py +++ b/backend/app/quant/local_engine.py @@ -927,6 +927,13 @@ class TopKBacktestRunner: def _unimplemented(self, curve_note: str | None = None) -> list[str]: notes = list(_DEFAULT_UNIMPLEMENTED) + unimplemented_notes(self.spec.portfolio) + # 如实说明买卖理由的覆盖边界:每个买卖点(含涨停/停牌/现金不足等未成交)都有理由, + # 但「排名在 TopN 之外、策略本来就无意买入」的候选不算买卖点(看选股明细即可)。 + notes.append( + "买卖说明覆盖 signal_history 里的每个买卖点(含涨停/停牌/现金不足等未成交情形);" + "「当日排名在 TopN 之外、策略本来就无意买入」的候选不计为买卖点," + "要看完整候选与名次请查选股明细(selection_history)" + ) if self._no_prev_close_symbols: notes.append( f"有 {len(self._no_prev_close_symbols)} 只标的成交时缺少上一有效收盘价," diff --git a/frontend/web/components/TradeReasons.tsx b/frontend/web/components/TradeReasons.tsx index 104e7dc..73c82a2 100644 --- a/frontend/web/components/TradeReasons.tsx +++ b/frontend/web/components/TradeReasons.tsx @@ -302,6 +302,9 @@ export function TradeReasonsCard({ {withReason < signals.length ? ` 有 ${signals.length - withReason} 条来自 2026-10 之前的旧归档,当时还没有结构化理由。` : ""} + {" "}覆盖边界:「当日排名在 TopN 之外、策略本来就无意买入」的候选不算买卖点 + (要看完整候选与名次,请看选股明细);每个真正的买卖点 —— 包括涨停、停牌、 + 跌停、现金不足这类没成交的 —— 都在上表里。 );