akg-factor-bridge/test_card.py

78 lines
3.6 KiB
Python
Raw Normal View History

"""card.judge() 纯逻辑单测(无需 DB、无需 pandas
覆盖候选 / 关注缺覆盖/ 关注缺确认/ 仅展示未启动/ 仅展示硬风险否决
/ 缺失文案 / 评分陈旧 / 旋钮 / 卡内序 / 坏信号集合与 pool 同源
跑法python3 test_card.py pytest test_card.py
"""
import card
BASE = dict(pointed=True, theme="散热器件", n_sources=2, pct0=4.1,
covered=True, upside=0.25, risk_name=False,
accum_state="明确吸筹·量在价先", accum_score=78, accum_age=3,
y_signal="WATCH", stale_snapshot=False)
def t(name, cond):
assert cond, name
print(" ok", name)
def main():
r = card.judge(dict(BASE))
t("三门槛全过且明确吸筹 -> 候选", r["verdict"] == "候选" and r["confirm"] and not r["risk"])
t("候选理由四条齐", len(r["reasons"]) == 4 and "已启动" in r["reasons"][1])
r = card.judge({**BASE, "covered": False, "upside": None})
t("只差覆盖 -> 关注", r["verdict"] == "关注" and "无券商覆盖" in r["missing"])
r = card.judge({**BASE, "accum_state": "潜在吸筹·低位企稳"})
t("门槛全过但潜在吸筹不算确认 -> 关注", r["verdict"] == "关注" and not r["confirm"])
r = card.judge({**BASE, "accum_state": ""})
t("无评分 -> 关注且缺失文案", r["verdict"] == "关注" and "无吸筹评分(未入池)" in r["missing"])
r = card.judge({**BASE, "pct0": 1.2})
t("未启动 -> 仅展示", r["verdict"] == "仅展示" and "started" in r["failed_gates"])
r = card.judge({**BASE, "pointed": False})
t("未被指向 -> 仅展示且标无传导", r["verdict"] == "仅展示" and "无传导" in r["missing"])
r = card.judge({**BASE, "y_signal": "sell"})
t("决策系统 SELL -> 硬风险否决为仅展示", r["verdict"] == "仅展示" and r["risk"])
r = card.judge({**BASE, "stale_snapshot": True})
t("快照日不符 -> 硬风险", r["verdict"] == "仅展示" and "传导快照日与计划日不符" in r["risk"])
r = card.judge({**BASE, "accum_state": "⚠️ 高位派发"})
t("高位派发 -> 硬风险", r["verdict"] == "仅展示" and "高位派发" in r["risk"])
r = card.judge({**BASE, "accum_age": 45})
t("评分陈旧 -> 不确认、关注、缺失文案", r["verdict"] == "关注" and not r["confirm"]
and any("陈旧 45" in m for m in r["missing"]))
r = card.judge({**BASE, "pct0": 2.5}, start_pct=2.0)
t("启动阈值旋钮生效", r["verdict"] == "候选")
r = card.judge({**BASE, "accum_age": 45}, accum_max_age=60)
t("评分日龄旋钮生效", r["verdict"] == "候选")
r = card.judge({**BASE, "upside": -0.05}, neg_tol=0.10)
t("负容忍线传入生效", r["verdict"] == "候选")
r = card.judge({**BASE, "upside": -0.05})
t("负容忍默认 0轻微为负不过覆盖门槛 -> 关注", r["verdict"] == "关注")
r = card.judge({**BASE, "risk_name": True})
t("ST 族 -> 仅展示", r["verdict"] == "仅展示" and "clean_name" in r["failed_gates"])
rows = [{"verdict": "关注", "pct0": 9.0}, {"verdict": "候选", "pct0": 3.5},
{"verdict": "候选", "pct0": 7.2}, {"verdict": "仅展示", "pct0": None}]
rows.sort(key=card.sort_key)
t("卡内序:候选在前、同判决涨幅降序、缺行情垫底",
[x["pct0"] for x in rows] == [7.2, 3.5, 9.0, None])
t("坏信号集合口径", card.BAD_SIGNALS == {"SELL", "AVOID", "DROPPED"})
print("ALL OK — 候选卡判决 / 关注两种 / 硬风险三种 / 缺失文案 / 旋钮 / 卡内序 全部通过")
if __name__ == "__main__":
main()