仅展示的跳过原因写出这一只自己的判决依据
页面上"今天没建仓的"那一栏,三只票原因完全不同却写成同一句话"选股系统判为 仅展示,不出提议"。09-04 实测的三只:天合光能是差一个当日涨幅(跌 3.21%, 门槛要涨 3%)、蓝黛科技是吸筹只到"潜在吸筹"没过确认线、上海港湾是被择时决策 系统昨夜的 SELL 信号一票否决。前两种明天可能就翻过来,第三种是技术面在走坏, 处置完全不同,而人从页面上看不出来。 判决依据是选股系统判决时当场写下的一句话,09-04 上午已经随计划下发到 PMS。 这次把它接到跳过原因上。改了三处:计划筛选层的仅展示名单从一串代码改成带判决 依据的行;提议服务照新形状读,旧形状兼容着读;动作引擎新增一个纯函数拼这句话, 判决依据缺失时退回原来那句固定文案,不让这一栏空掉。 测试:新增一例钉住"三种来路必须写出三句互不相同的话",另改一条断言跟上新形状。 例数从 652 到 653,测试入口的例数表同改。 Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
This commit is contained in:
parent
fd74dd197f
commit
68b8046540
|
|
@ -96,6 +96,23 @@ WHY_WATCH_CONFIRM = "选股系统判为关注(无法判断),交人裁决"
|
||||||
WHY_DISPLAY_ONLY = "选股系统判为仅展示"
|
WHY_DISPLAY_ONLY = "选股系统判为仅展示"
|
||||||
|
|
||||||
|
|
||||||
|
def display_only_why(row) -> str:
|
||||||
|
"""仅展示的票为什么没建仓, 尽量写出**这一只**的真实原因。
|
||||||
|
|
||||||
|
只写「选股系统判为仅展示」是不够的: 仅展示至少有三种来路, 差一个当日涨幅、
|
||||||
|
吸筹没到确认线、被昨夜坏信号一票否决, 三者的处置完全不同 —— 前两种明天可能就
|
||||||
|
翻过来, 第三种是技术面在走坏。三只票并排显示同一句话, 人看不出哪只该继续盯。
|
||||||
|
|
||||||
|
判决依据 (basis) 是选股系统在判决时**当场写下**的一句话, 2026-09-04 起随计划下发。
|
||||||
|
它在就直接用它, 不在 (旧版计划没有这个键) 就退回原来那句固定文案 —— 少一个字段
|
||||||
|
不该让这一栏整个空掉。
|
||||||
|
"""
|
||||||
|
basis = (str(row.get("basis") or "").strip() if isinstance(row, dict) else "")
|
||||||
|
if not basis:
|
||||||
|
return WHY_DISPLAY_ONLY + ",不出提议"
|
||||||
|
return f"{WHY_DISPLAY_ONLY},不出提议:{basis}"
|
||||||
|
|
||||||
|
|
||||||
def verdict_confirm_why(verdict):
|
def verdict_confirm_why(verdict):
|
||||||
"""按判决要不要强制人工确认: 要则回原因, 不要回 None。
|
"""按判决要不要强制人工确认: 要则回原因, 不要回 None。
|
||||||
|
|
||||||
|
|
@ -614,7 +631,7 @@ def scan_open(*, candidates: list, params: dict, caps: dict, room_amt: float,
|
||||||
# 仅展示的行不占名额、不占金额, 也不进后面任何一道闸; 放在名额判断之前,
|
# 仅展示的行不占名额、不占金额, 也不进后面任何一道闸; 放在名额判断之前,
|
||||||
# 是为了让它的跳过原因永远是「仅展示」而不是碰巧的「名额用完」。
|
# 是为了让它的跳过原因永远是「仅展示」而不是碰巧的「名额用完」。
|
||||||
skipped.append({"ts_code": code, "action": A_OPEN,
|
skipped.append({"ts_code": code, "action": A_OPEN,
|
||||||
"why": WHY_DISPLAY_ONLY + ",不出提议"})
|
"why": display_only_why(c)})
|
||||||
continue
|
continue
|
||||||
if slots <= 0:
|
if slots <= 0:
|
||||||
skipped.append({"ts_code": code, "action": A_OPEN,
|
skipped.append({"ts_code": code, "action": A_OPEN,
|
||||||
|
|
|
||||||
|
|
@ -343,7 +343,10 @@ def select_candidates(plan: dict, *, held=(), black=(), top_n: int = 30, tiers=N
|
||||||
continue
|
continue
|
||||||
if route_by_verdict and r.get("verdict") == VERDICT_DISPLAY:
|
if route_by_verdict and r.get("verdict") == VERDICT_DISPLAY:
|
||||||
dropped["display"] += 1 # 上游判为仅展示: 不进池, 单独列出让人看得见
|
dropped["display"] += 1 # 上游判为仅展示: 不进池, 单独列出让人看得见
|
||||||
display_only.append(c)
|
# 带上判决依据: 仅展示至少有三种来路 (差一个当日涨幅、吸筹没到确认线、
|
||||||
|
# 被昨夜坏信号一票否决), 三者处置完全不同。只记代码的话, 下游只能对所有
|
||||||
|
# 仅展示的票写同一句话, 人看不出哪只该继续盯。
|
||||||
|
display_only.append({"ts_code": c, "basis": r.get("basis")})
|
||||||
continue
|
continue
|
||||||
if exclude_st:
|
if exclude_st:
|
||||||
nm = (r.get("name") or "").strip()
|
nm = (r.get("name") or "").strip()
|
||||||
|
|
|
||||||
|
|
@ -197,9 +197,11 @@ def _scan_open(view, params, stock_params, skip, mkt, out) -> list:
|
||||||
return []
|
return []
|
||||||
# 仅展示的票逐只写跳过原因: disposition_snapshot 复用本函数, 页面「系统在盯的候选」
|
# 仅展示的票逐只写跳过原因: disposition_snapshot 复用本函数, 页面「系统在盯的候选」
|
||||||
# 由此显示「选股系统判为仅展示」而不是一个说不清的空白。
|
# 由此显示「选股系统判为仅展示」而不是一个说不清的空白。
|
||||||
for code in (sel.get("display_only") or []):
|
for row in (sel.get("display_only") or []):
|
||||||
out["skipped"].append({"ts_code": code, "action": ae.A_OPEN,
|
# 2026-09-04 起这里是带判决依据的行; 早先只是一串代码, 兼容着读。
|
||||||
"why": ae.WHY_DISPLAY_ONLY + ",不出提议"})
|
row = row if isinstance(row, dict) else {"ts_code": row}
|
||||||
|
out["skipped"].append({"ts_code": row.get("ts_code"), "action": ae.A_OPEN,
|
||||||
|
"why": ae.display_only_why(row)})
|
||||||
items = list(sel.get("items") or [])
|
items = list(sel.get("items") or [])
|
||||||
if not items:
|
if not items:
|
||||||
out["skipped"].append({"action": ae.A_OPEN,
|
out["skipped"].append({"action": ae.A_OPEN,
|
||||||
|
|
|
||||||
|
|
@ -47,16 +47,17 @@
|
||||||
无判决回退/判决认不出交人/仅展示不占名额/自动执行
|
无判决回退/判决认不出交人/仅展示不占名额/自动执行
|
||||||
开关四条件与任一缺失仍入队/信号来源区分/密钥名单
|
开关四条件与任一缺失仍入队/信号来源区分/密钥名单
|
||||||
含会话密钥/研判键放行与必答题/置信度进硬数字/
|
含会话密钥/研判键放行与必答题/置信度进硬数字/
|
||||||
页面静态守卫 (15 例)
|
仅展示的跳过原因写出这一只自己的判决依据/
|
||||||
|
页面静态守卫 (16 例)
|
||||||
test_batch21_units.py 减持的自动执行边界 (2026-09-03 安全修复): 保垫减仓照旧自动/
|
test_batch21_units.py 减持的自动执行边界 (2026-09-03 安全修复): 保垫减仓照旧自动/
|
||||||
强制入队对卖出同样有效/研判不可用的减持入队/研究证据走弱
|
强制入队对卖出同样有效/研判不可用的减持入队/研究证据走弱
|
||||||
来源必定交人裁决/风控高置信卖出与命令清仓不经提议分流 (15 例)
|
来源必定交人裁决/风控高置信卖出与命令清仓不经提议分流 (15 例)
|
||||||
test_wiring.py 装配自检: 服务层→核心→落表 全链路 (内存桩) +
|
test_wiring.py 装配自检: 服务层→核心→落表 全链路 (内存桩) +
|
||||||
目标价到价必定入队 (档位 full 也不自动卖) +
|
目标价到价必定入队 (档位 full 也不自动卖) +
|
||||||
用户设的止损价与目标价单独成列显示 (70 例)
|
用户设的止损价与目标价单独成列显示 (70 例)
|
||||||
共 652 例
|
共 653 例
|
||||||
(总数按实跑逐批相加校正过两次: 曾写 649 是笔误, 实为 650; 09-03 先后加了同轮只发一条
|
(总数按实跑逐批相加校正过两次: 曾写 649 是笔误, 实为 650; 09-03 先后加了同轮只发一条
|
||||||
减持与研究理由两键各一例, 现为 652)
|
减持与研究理由两键各一例, 到 652; 09-04 加了仅展示跳过原因一例, 现为 653)
|
||||||
任一子集失败即整体失败 (退出码 1)。
|
任一子集失败即整体失败 (退出码 1)。
|
||||||
|
|
||||||
哨兵位置清单 (2026-09-03 抄录; 改了对应的东西就得来这些地方改断言, 断言不动就是漏了):
|
哨兵位置清单 (2026-09-03 抄录; 改了对应的东西就得来这些地方改断言, 断言不动就是漏了):
|
||||||
|
|
|
||||||
|
|
@ -155,6 +155,29 @@ def _():
|
||||||
assert r["candidates"][0]["needs_user_confirm"] is False
|
assert r["candidates"][0]["needs_user_confirm"] is False
|
||||||
|
|
||||||
|
|
||||||
|
@case("判决分流·仅展示的跳过原因写出这一只自己的判决依据 (三种来路要分得开)")
|
||||||
|
def _():
|
||||||
|
# 三只都是仅展示, 但来路完全不同: 一只差当日涨幅、一只吸筹没到确认线、
|
||||||
|
# 一只被昨夜坏信号一票否决。写成同一句话的话, 人看不出哪只该继续盯。
|
||||||
|
rows = [cand("600000.SH", score=300, verdict="仅展示", basis="门槛未过:started"),
|
||||||
|
cand("600002.SH", score=290, verdict="仅展示",
|
||||||
|
basis="三门槛全过,但吸筹评分为「潜在吸筹」,未达确认线,不升格关注"),
|
||||||
|
cand("600003.SH", score=280, verdict="仅展示",
|
||||||
|
basis="硬风险否决:决策系统昨夜信号 SELL"),
|
||||||
|
cand("600004.SH", score=270, verdict="仅展示")] # 旧版计划: 没有判决依据
|
||||||
|
sk = {x["ts_code"]: x["why"] for x in _scan(rows, slots=3)["skipped"]}
|
||||||
|
assert sk["600000.SH"].endswith(":门槛未过:started"), sk["600000.SH"]
|
||||||
|
assert "潜在吸筹" in sk["600002.SH"], sk["600002.SH"]
|
||||||
|
assert "SELL" in sk["600003.SH"], sk["600003.SH"]
|
||||||
|
# 三句话必须互不相同 —— 这正是这条用例要守住的东西
|
||||||
|
assert len({sk["600000.SH"], sk["600002.SH"], sk["600003.SH"]}) == 3, sk
|
||||||
|
# 判决依据缺失时退回原来那句固定文案, 不许让这一栏空掉
|
||||||
|
assert sk["600004.SH"] == ae.WHY_DISPLAY_ONLY + ",不出提议", sk["600004.SH"]
|
||||||
|
# 每一句都仍以固定前缀开头, 页面按前缀分组的逻辑不受影响
|
||||||
|
for c in ("600000.SH", "600002.SH", "600003.SH", "600004.SH"):
|
||||||
|
assert sk[c].startswith(ae.WHY_DISPLAY_ONLY), sk[c]
|
||||||
|
|
||||||
|
|
||||||
@case("判决分流·没有判决 (旧版计划) 时开关开着与关着产出逐字一致, 五键全 None")
|
@case("判决分流·没有判决 (旧版计划) 时开关开着与关着产出逐字一致, 五键全 None")
|
||||||
def _():
|
def _():
|
||||||
rows = [cand("600000.SH", score=300), cand("600001.SH", score=200, rank=2)]
|
rows = [cand("600000.SH", score=300), cand("600001.SH", score=200, rank=2)]
|
||||||
|
|
|
||||||
|
|
@ -523,7 +523,10 @@ def _():
|
||||||
assert r["considered"] == r["eligible"] + dr["held"] + dr["black"] + dr["tier"] \
|
assert r["considered"] == r["eligible"] + dr["held"] + dr["black"] + dr["tier"] \
|
||||||
+ dr["score"] + dr["sources"] + dr["upside"] + dr["theme"] + dr["dup"] \
|
+ dr["score"] + dr["sources"] + dr["upside"] + dr["theme"] + dr["dup"] \
|
||||||
+ dr["display"], (r, dr)
|
+ dr["display"], (r, dr)
|
||||||
assert dr["display"] == 1 and r["display_only"] == ["600600.SH"], (dr, r["display_only"])
|
# 2026-09-04 起 display_only 是带判决依据的行, 不再是一串代码 —— 下游要靠它写出
|
||||||
|
# 这一只票**自己**的跳过原因。
|
||||||
|
assert dr["display"] == 1 and [x["ts_code"] for x in r["display_only"]] == ["600600.SH"], \
|
||||||
|
(dr, r["display_only"])
|
||||||
assert len(r["items"]) == r["eligible"] - dr["capped"] == 2
|
assert len(r["items"]) == r["eligible"] - dr["capped"] == 2
|
||||||
# 分流开关关着: 那只仅展示的票照常进池 (分数高, 会排到第一), 计数里 display 是 0
|
# 分流开关关着: 那只仅展示的票照常进池 (分数高, 会排到第一), 计数里 display 是 0
|
||||||
r0 = pf.select_candidates(p, held=["SH600418"], black=["SZ300952"], tiers=["强传导"],
|
r0 = pf.select_candidates(p, held=["SH600418"], black=["SZ300952"], tiers=["强传导"],
|
||||||
|
|
|
||||||
Loading…
Reference in New Issue