启动涨幅线做成整条开关,2026-09-04 按拍板关闭
原先只有一个阈值 CARD_START_PCT,想不要这条门槛只能把它调成一个很小的负数。那是绕法, 不是关闭:没有当日行情的票仍然会被拦下,而关掉门槛的本意是这一条根本不参与判断。 新增 CARD_REQUIRE_STARTED,置 0 即整条不参与。关掉后:涨跌多少都不拦,没有行情也不拦, 理由行改写成「当日涨幅 -3.2%(启动线已关闭,不作门槛)」——涨跌仍是人要看的信息, 只是不再当作一条通过的理由。 这条门槛回答的不是这只票好不好,是今天是不是买点:只在票已经动起来的当天买,不猜底。 关掉它等于改成左侧买入,好处是研究结论成立的当天就能进场,代价是要承受不确定的等待, 候选数会明显变多。09-04 关之前的实测:当日 83 只强传导票全部落到仅展示,绝大多数就 卡在这一条上。 九条用例钉住关掉后的行为,重点是不许顺带放松别的门槛:传导、覆盖、吸筹确认线、 硬风险四道都仍然照拦,判断不了的仍归关注。另有一条钉住默认值仍是开着的——改口径 要靠配置,不靠默认值悄悄变。 Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
This commit is contained in:
parent
29621432f5
commit
de7f83d18d
12
card.py
12
card.py
|
|
@ -73,7 +73,7 @@ def _num(v) -> float | None:
|
||||||
|
|
||||||
def judge(ev: dict[str, Any], *, start_pct: float = 3.0,
|
def judge(ev: dict[str, Any], *, start_pct: float = 3.0,
|
||||||
accum_max_age: int = 30, neg_tol: float = 0.0,
|
accum_max_age: int = 30, neg_tol: float = 0.0,
|
||||||
logic_stale_days: int = 90) -> dict[str, Any]:
|
logic_stale_days: int = 90, require_started: bool = True) -> dict[str, Any]:
|
||||||
"""一只票的证据字典 -> 判决字典。
|
"""一只票的证据字典 -> 判决字典。
|
||||||
|
|
||||||
ev 里认的键(缺键按缺失处理,不报错):
|
ev 里认的键(缺键按缺失处理,不报错):
|
||||||
|
|
@ -104,7 +104,10 @@ def judge(ev: dict[str, Any], *, start_pct: float = 3.0,
|
||||||
|
|
||||||
gates = {
|
gates = {
|
||||||
"pointed": bool(ev.get("pointed")),
|
"pointed": bool(ev.get("pointed")),
|
||||||
"started": pct0 is not None and pct0 >= float(start_pct),
|
# require_started 关掉时这一条恒过:涨跌多少都不拦,没有当日行情也不拦。
|
||||||
|
# 这是整条门槛的开关,不是把阈值调成一个很小的负数——那样"没有行情"的票
|
||||||
|
# 仍然会被拦下,而关掉门槛的本意是这一条根本不参与判断。
|
||||||
|
"started": (not require_started) or (pct0 is not None and pct0 >= float(start_pct)),
|
||||||
"covered": bool(ev.get("covered")) and upside is not None
|
"covered": bool(ev.get("covered")) and upside is not None
|
||||||
and upside >= -float(neg_tol or 0.0),
|
and upside >= -float(neg_tol or 0.0),
|
||||||
"clean_name": not bool(ev.get("risk_name")),
|
"clean_name": not bool(ev.get("risk_name")),
|
||||||
|
|
@ -154,8 +157,11 @@ def judge(ev: dict[str, Any], *, start_pct: float = 3.0,
|
||||||
theme = ev.get("theme") or "环节"
|
theme = ev.get("theme") or "环节"
|
||||||
n = ev.get("n_sources")
|
n = ev.get("n_sources")
|
||||||
reasons.append(f"所在环节「{theme}」被{n}路指向" if n else f"所在环节「{theme}」被指向")
|
reasons.append(f"所在环节「{theme}」被{n}路指向" if n else f"所在环节「{theme}」被指向")
|
||||||
if gates["started"]:
|
if gates["started"] and require_started:
|
||||||
reasons.append(f"当日已启动 {pct0:+.1f}%")
|
reasons.append(f"当日已启动 {pct0:+.1f}%")
|
||||||
|
elif not require_started and pct0 is not None:
|
||||||
|
# 门槛关着时它不再是一条理由,但当日涨跌仍是人要看的信息,照样写出来。
|
||||||
|
reasons.append(f"当日涨幅 {pct0:+.1f}%(启动线已关闭,不作门槛)")
|
||||||
if confirm:
|
if confirm:
|
||||||
reasons.append(f"明确吸筹(评分 {accum_score:.0f},{accum_age} 日前)"
|
reasons.append(f"明确吸筹(评分 {accum_score:.0f},{accum_age} 日前)"
|
||||||
if accum_score is not None else f"明确吸筹({accum_age} 日前)")
|
if accum_score is not None else f"明确吸筹({accum_age} 日前)")
|
||||||
|
|
|
||||||
11
config.py
11
config.py
|
|
@ -191,6 +191,17 @@ BIONIC_SCAN_KEY = os.environ.get("BIONIC_SCAN_KEY", "")
|
||||||
# 两个旋钮保持默认、不在历史样本上挑参数,等样本外复盘读数再拍(09-02 拍板)。
|
# 两个旋钮保持默认、不在历史样本上挑参数,等样本外复盘读数再拍(09-02 拍板)。
|
||||||
FACTOR_REGISTER_STATUS = os.environ.get("FACTOR_REGISTER_STATUS", "paused") # 平台注册状态:paused=不参评(方案 2.5,平台参评列表只取 active),active=参评
|
FACTOR_REGISTER_STATUS = os.environ.get("FACTOR_REGISTER_STATUS", "paused") # 平台注册状态:paused=不参评(方案 2.5,平台参评列表只取 active),active=参评
|
||||||
CARD_START_PCT = float(os.environ.get("CARD_START_PCT", "3")) # 门槛二:数据日涨幅达到几个百分点算已启动(与基座热点扫描同口径)
|
CARD_START_PCT = float(os.environ.get("CARD_START_PCT", "3")) # 门槛二:数据日涨幅达到几个百分点算已启动(与基座热点扫描同口径)
|
||||||
|
|
||||||
|
# 门槛二整条开关。置 0 就是不要这条门槛,涨跌多少都不拦。
|
||||||
|
#
|
||||||
|
# 这条门槛回答的不是"这只票好不好",是"今天是不是买点":只在票已经动起来的当天买,
|
||||||
|
# 不去猜底。关掉它等于改成左侧买入——好处是研究结论成立的当天就能进场,不用等市场
|
||||||
|
# 先认同;代价是要承受不确定的等待,而且候选数会明显变多。
|
||||||
|
#
|
||||||
|
# 2026-09-04 关掉。关之前的实测:当日 83 只强传导票全部落到仅展示,其中绝大多数就
|
||||||
|
# 卡在这一条上(那天大盘在跌,三只被问到的票分别跌 3.2%、1.8%、2.2%)。
|
||||||
|
# 想开回去就把这个值设回 1 并重建容器,判决当天即恢复原样。
|
||||||
|
CARD_REQUIRE_STARTED = os.environ.get("CARD_REQUIRE_STARTED", "1") == "1"
|
||||||
CARD_ACCUM_MAX_AGE = int(os.environ.get("CARD_ACCUM_MAX_AGE", "30")) # 确认线:吸筹评分日龄上限(交易日)
|
CARD_ACCUM_MAX_AGE = int(os.environ.get("CARD_ACCUM_MAX_AGE", "30")) # 确认线:吸筹评分日龄上限(交易日)
|
||||||
# 计划快照落点(容器内路径;仓库根挂在 /app,故默认落在仓库 data/plan/)。
|
# 计划快照落点(容器内路径;仓库根挂在 /app,故默认落在仓库 data/plan/)。
|
||||||
# 每日 JSON 含主榜与观察档全部行与全部证据线,是复盘与对账的唯一底本。
|
# 每日 JSON 含主榜与观察档全部行与全部证据线,是复盘与对账的唯一底本。
|
||||||
|
|
|
||||||
4
plan.py
4
plan.py
|
|
@ -149,7 +149,8 @@ def _assemble_cards(ds: str, codes: list, ev: dict, upside: pd.Series,
|
||||||
j = card.judge(evd, start_pct=config.CARD_START_PCT,
|
j = card.judge(evd, start_pct=config.CARD_START_PCT,
|
||||||
accum_max_age=config.CARD_ACCUM_MAX_AGE,
|
accum_max_age=config.CARD_ACCUM_MAX_AGE,
|
||||||
neg_tol=config.UPSIDE_NEG_TOLERANCE,
|
neg_tol=config.UPSIDE_NEG_TOLERANCE,
|
||||||
logic_stale_days=config.LOGIC_STALE_DAYS)
|
logic_stale_days=config.LOGIC_STALE_DAYS,
|
||||||
|
require_started=config.CARD_REQUIRE_STARTED)
|
||||||
cards[k] = {
|
cards[k] = {
|
||||||
**j,
|
**j,
|
||||||
"theme": theme, "n_sources": n_sources, "chain_fit": evd["chain_fit"],
|
"theme": theme, "n_sources": n_sources, "chain_fit": evd["chain_fit"],
|
||||||
|
|
@ -393,6 +394,7 @@ def collect(date: str | None = None, top: int = 20, obs_top: int = 10,
|
||||||
"观察档分=100+0.6z(传导)+0.4z(−热度)",
|
"观察档分=100+0.6z(传导)+0.4z(−热度)",
|
||||||
# ---- 候选卡(2026-09-02):分数与档位之外的另一份产物,不受 top / theme_cap 裁剪 ----
|
# ---- 候选卡(2026-09-02):分数与档位之外的另一份产物,不受 top / theme_cap 裁剪 ----
|
||||||
"card_params": {"start_pct": config.CARD_START_PCT,
|
"card_params": {"start_pct": config.CARD_START_PCT,
|
||||||
|
"require_started": config.CARD_REQUIRE_STARTED,
|
||||||
"accum_max_age": config.CARD_ACCUM_MAX_AGE,
|
"accum_max_age": config.CARD_ACCUM_MAX_AGE,
|
||||||
"neg_tol": config.UPSIDE_NEG_TOLERANCE,
|
"neg_tol": config.UPSIDE_NEG_TOLERANCE,
|
||||||
"logic_per_stock": config.LOGIC_CLAIMS_PER_STOCK,
|
"logic_per_stock": config.LOGIC_CLAIMS_PER_STOCK,
|
||||||
|
|
|
||||||
29
test_card.py
29
test_card.py
|
|
@ -161,6 +161,35 @@ def main():
|
||||||
r["logic_quality"] is None and not [m for m in r["missing"] if "论断" in m])
|
r["logic_quality"] is None and not [m for m in r["missing"] if "论断" in m])
|
||||||
t("论断为空列表时不产生提示", card.logic_notes([]) == [] and card.logic_quality([]) is None)
|
t("论断为空列表时不产生提示", card.logic_notes([]) == [] and card.logic_quality([]) is None)
|
||||||
|
|
||||||
|
# ---- 启动涨幅线的整条开关(2026-09-04 拍板关闭)----
|
||||||
|
r = card.judge({**BASE, "pct0": -3.2}, require_started=False)
|
||||||
|
t("门槛关掉后,当日下跌也能判候选", r["verdict"] == "候选" and r["failed_gates"] == [])
|
||||||
|
t("关掉后理由里写明这条线已关,不再说「当日已启动」",
|
||||||
|
any("启动线已关闭" in x for x in r["reasons"])
|
||||||
|
and not any("当日已启动" in x for x in r["reasons"]))
|
||||||
|
r = card.judge({**BASE, "pct0": None}, require_started=False)
|
||||||
|
t("关掉后连没有当日行情也不拦(这是整条关掉,不是把阈值调成负数)",
|
||||||
|
r["verdict"] == "候选")
|
||||||
|
t("但没有行情这件事仍写进缺失项", "没有当日行情" in r["missing"])
|
||||||
|
|
||||||
|
# 关掉这一条不许顺带放松别的门槛与风险
|
||||||
|
r = card.judge({**BASE, "pct0": -3.2, "pointed": False, "theme": None,
|
||||||
|
"n_sources": None}, require_started=False)
|
||||||
|
t("关掉启动线不影响传导门槛", r["verdict"] == "仅展示" and "pointed" in r["failed_gates"])
|
||||||
|
r = card.judge({**BASE, "pct0": -3.2, "covered": False, "upside": None},
|
||||||
|
require_started=False)
|
||||||
|
t("关掉启动线不影响覆盖门槛", r["verdict"] == "仅展示" and "covered" in r["failed_gates"])
|
||||||
|
r = card.judge({**BASE, "pct0": -3.2, "accum_state": "潜在吸筹"}, require_started=False)
|
||||||
|
t("关掉启动线不影响吸筹确认线", r["verdict"] == "仅展示" and not r["confirm"])
|
||||||
|
r = card.judge({**BASE, "pct0": -3.2, "y_signal": "SELL"}, require_started=False)
|
||||||
|
t("关掉启动线不影响硬风险否决", r["verdict"] == "仅展示" and r["risk"])
|
||||||
|
r = card.judge({**BASE, "pct0": -3.2, "accum_state": ""}, require_started=False)
|
||||||
|
t("关掉启动线后,判断不了的仍是关注", r["verdict"] == "关注")
|
||||||
|
|
||||||
|
r = card.judge({**BASE, "pct0": -3.2})
|
||||||
|
t("默认仍是开着的(改口径要靠配置,不靠默认值悄悄变)",
|
||||||
|
r["verdict"] == "仅展示" and "started" in r["failed_gates"])
|
||||||
|
|
||||||
# ---- 给人看的文字里不许出现代码名(2026-09-04)----
|
# ---- 给人看的文字里不许出现代码名(2026-09-04)----
|
||||||
# 起因:页面上出现过「选股系统判为仅展示,不出提议:门槛未过:started」。
|
# 起因:页面上出现过「选股系统判为仅展示,不出提议:门槛未过:started」。
|
||||||
# started 是门槛在代码里的变量名,SELL 是决策系统的枚举值,两者都只该在系统之间
|
# started 是门槛在代码里的变量名,SELL 是决策系统的枚举值,两者都只该在系统之间
|
||||||
|
|
|
||||||
Loading…
Reference in New Issue