复盘对账后两处口径修正:结论表只取不晚于数据日的最新行(历史重建防前视);复盘名单加观察档等权;关注环节表补未动环节的成员数与领涨者(不限档位表内)

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
This commit is contained in:
zlt 2026-09-02 16:53:13 +08:00
parent 769aa59e68
commit cb819198ea
3 changed files with 25 additions and 10 deletions

18
plan.py
View File

@ -75,8 +75,8 @@ def _names() -> dict:
def _evidence(ds: str): def _evidence(ds: str):
"""每股最强一条传导证据:主题、源数、已动比例;另返回涉及的行情快照日。""" """每股最强一条传导证据:主题、源数、已动比例;另返回涉及的行情快照日。"""
tr = db.read_pg( tr = db.read_pg(
"SELECT ts_code, target, n_sources, moved_ratio, mkt_trade_date " "SELECT ts_code, target, n_sources, moved_ratio, mkt_trade_date, "
"FROM v_factor_transmission WHERE scan_date = %s", (ds,)) "members_total, moved FROM v_factor_transmission WHERE scan_date = %s", (ds,))
if tr.empty: if tr.empty:
return {}, set() return {}, set()
tr["k"] = tr["ts_code"].map(common.to_prefix) tr["k"] = tr["ts_code"].map(common.to_prefix)
@ -84,7 +84,10 @@ def _evidence(ds: str):
tr["moved_ratio"] = pd.to_numeric(tr["moved_ratio"], errors="coerce").fillna(0) tr["moved_ratio"] = pd.to_numeric(tr["moved_ratio"], errors="coerce").fillna(0)
tr["strength"] = tr["n_sources"] * (1.0 - tr["moved_ratio"]) tr["strength"] = tr["n_sources"] * (1.0 - tr["moved_ratio"])
tr = tr.sort_values("strength", ascending=False).drop_duplicates("k") tr = tr.sort_values("strength", ascending=False).drop_duplicates("k")
ev = {r.k: (str(r.target), int(r.n_sources), float(r.moved_ratio)) # 元组前三项是既有口径(多处按下标取),第四、五项是环节的成员数与已动数(关注环节表用)
ev = {r.k: (str(r.target), int(r.n_sources), float(r.moved_ratio),
None if pd.isna(r.members_total) else int(r.members_total),
None if pd.isna(r.moved) else int(r.moved))
for r in tr.itertuples()} for r in tr.itertuples()}
days = {str(x) for x in tr["mkt_trade_date"].dropna().unique()} days = {str(x) for x in tr["mkt_trade_date"].dropna().unique()}
return ev, days return ev, days
@ -161,17 +164,22 @@ def _assemble_cards(ds: str, codes: list, ev: dict, upside: pd.Series,
if cards.get(k, {}).get("verdict") == card.VERDICT_CANDIDATE: if cards.get(k, {}).get("verdict") == card.VERDICT_CANDIDATE:
s["candidates"] += 1 s["candidates"] += 1
for e in ev.values(): # 只有未动名单的环节也是"被指向",列入但无已启动 for e in ev.values(): # 只有未动名单的环节也是"被指向",列入但无已启动
segs.setdefault(e[0], {"segment": e[0], "n_sources": e[1], "chain_fit": None, s = segs.setdefault(e[0], {"segment": e[0], "n_sources": e[1], "chain_fit": None,
"members_total": None, "moved": None, "members_total": None, "moved": None,
"started": [], "candidates": 0, "leader": None}) "started": [], "candidates": 0, "leader": None})
if s["members_total"] is None and len(e) > 4:
s["members_total"], s["moved"] = e[3], e[4]
for s in segs.values(): for s in segs.values():
best, best_pct = None, None best, best_pct = None, None
for k in s["started"]: for k in s["started"]:
p = cards.get(k, {}).get("pct0") # 领涨者按行情视图找,不限于档位表里的票:被指向且已启动但不在主榜与观察档的
# (无覆盖且不在赛道、或被风险闸挡)也要显示,这是"定位对不对"的读数
p = (daily.get(k) or {}).get("pct0")
if p is not None and (best_pct is None or p > best_pct): if p is not None and (best_pct is None or p > best_pct):
best, best_pct = k, p best, best_pct = k, p
s["leader"] = {"code": best, "pct0": best_pct} if best else None s["leader"] = {"code": best, "pct0": best_pct} if best else None
s["started_count"] = len(s["started"]) s["started_count"] = len(s["started"])
s["started_in_tiers"] = sum(1 for k in s["started"] if k in cards)
ordered = sorted(segs.values(), key=lambda s: (-s["candidates"], -(s["n_sources"] or 0), ordered = sorted(segs.values(), key=lambda s: (-s["candidates"], -(s["n_sources"] or 0),
-(s["chain_fit"] or 0), s["segment"])) -(s["chain_fit"] or 0), s["segment"]))
return cards, ordered return cards, ordered

View File

@ -183,6 +183,7 @@ def run(since: str, until: str | None, horizons: tuple, start: str, out_dir: str
full = data.get("_full") or {} full = data.get("_full") or {}
main_rows, obs_rows = full.get("main", []), full.get("observe", []) main_rows, obs_rows = full.get("main", []), full.get("observe", [])
main_codes = [r["code"] for r in main_rows] main_codes = [r["code"] for r in main_rows]
obs_codes = [r["code"] for r in obs_rows]
tier_of = {r["code"]: r.get("tier") for r in main_rows} tier_of = {r["code"]: r.get("tier") for r in main_rows}
verdict_of = {r["code"]: r.get("verdict") for r in main_rows + obs_rows} verdict_of = {r["code"]: r.get("verdict") for r in main_rows + obs_rows}
seg_codes = sorted({r["code"] for r in main_rows + obs_rows if r.get("evidence")}) seg_codes = sorted({r["code"] for r in main_rows + obs_rows if r.get("evidence")})
@ -212,7 +213,8 @@ def run(since: str, until: str | None, horizons: tuple, start: str, out_dir: str
if reg and reg.get("weak_day") is not None else "无标签") if reg and reg.get("weak_day") is not None else "无标签")
lists = { lists = {
"生产名单": prod, "候选单": cands, "关注单": watch, "环节名单": seg_codes, "生产名单": prod, "候选单": cands, "关注单": watch, "环节名单": seg_codes,
"主榜等权": main_codes, "全池等权": list(ret.dropna().index), "主榜等权": main_codes, "观察档等权": obs_codes,
"全池等权": list(ret.dropna().index),
} }
for name, codes in lists.items(): for name, codes in lists.items():
s = summarize(ret, codes, base_all, base_main) s = summarize(ret, codes, base_all, base_main)

View File

@ -93,13 +93,18 @@ def night_conclusions(codes, ds: str) -> dict[str, dict]:
codes = sorted({common.to_prefix(str(c).strip()) for c in codes if c}) codes = sorted({common.to_prefix(str(c).strip()) for c in codes if c})
if not codes: if not codes:
return {} return {}
# 只取不晚于数据日 ds 的结论行:生产上 ds 就是数据日,与"取最新一行"等价;
# 复盘重建历史日时这一条防前视(否则历史日会读到今天的吸筹状态)。
# trade_date 是整数 YYYYMMDD数据源盘点 §1a直接比大小。
ds_int = int(ds.replace("-", "")) if ds else 99999999
try: try:
marks = ",".join(["%s"] * len(codes)) marks = ",".join(["%s"] * len(codes))
df = db.read_mysql( df = db.read_mysql(
"pms", "pms",
f"SELECT stock_code, trade_date, signal_type, support_level, pressure_level, " f"SELECT stock_code, trade_date, signal_type, support_level, pressure_level, "
f"raw_logic_json FROM strategy_daily_results WHERE stock_code IN ({marks})", f"raw_logic_json FROM strategy_daily_results "
tuple(codes)) f"WHERE stock_code IN ({marks}) AND trade_date <= %s",
tuple(codes) + (ds_int,))
except Exception as e: # noqa: BLE001 except Exception as e: # noqa: BLE001
print(f" (决策系统结论表读取失败,吸筹确认线与坏信号风险整体缺席: {e!r}") print(f" (决策系统结论表读取失败,吸筹确认线与坏信号风险整体缺席: {e!r}")
return {} return {}