From 357250b411d0a20aa3c087cab2de7fce22824b94 Mon Sep 17 00:00:00 2001 From: zlt Date: Fri, 31 Jul 2026 09:39:24 +0800 Subject: [PATCH] =?UTF-8?q?=E4=B8=8D=E5=9C=A8=E5=8D=81=E6=9D=A1=E8=B5=9B?= =?UTF-8?q?=E9=81=93=E5=86=85=E8=A2=AB=E6=8C=A1=E5=87=BA=E7=9A=84=EF=BC=8C?= =?UTF-8?q?=E6=A0=87"=E8=B5=9B=E9=81=93=E9=97=B8=E5=A4=96"=EF=BC=9B?= =?UTF-8?q?=E5=87=AD=E8=B5=9B=E9=81=93=E6=88=90=E5=91=98=E8=BA=AB=E4=BB=BD?= =?UTF-8?q?=E8=BF=9B=E8=A7=82=E5=AF=9F=E6=A1=A3=E7=9A=84=EF=BC=8C=E6=A0=87?= =?UTF-8?q?"=E8=B5=9B=E9=81=93=E9=94=9A=E7=94=9F=E6=95=88"=EF=BC=9B?= =?UTF-8?q?=E7=9C=9F=E6=AD=A3=E8=A7=A6=20ST=20=E9=97=B8=E7=9A=84=E6=89=8D?= =?UTF-8?q?=E6=A0=87"=E9=A3=8E=E9=99=A9=E9=97=B8"?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- plan.py | 41 +++++++++++++++++++++++++++++++++++------ 1 file changed, 35 insertions(+), 6 deletions(-) diff --git a/plan.py b/plan.py index d7afa19..f55ef6a 100644 --- a/plan.py +++ b/plan.py @@ -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: # 升入观察档 - pt = _val(prev_tr, k) - return "新进传导链" if pt is None or pt <= 0 else "档位调整" + 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("|---|------|------|----|----------|------|")