加固包:去重集清除条件、持仓行转多留痕、登记弱表态开关、时钟用例改相对时刻
评审第五节四处小加固,每处开关关掉或默认值即旧行为: - 转空去重集只在 SAR 明确为多时才清除(原来「不是空就清」把缺读数也当翻多, 会重复减仓)。抽出纯函数 _keep_tech_exit_done,第三十批加三例。 - 持仓行的择时票带上当日盘中转多留痕:decorate_positions 从买入信号同一来源 取 flip_map,昨夜中性当日转多时择时票升看多。第二十八批加一例。 - 登记 PMS_CONSENSUS_WEAK_CONFIRM(默认开):合议方向看多但只有一方表态时 交人,关掉则放行。consensus.decide 加 weak_confirm 参数。第二十八批加两例。 - 第二十四批时钟用例改相对当前时刻构造陈旧告警,任何时刻跑都过(原来写死 今天09:35,开发机09:36前跑年龄算成负数)。 开发机全量单测846例 ALL SUITES PASS。未在155部署(收盘后随观察读数包一起)。 Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
This commit is contained in:
parent
f2d6b37fd2
commit
e21b73b46d
|
|
@ -16,7 +16,7 @@ def _reason(fund, tech, timing, direction, strength):
|
||||||
return head + " → " + direction + (("·" + strength) if strength else "")
|
return head + " → " + direction + (("·" + strength) if strength else "")
|
||||||
|
|
||||||
|
|
||||||
def _route(fund, tech, timing, direction, tech_phase, fund_required):
|
def _route(fund, tech, timing, direction, tech_phase, fund_required, weak_confirm=True):
|
||||||
"""附录丙路由表, 从上到下第一个命中。返回 (路由, 原因)。路由取值: 放行/观察/交人/跳过。"""
|
"""附录丙路由表, 从上到下第一个命中。返回 (路由, 原因)。路由取值: 放行/观察/交人/跳过。"""
|
||||||
if fund_required and fund == _NA:
|
if fund_required and fund == _NA:
|
||||||
return "跳过", "没有买方评析"
|
return "跳过", "没有买方评析"
|
||||||
|
|
@ -33,16 +33,21 @@ def _route(fund, tech, timing, direction, tech_phase, fund_required):
|
||||||
if timing == _BEAR: # 异议在择时系统
|
if timing == _BEAR: # 异议在择时系统
|
||||||
return "交人", "方向看多但择时系统有异议"
|
return "交人", "方向看多但择时系统有异议"
|
||||||
if len(voiced) <= 1: # 只有一方表态
|
if len(voiced) <= 1: # 只有一方表态
|
||||||
|
# 弱表态: weak_confirm 开着交人, 关掉放行 (2026-09-14 加固, 登记 PMS_CONSENSUS_WEAK_CONFIRM)
|
||||||
|
if weak_confirm:
|
||||||
return "交人", "方向看多但只有一方表态"
|
return "交人", "方向看多但只有一方表态"
|
||||||
|
return "放行", "方向看多但只有一方表态(弱表态放行)"
|
||||||
return "放行", "方向看多" # 看多强, 或异议只在技术面
|
return "放行", "方向看多" # 看多强, 或异议只在技术面
|
||||||
if fund == _NA and tech == _NA and timing == _NA:
|
if fund == _NA and tech == _NA and timing == _NA:
|
||||||
return "放行", "三方都无读数,机械方案"
|
return "放行", "三方都无读数,机械方案"
|
||||||
return "观察", "方向中性"
|
return "观察", "方向中性"
|
||||||
|
|
||||||
|
|
||||||
def decide(fund, tech, timing, *, tech_phase=None, fund_required=True) -> dict:
|
def decide(fund, tech, timing, *, tech_phase=None, fund_required=True, weak_confirm=True) -> dict:
|
||||||
"""fund/tech/timing 是三个立场字符串 (看多/看空/中性/无读数)。tech_phase 是技术面相位
|
"""fund/tech/timing 是三个立场字符串 (看多/看空/中性/无读数)。tech_phase 是技术面相位
|
||||||
(收口等待 等), 路由用。fund_required: 无买方评析是否拦 (开关 PMS_FUND_REQUIRED)。"""
|
(收口等待 等), 路由用。fund_required: 无买方评析是否拦 (开关 PMS_FUND_REQUIRED)。
|
||||||
|
weak_confirm: 方向看多但只有一方表态时是否交人 (开关 PMS_CONSENSUS_WEAK_CONFIRM, 默认真;
|
||||||
|
关掉则放行)。"""
|
||||||
votes = {"fund": fund, "tech": tech, "timing": timing}
|
votes = {"fund": fund, "tech": tech, "timing": timing}
|
||||||
voiced = [v for v in (fund, tech, timing) if v in (_BULL, _BEAR)]
|
voiced = [v for v in (fund, tech, timing) if v in (_BULL, _BEAR)]
|
||||||
bulls = sum(1 for v in voiced if v == _BULL)
|
bulls = sum(1 for v in voiced if v == _BULL)
|
||||||
|
|
@ -66,7 +71,8 @@ def decide(fund, tech, timing, *, tech_phase=None, fund_required=True) -> dict:
|
||||||
else:
|
else:
|
||||||
strength = "中"
|
strength = "中"
|
||||||
|
|
||||||
route, route_reason = _route(fund, tech, timing, direction, tech_phase, fund_required)
|
route, route_reason = _route(fund, tech, timing, direction, tech_phase, fund_required,
|
||||||
|
weak_confirm)
|
||||||
return {"direction": direction, "votes": votes, "strength": strength,
|
return {"direction": direction, "votes": votes, "strength": strength,
|
||||||
"reason": _reason(fund, tech, timing, direction, strength),
|
"reason": _reason(fund, tech, timing, direction, strength),
|
||||||
"route": route, "route_reason": route_reason}
|
"route": route, "route_reason": route_reason}
|
||||||
|
|
|
||||||
|
|
@ -27,6 +27,7 @@ def _params() -> dict:
|
||||||
"fund_stale_days": gi("PMS_FUND_STALE_DAYS", 120),
|
"fund_stale_days": gi("PMS_FUND_STALE_DAYS", 120),
|
||||||
"fund_consensus_good_min": gi("PMS_FUND_CONSENSUS_GOOD_MIN", 8),
|
"fund_consensus_good_min": gi("PMS_FUND_CONSENSUS_GOOD_MIN", 8),
|
||||||
"consensus_route": gb("PMS_CONSENSUS_ROUTE", True),
|
"consensus_route": gb("PMS_CONSENSUS_ROUTE", True),
|
||||||
|
"weak_confirm": gb("PMS_CONSENSUS_WEAK_CONFIRM", True),
|
||||||
"timing_stale_tdays": gi("PMS_REF_STALE_TDAYS", 3),
|
"timing_stale_tdays": gi("PMS_REF_STALE_TDAYS", 3),
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -72,7 +73,8 @@ def assemble(row, *, nightly=None, tech_states=None, flip_at=None, params=None)
|
||||||
# 合议参与分流的开关关掉时, 基本面无读数不由合议这层拦 (回到只按判决分流)
|
# 合议参与分流的开关关掉时, 基本面无读数不由合议这层拦 (回到只按判决分流)
|
||||||
con = consensus.decide(fund["stance"], tech.get("stance") or "无读数", tm["stance"],
|
con = consensus.decide(fund["stance"], tech.get("stance") or "无读数", tm["stance"],
|
||||||
tech_phase=tech.get("phase"),
|
tech_phase=tech.get("phase"),
|
||||||
fund_required=(p["fund_required"] and p["consensus_route"]))
|
fund_required=(p["fund_required"] and p["consensus_route"]),
|
||||||
|
weak_confirm=p.get("weak_confirm", True))
|
||||||
return {"fund": fund, "tech": tech, "timing": tm, "consensus": con}
|
return {"fund": fund, "tech": tech, "timing": tm, "consensus": con}
|
||||||
|
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -112,6 +112,8 @@ RUNTIME_EXTRA = {
|
||||||
"PMS_FUND_STALE_DAYS": (120, int, "买方评析报告超过这么多天算无读数"),
|
"PMS_FUND_STALE_DAYS": (120, int, "买方评析报告超过这么多天算无读数"),
|
||||||
"PMS_FUND_CONSENSUS_GOOD_MIN": (8, int, "十视角好达这么多个且估值贵 → 打共识已定价标记, 让首批推迟"),
|
"PMS_FUND_CONSENSUS_GOOD_MIN": (8, int, "十视角好达这么多个且估值贵 → 打共识已定价标记, 让首批推迟"),
|
||||||
"PMS_CONSENSUS_ROUTE": (True, bool, "三源合议参与新建仓分流 (拍板: 上线即开; 关掉即回到只按选股判决分流)"),
|
"PMS_CONSENSUS_ROUTE": (True, bool, "三源合议参与新建仓分流 (拍板: 上线即开; 关掉即回到只按选股判决分流)"),
|
||||||
|
# 2026-09-14 加固包 (方案第六节承诺、评审发现五): 方向看多但只有一方表态时是否交人。
|
||||||
|
"PMS_CONSENSUS_WEAK_CONFIRM": (True, bool, "合议方向看多但只有一方表态时交人确认 (默认开; 关掉则弱表态也放行)"),
|
||||||
# ── 2026-09-11 三源合议工作包二·接入 (台账 006/009): 持仓增持门与候选按质地排序。
|
# ── 2026-09-11 三源合议工作包二·接入 (台账 006/009): 持仓增持门与候选按质地排序。
|
||||||
"PMS_TECH_GATE_INCREASE": (True, bool, "持仓增持门: 合议看空停回踩补足/盈利加仓/补仓, 技术面看空停回踩补足与盈利加仓, 基本面看空停补仓 (减持不受影响; 关掉即回到不设门)"),
|
"PMS_TECH_GATE_INCREASE": (True, bool, "持仓增持门: 合议看空停回踩补足/盈利加仓/补仓, 技术面看空停回踩补足与盈利加仓, 基本面看空停补仓 (减持不受影响; 关掉即回到不设门)"),
|
||||||
"PMS_PLAN_RANK_BY_QUALITY": (True, bool, "候选先按基本面立场分桶 (质地看多的桶排前) 再按分数, 在主题限额与截断之前生效; 技术面不参与排序 (关掉即回到只按分数排)"),
|
"PMS_PLAN_RANK_BY_QUALITY": (True, bool, "候选先按基本面立场分桶 (质地看多的桶排前) 再按分数, 在主题限额与截断之前生效; 技术面不参与排序 (关掉即回到只按分数排)"),
|
||||||
|
|
|
||||||
|
|
@ -135,7 +135,14 @@ def scan_and_route(*, now=None, dry_run: bool = False) -> dict:
|
||||||
_lmap = _lss.state_map()
|
_lmap = _lss.state_map()
|
||||||
_crs = {code: (st or {}).get("company_review")
|
_crs = {code: (st or {}).get("company_review")
|
||||||
for code, st in (_lmap or {}).items()}
|
for code, st in (_lmap or {}).items()}
|
||||||
consensus_service.decorate_positions(view["held"], company_reviews=_crs)
|
# 持仓行的择时票也带上当日盘中转多留痕 (2026-09-14 加固, 评审发现四): 来源与候选
|
||||||
|
# 相同 —— 决策系统判过盘中转多的留痕 (_buy_signals_today)。没这留痕的行照旧只按
|
||||||
|
# 昨夜定性。持仓行昨夜中性而当日转多时, 择时票升为看多, 增持门的方向判断才跟得上。
|
||||||
|
_sig = _buy_signals_today()
|
||||||
|
_flip = {code: (v or {}).get("at") for code, v in (_sig or {}).items()
|
||||||
|
if (v or {}).get("at")}
|
||||||
|
consensus_service.decorate_positions(view["held"], company_reviews=_crs,
|
||||||
|
flip_map=_flip)
|
||||||
except Exception as e: # noqa: BLE001
|
except Exception as e: # noqa: BLE001
|
||||||
logger.warning("[合议] 持仓增持门装配失败, 本轮按无合议 (不设门): %s", e)
|
logger.warning("[合议] 持仓增持门装配失败, 本轮按无合议 (不设门): %s", e)
|
||||||
# 技术面转空自动离场 (2026-09-11 工作包三): 给持仓行挂技术面状态供 eval_tech_exit 读,
|
# 技术面转空自动离场 (2026-09-11 工作包三): 给持仓行挂技术面状态供 eval_tech_exit 读,
|
||||||
|
|
@ -147,8 +154,7 @@ def scan_and_route(*, now=None, dry_run: bool = False) -> dict:
|
||||||
_tech_states = _ts.state_map()
|
_tech_states = _ts.state_map()
|
||||||
_ts.attach(view["held"], _tech_states)
|
_ts.attach(view["held"], _tech_states)
|
||||||
_done = _load_tech_exit_done()
|
_done = _load_tech_exit_done()
|
||||||
params["tech_exit_done"] = {c for c in _done
|
params["tech_exit_done"] = _keep_tech_exit_done(_done, _tech_states)
|
||||||
if (_tech_states.get(c) or {}).get("sar_side") == "空"}
|
|
||||||
except Exception as e: # noqa: BLE001
|
except Exception as e: # noqa: BLE001
|
||||||
logger.warning("[技术面离场] 挂技术面状态失败, 本轮不评转空离场: %s", e)
|
logger.warning("[技术面离场] 挂技术面状态失败, 本轮不评转空离场: %s", e)
|
||||||
params["tech_exit_done"] = set()
|
params["tech_exit_done"] = set()
|
||||||
|
|
@ -242,6 +248,17 @@ def scan_and_route(*, now=None, dry_run: bool = False) -> dict:
|
||||||
_TECH_EXIT_DONE_KEY = "PMS_TECH_EXIT_DONE"
|
_TECH_EXIT_DONE_KEY = "PMS_TECH_EXIT_DONE"
|
||||||
|
|
||||||
|
|
||||||
|
def _keep_tech_exit_done(done, tech_states) -> set:
|
||||||
|
"""本次翻空去重集里, 本轮该保留 (不清除) 的代码集。
|
||||||
|
|
||||||
|
2026-09-14 加固 (评审发现三): 原来是「SAR 方向是空才保留」, 而「不是空」把「映射里没有
|
||||||
|
这只票」也算了进去 —— 停牌或决策系统那晚漏算的持仓票缺读数, 会被当成翻多而清出去重集;
|
||||||
|
若同一轮另有别的票处理了转空离场, 清过的集合会被写回, 第二天读数回来、翻空仍在两天内就
|
||||||
|
再减一次 (多减三分之一)。改成**只在 SAR 方向明确为多时才清除**: 翻空与缺读数一律保留。"""
|
||||||
|
return {c for c in (done or set())
|
||||||
|
if (tech_states.get(c) or {}).get("sar_side") != "多"}
|
||||||
|
|
||||||
|
|
||||||
def _load_tech_exit_done() -> set:
|
def _load_tech_exit_done() -> set:
|
||||||
"""本次翻空已处理过的代码集 (跨轮去重, 不动表结构; 与 tech_state_map 同一手法用运行参数承载)。
|
"""本次翻空已处理过的代码集 (跨轮去重, 不动表结构; 与 tech_state_map 同一手法用运行参数承载)。
|
||||||
读不到或坏了返回空集。清除 (SAR 翻多=新一轮) 在 scan_and_route 扫描前按当轮技术面映射做。"""
|
读不到或坏了返回空集。清除 (SAR 翻多=新一轮) 在 scan_and_route 扫描前按当轮技术面映射做。"""
|
||||||
|
|
|
||||||
|
|
@ -75,7 +75,7 @@
|
||||||
读数陈旧按无读数/参数登记与交易日差/upsert 用 VALUES 与按 matched 分页两个真机哨兵 (29 例)
|
读数陈旧按无读数/参数登记与交易日差/upsert 用 VALUES 与按 matched 分页两个真机哨兵 (29 例)
|
||||||
test_batch28_units.py 三源合议·工作包二纯逻辑 (2026-09-11): 基本面质地档映射与共识已定价/择时定性
|
test_batch28_units.py 三源合议·工作包二纯逻辑 (2026-09-11): 基本面质地档映射与共识已定价/择时定性
|
||||||
映射与盘中转多升级/合议多数决与强弱/附录丙路由表逐条/装配服务凑齐四块与六键/
|
映射与盘中转多升级/合议多数决与强弱/附录丙路由表逐条/装配服务凑齐四块与六键/
|
||||||
advise_v2 基本面×技术面仓位矩阵逐格 (37 例)
|
advise_v2 基本面×技术面仓位矩阵逐格 + 加固包弱表态开关两例与持仓行转多留痕一例 (40 例)
|
||||||
test_batch29_units.py 三源合议·工作包二接入下单链路 (2026-09-11): scan_open 合议分流(跳过/观察带
|
test_batch29_units.py 三源合议·工作包二接入下单链路 (2026-09-11): scan_open 合议分流(跳过/观察带
|
||||||
wait_tech/交人强制确认/放行/缺合议退旧路/开关关掉逐字回旧)/定档改用 advise_v2
|
wait_tech/交人强制确认/放行/缺合议退旧路/开关关掉逐字回旧)/定档改用 advise_v2
|
||||||
且六键进硬数字而不进送研判白名单/持仓增持门矩阵与 scan 端到端(减持不受影响、
|
且六键进硬数字而不进送研判白名单/持仓增持门矩阵与 scan 端到端(减持不受影响、
|
||||||
|
|
@ -85,7 +85,8 @@
|
||||||
夹紧/开关关掉逐字回旧)/卖出优先级目标价<研究走弱<技术面转空<保垫减仓/scan 端到端/
|
夹紧/开关关掉逐字回旧)/卖出优先级目标价<研究走弱<技术面转空<保垫减仓/scan 端到端/
|
||||||
策略票只看目标价按函数判(技术面转空对策略票不评)/跟踪止盈盘中 SAR 止损线
|
策略票只看目标价按函数判(技术面转空对策略票不评)/跟踪止盈盘中 SAR 止损线
|
||||||
(09:45 后跌破缓冲全清、当日一次、没刷进不判、开关关掉不触发)/SAR 线刷新腿只动
|
(09:45 后跌破缓冲全清、当日一次、没刷进不判、开关关掉不触发)/SAR 线刷新腿只动
|
||||||
自动跟踪止盈且无读数撤旧线/弱基本面试探仓紧止盈自动挂载(入场标记识别、回撤3%硬目标8%带SAR线、不占名额、冷却让路)/参数登记 (33 例)
|
自动跟踪止盈且无读数撤旧线/弱基本面试探仓紧止盈自动挂载(入场标记识别、回撤3%硬目标8%带SAR线、不占名额、冷却让路)/参数登记
|
||||||
|
+ 加固包去重集清除条件三例 (缺读数/明确多/明确空) (36 例)
|
||||||
test_batch31_units.py 观察读数包 (2026-09-14 台账 013): 动作引擎给合议跳过项打标签
|
test_batch31_units.py 观察读数包 (2026-09-14 台账 013): 动作引擎给合议跳过项打标签
|
||||||
(跳过分 fund/bear、观察 wait_tech、增持门 gate_block 带 gate_kind,
|
(跳过分 fund/bear、观察 wait_tech、增持门 gate_block 带 gate_kind,
|
||||||
开关关掉不打标签)/record_round 归类落表 (检查点写路由与装配两类、
|
开关关掉不打标签)/record_round 归类落表 (检查点写路由与装配两类、
|
||||||
|
|
@ -98,10 +99,10 @@
|
||||||
test_wiring.py 装配自检: 服务层→核心→落表 全链路 (内存桩) +
|
test_wiring.py 装配自检: 服务层→核心→落表 全链路 (内存桩) +
|
||||||
目标价到价必定入队 (档位 full 也不自动卖) +
|
目标价到价必定入队 (档位 full 也不自动卖) +
|
||||||
用户设的止损价与目标价单独成列显示 (70 例)
|
用户设的止损价与目标价单独成列显示 (70 例)
|
||||||
共 840 例
|
共 846 例
|
||||||
(总数按实跑逐批相加校正过两次: 曾写 649 是笔误, 实为 650; 09-03 先后加了同轮只发一条
|
(总数按实跑逐批相加校正过两次: 曾写 649 是笔误, 实为 650; 09-03 先后加了同轮只发一条
|
||||||
减持与研究理由两键各一例, 到 652; 09-04 加了仅展示跳过原因与空候选说明各一例, 到 654; 又加了页面文案守卫一例, 到 655; 09-07 审查修复加了跨轮减持等五例, 到 660; 第二件低把握驳回交人一例, 到 661; 第三件逻辑状态接入第二十二批十五例, 到 676; 第四件安全边际整句透传一例, 到 677; 参考目标价一例, 到 678; 催化事件与定价状态透传一例, 到 679; 两个期限的头一例, 现为 680; 09-10 建议档位对齐第二十六批十七例到 697; 09-11 技术面接入工作包一第二十七批二十七例到 724; 三源合议工作包二纯逻辑第二十八批三十七例到 763; 接入下单链路第二十九批二十四例到 787; 工作包三离场纪律第三十批十八例到 805; part3 盘中 SAR 止损线补九例到 814; part4 弱基本面紧止盈补六例到 820;
|
减持与研究理由两键各一例, 到 652; 09-04 加了仅展示跳过原因与空候选说明各一例, 到 654; 又加了页面文案守卫一例, 到 655; 09-07 审查修复加了跨轮减持等五例, 到 660; 第二件低把握驳回交人一例, 到 661; 第三件逻辑状态接入第二十二批十五例, 到 676; 第四件安全边际整句透传一例, 到 677; 参考目标价一例, 到 678; 催化事件与定价状态透传一例, 到 679; 两个期限的头一例, 现为 680; 09-10 建议档位对齐第二十六批十七例到 697; 09-11 技术面接入工作包一第二十七批二十七例到 724; 三源合议工作包二纯逻辑第二十八批三十七例到 763; 接入下单链路第二十九批二十四例到 787; 工作包三离场纪律第三十批十八例到 805; part3 盘中 SAR 止损线补九例到 814; part4 弱基本面紧止盈补六例到 820;
|
||||||
09-14 观察读数包第三十一批二十例, 现为 840)
|
09-14 观察读数包第三十一批二十例到 840; 加固包给第二十八批加三例、第三十批加三例, 现为 846)
|
||||||
任一子集失败即整体失败 (退出码 1)。
|
任一子集失败即整体失败 (退出码 1)。
|
||||||
|
|
||||||
哨兵位置清单 (2026-09-03 抄录; 改了对应的东西就得来这些地方改断言, 断言不动就是漏了):
|
哨兵位置清单 (2026-09-03 抄录; 改了对应的东西就得来这些地方改断言, 断言不动就是漏了):
|
||||||
|
|
|
||||||
|
|
@ -130,7 +130,11 @@ def test_parse_sources():
|
||||||
assert rows[1]["src"] == "decision" and rows[1]["time"] == "10:04" and rows[1]["confidence"] == 0.95, rows[1]
|
assert rows[1]["src"] == "decision" and rows[1]["time"] == "10:04" and rows[1]["confidence"] == 0.95, rows[1]
|
||||||
assert rows[1]["cat_label"] == "建议买入" and rows[1]["price"] == 43.15
|
assert rows[1]["cat_label"] == "建议买入" and rows[1]["price"] == 43.15
|
||||||
|
|
||||||
old = _today_ms(9, 35)
|
# 陈旧告警的时刻**相对当前时刻构造** (2026-09-14 加固): 取十分钟前, 不写死 09:35 ——
|
||||||
|
# 服务器收盘后跑测试碰不上, 但开发机在 09:35 之前跑, "今天 09:35" 落在未来、年龄算成负数,
|
||||||
|
# 陈旧判定与 age_min>=0 都会失败。十分钟前在任何时刻都已过 60 秒的陈旧线, 且年龄恒非负。
|
||||||
|
old = int((time.time() - 600) * 1000)
|
||||||
|
old_hhmm = time.strftime("%H:%M", time.localtime(old / 1000))
|
||||||
with _Patch() as p:
|
with _Patch() as p:
|
||||||
p(us, "_alert_max_age_sec", lambda: 60)
|
p(us, "_alert_max_age_sec", lambda: 60)
|
||||||
al = [("1-0", {"ts_code": "601126.SH", "source": "fund_flow_strength", "level": "HIGH",
|
al = [("1-0", {"ts_code": "601126.SH", "source": "fund_flow_strength", "level": "HIGH",
|
||||||
|
|
@ -139,7 +143,7 @@ def test_parse_sources():
|
||||||
"trigger_time": int(time.time() * 1000), "metadata": '{"direction": "down"}'})]
|
"trigger_time": int(time.time() * 1000), "metadata": '{"direction": "down"}'})]
|
||||||
arows, an, _ = us._bycode_alerts(_FakeRedis({"intraday_alerts:%s" % ymd: al}), ymd, "SH601126", 5000)
|
arows, an, _ = us._bycode_alerts(_FakeRedis({"intraday_alerts:%s" % ymd: al}), ymd, "SH601126", 5000)
|
||||||
assert an == 2 and len(arows) == 2
|
assert an == 2 and len(arows) == 2
|
||||||
stale = [r for r in arows if r["time"] == "09:35"][0]
|
stale = [r for r in arows if r["time"] == old_hhmm][0]
|
||||||
assert stale["stale"] is True and stale["cat"] and stale["age_min"] >= 0, stale
|
assert stale["stale"] is True and stale["cat"] and stale["age_min"] >= 0, stale
|
||||||
unknown = [r for r in arows if not r["stale"]][0]
|
unknown = [r for r in arows if not r["stale"]][0]
|
||||||
assert unknown["cat"] == "other" and unknown["direction"] == "看跌", unknown
|
assert unknown["cat"] == "other" and unknown["direction"] == "看跌", unknown
|
||||||
|
|
|
||||||
|
|
@ -283,6 +283,32 @@ def _():
|
||||||
assert _av2("看多", "看多", ls="逻辑存疑")["tier"] == "不建"
|
assert _av2("看多", "看多", ls="逻辑存疑")["tier"] == "不建"
|
||||||
|
|
||||||
|
|
||||||
|
# ================================================================ F 加固包 (2026-09-14)
|
||||||
|
@case("C 路由·弱表态开着 (weak_confirm 真): 方向看多但只有一方表态 → 交人")
|
||||||
|
def _():
|
||||||
|
r = cons.decide("看多", "无读数", "无读数", weak_confirm=True)
|
||||||
|
assert r["direction"] == "看多" and r["route"] == "交人"
|
||||||
|
|
||||||
|
|
||||||
|
@case("C 路由·弱表态关掉 (weak_confirm 假): 方向看多但只有一方表态 → 放行")
|
||||||
|
def _():
|
||||||
|
r = cons.decide("看多", "无读数", "无读数", weak_confirm=False)
|
||||||
|
assert r["direction"] == "看多" and r["route"] == "放行"
|
||||||
|
|
||||||
|
|
||||||
|
@case("D 装配·持仓行昨夜中性当日转多 → 择时票升看多 (decorate_positions 带 flip_map)")
|
||||||
|
def _():
|
||||||
|
from app.services import consensus_service as csv
|
||||||
|
from app.core import tradedays as _td
|
||||||
|
rows = [{"ts_code": "600000.SH"}]
|
||||||
|
csv.decorate_positions(
|
||||||
|
rows, nightly={"600000.SH": {"verdict": "WATCH", "trade_date": _td.ymd()}},
|
||||||
|
tech_states={}, company_reviews={"600000.SH": _cr(overall="好")},
|
||||||
|
flip_map={"600000.SH": "10:30"}, params=_ASM_P)
|
||||||
|
tmb = rows[0]["consensus_blocks"]["timing"]
|
||||||
|
assert tmb["stance"] == "看多" and tmb["intraday_flip_at"] == "10:30"
|
||||||
|
|
||||||
|
|
||||||
def main():
|
def main():
|
||||||
ok = 0
|
ok = 0
|
||||||
for name, fn in RESULTS:
|
for name, fn in RESULTS:
|
||||||
|
|
|
||||||
|
|
@ -411,6 +411,26 @@ def _():
|
||||||
assert abs(ps.RUNTIME_EXTRA["PMS_TECH_TIGHT_TRAIL_TARGET"][0] - 0.08) < 1e-9
|
assert abs(ps.RUNTIME_EXTRA["PMS_TECH_TIGHT_TRAIL_TARGET"][0] - 0.08) < 1e-9
|
||||||
|
|
||||||
|
|
||||||
|
# ================================================================ I 去重集清除条件 (2026-09-14 加固)
|
||||||
|
# 评审发现三: 原来「SAR 不是空就清」把缺读数也当翻多清掉, 会重复减仓。改成「明确为多才清」。
|
||||||
|
@case("I 去重集·映射里没这只票则保留 (缺读数不当翻多)")
|
||||||
|
def _():
|
||||||
|
from app.services import proposal_service as ps
|
||||||
|
assert ps._keep_tech_exit_done({"600000.SH"}, {}) == {"600000.SH"}
|
||||||
|
|
||||||
|
|
||||||
|
@case("I 去重集·SAR 明确为多则清除 (新一轮翻向)")
|
||||||
|
def _():
|
||||||
|
from app.services import proposal_service as ps
|
||||||
|
assert ps._keep_tech_exit_done({"600000.SH"}, {"600000.SH": {"sar_side": "多"}}) == set()
|
||||||
|
|
||||||
|
|
||||||
|
@case("I 去重集·SAR 明确为空则保留 (同一翻空不重复处理)")
|
||||||
|
def _():
|
||||||
|
from app.services import proposal_service as ps
|
||||||
|
assert ps._keep_tech_exit_done({"600000.SH"}, {"600000.SH": {"sar_side": "空"}}) == {"600000.SH"}
|
||||||
|
|
||||||
|
|
||||||
def main():
|
def main():
|
||||||
ok = 0
|
ok = 0
|
||||||
for name, fn in RESULTS:
|
for name, fn in RESULTS:
|
||||||
|
|
|
||||||
Loading…
Reference in New Issue