按评审修订系统自决包:清仓停买入侧三件、弱表态按强弱、真通过要求、逻辑存疑不送研判
到价自动清仓前改成与命令清仓同样的三件:撤方案、驳回该票待确认买入提议、撤在途买入指令 (原来只撤方案,已下发的网格买单会与清仓单对倒),且不再只在有活动方案时才做。 只有一方表态改按合议强弱打标记,弱表态开关关掉时不再按标准仓采纳。 自动采纳只认带应答体的真通过,动作不在研判范围的 PASS 一律交人。 decline/full 档逻辑存疑的新建仓不送研判直接放弃。到价清仓理由保留现价目标价股数。 页面加「系统已采纳」「系统已放弃」两种处置词。第三十四批加两例、装配自检加一例,共 900 例。 台账 015 补记,上午规格书附录己补真实仓三条。 Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
This commit is contained in:
parent
998ebb4ea8
commit
980235cb25
|
|
@ -1095,7 +1095,10 @@ def scan_open(*, candidates: list, params: dict, caps: dict, room_amt: float,
|
|||
mark_decide(cand, DK_VERDICT_WATCH if v == VERDICT_WATCH else DK_VERDICT_UNKNOWN)
|
||||
if params.get("open_route_by_logic") and logic_confirm_why(c.get("logic_state")):
|
||||
mark_decide(cand, DK_LOGIC_DOUBT)
|
||||
if con and con.get("route") == "交人":
|
||||
# 合议交人的两种, 以及**强弱为弱的一律按只有一方表态** (09-14 评审补): 弱表态开关
|
||||
# PMS_CONSENSUS_WEAK_CONFIRM 关掉时路由是放行而不是交人, 不看强弱就会按标准仓采纳 ——
|
||||
# 决定表里没有「放行·弱」这一格, 弱就是试探仓。
|
||||
if con and (con.get("route") == "交人" or con.get("strength") == "弱"):
|
||||
rr = con.get("route_reason") or ""
|
||||
mark_decide(cand, DK_FUND_BEAR_TECH_BULL if "基本面看空" in rr else DK_WEAK_CONSENSUS)
|
||||
out.append(cand)
|
||||
|
|
|
|||
|
|
@ -705,7 +705,8 @@ def disposition_snapshot(now=None) -> dict:
|
|||
for _k in _rejected_today_keys():
|
||||
_w = _rr.get(_k[0])
|
||||
skip[_k] = ("未通过合规检查:" + _w) if _w else "今天没通过合规检查,明天开盘会重新评估"
|
||||
for _k in _system_declined_today_keys(): # 系统自决今日已放弃 (明日重评)
|
||||
_sd_keys = _system_declined_today_keys() # 系统自决今日已放弃 (明日重评)
|
||||
for _k in _sd_keys:
|
||||
skip[_k] = "今日系统已判放弃,明日重评"
|
||||
for _k in _inflight_keys(): # 在途; 前端多半已按指令/提议标成「已下单/待你确认」
|
||||
skip[_k] = "系统正在处理这只票(见『待确认提议』或『当日指令』)"
|
||||
|
|
@ -727,6 +728,10 @@ def disposition_snapshot(now=None) -> dict:
|
|||
# 合议判观察的候选带 disp=wait_tech/wait_confirm, 其余不产出的按 deny。
|
||||
res["by_code"].setdefault(
|
||||
code, {"disp": s.get("disp") or "deny", "why": s.get("why") or "未产出候选"})
|
||||
# 系统自决今日放弃的候选打处置词 sys_decline (09-14 评审补): 页面显示「系统已放弃」而不是「未建仓」。
|
||||
for (_code, _act) in _sd_keys:
|
||||
if _act == ae.A_OPEN and _code in res["by_code"]:
|
||||
res["by_code"][_code]["disp"] = "sys_decline"
|
||||
# 候选栏三小块 (2026-09-14 页面收尾包): 每只候选的合议摘要, 从 consensus_seen 来。
|
||||
# 基本面立场与质地档 / 技术面立场与相位 / 合议方向与路由。挂到对应代码的 by_code 行上。
|
||||
for rec in (sink.get("consensus_seen") or []):
|
||||
|
|
@ -787,7 +792,14 @@ def _route_one(c, view, params, stock_params, brake_active, now, dry_run, out,
|
|||
|
||||
# ---- 二级: 研判闸 (补足/加仓/补仓/新建仓; 减持不送研判) ----
|
||||
verdict = {"verdict": judge.PASS, "reason": "", "degraded": False}
|
||||
if c.get("judge_required"):
|
||||
# 系统自决 decline/full 档: 逻辑存疑的新建仓不送研判 (09-14 评审补) —— 自决必然放弃它, 送研判
|
||||
# 只是白占一次调用与本轮时间预算。record 档不跳: 那一档要照旧进人工队列, 提议上得有真研判结论。
|
||||
_sd_mode = str((params or {}).get("self_decide") or "off").strip()
|
||||
if (is_open and c.get("decide_kind") == ae.DK_LOGIC_DOUBT
|
||||
and _sd_mode in ("decline", "full")):
|
||||
verdict = {"verdict": "SKIPPED", "reason": "逻辑存疑,不送研判(系统自决直接放弃)",
|
||||
"degraded": False, "raw": None, "confidence": None}
|
||||
elif c.get("judge_required"):
|
||||
if not _judge_budget_left(deadline):
|
||||
# 本轮研判时间用尽 —— 整条跳过, 下一跳重来。
|
||||
# **不当成「研判不可用」入人工队列**: 那会在自动档位下凭空造出一个人工确认队列,
|
||||
|
|
@ -955,7 +967,7 @@ def _route_one(c, view, params, stock_params, brake_active, now, dry_run, out,
|
|||
|
||||
if auto_exec:
|
||||
sd_accept = sd_decision == "accept"
|
||||
strat_cancelled = []
|
||||
strat_cancelled, buys_cancelled = [], []
|
||||
if sd_accept and side == "sell":
|
||||
# 目标价自动清仓两道前置 (2026-09-14): 按当前可卖量夹紧; 该票挂着方案先撤,
|
||||
# 免得清仓单与网格买入腿对倒 (附录乙那处冲突)。可卖量为零则不落, 明日重评。
|
||||
|
|
@ -966,25 +978,34 @@ def _route_one(c, view, params, stock_params, brake_active, now, dry_run, out,
|
|||
"why": "系统到价自动清仓: 当前可卖量为零, 本轮不清 (明日重评)"})
|
||||
return
|
||||
c["qty"] = clamped
|
||||
# 停买入侧三件 (09-14 评审补): 撤方案、驳回买入提议、撤在途买单 —— 不只在有活动方案时,
|
||||
# 一张在途的补仓/网格买单同样会与清仓单对倒。撤不成只记日志, 清仓照落。
|
||||
try:
|
||||
if code in (pms_repo.active_strategy_codes() or set()):
|
||||
r = strategy_service.cancel_by_code(
|
||||
code, reason="系统到价自动清仓: 先撤该票交易方案, 免得清仓单与方案买入腿对倒")
|
||||
code, reason="系统到价自动清仓: 先撤该票交易方案与在途买单, 免得清仓单与买入腿对倒")
|
||||
strat_cancelled = r.get("cancelled") or []
|
||||
buys_cancelled = r.get("instructions") or []
|
||||
if r.get("errors"):
|
||||
logger.warning("[自决清仓] 停买入侧有未成项 %s: %s", code, r["errors"])
|
||||
except Exception as e: # noqa: BLE001
|
||||
logger.warning("[自决清仓] 撤方案失败 %s (清仓照落): %s", code, e)
|
||||
logger.warning("[自决清仓] 停买入侧失败 %s (清仓照落): %s", code, e)
|
||||
elif sd_accept and is_open:
|
||||
# 试探仓类采纳: 把数量重算成建议档 (弱基本面那类还带上紧止盈标记 advice.tight_trail)。
|
||||
_apply_advice_tier(c, c.setdefault("hard_numbers", {}), verdict,
|
||||
force_trial=(sd.get("tier") == "trial"))
|
||||
if sd_accept:
|
||||
c["reason"] = sd.get("why") or c["reason"] # 指令与账本理由一致
|
||||
# 指令与账本理由一致。到价清仓把原句 (现价/目标价/股数) 接在后面, 复盘看得见到的是什么价 (09-14 评审补)。
|
||||
_why = sd.get("why") or c["reason"]
|
||||
if sd.get("rule") == ae.DK_TARGET_PRICE and c.get("reason"):
|
||||
_why = f"{_why};{c['reason']}"
|
||||
c["reason"] = _why
|
||||
iid = _make_instruction(c, price, now)
|
||||
if sd_accept:
|
||||
reason = sd.get("why") or c["reason"]
|
||||
reason = c["reason"]
|
||||
_dc = params.get("_sd_base_count", 0) + out.get("_sd_accepts", 0)
|
||||
hard = {**(c.get("hard_numbers") or {}),
|
||||
"self_decide": _self_decide_hard(c, verdict, sd, params, strat_cancelled, _dc)}
|
||||
"self_decide": _self_decide_hard(c, verdict, sd, params, strat_cancelled, _dc,
|
||||
buys_cancelled=buys_cancelled)}
|
||||
else:
|
||||
reason = verdict.get("reason") or c["reason"]
|
||||
if auto_why:
|
||||
|
|
@ -1228,6 +1249,12 @@ def _self_decide(c, verdict, params, today_auto_count) -> dict:
|
|||
# 三, 逻辑存疑: 自动放弃, 评审方 system
|
||||
if dk == ae.DK_LOGIC_DOUBT:
|
||||
return _mode({"decision": "decline", "why": "研究证据走弱(逻辑存疑),自动放弃"})
|
||||
# 四, 真通过 (规格 §3.5): 只认带应答体的 PASS。动作不在研判范围时研判直接回 PASS 且没有应答体,
|
||||
# 那不是决策系统的结论, 不能据此自动入场 —— 交人 (09-14 评审补; 把 OPEN 从研判范围里拿掉时
|
||||
# 全部新建仓都会落到这里, 方向是保守)。
|
||||
if not isinstance(verdict.get("raw"), dict):
|
||||
return _mode({"decision": "confirm",
|
||||
"why": "研判没有真正应答(动作不在研判范围或无应答体),交人裁决"})
|
||||
# 深亏补仓: 研判必答「杀逻辑还是杀情绪」, 契约规定杀逻辑或说不清一律 REJECT (REJECT 已在前面
|
||||
# 早退当放弃)。所以走到这里的 PASS 即杀情绪: 把握度不低于 60 自动补, 否则放弃。
|
||||
if is_dca_deep:
|
||||
|
|
@ -1263,8 +1290,10 @@ def _sd_consensus_brief(c) -> dict:
|
|||
"route": con.get("route")}
|
||||
|
||||
|
||||
def _self_decide_hard(c, verdict, sd, params, strat_cancelled, daily_count) -> dict:
|
||||
"""账本硬数字的 self_decide 块 (数据契约 §3.8): 决定种类、档、仓位档、合议三项、研判、当日计数、撤掉的方案。"""
|
||||
def _self_decide_hard(c, verdict, sd, params, strat_cancelled, daily_count, *,
|
||||
buys_cancelled=None) -> dict:
|
||||
"""账本硬数字的 self_decide 块 (数据契约 §3.8): 决定种类、档、仓位档、合议三项、研判、当日计数、
|
||||
撤掉的方案与在途买单。"""
|
||||
tier_cn = {"trial": "试探仓", "standard": "标准仓", "half": "减半仓"}.get(sd.get("tier"))
|
||||
return {"rule": sd.get("rule"), "mode": str((params or {}).get("self_decide") or "off"),
|
||||
"tier": tier_cn or sd.get("tier"),
|
||||
|
|
@ -1272,7 +1301,8 @@ def _self_decide_hard(c, verdict, sd, params, strat_cancelled, daily_count) -> d
|
|||
"judge": {"verdict": verdict.get("verdict"), "confidence": verdict.get("confidence")},
|
||||
"daily_count": daily_count,
|
||||
"daily_max": int((params or {}).get("self_decide_daily_max") or 0),
|
||||
"strategy_cancelled": list(strat_cancelled or [])}
|
||||
"strategy_cancelled": list(strat_cancelled or []),
|
||||
"buys_cancelled": list(buys_cancelled or [])}
|
||||
|
||||
|
||||
def _sd_decline_ledger(code, action, c, verdict, price, sd) -> None:
|
||||
|
|
|
|||
|
|
@ -138,19 +138,26 @@ def set_status(strategy_id: str, status: str, by: str = "user") -> dict:
|
|||
|
||||
|
||||
def cancel_by_code(ts_code: str, reason: str = "") -> dict:
|
||||
"""撤下某只票当前 ACTIVE 的全部交易方案 (2026-09-14 系统自决包)。
|
||||
"""停掉某只票的买入侧 (2026-09-14 系统自决包; 同日评审补齐): 与命令清仓那条
|
||||
command_service._stop_buyside_for_exit 同样的三件事 —— 撤该票 ACTIVE/PAUSED 的全部交易方案、
|
||||
驳回该票待确认的买入提议、撤该票当前在途的买入指令。
|
||||
|
||||
系统自决到价自动清仓前调它: 该票若挂着网格, 网格的买入腿会与清仓单对倒 (附录乙那处冲突),
|
||||
所以落清仓指令前先撤方案。与命令服务清仓覆盖冲突那段 (command_service) 同一手法, 抽到这里
|
||||
供两处复用。返回撤掉的方案编号列表; 读库或撤下失败只记进 errors, 不抛 —— 撤不成也不该拦住清仓。
|
||||
系统自决到价自动清仓前调它: 该票若挂着网格, 网格的买入腿会与清仓单对倒 (规格附录乙那处冲突)。
|
||||
只改方案状态不够 —— 已经下发到券商的那张网格买单还挂着继续成交, 命令清仓那条早就为此补过
|
||||
「撤在途买单」这一层 (2026-09-08), 这里照做。三步各自幂等、失败只记进 errors 不抛 ——
|
||||
撤不成也不该拦住清仓 (清仓单落表后由执行器照卖; 下游没撤成的买单单独列出来别吞)。
|
||||
返回 {ok, ts_code, cancelled: 方案编号, proposals: 驳回的提议, instructions: 撤掉的在途买单, errors}。
|
||||
"""
|
||||
out = {"ok": True, "ts_code": ts_code, "cancelled": [], "errors": []}
|
||||
out = {"ok": True, "ts_code": ts_code, "cancelled": [], "proposals": [], "instructions": [],
|
||||
"errors": []}
|
||||
why = reason or "系统到价自动清仓: 先撤该票交易方案与在途买单, 免得清仓单与买入腿对倒"
|
||||
# 一, 撤方案 (ACTIVE 与 PAUSED 都撤: 暂停的网格恢复后照样买)
|
||||
try:
|
||||
actives = pms_repo.list_strategies(ts_code=ts_code, statuses=["ACTIVE"], limit=20)
|
||||
actives = pms_repo.list_strategies(ts_code=ts_code, statuses=["ACTIVE", "PAUSED"], limit=20)
|
||||
except Exception as e: # noqa: BLE001
|
||||
actives = []
|
||||
out["ok"] = False
|
||||
out["errors"].append(f"读策略失败: {type(e).__name__}: {e}")
|
||||
return out
|
||||
for s in actives:
|
||||
try:
|
||||
r = set_status(s["strategy_id"], "CANCELLED", by="system") or {}
|
||||
|
|
@ -160,8 +167,7 @@ def cancel_by_code(ts_code: str, reason: str = "") -> dict:
|
|||
pms_repo.insert_ledger(
|
||||
ts_code=ts_code, action="CLEANUP", arbiter="system", verdict="PASS",
|
||||
price_at=0, ref_id=s["strategy_id"],
|
||||
hard_numbers={"strategy_id": s["strategy_id"]},
|
||||
reason=(reason or "系统到价自动清仓: 先撤该票交易方案, 免得清仓单与方案买入腿对倒")[:500])
|
||||
hard_numbers={"strategy_id": s["strategy_id"]}, reason=why[:500])
|
||||
except Exception as e: # noqa: BLE001
|
||||
logger.warning("[自决清仓] 撤方案留痕失败 %s: %s", ts_code, e)
|
||||
else:
|
||||
|
|
@ -170,6 +176,46 @@ def cancel_by_code(ts_code: str, reason: str = "") -> dict:
|
|||
except Exception as e: # noqa: BLE001
|
||||
out["ok"] = False
|
||||
out["errors"].append(f"{s.get('strategy_id')} 撤下失败: {type(e).__name__}: {e}")
|
||||
# 二, 驳回该票待确认的买入提议 (清仓了还等人点头买, 是自相矛盾)
|
||||
from app.services import command_service, executor
|
||||
try:
|
||||
props = [p for p in pms_repo.list_proposals(statuses=("WAIT_USER",), limit=500)
|
||||
if p.get("ts_code") == ts_code and p.get("action") in command_service.BUILD_ACTIONS]
|
||||
except Exception as e: # noqa: BLE001
|
||||
props = []
|
||||
out["ok"] = False
|
||||
out["errors"].append(f"读提议失败: {type(e).__name__}: {e}")
|
||||
for p in props:
|
||||
try:
|
||||
if pms_repo.decide_proposal(p["proposal_id"], "DECLINED"):
|
||||
out["proposals"].append(p["proposal_id"])
|
||||
except Exception as e: # noqa: BLE001
|
||||
out["ok"] = False
|
||||
out["errors"].append(f"驳回买入提议 {p.get('proposal_id')} 失败: {type(e).__name__}: {e}")
|
||||
# 三, 撤该票当前在途的买入指令 (已下发到券商的网格/补仓买单, 撤了方案它还挂着)
|
||||
try:
|
||||
live_buys = [i for i in pms_repo.list_instructions(
|
||||
statuses=list(command_service.LIVE_INSTR), side="buy", limit=500)
|
||||
if i.get("ts_code") == ts_code]
|
||||
except Exception as e: # noqa: BLE001
|
||||
live_buys = []
|
||||
out["ok"] = False
|
||||
out["errors"].append(f"读在途买单失败: {type(e).__name__}: {e}")
|
||||
for i in live_buys:
|
||||
iid = i.get("instruction_id")
|
||||
try:
|
||||
r = executor.cancel_instruction(iid, reason=why) or {}
|
||||
if r.get("ok"):
|
||||
out["instructions"].append(iid)
|
||||
else:
|
||||
out["ok"] = False
|
||||
out["errors"].append(f"撤在途买单 {iid} 未成: {r.get('error') or r.get('message')}")
|
||||
except Exception as e: # noqa: BLE001
|
||||
out["ok"] = False
|
||||
out["errors"].append(f"撤在途买单 {iid} 失败: {type(e).__name__}: {e}")
|
||||
if out["cancelled"] or out["proposals"] or out["instructions"]:
|
||||
logger.warning("[自决清仓] %s 停买入侧: 撤方案 %s, 驳回买入提议 %s, 撤在途买单 %s",
|
||||
ts_code, out["cancelled"], out["proposals"], out["instructions"])
|
||||
return out
|
||||
|
||||
|
||||
|
|
|
|||
|
|
@ -2937,7 +2937,11 @@ createApp({
|
|||
function dispOf(code) {
|
||||
const inst = (instructions.value || []).find(i => i.ts_code === code && i.action === 'OPEN'
|
||||
&& (_LIVE_INS.includes(i.status) || _sameDayIns(i))); // 旧的终态单不算「已下单」(2026-08-28)
|
||||
if (inst) return { cls: 'done', label: '已下单', why: '在『当日指令』(' + tx('insStatus', inst.status) + ')' };
|
||||
if (inst) {
|
||||
// 系统自决自动采纳的指令 (2026-09-14): 进度里 auto 为真且理由以「系统采纳」开头, 显示「系统已采纳」。
|
||||
const autoAcc = inst.progress && inst.progress.auto && String(inst.progress.reason || '').startsWith('系统采纳');
|
||||
return { cls: 'done', label: autoAcc ? '系统已采纳' : '已下单', why: '在『当日指令』(' + tx('insStatus', inst.status) + ')' };
|
||||
}
|
||||
const prop = (proposals.value || []).find(p => p.ts_code === code && p.action === 'OPEN');
|
||||
if (prop) return { cls: 'queue', label: '待确认', why: '已生成提议,在『待确认提议』等人工裁决' };
|
||||
const d = (openScan.value.by_code || {})[code];
|
||||
|
|
@ -2950,6 +2954,8 @@ createApp({
|
|||
if (d.disp === 'wait_confirm') return { cls: 'wait', label: '等盘中确认', why: d.why };
|
||||
// 系统自决 (2026-09-14): 方向看多但择时看空, 进观察等择时转多, 也是「在盯」不是「被否决」。
|
||||
if (d.disp === 'wait_timing') return { cls: 'wait', label: '择时待转多', why: d.why };
|
||||
// 系统自决今日已放弃 (2026-09-14): 明日重评, 与「未建仓」分开显示。
|
||||
if (d.disp === 'sys_decline') return { cls: 'deny', label: '系统已放弃', why: d.why };
|
||||
return { cls: 'deny', label: '未建仓', why: d.why };
|
||||
}
|
||||
return { cls: 'wait', label: '候选观察', why: '' };
|
||||
|
|
|
|||
|
|
@ -514,6 +514,9 @@ consensus.decide 加参数 weak_confirm,默认真。_route 里「只有一方
|
|||
| PMS_TECH_ENABLED | true | 前提是接口根地址指向 188 的决策系统 |
|
||||
| PMS_FUND_REQUIRED | true | 无评析不建仓 |
|
||||
| PMS_TOTAL_SCALE | 按真实资金 | 与账户实际资金要对得上 |
|
||||
| PMS_SELF_DECIDE | off | 系统自决关,全部交人(2026-09-14 下午加) |
|
||||
| PMS_SELF_DECIDE_TRIAL | false | 试探仓类不自动采纳(2026-09-14 下午加) |
|
||||
| PMS_SELF_DECIDE_DAILY_MAX | 1 | 每日自动新建仓上限一只(2026-09-14 下午加) |
|
||||
|
||||
### 脚本
|
||||
|
||||
|
|
|
|||
|
|
@ -231,3 +231,5 @@
|
|||
**台账线(只作提示,调阈值先回台账)。** 研判不可用占比应低于两成,超过要查决策系统那侧。上线头几天自动采纳的股数应是试探仓那批,不是标准仓。
|
||||
|
||||
**复核日期。** 部署后的下一个交易日盘中先只读试算一次,看 executed 里有没有 route 为 auto、why 含「系统采纳」的条目,或 rejected 里有没有评审方 system 的条目。收盘后跑 make consensus-review 看第五张表系统自决逐条、第六张表研判不可用占比。
|
||||
|
||||
**评审补记(2026-09-14 下午,同日评审后修订)。** 四处补齐,不改上面的决定。一,到价自动清仓前停买入侧改成与命令清仓同样的三件:撤方案、驳回该票待确认的买入提议、撤该票在途的买入指令;原来只撤方案,已下发的网格买单会与清仓单对倒。不再只在有活动方案时才做,一张在途补仓买单同样要撤。二,只有一方表态改按合议强弱打标记:强弱为弱一律按试探仓,不看路由;原来只在路由为交人时打,弱表态开关 PMS_CONSENSUS_WEAK_CONFIRM 关掉后会按标准仓采纳。三,自动采纳只认带应答体的真通过:动作不在研判范围时研判直接回通过且没有应答体,这种一律交人。四,decline 与 full 档下逻辑存疑的新建仓不送研判,直接放弃,省一次研判调用与时间预算;record 档照旧送研判。页面加两种处置词:系统已采纳、系统已放弃。
|
||||
|
|
|
|||
|
|
@ -106,7 +106,8 @@
|
|||
与拼句 (预设码/其他必填补充/旧自由文本兼容/码串表报错)/决策核心 _self_decide
|
||||
(off 不管、放行强采标准仓、低把握转试探仓、只有一方表态与异议只在技术面按 TRIAL
|
||||
开关、四类交人、逻辑存疑放弃、深亏补仓三分支、目标价直接采纳、每日上限、record/decline
|
||||
降级)/mark_decide 优先级/eval_dca 深档打 decide_kind/三参数登记与范围/两个 repo 查询 (22 例)
|
||||
降级)/mark_decide 优先级/eval_dca 深档打 decide_kind/三参数登记与范围/两个 repo 查询
|
||||
+ 评审补: 真通过只认带应答体的 PASS、弱表态按强弱打标记 (24 例)
|
||||
test_page_copy_guard.py 页面文案守卫·口语化 (2026-09-14 页面专业化包): 禁用词清单
|
||||
(第一二人称/拟人口语/生造动词) 扫显示文本, 剥三种注释、成对豁免、
|
||||
网址不误截、自检样例 (1 例)
|
||||
|
|
@ -114,14 +115,15 @@
|
|||
直接印到页面上 / 判据码显示前必须剥前缀 / 不许把整个对象
|
||||
打给交易员看 / 翻译兜底不许让英文码单独当句子 (1 例)
|
||||
test_wiring.py 装配自检: 服务层→核心→落表 全链路 (内存桩) +
|
||||
目标价到价自决关时入队、自决开时自动清仓按可卖量夹紧 +
|
||||
用户设的止损价与目标价单独成列显示 (71 例)
|
||||
共 894 例
|
||||
目标价到价自决关时入队、自决开时自动清仓按可卖量夹紧、挂着方案且有在途买单时先撤再清 +
|
||||
用户设的止损价与目标价单独成列显示 (72 例)
|
||||
共 900 例
|
||||
(总数按实跑逐批相加校正过两次: 曾写 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;
|
||||
09-14 观察读数包第三十一批二十例到 840; 加固包给第二十八批加三例、第三十批加三例到 846; 评审修订第三十一批加一例到 847; 09-14 盘中确认包第三十二批十九例到 866; 页面收尾包第三十三批五例到 871;
|
||||
09-14 系统自决包第三十四批二十二例、test_wiring 目标价自动清仓加一例到 894;
|
||||
09-14 选股打分包 §5.3 来源字段给第七批加两例到 896; 页面专业化包加文案守卫一例, 现为 897)
|
||||
09-14 选股打分包 §5.3 来源字段给第七批加两例到 896; 页面专业化包加文案守卫一例到 897;
|
||||
09-14 评审补第三十四批两例、test_wiring 撤方案与在途买单一例, 现为 900)
|
||||
任一子集失败即整体失败 (退出码 1)。
|
||||
|
||||
哨兵位置清单 (2026-09-03 抄录; 改了对应的东西就得来这些地方改断言, 断言不动就是漏了):
|
||||
|
|
|
|||
|
|
@ -33,8 +33,12 @@ def case(name):
|
|||
return deco
|
||||
|
||||
|
||||
def _v(verdict=judge.PASS, conf=78, degraded=False):
|
||||
return {"verdict": verdict, "confidence": conf, "degraded": degraded, "reason": ""}
|
||||
def _v(verdict=judge.PASS, conf=78, degraded=False, raw="auto"):
|
||||
"""研判结果桩。raw 默认给一个字典 (带应答体的真通过); 传 None 模拟「动作不在研判范围」那种
|
||||
没有应答体的 PASS。"""
|
||||
r = {"verdict": verdict, "confidence": conf, "degraded": degraded, "reason": ""}
|
||||
r["raw"] = {"verdict": verdict} if raw == "auto" else raw
|
||||
return r
|
||||
|
||||
|
||||
def _cand(action=ae.A_OPEN, decide_kind=None, consensus=True, risk=None):
|
||||
|
|
@ -150,6 +154,20 @@ def _():
|
|||
assert _self_decide(_cand(decide_kind=ae.DK_VERDICT_UNKNOWN), _v(), fp, 0)["decision"] == "confirm"
|
||||
|
||||
|
||||
@case("C 真通过·PASS 但没有应答体 (动作不在研判范围) → 交人, 不按试探仓采纳")
|
||||
def _():
|
||||
fp = {"self_decide": "full", "self_decide_trial": True}
|
||||
d = _self_decide(_cand(), _v(conf=None, raw=None), fp, 0)
|
||||
assert d["decision"] == "confirm" and "应答" in d["why"], d
|
||||
# 深亏补仓同样只认带应答体的 PASS
|
||||
dca = _cand(action=ae.A_DCA, decide_kind=ae.DK_DCA_DEEP, consensus=False)
|
||||
assert _self_decide(dca, _v(conf=70, raw=None), fp, 0)["decision"] == "confirm"
|
||||
# 逻辑存疑排在真通过之前: 不送研判 (raw 为空) 也照样放弃
|
||||
ld = _cand(decide_kind=ae.DK_LOGIC_DOUBT)
|
||||
assert _self_decide(ld, {"verdict": "SKIPPED", "raw": None, "degraded": False,
|
||||
"confidence": None, "reason": ""}, fp, 0)["decision"] == "decline"
|
||||
|
||||
|
||||
@case("C 研判不可用带 judge_unavail 标记 (研判不可用占比复核用)")
|
||||
def _():
|
||||
d = _self_decide(_cand(), _v(degraded=True), {"self_decide": "full"}, 0)
|
||||
|
|
@ -233,6 +251,51 @@ def _():
|
|||
assert on and on.get("decide_kind") == ae.DK_DCA_DEEP, on
|
||||
|
||||
|
||||
def _so_params(**kw):
|
||||
p = {"scale": 2_000_000.0, "stock_target_default": 0.06,
|
||||
"batch_split": (0.5, 0.25, 0.25), "min_lot_merge": True,
|
||||
"consensus_route": True, "self_decide": "full"}
|
||||
p.update(kw)
|
||||
return p
|
||||
|
||||
|
||||
def _so_caps():
|
||||
return {"scale": 2_000_000.0, "portfolio_cap": 0.70, "stock_cap": 0.08,
|
||||
"max_names": 20, "portfolio_mv": 0.0, "names_count": 0,
|
||||
"stock_mv": 0.0, "is_new_name": False, "sector": None,
|
||||
"sector_names": 0, "sector_mv": 0.0, "sector_names_map": {}, "sector_mv_map": {},
|
||||
"sector_max_names": 4, "sector_max_ratio": 0.40, "cash_reserve": 0.0,
|
||||
"sector_source_ready": True, "cash_avail": None, "cash_source": "estimate"}
|
||||
|
||||
|
||||
def _so_cand(route, strength, reason):
|
||||
con = {"route": route, "route_reason": reason, "direction": "看多", "strength": strength,
|
||||
"votes": {"fund": "中性", "tech": "看多", "timing": "无读数"}, "reason": "一票 → 看多"}
|
||||
return {"ts_code": "600000.SH", "price": 10.0, "score": 100.0, "sector": None, "theme": None,
|
||||
"tier": "强传导", "upside": 0.3, "heat": 0.5, "rank": 1, "bucket": "main",
|
||||
"src": "plan_api", "consensus": con,
|
||||
"consensus_blocks": {"fund": {"stance": "中性"}, "tech": {"stance": "看多"},
|
||||
"timing": {"stance": "无读数"}, "consensus": con}}
|
||||
|
||||
|
||||
@case("D scan_open·弱表态开关关掉 (路由放行但强弱为弱) → 仍打 weak_consensus; 关掉自决不打")
|
||||
def _():
|
||||
weak_pass = _so_cand("放行", "弱", "方向看多但只有一方表态(弱表态放行)")
|
||||
r = ae.scan_open(candidates=[dict(weak_pass)], params=_so_params(), caps=_so_caps(),
|
||||
room_amt=1e6, slots=10, skip={})
|
||||
assert r["candidates"], r["skipped"]
|
||||
assert r["candidates"][0].get("decide_kind") == ae.DK_WEAK_CONSENSUS, r["candidates"][0]
|
||||
# 弱表态开关开着 (路由交人) 同样是 weak_consensus
|
||||
weak_confirm = _so_cand("交人", "弱", "方向看多但只有一方表态")
|
||||
r2 = ae.scan_open(candidates=[dict(weak_confirm)], params=_so_params(), caps=_so_caps(),
|
||||
room_amt=1e6, slots=10, skip={})
|
||||
assert r2["candidates"][0].get("decide_kind") == ae.DK_WEAK_CONSENSUS, r2["candidates"][0]
|
||||
# 自决 off: 不打 decide_kind (逐字回旧)
|
||||
r3 = ae.scan_open(candidates=[dict(weak_pass)], params=_so_params(self_decide="off"),
|
||||
caps=_so_caps(), room_amt=1e6, slots=10, skip={})
|
||||
assert "decide_kind" not in r3["candidates"][0], r3["candidates"][0]
|
||||
|
||||
|
||||
@case("D 参数登记·三键在 RUNTIME_EXTRA, 合法值与范围校验")
|
||||
def _():
|
||||
for k in ("PMS_SELF_DECIDE", "PMS_SELF_DECIDE_TRIAL", "PMS_SELF_DECIDE_DAILY_MAX"):
|
||||
|
|
|
|||
|
|
@ -1819,6 +1819,44 @@ def _():
|
|||
assert led and "目标价" in (led[0]["reason"] or ""), led
|
||||
|
||||
|
||||
@case("自主提议·目标价到价自决开: 挂着方案且有在途买单时, 先撤方案、撤在途买单再清仓")
|
||||
def _():
|
||||
from app.services import proposal_service as ps
|
||||
# 09-14 评审补: 只撤方案不撤在途买单, 已下发的网格买单会与清仓单对倒。三件事要一起做。
|
||||
fake = install_fakes(
|
||||
prices={"600000.SH": 13.0}, high5={"600000.SH": 19.5},
|
||||
params={"PMS_TOTAL_SCALE": "2000000", "PMS_AUTONOMY": "full",
|
||||
"PMS_SELF_DECIDE": "full", "PMS_SELF_DECIDE_TRIAL": "1"},
|
||||
positions=[{"ts_code": "600000.SH", "total_qty": 6050, "avail_qty": 6050,
|
||||
"base_qty": 6000, "avg_cost": 10.0, "cushion_peak": 0.0,
|
||||
"target_pct": 0.06}])
|
||||
fake.insert_strategy(strategy_id="S_GRID_600000", ts_code="600000.SH", stype="GRID",
|
||||
autonomy="auto", params={}, state={}, status="ACTIVE")
|
||||
fake.insert_instruction(instruction_id="I_BUY_600000", origin_type="strategy",
|
||||
origin_id="S_GRID_600000", ts_code="600000.SH", action="ADD",
|
||||
side="buy", qty=500, status="DISPATCHED", progress={"children": []})
|
||||
from app.services import command_service as csvc
|
||||
restore = _stub_sp(csvc, {"600000.SH": {"target_price": 12.5}})
|
||||
try:
|
||||
r = ps.scan_and_route()
|
||||
finally:
|
||||
restore()
|
||||
assert r["ok"], r
|
||||
assert any(x["action"] == "EXIT" for x in r["executed"]), (r["executed"], r["queued"], r["skipped"])
|
||||
# 方案撤了, 在途买单撤了, 清仓单落了
|
||||
assert fake.strategies["S_GRID_600000"]["status"] == "CANCELLED", fake.strategies["S_GRID_600000"]
|
||||
assert fake.instructions["I_BUY_600000"]["status"] == "CANCELLED", fake.instructions["I_BUY_600000"]
|
||||
exits = [i for i in fake.instructions.values() if i["action"] == "EXIT"]
|
||||
assert exits and exits[0]["qty"] == 6050, exits
|
||||
# 账本 self_decide 块记下撤了什么
|
||||
led = [x for x in fake.ledger if x["action"] == "EXIT" and x["verdict"] == "PASS"]
|
||||
assert led, fake.ledger
|
||||
sd = (led[0].get("hard_numbers") or {}).get("self_decide") or {}
|
||||
assert sd.get("strategy_cancelled") == ["S_GRID_600000"], sd
|
||||
assert sd.get("buys_cancelled") == ["I_BUY_600000"], sd
|
||||
assert "目标价" in (led[0]["reason"] or "") and "12.5" in (led[0]["reason"] or ""), led[0]["reason"]
|
||||
|
||||
|
||||
@case("自主提议·目标价未到 / 没设 一律不产出 (拿不到价绝不折成到价)")
|
||||
def _():
|
||||
from app.services import proposal_service as ps
|
||||
|
|
|
|||
Loading…
Reference in New Issue