不在十条赛道内被挡出的,标"赛道闸外";凭赛道成员身份进观察档的,标"赛道锚生效";真正触 ST 闸的才标"风险闸"

This commit is contained in:
zlt 2026-07-31 09:39:24 +08:00
parent 235731855b
commit 357250b411
1 changed files with 35 additions and 6 deletions

37
plan.py
View File

@ -17,6 +17,7 @@ import os
import pandas as pd
import common
import config
import db
@ -173,6 +174,16 @@ def collect(date: str | None = None, top: int = 20, obs_top: int = 10,
except Exception: # noqa: BLE001 —— 兜底失败则原因退化为通用文案
pass
prev_tr = _factor("t_factor_akg_transmission", prev_ds)
# 赛道闸与风险闸的成员集合07-31 修:赛道闸开启后,"不在赛道"曾被
# 误标成"风险闸/档位调整"、"赛道锚生效"曾被误标成"新进传导链")。
# 两个集合都尊重各自开关:闸没开时对应原因自然不会出现。
tset, risk = None, set()
try:
import factors
tset = factors._track_set() # noqa: SLF001 —— 同仓自用
risk = factors._risk_set() # noqa: SLF001
except Exception: # noqa: BLE001 —— 拿不到就退化为通用文案
pass
def _why(k: str, pg: float, cg: float) -> str:
if cg == 2.0: # 升入主榜
@ -181,15 +192,29 @@ def collect(date: str | None = None, top: int = 20, obs_top: int = 10,
return "首次覆盖"
return "估值转正" if pu < 0 else "重获资格"
if cg == 1.0: # 升入观察档
if k in ev: # 今天真在传导链上
pt = _val(prev_tr, k)
return "新进传导链" if pt is None or pt <= 0 else "档位调整"
if tset is not None and k in tset:
return "赛道锚生效" # 赛道成员身份给的锚(闸切换/成员变动)
return "档位调整"
if pg == 2.0: # 从主榜降出
if k in risk:
return "风险闸"
cu = _val(upside, k)
if cu is None:
return "覆盖脱落"
return "估值转负" if cu < 0 else "风险闸/档位调整"
if cu < 0:
return "估值转负"
if tset is not None and k not in tset:
return "赛道闸外" # 有覆盖也不贵,但不在十赛道内
return "档位调整"
if pg == 1.0: # 从观察档降出
return "离开传导链" if k not in ev else "风险闸/档位调整"
if k in risk:
return "风险闸"
if k not in ev and (tset is None or k not in tset):
return "离开传导链"
return "档位调整"
return "出入面板"
def _mv(d: pd.DataFrame):
@ -216,6 +241,7 @@ def collect(date: str | None = None, top: int = 20, obs_top: int = 10,
"observe": [_row(i, k, s, False)
for i, (k, s) in enumerate(_pick(obs, obs_top), 1)],
"changes": changes,
"gate_on": bool(config.ENABLE_TRACK_GATE),
"encoding": "主榜分=200+传导档位×20+组内分(还没热、还便宜);"
"观察档分=100+0.6z(传导)+0.4z(−热度)",
}
@ -247,7 +273,9 @@ def render_md(d: dict) -> str:
L.append("")
cap_txt = f",每主题限额 {d['theme_cap']}" if d["theme_cap"] else ""
L.append(f"## 主榜 Top {len(d['main'])}(有券商预期、目标价不低于现价{cap_txt}")
gate_txt = "、在十五五赛道内" if d.get("gate_on") else ""
L.append(f"## 主榜 Top {len(d['main'])}"
f"(有券商预期、目标价不低于现价{gate_txt}{cap_txt}")
L.append("")
L.append("| # | 代码 | 名称 | 总分 | 档位 | 传导证据 | 热度 | 预期空间 |")
L.append("|---|------|------|------|------|----------|------|----------|")
@ -258,7 +286,8 @@ def render_md(d: dict) -> str:
L.append("")
L.append(f"## 观察档 Top {len(d['observe'])}"
f"(无券商预期、但在传导链上——没有估值锚,置信度低{cap_txt}")
f"(无券商预期、但{'在赛道或传导链上' if d.get('gate_on') else '在传导链上'}"
f"——没有估值锚,置信度低{cap_txt}")
L.append("")
L.append("| # | 代码 | 名称 | 分 | 传导证据 | 热度 |")
L.append("|---|------|------|----|----------|------|")