页面专业化包·文案与守卫:全页口语化文案改交易术语(台账 015)
板块标题(等我拍板→待确认提议、我的持仓→持仓、今日在办→当日指令、今天发生了什么→评审账本、 我的策略→交易方案、系统在盯的候选→建仓候选等)、处置词、六个合成句、T 表六组、后端随行句与参数 说明全部改成交易术语,去第一二人称与拟人口语。新增 test_page_copy_guard.py 守卫(禁用词扫显示文本、 剥三种注释、成对豁免、网址不误截),登记进 run_tests。全量 897 例 ALL SUITES PASS。 Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
This commit is contained in:
parent
de06c778cb
commit
c94787acd1
|
|
@ -154,7 +154,7 @@ SRC_TARGET_PRICE = "target_price" # 用户设的目标价到价 —— 必
|
||||||
SRC_TECH_EXIT = "tech_exit"
|
SRC_TECH_EXIT = "tech_exit"
|
||||||
|
|
||||||
WHY_RESEARCH_WEAK_CONFIRM = "研究证据走弱触发的减持,必须交人裁决,不自动卖出"
|
WHY_RESEARCH_WEAK_CONFIRM = "研究证据走弱触发的减持,必须交人裁决,不自动卖出"
|
||||||
WHY_TARGET_PRICE_CONFIRM = "你设的目标价到价,卖不卖由你决定,系统不自动卖出"
|
WHY_TARGET_PRICE_CONFIRM = "目标价到价,需人工确认,系统不自动卖出"
|
||||||
|
|
||||||
# 必定强制入人工队列的来源: {来源: 交给人的原因}。
|
# 必定强制入人工队列的来源: {来源: 交给人的原因}。
|
||||||
# 写成一份名单而不是一个 if, 是为了将来再出现「不许自动执行」的来源时加一行就够,
|
# 写成一份名单而不是一个 if, 是为了将来再出现「不许自动执行」的来源时加一行就够,
|
||||||
|
|
@ -435,7 +435,7 @@ def eval_dca(p: dict, params: dict, mkt: dict):
|
||||||
c = _cand(p, A_DCA, BUY, qty,
|
c = _cand(p, A_DCA, BUY, qty,
|
||||||
f"补仓评估: 浮亏 {_f(cushion):.2%} 触及 {depth:.0%} 档 (第 {stage} 档), "
|
f"补仓评估: 浮亏 {_f(cushion):.2%} 触及 {depth:.0%} 档 (第 {stage} 档), "
|
||||||
f"拟补 {qty} 股 (≤底仓 {max_ratio:.0%})"
|
f"拟补 {qty} 股 (≤底仓 {max_ratio:.0%})"
|
||||||
+ ("; **深档: 必须用户确认**, 研判须回答杀逻辑还是杀情绪" if is_deep else ""),
|
+ ("; **深档补仓: 需人工确认**, 研判须回答杀逻辑还是杀情绪" if is_deep else ""),
|
||||||
{"cushion_pct": cushion, "stage": stage, "trigger": depth,
|
{"cushion_pct": cushion, "stage": stage, "trigger": depth,
|
||||||
"base_qty": base_qty, "price": price, "stop_ref": p.get("stop_ref")},
|
"base_qty": base_qty, "price": price, "stop_ref": p.get("stop_ref")},
|
||||||
confirm=is_deep)
|
confirm=is_deep)
|
||||||
|
|
@ -510,7 +510,7 @@ def eval_target(p: dict, params: dict = None, mkt: dict = None):
|
||||||
note = "" if qty == total else f" (总持仓 {total} 股, 今天只能卖 T+1 可卖的 {qty} 股)"
|
note = "" if qty == total else f" (总持仓 {total} 股, 今天只能卖 T+1 可卖的 {qty} 股)"
|
||||||
c = _cand(p, A_EXIT, SELL, qty,
|
c = _cand(p, A_EXIT, SELL, qty,
|
||||||
f"目标价到价: 现价 {price} 已到你设的目标价 {target}, "
|
f"目标价到价: 现价 {price} 已到你设的目标价 {target}, "
|
||||||
f"拟清仓 {qty} 股 (含零股){note}; 卖不卖由你拍板, 系统不自动卖",
|
f"拟清仓 {qty} 股 (含零股){note}; 卖出需人工确认, 系统不自动卖",
|
||||||
{"target_price": target, "price": price, "total_qty": total,
|
{"target_price": target, "price": price, "total_qty": total,
|
||||||
"avail_qty": int(p.get("avail_qty") or 0),
|
"avail_qty": int(p.get("avail_qty") or 0),
|
||||||
"cushion_pct": p.get("cushion_pct"), "avg_cost": p.get("avg_cost")},
|
"cushion_pct": p.get("cushion_pct"), "avg_cost": p.get("avg_cost")},
|
||||||
|
|
@ -545,7 +545,7 @@ def eval_weak_research(p: dict, params: dict, mkt: dict = None):
|
||||||
qty = clamp_sell_qty(int(total * ratio // LOT) * LOT, p)
|
qty = clamp_sell_qty(int(total * ratio // LOT) * LOT, p)
|
||||||
if qty < lot_of(p.get("ts_code")):
|
if qty < lot_of(p.get("ts_code")):
|
||||||
return None
|
return None
|
||||||
why = logic_doubt_text(st, f"研究证据走弱: 拟减 {qty} 股 (持仓 {total} 股的 {ratio:.0%}), 卖不卖由你拍板")
|
why = logic_doubt_text(st, f"研究证据走弱: 拟减 {qty} 股 (持仓 {total} 股的 {ratio:.0%}), 卖出需人工确认")
|
||||||
return reduce_on_weak_research(
|
return reduce_on_weak_research(
|
||||||
p["ts_code"], qty=qty, reason=why,
|
p["ts_code"], qty=qty, reason=why,
|
||||||
hard_numbers={"logic_state": st, "price": p.get("price"),
|
hard_numbers={"logic_state": st, "price": p.get("price"),
|
||||||
|
|
|
||||||
|
|
@ -135,7 +135,7 @@ SPECS = {
|
||||||
"switch_key": "PMS_GLOBAL_BUY_HALT", "switch_value": False,
|
"switch_key": "PMS_GLOBAL_BUY_HALT", "switch_value": False,
|
||||||
},
|
},
|
||||||
"HALT_ALL": {
|
"HALT_ALL": {
|
||||||
"cls": CLS_TASK, "label": "全局暂停执行(休假模式)", "group": "B", "scope": "global",
|
"cls": CLS_TASK, "label": "全局暂停执行", "group": "B", "scope": "global",
|
||||||
"fields": {}, "planner": "halt_all", "instant": True,
|
"fields": {}, "planner": "halt_all", "instant": True,
|
||||||
"switch_key": "PMS_GLOBAL_EXEC_HALT", "switch_value": True,
|
"switch_key": "PMS_GLOBAL_EXEC_HALT", "switch_value": True,
|
||||||
"note": "所有自动动作暂停, 仅保留账本对账与日报",
|
"note": "所有自动动作暂停, 仅保留账本对账与日报",
|
||||||
|
|
@ -266,7 +266,7 @@ _CONFLICT_PAIRS = [
|
||||||
({"LIQUIDATE_ALL"}, {"INCREASE_EXPOSURE", "OPEN_TARGET"}, "global", "一键清仓在途, 不应再建仓"),
|
({"LIQUIDATE_ALL"}, {"INCREASE_EXPOSURE", "OPEN_TARGET"}, "global", "一键清仓在途, 不应再建仓"),
|
||||||
({"HALT_BUY"}, {"INCREASE_EXPOSURE", "OPEN_TARGET"}, "global", "全局暂停买入生效中"),
|
({"HALT_BUY"}, {"INCREASE_EXPOSURE", "OPEN_TARGET"}, "global", "全局暂停买入生效中"),
|
||||||
({"HALT_ALL"}, {"REDUCE_EXPOSURE", "INCREASE_EXPOSURE", "OPEN_TARGET",
|
({"HALT_ALL"}, {"REDUCE_EXPOSURE", "INCREASE_EXPOSURE", "OPEN_TARGET",
|
||||||
"EXIT_STOCK", "REDUCE_STOCK", "LIQUIDATE_ALL"}, "global", "休假模式生效中"),
|
"EXIT_STOCK", "REDUCE_STOCK", "LIQUIDATE_ALL"}, "global", "暂停执行生效中"),
|
||||||
({"OPEN_TARGET"}, {"EXIT_STOCK", "REDUCE_STOCK"}, "stock", "同股建仓与减持方向相反"),
|
({"OPEN_TARGET"}, {"EXIT_STOCK", "REDUCE_STOCK"}, "stock", "同股建仓与减持方向相反"),
|
||||||
({"EXIT_STOCK"}, {"OPEN_TARGET", "REDUCE_STOCK"}, "stock", "同股清仓在途"),
|
({"EXIT_STOCK"}, {"OPEN_TARGET", "REDUCE_STOCK"}, "stock", "同股清仓在途"),
|
||||||
({"FREEZE_STOCK"}, {"OPEN_TARGET"}, "stock", "该股已被命令冻结(禁增持)"),
|
({"FREEZE_STOCK"}, {"OPEN_TARGET"}, "stock", "该股已被命令冻结(禁增持)"),
|
||||||
|
|
|
||||||
|
|
@ -586,10 +586,10 @@ def plan_halt_buy(*, pending_buys: list) -> dict:
|
||||||
|
|
||||||
|
|
||||||
def plan_halt_all(*, pending_instructions: list) -> dict:
|
def plan_halt_all(*, pending_instructions: list) -> dict:
|
||||||
"""全局暂停执行 (休假模式): 撤销全部在途指令, 仅保留对账与日报。"""
|
"""全局暂停执行: 撤销全部在途指令, 仅保留对账与日报。"""
|
||||||
items = [_item(b.get("ts_code"), A_HALT, SIDE_NONE, b.get("qty") or 0,
|
items = [_item(b.get("ts_code"), A_HALT, SIDE_NONE, b.get("qty") or 0,
|
||||||
b.get("amount") or 0, P_HALT,
|
b.get("amount") or 0, P_HALT,
|
||||||
f"休假模式: 撤销在途{'买入' if b.get('side') == SIDE_BUY else '卖出'}指令",
|
f"暂停执行: 撤销在途{'买入' if b.get('side') == SIDE_BUY else '卖出'}指令",
|
||||||
tier="停执行", cancel_instruction_id=b.get("instruction_id"))
|
tier="停执行", cancel_instruction_id=b.get("instruction_id"))
|
||||||
for b in (pending_instructions or [])]
|
for b in (pending_instructions or [])]
|
||||||
return {"ok": True, "items": items, "target_amount": 0.0, "planned_amount": 0.0,
|
return {"ok": True, "items": items, "target_amount": 0.0, "planned_amount": 0.0,
|
||||||
|
|
|
||||||
|
|
@ -108,7 +108,7 @@ def check(*, side: str, action: str, qty: int, price: float, ctx: dict) -> dict:
|
||||||
if day.get("halted"):
|
if day.get("halted"):
|
||||||
failed.append("HALTED: 停牌, 当日跳过顺延")
|
failed.append("HALTED: 停牌, 当日跳过顺延")
|
||||||
if flg.get("exec_halt"):
|
if flg.get("exec_halt"):
|
||||||
failed.append("EXEC_HALT: 全局暂停执行 (休假模式) 生效中")
|
failed.append("EXEC_HALT: 全局暂停执行 生效中")
|
||||||
|
|
||||||
_check_user_stop(warns, side, price, flg, hard)
|
_check_user_stop(warns, side, price, flg, hard)
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -20,7 +20,7 @@
|
||||||
三条守卫:
|
三条守卫:
|
||||||
1. 交易日守卫 —— 非交易日任务直接返回 (chinesecalendar; 未装则退化为周一至周五)。
|
1. 交易日守卫 —— 非交易日任务直接返回 (chinesecalendar; 未装则退化为周一至周五)。
|
||||||
2. 故障即守成 —— 任务内异常一律吞掉并记 ERROR 日志, 绝不因调度异常产生新指令。
|
2. 故障即守成 —— 任务内异常一律吞掉并记 ERROR 日志, 绝不因调度异常产生新指令。
|
||||||
3. 全局暂停执行 (休假模式) —— 除对账与日报外的任务全部跳过。
|
3. 全局暂停执行 —— 除对账与日报外的任务全部跳过。
|
||||||
|
|
||||||
启用: docker compose --profile sched up -d
|
启用: docker compose --profile sched up -d
|
||||||
"""
|
"""
|
||||||
|
|
@ -74,7 +74,7 @@ def guard(*, trade_day=True, session=False, respect_exec_halt=True):
|
||||||
try:
|
try:
|
||||||
from app.services import param_store
|
from app.services import param_store
|
||||||
if param_store.get_bool("PMS_GLOBAL_EXEC_HALT", False):
|
if param_store.get_bool("PMS_GLOBAL_EXEC_HALT", False):
|
||||||
logger.info("[%s] 全局暂停执行 (休假模式), 跳过", name)
|
logger.info("[%s] 全局暂停执行, 跳过", name)
|
||||||
return {"skipped": "exec_halt"}
|
return {"skipped": "exec_halt"}
|
||||||
except Exception as e:
|
except Exception as e:
|
||||||
logger.warning("[%s] 读取暂停开关失败 (按未暂停继续): %s", name, e)
|
logger.warning("[%s] 读取暂停开关失败 (按未暂停继续): %s", name, e)
|
||||||
|
|
@ -148,7 +148,7 @@ def _brief(r):
|
||||||
|
|
||||||
# ================================================================ 任务
|
# ================================================================ 任务
|
||||||
@celery_app.task(name="pms.tech_pull")
|
@celery_app.task(name="pms.tech_pull")
|
||||||
@guard(trade_day=True, respect_exec_halt=False) # 取数动作, 休假模式照跑 (只落读数更新映射, 不产生指令)
|
@guard(trade_day=True, respect_exec_halt=False) # 取数动作, 暂停执行照跑 (只落读数更新映射, 不产生指令)
|
||||||
def tech_pull():
|
def tech_pull():
|
||||||
"""盘前很早 (06:30) 拉决策系统的全市场技术面读数, 落 pms_tech_daily 并建映射。
|
"""盘前很早 (06:30) 拉决策系统的全市场技术面读数, 落 pms_tech_daily 并建映射。
|
||||||
|
|
||||||
|
|
@ -161,7 +161,7 @@ def tech_pull():
|
||||||
|
|
||||||
|
|
||||||
@celery_app.task(name="pms.plan_pull")
|
@celery_app.task(name="pms.plan_pull")
|
||||||
@guard(trade_day=True, respect_exec_halt=False) # 取数动作, 休假模式照跑 (只更新映射不产生指令)
|
@guard(trade_day=True, respect_exec_halt=False) # 取数动作, 暂停执行照跑 (只更新映射不产生指令)
|
||||||
def plan_pull():
|
def plan_pull():
|
||||||
"""盘前拉上游选股计划: 刷候选池缓存 + evidence.theme 灌进 pms_industry_map。
|
"""盘前拉上游选股计划: 刷候选池缓存 + evidence.theme 灌进 pms_industry_map。
|
||||||
|
|
||||||
|
|
@ -288,7 +288,7 @@ def strategy_attach():
|
||||||
|
|
||||||
|
|
||||||
@celery_app.task(name="pms.t0_close")
|
@celery_app.task(name="pms.t0_close")
|
||||||
@guard(trade_day=True, respect_exec_halt=False) # 平回是守成收敛动作: 盘中开休假模式
|
@guard(trade_day=True, respect_exec_halt=False) # 平回是守成收敛动作: 盘中开暂停执行
|
||||||
def t0_close(): # 也不能让当日 T 仓裸奔过夜 (2026-08-28)
|
def t0_close(): # 也不能让当日 T 仓裸奔过夜 (2026-08-28)
|
||||||
"""T 仓强制平回 (14:50): 对挂了做T策略且当日仍有未平腿的票, 立刻发对向平仓腿打平,
|
"""T 仓强制平回 (14:50): 对挂了做T策略且当日仍有未平腿的票, 立刻发对向平仓腿打平,
|
||||||
绝不过夜 (设计 §六 rails)。平回后再自证账面 T 仓, 残留的记 WARN 交人工核查。
|
绝不过夜 (设计 §六 rails)。平回后再自证账面 T 仓, 残留的记 WARN 交人工核查。
|
||||||
|
|
@ -307,12 +307,12 @@ def t0_close(): # 也不能让当日 T 仓裸
|
||||||
|
|
||||||
|
|
||||||
@celery_app.task(name="pms.daily_settle")
|
@celery_app.task(name="pms.daily_settle")
|
||||||
@guard(trade_day=True, respect_exec_halt=False) # 对账属守成动作, 休假模式下照跑
|
@guard(trade_day=True, respect_exec_halt=False) # 对账属守成动作, 暂停执行下照跑
|
||||||
def daily_settle():
|
def daily_settle():
|
||||||
from app.services import executor, ledger_service
|
from app.services import executor, ledger_service
|
||||||
# 先把尾窗成交入账 (2026-08-28 审查修): replay_fills 15:05 停跑而这里 15:10 全量对账,
|
# 先把尾窗成交入账 (2026-08-28 审查修): replay_fills 15:05 停跑而这里 15:10 全量对账,
|
||||||
# 15:05~15:10 落进 inbox 的成交若不先消化, 会被对账当数量差异按 RECON 补一次,
|
# 15:05~15:10 落进 inbox 的成交若不先消化, 会被对账当数量差异按 RECON 补一次,
|
||||||
# 次日开盘回放又正常入账一次 —— 同一笔账两份。休假模式下 replay 任务被闸住,
|
# 次日开盘回放又正常入账一次 —— 同一笔账两份。暂停执行下 replay 任务被闸住,
|
||||||
# 这一步同样兜住 (入账是守成, 不产生新指令)。
|
# 这一步同样兜住 (入账是守成, 不产生新指令)。
|
||||||
pre = {}
|
pre = {}
|
||||||
try:
|
try:
|
||||||
|
|
@ -337,7 +337,7 @@ def daily_settle():
|
||||||
|
|
||||||
|
|
||||||
@celery_app.task(name="pms.nav_snapshot")
|
@celery_app.task(name="pms.nav_snapshot")
|
||||||
@guard(trade_day=True, respect_exec_halt=False) # 记账类动作, 休假模式照跑
|
@guard(trade_day=True, respect_exec_halt=False) # 记账类动作, 暂停执行照跑
|
||||||
def nav_snapshot():
|
def nav_snapshot():
|
||||||
"""每日净值快照 (15:20): 按公示口径记一行净值 (pms_nav_daily), 供公示表导出的
|
"""每日净值快照 (15:20): 按公示口径记一行净值 (pms_nav_daily), 供公示表导出的
|
||||||
净值序列使用。排在日终结算 (15:10) 之后 —— 尾窗成交已入账、收盘价已落定;
|
净值序列使用。排在日终结算 (15:10) 之后 —— 尾窗成交已入账、收盘价已落定;
|
||||||
|
|
|
||||||
|
|
@ -41,7 +41,7 @@ SECRET_KEYS = ("PMS_QMT_SIGN_SEED_HEX", "PMS_QMT_PEER_PUBKEY_B64", "PMS_SESSION_
|
||||||
# 运行态开关: key -> (默认值, 类型, 说明)
|
# 运行态开关: key -> (默认值, 类型, 说明)
|
||||||
RUNTIME_EXTRA = {
|
RUNTIME_EXTRA = {
|
||||||
"PMS_GLOBAL_BUY_HALT": (False, bool, "全局暂停买入 (HALT_BUY 命令置位)"),
|
"PMS_GLOBAL_BUY_HALT": (False, bool, "全局暂停买入 (HALT_BUY 命令置位)"),
|
||||||
"PMS_GLOBAL_EXEC_HALT": (False, bool, "全局暂停执行 / 休假模式"),
|
"PMS_GLOBAL_EXEC_HALT": (False, bool, "全局暂停执行"),
|
||||||
"PMS_BRAKE_UNTIL": (0, int, "组合刹车解除日 YYYYMMDD, 0=未刹车"),
|
"PMS_BRAKE_UNTIL": (0, int, "组合刹车解除日 YYYYMMDD, 0=未刹车"),
|
||||||
"PMS_HIGH_WATER": (0.0, float, "组合市值高水位 (刹车判定基准)"),
|
"PMS_HIGH_WATER": (0.0, float, "组合市值高水位 (刹车判定基准)"),
|
||||||
"PMS_REPLAY_CURSOR": ("", str, "成交回放游标 (trading_order.order_id)。留空=首次启动时自动对齐到当前最新成交且不追认历史; 填某个 order_id=从它之后开始; 填 ALL=从头全量回放"),
|
"PMS_REPLAY_CURSOR": ("", str, "成交回放游标 (trading_order.order_id)。留空=首次启动时自动对齐到当前最新成交且不追认历史; 填某个 order_id=从它之后开始; 填 ALL=从头全量回放"),
|
||||||
|
|
@ -63,7 +63,7 @@ RUNTIME_EXTRA = {
|
||||||
"PMS_OPEN_REASK_ENABLED": (True, bool, "解锁重问总开关: 新建仓被研判驳回后, 盘中输入确实变了就解除当日闸、重问一次。关掉就回到「驳回即当天不再问」"),
|
"PMS_OPEN_REASK_ENABLED": (True, bool, "解锁重问总开关: 新建仓被研判驳回后, 盘中输入确实变了就解除当日闸、重问一次。关掉就回到「驳回即当天不再问」"),
|
||||||
"PMS_OPEN_REASK_MIN_GAP_MIN": (20, int, "距上次驳回至少多少分钟才准重问。20 分钟等于四根五分钟线, 够量能确认成形"),
|
"PMS_OPEN_REASK_MIN_GAP_MIN": (20, int, "距上次驳回至少多少分钟才准重问。20 分钟等于四根五分钟线, 够量能确认成形"),
|
||||||
"PMS_OPEN_REASK_MAX_PER_DAY": (1, int, "每只票每天最多重问几次。填 1 是硬约束: 两次以上等于取消当日闸, 而当日闸挡的是账本刷屏这个真问题"),
|
"PMS_OPEN_REASK_MAX_PER_DAY": (1, int, "每只票每天最多重问几次。填 1 是硬约束: 两次以上等于取消当日闸, 而当日闸挡的是账本刷屏这个真问题"),
|
||||||
"PMS_OPEN_REASK_WINDOW": ("0945-1430", str, "重问时段。早于 09:45 时段折算量比不可靠; 晚于 14:30 即便放行也来不及走完提议到拍板到下单"),
|
"PMS_OPEN_REASK_WINDOW": ("0945-1430", str, "重问时段。早于 09:45 时段折算量比不可靠; 晚于 14:30 即便放行也来不及走完提议到确认到下单"),
|
||||||
"PMS_OPEN_REASK_REF_DRIFT": (0.03, float, "解锁条件二: 昨夜支撑或压力相对驳回时漂过这个比例就算底牌变了。与 PMS_OPEN_REF_DRIFT_MAX 同值同语义, 复盘能对照"),
|
"PMS_OPEN_REASK_REF_DRIFT": (0.03, float, "解锁条件二: 昨夜支撑或压力相对驳回时漂过这个比例就算底牌变了。与 PMS_OPEN_REF_DRIFT_MAX 同值同语义, 复盘能对照"),
|
||||||
"PMS_OPEN_REASK_UP": (0.02, float, "解锁条件三之一: 现价较驳回时至少涨这么多。低于 2% 是噪声"),
|
"PMS_OPEN_REASK_UP": (0.02, float, "解锁条件三之一: 现价较驳回时至少涨这么多。低于 2% 是噪声"),
|
||||||
"PMS_OPEN_REASK_VOL_MIN": (1.5, float, "解锁条件三之二: 时段折算量比的绝对下限"),
|
"PMS_OPEN_REASK_VOL_MIN": (1.5, float, "解锁条件三之二: 时段折算量比的绝对下限"),
|
||||||
|
|
@ -108,10 +108,10 @@ RUNTIME_EXTRA = {
|
||||||
"PMS_TECH_FLIP_FRESH_DAYS": (2, int, "SAR 翻向后这么多个交易日内算「刚转向」(转多/转空相位用)"),
|
"PMS_TECH_FLIP_FRESH_DAYS": (2, int, "SAR 翻向后这么多个交易日内算「刚转向」(转多/转空相位用)"),
|
||||||
"PMS_TECH_STATE_MAP": ("", str, "技术面读数映射 (tech_pull 与 08:40 补拉写入的 JSON: 时刻/读数日/逐票紧凑立场), 扫描层与页面只读, 勿手改"),
|
"PMS_TECH_STATE_MAP": ("", str, "技术面读数映射 (tech_pull 与 08:40 补拉写入的 JSON: 时刻/读数日/逐票紧凑立场), 扫描层与页面只读, 勿手改"),
|
||||||
# ── 2026-09-11 三源合议工作包二 (台账 007/009): 基本面立场与合议分流的开关。
|
# ── 2026-09-11 三源合议工作包二 (台账 007/009): 基本面立场与合议分流的开关。
|
||||||
"PMS_FUND_REQUIRED": (True, bool, "无买方评析不建仓 (拍板: 没有读数或超 120 天的候选一律不建仓)"),
|
"PMS_FUND_REQUIRED": (True, bool, "无买方评析不建仓 (既定: 没有读数或超 120 天的候选一律不建仓)"),
|
||||||
"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 加固包 (方案第六节承诺、评审发现五): 方向看多但只有一方表态时是否交人。
|
# 2026-09-14 加固包 (方案第六节承诺、评审发现五): 方向看多但只有一方表态时是否交人。
|
||||||
"PMS_CONSENSUS_WEAK_CONFIRM": (True, bool, "合议方向看多但只有一方表态时交人确认 (默认开; 关掉则弱表态也放行)"),
|
"PMS_CONSENSUS_WEAK_CONFIRM": (True, bool, "合议方向看多但只有一方表态时交人确认 (默认开; 关掉则弱表态也放行)"),
|
||||||
# ── 2026-09-11 三源合议工作包二·接入 (台账 006/009): 持仓增持门与候选按质地排序。
|
# ── 2026-09-11 三源合议工作包二·接入 (台账 006/009): 持仓增持门与候选按质地排序。
|
||||||
|
|
@ -119,7 +119,7 @@ RUNTIME_EXTRA = {
|
||||||
"PMS_PLAN_RANK_BY_QUALITY": (True, bool, "候选先按基本面立场分桶 (质地看多的桶排前) 再按分数, 在主题限额与截断之前生效; 技术面不参与排序 (关掉即回到只按分数排)"),
|
"PMS_PLAN_RANK_BY_QUALITY": (True, bool, "候选先按基本面立场分桶 (质地看多的桶排前) 再按分数, 在主题限额与截断之前生效; 技术面不参与排序 (关掉即回到只按分数排)"),
|
||||||
"PMS_PLAN_OBSERVE_IF_FUND_BULL": (False, bool, "把观察档里质地看多的票也纳入候选池 (默认关, 守宁缺毋滥; 开了它们与主榜一同参与分桶排序)"),
|
"PMS_PLAN_OBSERVE_IF_FUND_BULL": (False, bool, "把观察档里质地看多的票也纳入候选池 (默认关, 守宁缺毋滥; 开了它们与主榜一同参与分桶排序)"),
|
||||||
# ── 2026-09-11 三源合议工作包三·离场纪律 (台账 010): SAR 转空自动离场。翻向新鲜天数复用 PMS_TECH_FLIP_FRESH_DAYS。
|
# ── 2026-09-11 三源合议工作包三·离场纪律 (台账 010): SAR 转空自动离场。翻向新鲜天数复用 PMS_TECH_FLIP_FRESH_DAYS。
|
||||||
"PMS_TECH_EXIT_AUTONOMY": ("full", str, "技术面转空离场档位: off 不评 / propose_only 交人确认 / full 自动执行 (与保垫减仓同档, 按拍板默认)"),
|
"PMS_TECH_EXIT_AUTONOMY": ("full", str, "技术面转空离场档位: off 不评 / propose_only 交人确认 / full 自动执行 (与保垫减仓同档, 按既定默认)"),
|
||||||
"PMS_TECH_EXIT_TRIM_RATIO": (0.3333, float, "转空未确认时的减持比例 (占总持仓; 确认转空则清仓全部可卖量; 默认三分之一)"),
|
"PMS_TECH_EXIT_TRIM_RATIO": (0.3333, float, "转空未确认时的减持比例 (占总持仓; 确认转空则清仓全部可卖量; 默认三分之一)"),
|
||||||
"PMS_TECH_EXIT_DONE": ("", str, "本次翻空已处理过的代码集 (scan_and_route 写入的 JSON: codes/at; 跨轮去重, 页面只读, 勿手改)"),
|
"PMS_TECH_EXIT_DONE": ("", str, "本次翻空已处理过的代码集 (scan_and_route 写入的 JSON: codes/at; 跨轮去重, 页面只读, 勿手改)"),
|
||||||
# 跟踪止盈的盘中 SAR 止损线 (2026-09-11 工作包三 part 3): 每天 09:40 把技术面 SAR 值刷进自动跟踪止盈, 盘中跌破即卖。
|
# 跟踪止盈的盘中 SAR 止损线 (2026-09-11 工作包三 part 3): 每天 09:40 把技术面 SAR 值刷进自动跟踪止盈, 盘中跌破即卖。
|
||||||
|
|
|
||||||
|
|
@ -78,7 +78,7 @@ def scan_and_route(*, now=None, dry_run: bool = False) -> dict:
|
||||||
out["skipped"].append({"why": "自主档位 off, 不扫描 (新建仓一并停 —— off 是总闸)"})
|
out["skipped"].append({"why": "自主档位 off, 不扫描 (新建仓一并停 —— off 是总闸)"})
|
||||||
return out
|
return out
|
||||||
if param_store.get_bool("PMS_GLOBAL_EXEC_HALT", False):
|
if param_store.get_bool("PMS_GLOBAL_EXEC_HALT", False):
|
||||||
out["skipped"].append({"why": "全局暂停执行 (休假模式)"})
|
out["skipped"].append({"why": "全局暂停执行"})
|
||||||
return out
|
return out
|
||||||
open_autonomy = param_store.get("PMS_OPEN_AUTONOMY", AUTONOMY_FULL)
|
open_autonomy = param_store.get("PMS_OPEN_AUTONOMY", AUTONOMY_FULL)
|
||||||
out["open_autonomy"] = open_autonomy
|
out["open_autonomy"] = open_autonomy
|
||||||
|
|
@ -672,7 +672,7 @@ def disposition_snapshot(now=None) -> dict:
|
||||||
res["notes"].append("自主档位 off —— 本轮不扫描候选池 (off 是总闸)")
|
res["notes"].append("自主档位 off —— 本轮不扫描候选池 (off 是总闸)")
|
||||||
return res
|
return res
|
||||||
if param_store.get_bool("PMS_GLOBAL_EXEC_HALT", False):
|
if param_store.get_bool("PMS_GLOBAL_EXEC_HALT", False):
|
||||||
res["notes"].append("全局暂停执行 (休假模式) —— 本轮不扫描候选池")
|
res["notes"].append("全局暂停执行 —— 本轮不扫描候选池")
|
||||||
return res
|
return res
|
||||||
if res["open_autonomy"] == AUTONOMY_OFF:
|
if res["open_autonomy"] == AUTONOMY_OFF:
|
||||||
res["notes"].append("新建仓档位 off —— 不扫描候选池")
|
res["notes"].append("新建仓档位 off —— 不扫描候选池")
|
||||||
|
|
@ -708,7 +708,7 @@ def disposition_snapshot(now=None) -> dict:
|
||||||
for _k in _system_declined_today_keys(): # 系统自决今日已放弃 (明日重评)
|
for _k in _system_declined_today_keys(): # 系统自决今日已放弃 (明日重评)
|
||||||
skip[_k] = "今日系统已判放弃,明日重评"
|
skip[_k] = "今日系统已判放弃,明日重评"
|
||||||
for _k in _inflight_keys(): # 在途; 前端多半已按指令/提议标成「已下单/待你确认」
|
for _k in _inflight_keys(): # 在途; 前端多半已按指令/提议标成「已下单/待你确认」
|
||||||
skip[_k] = "系统已经在处理这只票(见『等我拍板』或『今日在办』)"
|
skip[_k] = "系统正在处理这只票(见『待确认提议』或『当日指令』)"
|
||||||
sink = {"skipped": []} # 丢弃用: _scan_open 只往里 append/extend, 不读它
|
sink = {"skipped": []} # 丢弃用: _scan_open 只往里 append/extend, 不读它
|
||||||
would = _scan_open(view, params, stock_params, skip, {}, sink, now=now)
|
would = _scan_open(view, params, stock_params, skip, {}, sink, now=now)
|
||||||
except Exception as e:
|
except Exception as e:
|
||||||
|
|
@ -885,7 +885,7 @@ def _route_one(c, view, params, stock_params, brake_active, now, dry_run, out,
|
||||||
# "这个变化够不够动用一个持仓名额"是边界情形, 按既有分工归人。
|
# "这个变化够不够动用一个持仓名额"是边界情形, 按既有分工归人。
|
||||||
# 三, 这次要解决的是**连提议都不出**, 不是"一定要买到"。把牌摊在人面前, 人拍板;
|
# 三, 这次要解决的是**连提议都不出**, 不是"一定要买到"。把牌摊在人面前, 人拍板;
|
||||||
# 拍了板买不上是择时区间和追高原则在起作用, 那两条一个字没改。
|
# 拍了板买不上是择时区间和追高原则在起作用, 那两条一个字没改。
|
||||||
reask_why = ("盘中重问放行,交你拍板:" + str((c.get("reask") or {}).get("why") or "")
|
reask_why = ("盘中重问放行,需人工确认:" + str((c.get("reask") or {}).get("why") or "")
|
||||||
+ "。能不能买到由择时区间决定,可能当日买不上。") if c.get("reask") else None
|
+ "。能不能买到由择时区间决定,可能当日买不上。") if c.get("reask") else None
|
||||||
# 上游标了硬风险的票必须交人 (2026-09-10 补的洞)。
|
# 上游标了硬风险的票必须交人 (2026-09-10 补的洞)。
|
||||||
#
|
#
|
||||||
|
|
@ -901,7 +901,7 @@ def _route_one(c, view, params, stock_params, brake_active, now, dry_run, out,
|
||||||
risk_why = None
|
risk_why = None
|
||||||
if _hn_risk:
|
if _hn_risk:
|
||||||
_rl = _hn_risk if isinstance(_hn_risk, (list, tuple)) else [_hn_risk]
|
_rl = _hn_risk if isinstance(_hn_risk, (list, tuple)) else [_hn_risk]
|
||||||
risk_why = "选股系统标了风险,交你拍板:" + ";".join(str(x) for x in _rl if x)
|
risk_why = "选股系统标了风险,需人工确认:" + ";".join(str(x) for x in _rl if x)
|
||||||
force_queue = (bool(c.get("needs_user_confirm")) or bool(src_why)
|
force_queue = (bool(c.get("needs_user_confirm")) or bool(src_why)
|
||||||
or bool(verdict.get("degraded")) or bool(reask_why) or bool(risk_why))
|
or bool(verdict.get("degraded")) or bool(reask_why) or bool(risk_why))
|
||||||
auto_exec = (not force_queue) and (side == "sell" or autonomy == AUTONOMY_FULL)
|
auto_exec = (not force_queue) and (side == "sell" or autonomy == AUTONOMY_FULL)
|
||||||
|
|
|
||||||
|
|
@ -396,7 +396,7 @@ def scan(*, dry_run: bool = False, now=None) -> dict:
|
||||||
"挂了也不会跑, 本轮不挂"})
|
"挂了也不会跑, 本轮不挂"})
|
||||||
return _record_scan(out, dry_run, now)
|
return _record_scan(out, dry_run, now)
|
||||||
if param_store.get_bool("PMS_GLOBAL_EXEC_HALT", False):
|
if param_store.get_bool("PMS_GLOBAL_EXEC_HALT", False):
|
||||||
out["skipped"].append({"why": "全局暂停执行 (休假模式)"})
|
out["skipped"].append({"why": "全局暂停执行"})
|
||||||
return _record_scan(out, dry_run, now)
|
return _record_scan(out, dry_run, now)
|
||||||
|
|
||||||
from app.services import command_service, portfolio, strategy_service
|
from app.services import command_service, portfolio, strategy_service
|
||||||
|
|
|
||||||
|
|
@ -220,7 +220,7 @@ pre.json{background:var(--surface-2);border:1px solid var(--hair);border-radius:
|
||||||
/* 运维视图保持原来的居中宽度; 交易员视图铺开用两侧留白 */
|
/* 运维视图保持原来的居中宽度; 交易员视图铺开用两侧留白 */
|
||||||
.ops-wrap{max-width:1280px;margin:0 auto;}
|
.ops-wrap{max-width:1280px;margin:0 auto;}
|
||||||
|
|
||||||
/* ── 融合消息栏: 上游告警 + 系统下单 + 等我拍板 + 策略与风控, 混成一条时间线 ── */
|
/* ── 融合消息栏: 上游告警 + 系统下单 + 待确认提议 + 策略与风控, 混成一条时间线 ── */
|
||||||
.msgbar{position:sticky;top:46px;z-index:40;display:flex;align-items:center;gap:9px;
|
.msgbar{position:sticky;top:46px;z-index:40;display:flex;align-items:center;gap:9px;
|
||||||
background:var(--surface);border:1px solid var(--hair);border-left:3px solid var(--accent);
|
background:var(--surface);border:1px solid var(--hair);border-left:3px solid var(--accent);
|
||||||
border-radius:12px;padding:7px 12px;margin:10px 0 4px;box-shadow:var(--shadow);font-size:13px;}
|
border-radius:12px;padding:7px 12px;margin:10px 0 4px;box-shadow:var(--shadow);font-size:13px;}
|
||||||
|
|
@ -450,7 +450,7 @@ body.dock-r:not(.r-fold) .side-r .strip{display:none;}
|
||||||
<span class="chip" :class="marketOpen?'ok':'idle'"><span class="dot"></span>{{ marketState }}</span>
|
<span class="chip" :class="marketOpen?'ok':'idle'"><span class="dot"></span>{{ marketState }}</span>
|
||||||
<span class="chip hd-buy" :class="ov.buy_halt?'warn':'ok'"><span class="dot"></span>买入 {{ ov.buy_halt ? '已暂停':'正常' }}</span>
|
<span class="chip hd-buy" :class="ov.buy_halt?'warn':'ok'"><span class="dot"></span>买入 {{ ov.buy_halt ? '已暂停':'正常' }}</span>
|
||||||
<span class="chip hd-exec" :class="ov.exec_halt?'warn':'ok'"><span class="dot"></span>执行 {{ ov.exec_halt ? '已暂停':'正常' }}</span>
|
<span class="chip hd-exec" :class="ov.exec_halt?'warn':'ok'"><span class="dot"></span>执行 {{ ov.exec_halt ? '已暂停':'正常' }}</span>
|
||||||
<span class="chip" :class="ov.autonomy==='off'?'warn':'ok'"><span class="dot"></span>自主 {{ tx('autonomy', ov.autonomy) }}<span class="help" data-tip="系统能自己做主到什么程度。全自动=各项检查通过就直接下单;只提议=先在「等我拍板」问过你;暂停=系统不自己动。">?</span></span>
|
<span class="chip" :class="ov.autonomy==='off'?'warn':'ok'"><span class="dot"></span>自主 {{ tx('autonomy', ov.autonomy) }}<span class="help" data-tip="系统能自主到什么程度。全自动=各项检查通过即直接下单;仅提议=先在「待确认提议」等人工确认;暂停执行=系统不自动动作。">?</span></span>
|
||||||
<span class="chip hd-plan" :class="planStatus.ok===false?'warn':'ok'"><span class="dot"></span>选股计划<span class="hd-plan-d"> {{ planStatus.ok===false ? '读不到' : (planStatus.date || '-') }}</span></span>
|
<span class="chip hd-plan" :class="planStatus.ok===false?'warn':'ok'"><span class="dot"></span>选股计划<span class="hd-plan-d"> {{ planStatus.ok===false ? '读不到' : (planStatus.date || '-') }}</span></span>
|
||||||
<span class="chip hd-tech" :class="techChipCls"><span class="dot"></span>技术面<span class="hd-plan-d"> {{ techChipText }}</span><span class="help" data-tip="每晚算的技术面读数(布林线、多空布林线、SAR)是哪天的。绿=今天的映射新鲜;黄=读不到或已过期;灰=技术面这一层没开。">?</span></span>
|
<span class="chip hd-tech" :class="techChipCls"><span class="dot"></span>技术面<span class="hd-plan-d"> {{ techChipText }}</span><span class="help" data-tip="每晚算的技术面读数(布林线、多空布林线、SAR)是哪天的。绿=今天的映射新鲜;黄=读不到或已过期;灰=技术面这一层没开。">?</span></span>
|
||||||
<el-tooltip placement="bottom-start" effect="light" :show-after="120">
|
<el-tooltip placement="bottom-start" effect="light" :show-after="120">
|
||||||
|
|
@ -470,7 +470,7 @@ body.dock-r:not(.r-fold) .side-r .strip{display:none;}
|
||||||
:title="h.ymd + ' · ' + (h.value==null ? '不可用' : h.value) + ' · ' + mzLabel(h.zone)"></span>
|
:title="h.ymd + ' · ' + (h.value==null ? '不可用' : h.value) + ' · ' + mzLabel(h.zone)"></span>
|
||||||
</div>
|
</div>
|
||||||
<div class="muted" style="font-size:11px">近 {{ (mzSig.history||[]).length }} 个交易日 · 热 +{{ (macro.params||{}).hot_th }} / 冷 {{ (macro.params||{}).cold_th }} / 退出带 ±{{ (macro.params||{}).exit_band }} · 档位 {{ macro.autonomy==='full' ? '全自动' : (macro.autonomy==='propose_only' ? '只出建议' : '只算不动') }}</div>
|
<div class="muted" style="font-size:11px">近 {{ (mzSig.history||[]).length }} 个交易日 · 热 +{{ (macro.params||{}).hot_th }} / 冷 {{ (macro.params||{}).cold_th }} / 退出带 ±{{ (macro.params||{}).exit_band }} · 档位 {{ macro.autonomy==='full' ? '全自动' : (macro.autonomy==='propose_only' ? '只出建议' : '只算不动') }}</div>
|
||||||
<div v-if="macroGateOn" style="color:var(--warn)">大盘偏热,系统已经停掉它自己发起的买入,以及策略里的买入动作。卖出不受影响,你自己下的命令也不受影响。</div>
|
<div v-if="macroGateOn" style="color:var(--warn)">大盘偏热,系统已经停掉自身发起的买入,以及策略里的买入动作。卖出不受影响,人工下的命令也不受影响。</div>
|
||||||
<div v-if="mzSig.advice && !mzSig.advice.adopted" style="margin-top:4px">
|
<div v-if="mzSig.advice && !mzSig.advice.adopted" style="margin-top:4px">
|
||||||
<b>今日建议:{{ mzSig.advice.direction==='REDUCE' ? '降仓' : '升仓' }} {{ (mzSig.advice.pct*100).toFixed(1) }}%</b>
|
<b>今日建议:{{ mzSig.advice.direction==='REDUCE' ? '降仓' : '升仓' }} {{ (mzSig.advice.pct*100).toFixed(1) }}%</b>
|
||||||
<button class="tbtn pri" style="margin-left:8px" @click="macroAdopt(mzSig.key)">采纳并下命令</button>
|
<button class="tbtn pri" style="margin-left:8px" @click="macroAdopt(mzSig.key)">采纳并下命令</button>
|
||||||
|
|
@ -530,17 +530,17 @@ body.dock-r:not(.r-fold) .side-r .strip{display:none;}
|
||||||
:title="'现在是影子模式,只记录不真下单 —— ' + (dm.hint || '')"></el-alert>
|
:title="'现在是影子模式,只记录不真下单 —— ' + (dm.hint || '')"></el-alert>
|
||||||
|
|
||||||
<el-alert v-if="macroGateOn" class="banner" type="warning" effect="dark" show-icon :closable="false"
|
<el-alert v-if="macroGateOn" class="banner" type="warning" effect="dark" show-icon :closable="false"
|
||||||
:title="'大盘偏热,系统已经暂停两件事:它自己发起的买入,以及策略里的买入动作。卖出不受影响,你自己下的命令也不受影响。' + ((macro.gate||{}).why||'')"></el-alert>
|
:title="'大盘偏热,系统已经暂停两件事:自身发起的买入,以及策略里的买入动作。卖出不受影响,人工下的命令也不受影响。' + ((macro.gate||{}).why||'')"></el-alert>
|
||||||
<el-alert v-if="macroUnavailable" class="banner" type="warning" show-icon :closable="false"
|
<el-alert v-if="macroUnavailable" class="banner" type="warning" show-icon :closable="false"
|
||||||
:title="'大盘冷热的数据读不到,今天这一层不做任何动作、也不加限制: ' + macroUnavailable"></el-alert>
|
:title="'大盘冷热的数据读不到,今天这一层不做任何动作、也不加限制: ' + macroUnavailable"></el-alert>
|
||||||
|
|
||||||
<el-alert v-if="scaleGap" class="banner" type="warning" effect="dark" show-icon :closable="false"
|
<el-alert v-if="scaleGap" class="banner" type="warning" effect="dark" show-icon :closable="false"
|
||||||
:title="'你设的总规模和账户实际对不上 —— 参数里写的是 ' + money(ov.scale) + ',账户总资产是 ' + money(ov.total_asset) + '(差 ' + pct(scaleGap) + ')。仓位上限按参数算,钱够不够按账户算。两个数差得远,方案会通过检查但下不出去。'"></el-alert>
|
:title="'参数总规模与账户实际对不上 —— 参数里写的是 ' + money(ov.scale) + ',账户总资产是 ' + money(ov.total_asset) + '(差 ' + pct(scaleGap) + ')。仓位上限按参数算,钱够不够按账户算。两个数差得远,方案会通过检查但下不出去。'"></el-alert>
|
||||||
|
|
||||||
<!-- ==================== 交易员视图 (2026-08-26 三栏改版) ==================== -->
|
<!-- ==================== 交易员视图 (2026-08-26 三栏改版) ==================== -->
|
||||||
<div v-show="mode==='trader'">
|
<div v-show="mode==='trader'">
|
||||||
|
|
||||||
<!-- 0) 融合消息栏: 行情告警 / 系统下单 / 等我拍板 / 策略与风控, 按时间倒序混一条流。
|
<!-- 0) 融合消息栏: 行情告警 / 系统下单 / 待确认提议 / 策略与风控, 按时间倒序混一条流。
|
||||||
点「展开全部」就地往下拉一块面板, 不跳转不滚动。 -->
|
点「展开全部」就地往下拉一块面板, 不跳转不滚动。 -->
|
||||||
<div class="msgbar" :class="'lv-'+msgLevelClass">
|
<div class="msgbar" :class="'lv-'+msgLevelClass">
|
||||||
<span class="mb-label"><span class="bell"></span>消息</span>
|
<span class="mb-label"><span class="bell"></span>消息</span>
|
||||||
|
|
@ -642,7 +642,7 @@ body.dock-r:not(.r-fold) .side-r .strip{display:none;}
|
||||||
<template v-if="sigTab==='brief'">
|
<template v-if="sigTab==='brief'">
|
||||||
<div class="sig-fail" v-if="srcFail('alerts') || srcFail('intraday')">
|
<div class="sig-fail" v-if="srcFail('alerts') || srcFail('intraday')">
|
||||||
上游信号读不到({{ (srcFail('alerts')||srcFail('intraday')).error }})。
|
上游信号读不到({{ (srcFail('alerts')||srcFail('intraday')).error }})。
|
||||||
这不是「今天没有信号」,是这一路取不到数——别当成平安无事。</div>
|
这不是「今天没有信号」,是这一路取不到数——不要当成一切正常。</div>
|
||||||
<div class="muted" v-else-if="!sigToday.length">持仓的票今天没有信号,全场也没有重要或警告级的信号。</div>
|
<div class="muted" v-else-if="!sigToday.length">持仓的票今天没有信号,全场也没有重要或警告级的信号。</div>
|
||||||
<template v-if="sigToday.length">
|
<template v-if="sigToday.length">
|
||||||
<div v-for="(g,i) in sigBriefHeld.slice(0,60)" :key="'h'+i" class="sigrow2" :class="{old:g.stale}">
|
<div v-for="(g,i) in sigBriefHeld.slice(0,60)" :key="'h'+i" class="sigrow2" :class="{old:g.stale}">
|
||||||
|
|
@ -855,7 +855,7 @@ body.dock-r:not(.r-fold) .side-r .strip{display:none;}
|
||||||
</teleport>
|
</teleport>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<!-- ─── 中区: 一览 / 需要注意 / 我的持仓 / 今日在办 ─── -->
|
<!-- ─── 中区: 一览 / 运行提示 / 持仓 / 当日指令 ─── -->
|
||||||
<main class="tmain">
|
<main class="tmain">
|
||||||
|
|
||||||
<div class="metrics">
|
<div class="metrics">
|
||||||
|
|
@ -869,24 +869,24 @@ body.dock-r:not(.r-fold) .side-r .strip{display:none;}
|
||||||
<div class="v" :class="(ov.float_pnl||0)>=0?'up':'down'">{{ money(ov.float_pnl) }}<span style="font-size:12px"> {{ ov.float_pnl_pct==null?'':pct(ov.float_pnl_pct) }}</span></div></div>
|
<div class="v" :class="(ov.float_pnl||0)>=0?'up':'down'">{{ money(ov.float_pnl) }}<span style="font-size:12px"> {{ ov.float_pnl_pct==null?'':pct(ov.float_pnl_pct) }}</span></div></div>
|
||||||
<div class="m"><div class="k">持仓只数<span class="muted" style="margin-left:4px">上限 {{ ov.max_names }}</span></div>
|
<div class="m"><div class="k">持仓只数<span class="muted" style="margin-left:4px">上限 {{ ov.max_names }}</span></div>
|
||||||
<div class="v">{{ ov.names_count }}</div></div>
|
<div class="v">{{ ov.names_count }}</div></div>
|
||||||
<div class="m" style="cursor:pointer" @click="openRailSide('r')"><div class="k">等我拍板</div>
|
<div class="m" style="cursor:pointer" @click="openRailSide('r')"><div class="k">待确认提议</div>
|
||||||
<div class="v" :class="proposals.length?'up':''">{{ proposals.length }}<span class="muted" style="font-size:12px"> 条</span></div></div>
|
<div class="v" :class="proposals.length?'up':''">{{ proposals.length }}<span class="muted" style="font-size:12px"> 条</span></div></div>
|
||||||
<div class="m"><div class="k">自主档位<span class="help" data-tip="系统能自己做主到什么程度。全自动=各项检查通过就直接下单;只提议=先在「等我拍板」问过你;暂停=系统不自己动。">?</span></div>
|
<div class="m"><div class="k">自主档位<span class="help" data-tip="系统能自主到什么程度。全自动=各项检查通过即直接下单;仅提议=先在「待确认提议」等人工确认;暂停执行=系统不自动动作。">?</span></div>
|
||||||
<div class="v" style="font-size:14px;padding-top:3px">{{ tx('autonomy', ov.autonomy) }}</div></div>
|
<div class="v" style="font-size:14px;padding-top:3px">{{ tx('autonomy', ov.autonomy) }}</div></div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div class="panel" v-if="attention.length" style="border-left:3px solid var(--warn)">
|
<div class="panel" v-if="attention.length" style="border-left:3px solid var(--warn)">
|
||||||
<h3 style="color:var(--warn);margin-bottom:6px">需要注意</h3>
|
<h3 style="color:var(--warn);margin-bottom:6px">运行提示</h3>
|
||||||
<div v-for="(a,i) in attention" :key="i" class="attn">· {{ a }}</div>
|
<div v-for="(a,i) in attention" :key="i" class="attn">· {{ a }}</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<!-- 我的持仓: 操作列钉在右边, 表格横向再挤也不会被切掉 -->
|
<!-- 持仓: 操作列钉在右边, 表格横向再挤也不会被切掉 -->
|
||||||
<div class="panel tblk postbl" id="t-pos">
|
<div class="panel tblk postbl" id="t-pos">
|
||||||
<h3>我的持仓 <span class="muted" style="font-weight:400">· {{ heldPositions.length }} 只</span></h3>
|
<h3>持仓 <span class="muted" style="font-weight:400">· {{ heldPositions.length }} 只</span></h3>
|
||||||
<div class="sub2">安全垫<span class="help" data-tip="现在的浮盈够不够厚,够不够扛一次回撤。厚垫=赚得多,可以考虑加仓;薄垫=刚开始赚;浮亏无垫=还在亏,不加仓。">?</span>
|
<div class="sub2">安全垫<span class="help" data-tip="现在的浮盈够不够厚,够不够扛一次回撤。厚垫=赚得多,可以考虑加仓;薄垫=刚开始赚;浮亏无垫=还在亏,不加仓。">?</span>
|
||||||
看的是浮盈厚不厚:厚垫可以考虑加仓,薄垫先放着,浮亏无垫不加仓。
|
看的是浮盈厚不厚:厚垫可以考虑加仓,薄垫先放着,浮亏无垫不加仓。
|
||||||
成本是各批买入价的加权平均,占比是占总操作规模的百分比。
|
成本是各批买入价的加权平均,占比是占总操作规模的百分比。
|
||||||
「我设的价」这一列是你自己下命令定的止损价与目标价,一直在,你改了才变;
|
「人工设定价」这一列是人工通过命令设定的止损价与目标价,一直在,人工改动才变;
|
||||||
系统每天早上自己算的参考位是另一回事,在管理视图的持仓总览里看。</div>
|
系统每天早上自己算的参考位是另一回事,在管理视图的持仓总览里看。</div>
|
||||||
<el-table :data="heldPositions" size="small" border max-height="480" @expand-change="onPosExpand">
|
<el-table :data="heldPositions" size="small" border max-height="480" @expand-change="onPosExpand">
|
||||||
<el-table-column type="expand" width="40">
|
<el-table-column type="expand" width="40">
|
||||||
|
|
@ -924,7 +924,7 @@ body.dock-r:not(.r-fold) .side-r .strip{display:none;}
|
||||||
<!-- 我设的止损价与目标价: 读命令表带出来的 user_stop_price / user_target_price,
|
<!-- 我设的止损价与目标价: 读命令表带出来的 user_stop_price / user_target_price,
|
||||||
不是持仓行上的 stop_ref。stop_ref 每天早上会被系统重算的参考位整列盖掉,
|
不是持仓行上的 stop_ref。stop_ref 每天早上会被系统重算的参考位整列盖掉,
|
||||||
拿它显示会让人以为自己设的价没生效。 -->
|
拿它显示会让人以为自己设的价没生效。 -->
|
||||||
<el-table-column label="我设的价" width="118">
|
<el-table-column label="人工设定价" width="118">
|
||||||
<template #default="s">
|
<template #default="s">
|
||||||
<span class="muted">止损 </span><span v-if="s.row.user_stop_price!=null">{{ s.row.user_stop_price }}</span><span
|
<span class="muted">止损 </span><span v-if="s.row.user_stop_price!=null">{{ s.row.user_stop_price }}</span><span
|
||||||
v-else class="muted">未设</span>
|
v-else class="muted">未设</span>
|
||||||
|
|
@ -958,7 +958,7 @@ body.dock-r:not(.r-fold) .side-r .strip{display:none;}
|
||||||
<span class="muted" v-else>无读数</span>
|
<span class="muted" v-else>无读数</span>
|
||||||
</template>
|
</template>
|
||||||
</el-table-column>
|
</el-table-column>
|
||||||
<el-table-column label="今天的信号" width="108">
|
<el-table-column label="当日信号" width="108">
|
||||||
<template #default="s">
|
<template #default="s">
|
||||||
<template v-if="heldSigOf(s.row.ts_code).length">
|
<template v-if="heldSigOf(s.row.ts_code).length">
|
||||||
<span style="cursor:pointer;text-decoration:underline dotted"
|
<span style="cursor:pointer;text-decoration:underline dotted"
|
||||||
|
|
@ -1005,14 +1005,14 @@ body.dock-r:not(.r-fold) .side-r .strip{display:none;}
|
||||||
</el-table>
|
</el-table>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<!-- 今日在办 -->
|
<!-- 当日指令 -->
|
||||||
<div class="panel tblk" id="t-ins">
|
<div class="panel tblk" id="t-ins">
|
||||||
<h3>今日在办</h3>
|
<h3>当日指令</h3>
|
||||||
<div class="sub2">今天下发的买卖单,按进展分档</div>
|
<div class="sub2">今天下发的买卖单,按进展分档</div>
|
||||||
<el-tabs v-model="insTab">
|
<el-tabs v-model="insTab">
|
||||||
<el-tab-pane name="live">
|
<el-tab-pane name="live">
|
||||||
<template #label>还没成交 <span class="tabn">{{ insLive.length }}</span></template>
|
<template #label>未成交 <span class="tabn">{{ insLive.length }}</span></template>
|
||||||
<el-table :data="insLive" size="small" max-height="280" empty-text="现在没有还没成交的买卖单">
|
<el-table :data="insLive" size="small" max-height="280" empty-text="现在没有未成交的买卖单">
|
||||||
<el-table-column label="股票" min-width="150"><template #default="s"><b>{{ nm(s.row.ts_code) }}</b> <span class="muted mono">{{ s.row.ts_code }}</span></template></el-table-column>
|
<el-table-column label="股票" min-width="150"><template #default="s"><b>{{ nm(s.row.ts_code) }}</b> <span class="muted mono">{{ s.row.ts_code }}</span></template></el-table-column>
|
||||||
<el-table-column label="做什么" width="150"><template #default="s">{{ tx('action', s.row.action) }} · {{ tx('side', s.row.side) }}</template></el-table-column>
|
<el-table-column label="做什么" width="150"><template #default="s">{{ tx('action', s.row.action) }} · {{ tx('side', s.row.side) }}</template></el-table-column>
|
||||||
<el-table-column label="下单量 / 已成交" width="130" align="right"><template #default="s"><span class="mono">{{ s.row.qty }} / {{ s.row.exec_qty||0 }}</span></template></el-table-column>
|
<el-table-column label="下单量 / 已成交" width="130" align="right"><template #default="s"><span class="mono">{{ s.row.qty }} / {{ s.row.exec_qty||0 }}</span></template></el-table-column>
|
||||||
|
|
@ -1022,7 +1022,7 @@ body.dock-r:not(.r-fold) .side-r .strip{display:none;}
|
||||||
</el-table>
|
</el-table>
|
||||||
</el-tab-pane>
|
</el-tab-pane>
|
||||||
<el-tab-pane name="done">
|
<el-tab-pane name="done">
|
||||||
<template #label>今日成交 <span class="tabn">{{ insDone.length }}</span></template>
|
<template #label>已成交 <span class="tabn">{{ insDone.length }}</span></template>
|
||||||
<el-table :data="insDone" size="small" max-height="280" empty-text="今天还没有成交">
|
<el-table :data="insDone" size="small" max-height="280" empty-text="今天还没有成交">
|
||||||
<el-table-column label="股票" min-width="150"><template #default="s"><b>{{ nm(s.row.ts_code) }}</b> <span class="muted mono">{{ s.row.ts_code }}</span></template></el-table-column>
|
<el-table-column label="股票" min-width="150"><template #default="s"><b>{{ nm(s.row.ts_code) }}</b> <span class="muted mono">{{ s.row.ts_code }}</span></template></el-table-column>
|
||||||
<el-table-column label="做什么" width="150"><template #default="s">{{ tx('action', s.row.action) }} · {{ tx('side', s.row.side) }}</template></el-table-column>
|
<el-table-column label="做什么" width="150"><template #default="s">{{ tx('action', s.row.action) }} · {{ tx('side', s.row.side) }}</template></el-table-column>
|
||||||
|
|
@ -1031,7 +1031,7 @@ body.dock-r:not(.r-fold) .side-r .strip{display:none;}
|
||||||
</el-table>
|
</el-table>
|
||||||
</el-tab-pane>
|
</el-tab-pane>
|
||||||
<el-tab-pane name="end">
|
<el-tab-pane name="end">
|
||||||
<template #label>已经结束 <span class="tabn">{{ insEnd.length }}</span></template>
|
<template #label>已终止 <span class="tabn">{{ insEnd.length }}</span></template>
|
||||||
<el-table :data="insEnd" size="small" max-height="280" empty-text="今天没有结束掉的单">
|
<el-table :data="insEnd" size="small" max-height="280" empty-text="今天没有结束掉的单">
|
||||||
<el-table-column label="股票" min-width="150"><template #default="s"><b>{{ nm(s.row.ts_code) }}</b> <span class="muted mono">{{ s.row.ts_code }}</span></template></el-table-column>
|
<el-table-column label="股票" min-width="150"><template #default="s"><b>{{ nm(s.row.ts_code) }}</b> <span class="muted mono">{{ s.row.ts_code }}</span></template></el-table-column>
|
||||||
<el-table-column label="做什么" width="150"><template #default="s">{{ tx('action', s.row.action) }} · {{ tx('side', s.row.side) }}</template></el-table-column>
|
<el-table-column label="做什么" width="150"><template #default="s">{{ tx('action', s.row.action) }} · {{ tx('side', s.row.side) }}</template></el-table-column>
|
||||||
|
|
@ -1039,8 +1039,8 @@ body.dock-r:not(.r-fold) .side-r .strip{display:none;}
|
||||||
</el-table>
|
</el-table>
|
||||||
</el-tab-pane>
|
</el-tab-pane>
|
||||||
<el-tab-pane name="deny">
|
<el-tab-pane name="deny">
|
||||||
<template #label>今天没建仓的 <span class="tabn">{{ denyToday.length }}</span></template>
|
<template #label>未建仓候选 <span class="tabn">{{ denyToday.length }}</span></template>
|
||||||
<div class="sub2" style="margin:8px 2px 4px">今天没建仓的</div>
|
<div class="sub2" style="margin:8px 2px 4px">未建仓候选</div>
|
||||||
<el-table :data="denyToday" size="small" max-height="320" empty-text="今天没有被否决的候选">
|
<el-table :data="denyToday" size="small" max-height="320" empty-text="今天没有被否决的候选">
|
||||||
<el-table-column label="股票" width="180"><template #default="s"><b>{{ s.row.name }}</b> <span class="muted mono">{{ s.row.ts_code }}</span></template></el-table-column>
|
<el-table-column label="股票" width="180"><template #default="s"><b>{{ s.row.name }}</b> <span class="muted mono">{{ s.row.ts_code }}</span></template></el-table-column>
|
||||||
<el-table-column label="为什么没建仓" min-width="380"><template #default="s"><div class="why" style="margin:0">{{ s.row.why || '—' }}</div></template></el-table-column>
|
<el-table-column label="为什么没建仓" min-width="380"><template #default="s"><div class="why" style="margin:0">{{ s.row.why || '—' }}</div></template></el-table-column>
|
||||||
|
|
@ -1049,9 +1049,9 @@ body.dock-r:not(.r-fold) .side-r .strip{display:none;}
|
||||||
</el-tabs>
|
</el-tabs>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<!-- 今天发生了什么: 账本 + 操作记录, 默认收起 -->
|
<!-- 评审账本: 账本 + 操作记录, 默认收起 -->
|
||||||
<div class="panel tblk">
|
<div class="panel tblk">
|
||||||
<h3 style="cursor:pointer" @click="showLedger=!showLedger">今天发生了什么
|
<h3 style="cursor:pointer" @click="showLedger=!showLedger">评审账本
|
||||||
<span class="muted" style="font-weight:400">· {{ ledgerToday.length }} 条</span>
|
<span class="muted" style="font-weight:400">· {{ ledgerToday.length }} 条</span>
|
||||||
<span class="muted" style="font-size:12px;font-weight:normal">{{ showLedger?'▲ 收起':'▼ 展开' }}</span></h3>
|
<span class="muted" style="font-size:12px;font-weight:normal">{{ showLedger?'▲ 收起':'▼ 展开' }}</span></h3>
|
||||||
<template v-if="showLedger">
|
<template v-if="showLedger">
|
||||||
|
|
@ -1062,7 +1062,7 @@ body.dock-r:not(.r-fold) .side-r .strip{display:none;}
|
||||||
<b> {{ nm(l.ts_code) }}</b>:{{ tx('arbiter', l.arbiter) }}{{ tx('verdict', l.verdict) }} {{ tx('action', l.action) }}<span v-if="l.reason" class="muted"> —— {{ l.reason }}</span>
|
<b> {{ nm(l.ts_code) }}</b>:{{ tx('arbiter', l.arbiter) }}{{ tx('verdict', l.verdict) }} {{ tx('action', l.action) }}<span v-if="l.reason" class="muted"> —— {{ l.reason }}</span>
|
||||||
</div>
|
</div>
|
||||||
</template>
|
</template>
|
||||||
<h3 style="margin-top:12px;cursor:pointer" @click="showOpLog=!showOpLog">你做过的操作
|
<h3 style="margin-top:12px;cursor:pointer" @click="showOpLog=!showOpLog">操作日志
|
||||||
<span class="muted" style="font-weight:400">· {{ opLog.length }} 条</span>
|
<span class="muted" style="font-weight:400">· {{ opLog.length }} 条</span>
|
||||||
<span class="muted" style="font-size:12px;font-weight:normal">{{ showOpLog?'▲ 收起':'▼ 展开' }}</span></h3>
|
<span class="muted" style="font-size:12px;font-weight:normal">{{ showOpLog?'▲ 收起':'▼ 展开' }}</span></h3>
|
||||||
<template v-if="showOpLog">
|
<template v-if="showOpLog">
|
||||||
|
|
@ -1096,12 +1096,12 @@ body.dock-r:not(.r-fold) .side-r .strip{display:none;}
|
||||||
</div>
|
</div>
|
||||||
<div class="rail-body">
|
<div class="rail-body">
|
||||||
|
|
||||||
<!-- 等我拍板 -->
|
<!-- 待确认提议 -->
|
||||||
<div class="sect" :class="{closed:sectClosed.prop}" id="t-prop">
|
<div class="sect" :class="{closed:sectClosed.prop}" id="t-prop">
|
||||||
<div class="secth" @click="toggleSect('prop')">等我拍板
|
<div class="secth" @click="toggleSect('prop')">待确认提议
|
||||||
<span class="n">· {{ proposals.length }} 条</span><span class="car">{{ sectClosed.prop?'▸':'▾' }}</span></div>
|
<span class="n">· {{ proposals.length }} 条</span><span class="car">{{ sectClosed.prop?'▸':'▾' }}</span></div>
|
||||||
<div class="sectb">
|
<div class="sectb">
|
||||||
<div class="muted" v-if="!proposals.length">现在没有要你确认的事。</div>
|
<div class="muted" v-if="!proposals.length">当前没有需人工确认的事。</div>
|
||||||
<div v-for="p in proposals" :key="p.proposal_id" class="mini">
|
<div v-for="p in proposals" :key="p.proposal_id" class="mini">
|
||||||
<div class="top"><b>{{ nm(p.ts_code) }}</b>
|
<div class="top"><b>{{ nm(p.ts_code) }}</b>
|
||||||
<span class="muted mono" style="font-size:11px">{{ p.ts_code }}</span>
|
<span class="muted mono" style="font-size:11px">{{ p.ts_code }}</span>
|
||||||
|
|
@ -1156,14 +1156,14 @@ body.dock-r:not(.r-fold) .side-r .strip{display:none;}
|
||||||
<el-button type="success" size="small" @click="decide(p,'ACCEPTED')">采纳</el-button>
|
<el-button type="success" size="small" @click="decide(p,'ACCEPTED')">采纳</el-button>
|
||||||
<el-button type="danger" size="small" @click="decide(p,'DECLINED')">驳回</el-button>
|
<el-button type="danger" size="small" @click="decide(p,'DECLINED')">驳回</el-button>
|
||||||
</div>
|
</div>
|
||||||
<div class="muted" style="font-size:11px;margin-top:4px">采纳=记一笔待执行的单,系统会在允许下单的时间段里挑时机下出去;驳回=今天不再提这只。</div>
|
<div class="muted" style="font-size:11px;margin-top:4px">采纳=生成一笔待执行的指令,系统会在允许下单的时间段里挑时机下出去;驳回=今天不再提这只。</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<!-- 我的策略 -->
|
<!-- 交易方案 -->
|
||||||
<div class="sect" :class="{closed:sectClosed.strat}" id="t-strat">
|
<div class="sect" :class="{closed:sectClosed.strat}" id="t-strat">
|
||||||
<div class="secth" @click="toggleSect('strat')">我的策略
|
<div class="secth" @click="toggleSect('strat')">交易方案
|
||||||
<span class="n">· {{ strategies.length }} 个</span><span class="car">{{ sectClosed.strat?'▸':'▾' }}</span></div>
|
<span class="n">· {{ strategies.length }} 个</span><span class="car">{{ sectClosed.strat?'▸':'▾' }}</span></div>
|
||||||
<div class="sectb">
|
<div class="sectb">
|
||||||
<div class="muted" style="margin-bottom:6px">
|
<div class="muted" style="margin-bottom:6px">
|
||||||
|
|
@ -1179,7 +1179,7 @@ body.dock-r:not(.r-fold) .side-r .strip{display:none;}
|
||||||
<div class="row" style="margin-bottom:8px">
|
<div class="row" style="margin-bottom:8px">
|
||||||
<el-checkbox v-model="showAllStrategies" @change="loadStrategies" size="small" style="font-weight:normal">显示已撤下和已完成</el-checkbox>
|
<el-checkbox v-model="showAllStrategies" @change="loadStrategies" size="small" style="font-weight:normal">显示已撤下和已完成</el-checkbox>
|
||||||
<el-button v-if="isAdmin" size="small" :loading="autoScanBusy" @click="autoScanPreview">试算一遍</el-button>
|
<el-button v-if="isAdmin" size="small" :loading="autoScanBusy" @click="autoScanPreview">试算一遍</el-button>
|
||||||
<span class="help" data-tip="按现在的行情和信号算一遍,看这一轮自动挂方案会做什么。只算给你看,不执行、不留任何记录。">?</span>
|
<span class="help" data-tip="按现在的行情和信号算一遍,看这一轮自动挂方案会做什么。只试算展示,不执行、不留任何记录。">?</span>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div v-if="autoScanRes" class="mini">
|
<div v-if="autoScanRes" class="mini">
|
||||||
|
|
@ -1220,11 +1220,11 @@ body.dock-r:not(.r-fold) .side-r .strip{display:none;}
|
||||||
这一轮不会做任何事:没有票符合触发条件。</div>
|
这一轮不会做任何事:没有票符合触发条件。</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div class="muted" v-if="!strategies.length">还没有挂过交易方案。在「我的持仓」行里点「⋯ → 挂交易方案」创建。</div>
|
<div class="muted" v-if="!strategies.length">尚未挂交易方案。在「持仓」行里点「⋯ → 挂交易方案」创建。</div>
|
||||||
<div v-for="s in strategiesShown" :key="s.strategy_id" class="mini">
|
<div v-for="s in strategiesShown" :key="s.strategy_id" class="mini">
|
||||||
<div class="top"><b>{{ nm(s.ts_code) }}</b>
|
<div class="top"><b>{{ nm(s.ts_code) }}</b>
|
||||||
<el-tag size="small" effect="dark">{{ tx('stype', s.type) }}</el-tag>
|
<el-tag size="small" effect="dark">{{ tx('stype', s.type) }}</el-tag>
|
||||||
<span class="muted" style="font-size:11px">{{ s.autonomy==='auto'?'自动执行':'待我拍板' }}</span>
|
<span class="muted" style="font-size:11px">{{ s.autonomy==='auto'?'自动执行':'需人工确认' }}</span>
|
||||||
<span class="spacer"></span>
|
<span class="spacer"></span>
|
||||||
<el-tag size="small" :type="s.status==='ACTIVE'?'primary':'info'">{{ tx('stratStatus', s.status) }}</el-tag></div>
|
<el-tag size="small" :type="s.status==='ACTIVE'?'primary':'info'">{{ tx('stratStatus', s.status) }}</el-tag></div>
|
||||||
<div class="st">{{ stratStateText(s) }}</div>
|
<div class="st">{{ stratStateText(s) }}</div>
|
||||||
|
|
@ -1244,17 +1244,17 @@ body.dock-r:not(.r-fold) .side-r .strip{display:none;}
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<!-- 系统在盯的候选 -->
|
<!-- 建仓候选 -->
|
||||||
<div class="sect" :class="{closed:sectClosed.pool}">
|
<div class="sect" :class="{closed:sectClosed.pool}">
|
||||||
<div class="secth" @click="toggleSect('pool')">系统在盯的候选
|
<div class="secth" @click="toggleSect('pool')">建仓候选
|
||||||
<span class="n">· {{ watchCandidates.length }} 只</span><span class="car">{{ sectClosed.pool?'▸':'▾' }}</span></div>
|
<span class="n">· {{ watchCandidates.length }} 只</span><span class="car">{{ sectClosed.pool?'▸':'▾' }}</span></div>
|
||||||
<div class="sectb">
|
<div class="sectb">
|
||||||
<div class="muted" style="margin-bottom:5px">今天选股计划里、系统还盯着、可能会建仓的票。计划日 {{ planStatus.date || '—' }}。
|
<div class="muted" style="margin-bottom:5px">今天选股计划里、系统仍在评估、可能建仓的票。计划日 {{ planStatus.date || '—' }}。
|
||||||
已经下单的、等你拍板的、被否决的,分别在「今日在办」和「等我拍板」。</div>
|
已下单、待确认、未建仓的,分别在「当日指令」和「待确认提议」。</div>
|
||||||
<div class="pool-note" v-if="(openScan.notes||[]).length">
|
<div class="pool-note" v-if="(openScan.notes||[]).length">
|
||||||
<span v-for="(n,i) in openScan.notes" :key="i">{{ n }}<span v-if="i < openScan.notes.length-1">;</span></span></div>
|
<span v-for="(n,i) in openScan.notes" :key="i">{{ n }}<span v-if="i < openScan.notes.length-1">;</span></span></div>
|
||||||
<div class="muted" v-if="planStatus.ok===false">上游的选股计划现在读不到。</div>
|
<div class="muted" v-if="planStatus.ok===false">上游的选股计划现在读不到。</div>
|
||||||
<div class="muted" v-else-if="!watchCandidates.length">今天没有还在盯着待建仓的候选,都已经处理过了。</div>
|
<div class="muted" v-else-if="!watchCandidates.length">当前没有待建仓的候选,都已处理。</div>
|
||||||
<div v-else v-for="r in watchCandidates" :key="r.ts_code" class="sigrow2">
|
<div v-else v-for="r in watchCandidates" :key="r.ts_code" class="sigrow2">
|
||||||
<span class="muted mono t">第{{ r.rank }}</span>
|
<span class="muted mono t">第{{ r.rank }}</span>
|
||||||
<span class="nm">{{ r.name || nm(r.ts_code) }}</span>
|
<span class="nm">{{ r.name || nm(r.ts_code) }}</span>
|
||||||
|
|
@ -1273,18 +1273,18 @@ body.dock-r:not(.r-fold) .side-r .strip{display:none;}
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<!-- 整个组合的操作 -->
|
<!-- 组合操作 -->
|
||||||
<div class="sect" :class="{closed:sectClosed.ops}">
|
<div class="sect" :class="{closed:sectClosed.ops}">
|
||||||
<div class="secth" @click="toggleSect('ops')">整个组合的操作<span class="car">{{ sectClosed.ops?'▸':'▾' }}</span></div>
|
<div class="secth" @click="toggleSect('ops')">组合操作<span class="car">{{ sectClosed.ops?'▸':'▾' }}</span></div>
|
||||||
<div class="sectb">
|
<div class="sectb">
|
||||||
<div class="muted" style="margin-bottom:6px">数值都按当前实际持仓算好并预览影响金额,不用盲填;危险动作要确认两次。</div>
|
<div class="muted" style="margin-bottom:6px">数值都按当前实际持仓算好并预览影响金额,不用盲填;危险动作要确认两次。</div>
|
||||||
<div class="oprow2">
|
<div class="oprow2">
|
||||||
<span class="b"><el-button size="small" :type="ov.buy_halt?'success':'warning'" @click="ov.buy_halt?pResumeBuy():pHaltBuy()">{{ ov.buy_halt?'恢复买入':'暂停买入' }}</el-button></span>
|
<span class="b"><el-button size="small" :type="ov.buy_halt?'success':'warning'" @click="ov.buy_halt?pResumeBuy():pHaltBuy()">{{ ov.buy_halt?'恢复买入':'暂停买入' }}</el-button></span>
|
||||||
<span class="d">停掉所有新的买入,并撤掉已经挂出去、还没成交的买单。卖出和止损照常。现在:{{ ov.buy_halt?'已暂停买入':'买入正常' }}。</span>
|
<span class="d">停掉所有新的买入,并撤掉已经挂出去、未成交的买单。卖出和止损照常。现在:{{ ov.buy_halt?'已暂停买入':'买入正常' }}。</span>
|
||||||
</div>
|
</div>
|
||||||
<div class="oprow2">
|
<div class="oprow2">
|
||||||
<span class="b"><el-button size="small" :type="ov.exec_halt?'success':''" @click="ov.exec_halt?pResumeAll():pHaltAll()">{{ ov.exec_halt?'结束休假':'休假模式' }}</el-button></span>
|
<span class="b"><el-button size="small" :type="ov.exec_halt?'success':''" @click="ov.exec_halt?pResumeAll():pHaltAll()">{{ ov.exec_halt?'恢复执行':'暂停执行' }}</el-button></span>
|
||||||
<span class="d">系统的自动动作全部停下,只保留每天的对账和日报。现在:{{ ov.exec_halt?'休假中':'正常运行' }}。</span>
|
<span class="d">系统自动动作全部停下,只保留每日对账与日报。当前:{{ ov.exec_halt?'已暂停执行':'正常运行' }}。</span>
|
||||||
</div>
|
</div>
|
||||||
<div class="oprow2">
|
<div class="oprow2">
|
||||||
<span class="b"><el-button size="small" type="primary" :disabled="!posMoveValid" @click="doPosMove()">调整仓位</el-button></span>
|
<span class="b"><el-button size="small" type="primary" :disabled="!posMoveValid" @click="doPosMove()">调整仓位</el-button></span>
|
||||||
|
|
@ -1315,7 +1315,7 @@ body.dock-r:not(.r-fold) .side-r .strip{display:none;}
|
||||||
<div class="sect" :class="{closed:sectClosed.set}">
|
<div class="sect" :class="{closed:sectClosed.set}">
|
||||||
<div class="secth" @click="toggleSect('set')">设置<span class="car">{{ sectClosed.set?'▸':'▾' }}</span></div>
|
<div class="secth" @click="toggleSect('set')">设置<span class="car">{{ sectClosed.set?'▸':'▾' }}</span></div>
|
||||||
<div class="sectb">
|
<div class="sectb">
|
||||||
<div class="muted" style="margin-bottom:8px">每组先给一句「现在是什么状态」,再让你改其中的项。改完点保存就生效。</div>
|
<div class="muted" style="margin-bottom:8px">每组先给一句「当前状态」,再改其中的项。改完点保存就生效。</div>
|
||||||
<div class="set-card">
|
<div class="set-card">
|
||||||
<div class="set-sum">资金与仓位纪律:{{ sumPosition }}</div>
|
<div class="set-sum">资金与仓位纪律:{{ sumPosition }}</div>
|
||||||
<div class="set-fields" v-if="pmap['PMS_TOTAL_SCALE']">
|
<div class="set-fields" v-if="pmap['PMS_TOTAL_SCALE']">
|
||||||
|
|
@ -1343,7 +1343,7 @@ body.dock-r:not(.r-fold) .side-r .strip{display:none;}
|
||||||
<div class="set-fields" v-if="pmap['PMS_AUTONOMY']">
|
<div class="set-fields" v-if="pmap['PMS_AUTONOMY']">
|
||||||
<el-select v-model="pmap['PMS_AUTONOMY'].value" size="small" style="width:180px">
|
<el-select v-model="pmap['PMS_AUTONOMY'].value" size="small" style="width:180px">
|
||||||
<el-option label="全自动" value="full"></el-option>
|
<el-option label="全自动" value="full"></el-option>
|
||||||
<el-option label="只提议,等你拍板" value="propose_only"></el-option>
|
<el-option label="仅提议,需人工确认" value="propose_only"></el-option>
|
||||||
<el-option label="暂停" value="off"></el-option>
|
<el-option label="暂停" value="off"></el-option>
|
||||||
</el-select>
|
</el-select>
|
||||||
</div>
|
</div>
|
||||||
|
|
@ -1442,7 +1442,7 @@ body.dock-r:not(.r-fold) .side-r .strip{display:none;}
|
||||||
<el-form-item label="自主档位">
|
<el-form-item label="自主档位">
|
||||||
<el-radio-group v-model="stratDlg.autonomy">
|
<el-radio-group v-model="stratDlg.autonomy">
|
||||||
<el-radio-button label="auto">自动执行</el-radio-button>
|
<el-radio-button label="auto">自动执行</el-radio-button>
|
||||||
<el-radio-button label="confirm">待我拍板</el-radio-button>
|
<el-radio-button label="confirm">需人工确认</el-radio-button>
|
||||||
</el-radio-group>
|
</el-radio-group>
|
||||||
</el-form-item>
|
</el-form-item>
|
||||||
</el-form>
|
</el-form>
|
||||||
|
|
@ -1729,7 +1729,7 @@ body.dock-r:not(.r-fold) .side-r .strip{display:none;}
|
||||||
class="muted">今天没有读数</span>
|
class="muted">今天没有读数</span>
|
||||||
</template>
|
</template>
|
||||||
</el-table-column>
|
</el-table-column>
|
||||||
<!-- 研究面列 (2026-09-14 页面收尾包): 与「我的持仓」那一列同款, 点技术面立场看研究面抽屉。 -->
|
<!-- 研究面列 (2026-09-14 页面收尾包): 与「持仓」那一列同款, 点技术面立场看研究面抽屉。 -->
|
||||||
<el-table-column label="研究面" width="92">
|
<el-table-column label="研究面" width="92">
|
||||||
<template #default="s">
|
<template #default="s">
|
||||||
<template v-if="s.row.tech">
|
<template v-if="s.row.tech">
|
||||||
|
|
@ -1779,9 +1779,9 @@ body.dock-r:not(.r-fold) .side-r .strip{display:none;}
|
||||||
<el-table-column label="兜底" width="70">
|
<el-table-column label="兜底" width="70">
|
||||||
<template #default="c">{{ c.row.forced ? '是' : '—' }}</template>
|
<template #default="c">{{ c.row.forced ? '是' : '—' }}</template>
|
||||||
</el-table-column>
|
</el-table-column>
|
||||||
<el-table-column prop="reason" label="出手依据" min-width="260"></el-table-column>
|
<el-table-column prop="reason" label="下单依据" min-width="260"></el-table-column>
|
||||||
</el-table>
|
</el-table>
|
||||||
<div v-else class="muted" style="margin-top:6px">尚无出手记录</div>
|
<div v-else class="muted" style="margin-top:6px">尚无下单记录</div>
|
||||||
</div>
|
</div>
|
||||||
</template>
|
</template>
|
||||||
</el-table-column>
|
</el-table-column>
|
||||||
|
|
@ -1840,7 +1840,7 @@ body.dock-r:not(.r-fold) .side-r .strip{display:none;}
|
||||||
<h3>待确认队列(现在是{{ tx('autonomy', ov.autonomy) }})</h3>
|
<h3>待确认队列(现在是{{ tx('autonomy', ov.autonomy) }})</h3>
|
||||||
<div class="muted" style="margin-bottom:8px">
|
<div class="muted" style="margin-bottom:8px">
|
||||||
propose_only 档位下, 增持类自主动作全部在此等待裁决; 减持方向不设确认门槛。
|
propose_only 档位下, 增持类自主动作全部在此等待裁决; 减持方向不设确认门槛。
|
||||||
采纳后先落指令表 (先记账后动作), 由择时执行器分日出手。
|
采纳后先落指令表 (先记账后动作), 由择时执行器分日下单。
|
||||||
</div>
|
</div>
|
||||||
<el-alert v-if="dm.judge && !dm.judge.available" type="warning" effect="dark" show-icon
|
<el-alert v-if="dm.judge && !dm.judge.available" type="warning" effect="dark" show-icon
|
||||||
:closable="false" style="margin-bottom:10px"
|
:closable="false" style="margin-bottom:10px"
|
||||||
|
|
@ -2065,7 +2065,7 @@ body.dock-r:not(.r-fold) .side-r .strip{display:none;}
|
||||||
<div class="cs-scrim" @click="closeCodeSignals"></div>
|
<div class="cs-scrim" @click="closeCodeSignals"></div>
|
||||||
<aside class="cs-drawer">
|
<aside class="cs-drawer">
|
||||||
<div class="cs-h">
|
<div class="cs-h">
|
||||||
<span class="ttl">{{ nm(csCode) }} 今天的信号</span>
|
<span class="ttl">{{ nm(csCode) }} · 个股详情</span>
|
||||||
<span class="muted mono" style="font-size:11.5px">{{ csCode }}</span>
|
<span class="muted mono" style="font-size:11.5px">{{ csCode }}</span>
|
||||||
<span class="pin" v-if="csHeld(csCode)">持仓</span>
|
<span class="pin" v-if="csHeld(csCode)">持仓</span>
|
||||||
<span class="pin plan" v-if="!csHeld(csCode) && csInPlan(csCode)">计划</span>
|
<span class="pin plan" v-if="!csHeld(csCode) && csInPlan(csCode)">计划</span>
|
||||||
|
|
@ -2078,7 +2078,7 @@ body.dock-r:not(.r-fold) .side-r .strip{display:none;}
|
||||||
<div class="cs-b">
|
<div class="cs-b">
|
||||||
<div class="cs-warn" v-if="csErr">
|
<div class="cs-warn" v-if="csErr">
|
||||||
完整信号没取到({{ csErr }})。下面是页面手上已有的那份快照,它只留了每类最新的若干条,
|
完整信号没取到({{ csErr }})。下面是页面手上已有的那份快照,它只留了每类最新的若干条,
|
||||||
早盘的信号可能已经被挤掉。
|
早盘的信号可能已被覆盖。
|
||||||
<el-button size="small" style="margin-left:6px" @click="csLoadFull">重试</el-button>
|
<el-button size="small" style="margin-left:6px" @click="csLoadFull">重试</el-button>
|
||||||
</div>
|
</div>
|
||||||
<div class="cs-warn" v-if="csTruncated.length">
|
<div class="cs-warn" v-if="csTruncated.length">
|
||||||
|
|
@ -2210,8 +2210,8 @@ body.dock-r:not(.r-fold) .side-r .strip{display:none;}
|
||||||
<el-button @click="ops('plan-pending')" :loading="opsLoading">推进待规划命令</el-button>
|
<el-button @click="ops('plan-pending')" :loading="opsLoading">推进待规划命令</el-button>
|
||||||
<el-button @click="ops('materialize')" :loading="opsLoading">方案转指令</el-button>
|
<el-button @click="ops('materialize')" :loading="opsLoading">方案转指令</el-button>
|
||||||
<el-button type="primary" @click="ops('exec-tick?dry_run=true')" :loading="opsLoading">
|
<el-button type="primary" @click="ops('exec-tick?dry_run=true')" :loading="opsLoading">
|
||||||
出手试算 (不下发)</el-button>
|
下单试算 (不下发)</el-button>
|
||||||
<el-button @click="ops('exec-tick')" :loading="opsLoading">出手一跳</el-button>
|
<el-button @click="ops('exec-tick')" :loading="opsLoading">执行一轮下单</el-button>
|
||||||
<el-button @click="ops('sweep-windows')" :loading="opsLoading">窗口收口</el-button>
|
<el-button @click="ops('sweep-windows')" :loading="opsLoading">窗口收口</el-button>
|
||||||
</div>
|
</div>
|
||||||
<div class="row" style="margin-top:8px">
|
<div class="row" style="margin-top:8px">
|
||||||
|
|
@ -2325,7 +2325,7 @@ body.dock-r:not(.r-fold) .side-r .strip{display:none;}
|
||||||
empty-text="持仓票没被摘也没降档">
|
empty-text="持仓票没被摘也没降档">
|
||||||
<el-table-column prop="ts_code" label="代码" width="104"></el-table-column>
|
<el-table-column prop="ts_code" label="代码" width="104"></el-table-column>
|
||||||
<el-table-column prop="name" label="名称" width="92"></el-table-column>
|
<el-table-column prop="name" label="名称" width="92"></el-table-column>
|
||||||
<el-table-column prop="why" label="发生了什么" min-width="200">
|
<el-table-column prop="why" label="评审结论" min-width="200">
|
||||||
<template #default="s"><span style="color:#F56C6C">{{ s.row.why }}</span></template>
|
<template #default="s"><span style="color:#F56C6C">{{ s.row.why }}</span></template>
|
||||||
</el-table-column>
|
</el-table-column>
|
||||||
<el-table-column prop="rank" label="上版 rank" width="90"></el-table-column>
|
<el-table-column prop="rank" label="上版 rank" width="90"></el-table-column>
|
||||||
|
|
@ -2341,7 +2341,7 @@ body.dock-r:not(.r-fold) .side-r .strip{display:none;}
|
||||||
<el-table :data="chgWatch" size="small" border max-height="220" empty-text="无">
|
<el-table :data="chgWatch" size="small" border max-height="220" empty-text="无">
|
||||||
<el-table-column prop="ts_code" label="代码" width="104"></el-table-column>
|
<el-table-column prop="ts_code" label="代码" width="104"></el-table-column>
|
||||||
<el-table-column prop="name" label="名称" width="92"></el-table-column>
|
<el-table-column prop="name" label="名称" width="92"></el-table-column>
|
||||||
<el-table-column prop="why" label="发生了什么" min-width="180"></el-table-column>
|
<el-table-column prop="why" label="评审结论" min-width="180"></el-table-column>
|
||||||
<el-table-column prop="rank" label="上版 rank" width="90"></el-table-column>
|
<el-table-column prop="rank" label="上版 rank" width="90"></el-table-column>
|
||||||
<el-table-column label="持仓" width="70">
|
<el-table-column label="持仓" width="70">
|
||||||
<template #default="s">
|
<template #default="s">
|
||||||
|
|
@ -2485,7 +2485,7 @@ createApp({
|
||||||
// 「显示已完成/已移除」开关 (默认关: 只看进行中)。命令与策略勾选时会拉全量含已归档 (loadX 传
|
// 「显示已完成/已移除」开关 (默认关: 只看进行中)。命令与策略勾选时会拉全量含已归档 (loadX 传
|
||||||
// include_archived); 在途指令纯前端过滤, 不重拉。
|
// include_archived); 在途指令纯前端过滤, 不重拉。
|
||||||
const showAllCommands = ref(false), showAllStrategies = ref(false), showAllInstr = ref(false);
|
const showAllCommands = ref(false), showAllStrategies = ref(false), showAllInstr = ref(false);
|
||||||
const showLedger = ref(false); // 「今天发生了什么」默认折叠
|
const showLedger = ref(false); // 「评审账本」默认折叠
|
||||||
const showOpLog = ref(false); // 操作日志默认折叠
|
const showOpLog = ref(false); // 操作日志默认折叠
|
||||||
const showSettings = ref(false); // 设置默认折叠
|
const showSettings = ref(false); // 设置默认折叠
|
||||||
const lastRefresh = ref('');
|
const lastRefresh = ref('');
|
||||||
|
|
@ -2649,14 +2649,14 @@ createApp({
|
||||||
ATTACH:'挂方案', HANDOFF:'方案接力', NOTE:'记一笔' },
|
ATTACH:'挂方案', HANDOFF:'方案接力', NOTE:'记一笔' },
|
||||||
// none 是停手那一类动作真实产出的方向(planner.py:34),不补会印成原码。
|
// none 是停手那一类动作真实产出的方向(planner.py:34),不补会印成原码。
|
||||||
side: { buy:'买入', sell:'卖出', none:'不动' },
|
side: { buy:'买入', sell:'卖出', none:'不动' },
|
||||||
autonomy: { full:'全自动', propose_only:'只提议,等你拍板', off:'暂停' },
|
autonomy: { full:'全自动', propose_only:'仅提议,需人工确认', off:'暂停执行' },
|
||||||
cushion: { SOLID:'厚垫', THIN:'薄垫', NONE:'浮亏无垫' },
|
cushion: { SOLID:'厚垫', THIN:'薄垫', NONE:'浮亏无垫' },
|
||||||
lot: { BASE:'首批', FILL:'回落补足', ADD:'盈利加仓', DCA:'浮亏补仓', T0:'做T仓', RECON:'对账修正' },
|
lot: { BASE:'首批', FILL:'回落补足', ADD:'盈利加仓', DCA:'浮亏补仓', T0:'做T仓', RECON:'对账修正' },
|
||||||
frozen: { NONE:'', COMMAND_HALT:'命令要求只卖不买', BRAKE:'组合刹车,只卖不买', MANUAL:'你设的只卖不买' },
|
frozen: { NONE:'', COMMAND_HALT:'命令要求只卖不买', BRAKE:'组合刹车,只卖不买', MANUAL:'人工冻结,只卖不买' },
|
||||||
posStatus: { PLANNED:'计划建仓', OPENING:'建仓中', HOLDING:'持有中', EXITING:'清仓中', CLOSED:'已清仓' },
|
posStatus: { PLANNED:'计划建仓', OPENING:'建仓中', HOLDING:'持有中', EXITING:'清仓中', CLOSED:'已清仓' },
|
||||||
insStatus: { PROPOSED:'等着校验', RULE_PASSED:'合规检查已过', JUDGE_PASSED:'决策系统复核已过', DISPATCHED:'已挂单,等成交',
|
insStatus: { PROPOSED:'待校验', RULE_PASSED:'合规检查已过', JUDGE_PASSED:'决策系统复核已过', DISPATCHED:'已挂单待成交',
|
||||||
CONFIRMED:'已成交', REJECTED:'被挡下', EXPIRED:'到期作废', CANCELLED:'已撤销' },
|
CONFIRMED:'已成交', REJECTED:'已拦截', EXPIRED:'到期作废', CANCELLED:'已撤销' },
|
||||||
arbiter: { rule:'合规检查', judge:'决策系统', user:'你', system:'系统自动' },
|
arbiter: { rule:'合规检查', judge:'决策系统', user:'人工', system:'系统自动' },
|
||||||
// 这一组是**评审账本**的结论,只有四个值。研判闸的结论是另一组 judgeVerdict,
|
// 这一组是**评审账本**的结论,只有四个值。研判闸的结论是另一组 judgeVerdict,
|
||||||
// 两者别混:账本记的是「这一步放行还是驳回」,研判记的是「决策系统怎么说」。
|
// 两者别混:账本记的是「这一步放行还是驳回」,研判记的是「决策系统怎么说」。
|
||||||
verdict: { PASS:'放行', REJECT:'驳回', NOTE:'记一笔', WARN:'留了提醒',
|
verdict: { PASS:'放行', REJECT:'驳回', NOTE:'记一笔', WARN:'留了提醒',
|
||||||
|
|
@ -2664,15 +2664,15 @@ createApp({
|
||||||
// 参考位这一列的数是谁给的。系统每个交易日盘前重算一遍并把整列重写, 所以它基本上
|
// 参考位这一列的数是谁给的。系统每个交易日盘前重算一遍并把整列重写, 所以它基本上
|
||||||
// 永远是系统口径; 用户设的止损价只在下达当天投影到这一列上, 第二天早上就换回系统值了。
|
// 永远是系统口径; 用户设的止损价只在下达当天投影到这一列上, 第二天早上就换回系统值了。
|
||||||
// 用户自己设的两个价另有「我设的」一列, 那一列读命令表, 不会被盖。
|
// 用户自己设的两个价另有「我设的」一列, 那一列读命令表, 不会被盖。
|
||||||
refSource: { bionic:'决策系统给的', self_calc:'系统自算', user:'你今天设的', none:'取不到' },
|
refSource: { bionic:'决策系统给的', self_calc:'系统自算', user:'人工当日设定', none:'取不到' },
|
||||||
stype: { T0:'做T', GRID:'网格', TRAIL:'跟踪止盈' },
|
stype: { T0:'做T', GRID:'网格', TRAIL:'跟踪止盈' },
|
||||||
stratStatus: { ACTIVE:'运行中', PAUSED:'已暂停', CANCELLED:'已撤下', DONE:'已完成' },
|
stratStatus: { ACTIVE:'运行中', PAUSED:'已暂停', CANCELLED:'已撤下', DONE:'已完成' },
|
||||||
// 研判闸的结论。注意 PASS 不代表真问过决策系统:不在研判范围内的动作直接回 PASS。
|
// 研判闸的结论。注意 PASS 不代表真问过决策系统:不在研判范围内的动作直接回 PASS。
|
||||||
judgeVerdict: { PASS:'通过', REJECT:'决策系统驳回', UNAVAILABLE:'决策系统读不到',
|
judgeVerdict: { PASS:'通过', REJECT:'决策系统驳回', UNAVAILABLE:'决策系统读不到',
|
||||||
STRATEGY:'交易方案发起', STRATEGY_RISK:'方案风控发起' },
|
STRATEGY:'交易方案发起', STRATEGY_RISK:'方案风控发起' },
|
||||||
// 择时执行器的出手判定。不是评审结论,别并进 verdict 组。
|
// 择时执行器的出手判定。不是评审结论,别并进 verdict 组。
|
||||||
execAct: { FIRE:'出手', WAIT:'条件没到', SKIP:'当日跳过', STOP:'今日不再出手',
|
execAct: { FIRE:'下单', WAIT:'条件未满足', SKIP:'当日跳过', STOP:'当日停止下单',
|
||||||
REJECT:'终检没过' },
|
REJECT:'终检未通过' },
|
||||||
cmdStatus: { PENDING:'待处理', EFFECTIVE:'生效中', SUPERSEDED:'已被覆盖',
|
cmdStatus: { PENDING:'待处理', EFFECTIVE:'生效中', SUPERSEDED:'已被覆盖',
|
||||||
PLANNING:'规划中', EXECUTING:'执行中', PARTIAL:'部分完成',
|
PLANNING:'规划中', EXECUTING:'执行中', PARTIAL:'部分完成',
|
||||||
DONE:'已完成', CANCELLED:'已撤销' },
|
DONE:'已完成', CANCELLED:'已撤销' },
|
||||||
|
|
@ -2690,7 +2690,7 @@ createApp({
|
||||||
OFFLINE:'掉线', STOPPED:'已停' },
|
OFFLINE:'掉线', STOPPED:'已停' },
|
||||||
// 个股交易方案的自主档,取值与全局参数那套(full/propose_only/off)完全不同,
|
// 个股交易方案的自主档,取值与全局参数那套(full/propose_only/off)完全不同,
|
||||||
// 字段同名但不是一回事,绝不能合并成一组。
|
// 字段同名但不是一回事,绝不能合并成一组。
|
||||||
stratAutonomy: { auto:'自动执行', confirm:'待我拍板' },
|
stratAutonomy: { auto:'自动执行', confirm:'需人工确认' },
|
||||||
dispatchMode: { shadow:'影子·不真下单', ws:'实盘下发' },
|
dispatchMode: { shadow:'影子·不真下单', ws:'实盘下发' },
|
||||||
};
|
};
|
||||||
const tx = (g, c) => {
|
const tx = (g, c) => {
|
||||||
|
|
@ -2768,14 +2768,14 @@ createApp({
|
||||||
if (!v) return '';
|
if (!v) return '';
|
||||||
const basis = String(h.basis || '').trim();
|
const basis = String(h.basis || '').trim();
|
||||||
const missing = (h.missing || []).filter(Boolean);
|
const missing = (h.missing || []).filter(Boolean);
|
||||||
if (v === '候选') return basis || '选股系统认为今天可以买';
|
if (v === '候选') return basis || '选股系统判为可建仓';
|
||||||
if (v === '关注') {
|
if (v === '关注') {
|
||||||
return missing.length
|
return missing.length
|
||||||
? ('它自己也拿不准,还缺:' + missing.join(';') + ',要你来定')
|
? ('选股系统无法判断,还缺:' + missing.join(';') + ',需人工裁决')
|
||||||
: '它自己也拿不准,要你来定';
|
: '选股系统无法判断,需人工裁决';
|
||||||
}
|
}
|
||||||
if (v === '仅展示') return basis || '选股系统今天不建议买入';
|
if (v === '仅展示') return basis || '选股系统今日不建议买入';
|
||||||
return '选股系统给的结论是「' + v + '」,本系统没见过这个说法,按「要你来定」处理';
|
return '选股系统给的结论是「' + v + '」,词表无法识别,按需人工裁决处理';
|
||||||
};
|
};
|
||||||
|
|
||||||
// 操作日志里那一列, 后端落库的是函数名式的操作码, 例如 issue_command:EXIT_STOCK、
|
// 操作日志里那一列, 后端落库的是函数名式的操作码, 例如 issue_command:EXIT_STOCK、
|
||||||
|
|
@ -2863,10 +2863,10 @@ createApp({
|
||||||
}
|
}
|
||||||
|
|
||||||
const heldPositions = computed(() => (positions.value || []).filter(p => (p.total_qty || 0) > 0));
|
const heldPositions = computed(() => (positions.value || []).filter(p => (p.total_qty || 0) > 0));
|
||||||
// ---- 今日在办分档 + 候选处置 (2026-08-12 重构新增) ----
|
// ---- 当日指令分档 + 候选处置 (2026-08-12 重构新增) ----
|
||||||
const todayYmd = computed(() => (String(health.value.now || '').slice(0, 10).replace(/-/g, '')) || '');
|
const todayYmd = computed(() => (String(health.value.now || '').slice(0, 10).replace(/-/g, '')) || '');
|
||||||
const todayDateStr = computed(() => String(health.value.now || '').slice(0, 10));
|
const todayDateStr = computed(() => String(health.value.now || '').slice(0, 10));
|
||||||
// 「今天发生了什么 / 这只票今天的记录」只收 decided_at 是今天的行 (2026-08-28):
|
// 「评审账本 / 这只票今天的记录」只收 decided_at 是今天的行 (2026-08-28):
|
||||||
// /api/ledger 按 id 倒序取近 60 条不带日期条件, 当天记录少时列表被前几天的行填满,
|
// /api/ledger 按 id 倒序取近 60 条不带日期条件, 当天记录少时列表被前几天的行填满,
|
||||||
// 主面板又只显示时分 —— 昨天 14:31 的驳回看起来就是今天 14:31 发生的。
|
// 主面板又只显示时分 —— 昨天 14:31 的驳回看起来就是今天 14:31 发生的。
|
||||||
const ledgerToday = computed(() => {
|
const ledgerToday = computed(() => {
|
||||||
|
|
@ -2889,9 +2889,9 @@ createApp({
|
||||||
const _sameDayIns = r => { const y = todayYmd.value;
|
const _sameDayIns = r => { const y = todayYmd.value;
|
||||||
const id = String(r.instruction_id || '');
|
const id = String(r.instruction_id || '');
|
||||||
// 策略指令的前缀是 STR{ymd} (strategy_runner), 不是 INS_ —— 原来它们一进终态就从
|
// 策略指令的前缀是 STR{ymd} (strategy_runner), 不是 INS_ —— 原来它们一进终态就从
|
||||||
// 「今日成交/已经结束」和消息栏同时消失, 交易员眼看着自己的单子凭空不见 (2026-08-28)
|
// 「已成交/已终止」和消息栏同时消失, 交易员眼看着自己的单子凭空不见 (2026-08-28)
|
||||||
return !y || id.indexOf('INS_' + y + '_') === 0 || id.indexOf('STR' + y) === 0; };
|
return !y || id.indexOf('INS_' + y + '_') === 0 || id.indexOf('STR' + y) === 0; };
|
||||||
// 已归档的一律不进「今日在办」三档 —— 归档=从所有面板隐藏。
|
// 已归档的一律不进「当日指令」三档 —— 归档=从所有面板隐藏。
|
||||||
const insLive = computed(() => (instructions.value || []).filter(r => _LIVE_INS.includes(r.status) && !_isArch(r)));
|
const insLive = computed(() => (instructions.value || []).filter(r => _LIVE_INS.includes(r.status) && !_isArch(r)));
|
||||||
const insDone = computed(() => (instructions.value || []).filter(r => r.status === 'CONFIRMED' && _sameDayIns(r) && !_isArch(r)));
|
const insDone = computed(() => (instructions.value || []).filter(r => r.status === 'CONFIRMED' && _sameDayIns(r) && !_isArch(r)));
|
||||||
const insEnd = computed(() => (instructions.value || []).filter(r => ['EXPIRED', 'CANCELLED', 'REJECTED'].includes(r.status) && _sameDayIns(r) && !_isArch(r)));
|
const insEnd = computed(() => (instructions.value || []).filter(r => ['EXPIRED', 'CANCELLED', 'REJECTED'].includes(r.status) && _sameDayIns(r) && !_isArch(r)));
|
||||||
|
|
@ -2911,12 +2911,12 @@ createApp({
|
||||||
function dispOf(code) {
|
function dispOf(code) {
|
||||||
const inst = (instructions.value || []).find(i => i.ts_code === code && i.action === 'OPEN'
|
const inst = (instructions.value || []).find(i => i.ts_code === code && i.action === 'OPEN'
|
||||||
&& (_LIVE_INS.includes(i.status) || _sameDayIns(i))); // 旧的终态单不算「已下单」(2026-08-28)
|
&& (_LIVE_INS.includes(i.status) || _sameDayIns(i))); // 旧的终态单不算「已下单」(2026-08-28)
|
||||||
if (inst) return { cls: 'done', label: '已下单', why: '在『今日在办』(' + tx('insStatus', inst.status) + ')' };
|
if (inst) return { cls: 'done', label: '已下单', why: '在『当日指令』(' + tx('insStatus', inst.status) + ')' };
|
||||||
const prop = (proposals.value || []).find(p => p.ts_code === code && p.action === 'OPEN');
|
const prop = (proposals.value || []).find(p => p.ts_code === code && p.action === 'OPEN');
|
||||||
if (prop) return { cls: 'queue', label: '待你确认', why: '已生成提议,在『等我拍板』等你点头' };
|
if (prop) return { cls: 'queue', label: '待确认', why: '已生成提议,在『待确认提议』等人工裁决' };
|
||||||
const d = (openScan.value.by_code || {})[code];
|
const d = (openScan.value.by_code || {})[code];
|
||||||
if (d) {
|
if (d) {
|
||||||
if (d.disp === 'would') return { cls: 'would', label: '将建仓', why: d.why };
|
if (d.disp === 'would') return { cls: 'would', label: '拟建仓', why: d.why };
|
||||||
// 三源合议判观察 (2026-09-11): 等技术面开口, 是「在盯」不是「被否决」——
|
// 三源合议判观察 (2026-09-11): 等技术面开口, 是「在盯」不是「被否决」——
|
||||||
// 归到 watch 那一档而不是今天没建仓那一档, 因为它还会回来。
|
// 归到 watch 那一档而不是今天没建仓那一档, 因为它还会回来。
|
||||||
if (d.disp === 'wait_tech') return { cls: 'wait', label: '等技术面开口', why: d.why };
|
if (d.disp === 'wait_tech') return { cls: 'wait', label: '等技术面开口', why: d.why };
|
||||||
|
|
@ -2924,9 +2924,9 @@ createApp({
|
||||||
if (d.disp === 'wait_confirm') return { cls: 'wait', label: '等盘中确认', why: d.why };
|
if (d.disp === 'wait_confirm') return { cls: 'wait', label: '等盘中确认', why: d.why };
|
||||||
// 系统自决 (2026-09-14): 方向看多但择时看空, 进观察等择时转多, 也是「在盯」不是「被否决」。
|
// 系统自决 (2026-09-14): 方向看多但择时看空, 进观察等择时转多, 也是「在盯」不是「被否决」。
|
||||||
if (d.disp === 'wait_timing') return { cls: 'wait', label: '择时待转多', why: d.why };
|
if (d.disp === 'wait_timing') return { cls: 'wait', label: '择时待转多', why: d.why };
|
||||||
return { cls: 'deny', label: '被否决', why: d.why };
|
return { cls: 'deny', label: '未建仓', why: d.why };
|
||||||
}
|
}
|
||||||
return { cls: 'wait', label: '在盯', why: '' };
|
return { cls: 'wait', label: '候选观察', why: '' };
|
||||||
}
|
}
|
||||||
// 候选行的三源合议摘要 (2026-09-14 页面收尾包): 后端候选处置快照 by_code 里挂的 consensus 小块
|
// 候选行的三源合议摘要 (2026-09-14 页面收尾包): 后端候选处置快照 by_code 里挂的 consensus 小块
|
||||||
// (fund/overall/tech/phase/direction/route)。没有就返回 null, 芯片整段不显示。
|
// (fund/overall/tech/phase/direction/route)。没有就返回 null, 芯片整段不显示。
|
||||||
|
|
@ -2949,17 +2949,17 @@ createApp({
|
||||||
|
|
||||||
const attention = computed(() => {
|
const attention = computed(() => {
|
||||||
const o = ov.value || {}, out = [];
|
const o = ov.value || {}, out = [];
|
||||||
if (o.buy_halt) out.push('买入已经暂停,卖出和止损照常');
|
if (o.buy_halt) out.push('买入已暂停,卖出与止损照常');
|
||||||
if (o.exec_halt) out.push('休假模式:系统的自动动作全停了,只留每天的对账和日报');
|
if (o.exec_halt) out.push('已暂停执行:系统自动动作全停,只留每日对账与日报');
|
||||||
if (o.brake_until) out.push('组合刹车中,连续亏损后系统自己停了手,' + o.brake_until + ' 解除');
|
if (o.brake_until) out.push('组合刹车中,连续亏损后系统已停止自动动作,' + o.brake_until + ' 解除');
|
||||||
if (marketOpen.value && (o.price_missing || []).length)
|
if (marketOpen.value && (o.price_missing || []).length)
|
||||||
out.push(o.price_missing.length + ' 只取不到实时价(停牌或者行情断了),它们的盈亏和安全垫暂时算不出来');
|
out.push(o.price_missing.length + ' 只取不到实时价(停牌或行情中断),其盈亏与安全垫暂时无法计算');
|
||||||
if (o.sector_ready === false) out.push('行业数据还没就绪,按行业的操作暂时点不动');
|
if (o.sector_ready === false) out.push('行业数据尚未就绪,按行业的操作暂时不可用');
|
||||||
const j = (dm.value || {}).judge;
|
const j = (dm.value || {}).judge;
|
||||||
if (j && j.available === false) out.push('决策系统现在读不到:回落补足、盈利加仓、浮亏补仓一律改成要你拍板');
|
if (j && j.available === false) out.push('决策系统读不到:回落补足、盈利加仓、浮亏补仓一律改为需人工确认');
|
||||||
const c = wsc.value || {};
|
const c = wsc.value || {};
|
||||||
if (c.conn_state && c.conn_state !== 'ONLINE') out.push('行情和成交的通道没连上(' + c.conn_state + ')');
|
if (c.conn_state && c.conn_state !== 'ONLINE') out.push('行情与成交通道未连上(' + c.conn_state + ')');
|
||||||
if (c.resync_required) out.push('通道要人工做一次全量对账');
|
if (c.resync_required) out.push('通道需人工做一次全量对账');
|
||||||
return out;
|
return out;
|
||||||
});
|
});
|
||||||
|
|
||||||
|
|
@ -3070,7 +3070,7 @@ createApp({
|
||||||
const propJudgeTag = (p) => {
|
const propJudgeTag = (p) => {
|
||||||
if (!p.judge_reason && !p.judge_verdict) return null;
|
if (!p.judge_reason && !p.judge_verdict) return null;
|
||||||
return String(p.judge_verdict || '').trim() === 'PASS'
|
return String(p.judge_verdict || '').trim() === 'PASS'
|
||||||
? { text: '通过', cls: 'accent' } : { text: '交你定', cls: 'warn' };
|
? { text: '通过', cls: 'accent' } : { text: '需人工确认', cls: 'warn' };
|
||||||
};
|
};
|
||||||
const propCompanyTag = (p) => {
|
const propCompanyTag = (p) => {
|
||||||
const c = cr(p);
|
const c = cr(p);
|
||||||
|
|
@ -3152,10 +3152,10 @@ createApp({
|
||||||
return Array.isArray(l) && l.length > 1 ? (l.length - 1) : 0;
|
return Array.isArray(l) && l.length > 1 ? (l.length - 1) : 0;
|
||||||
}
|
}
|
||||||
function nextStep(r) {
|
function nextStep(r) {
|
||||||
if (r.frozen_reason && r.frozen_reason !== 'NONE') return '这只票现在只卖不买。';
|
if (r.frozen_reason && r.frozen_reason !== 'NONE') return '这只票当前只卖不买。';
|
||||||
if (r.cushion_state === 'SOLID') return '浮盈够厚,条件符合就会自动加仓。';
|
if (r.cushion_state === 'SOLID') return '浮盈够厚,条件符合即自动加仓。';
|
||||||
if ((r.cushion_pct || 0) < 0) return '还在亏。跌到设定的补仓线会考虑补仓;亏得更深的那一档要你点头才补。';
|
if ((r.cushion_pct || 0) < 0) return '仍在浮亏。跌到补仓线考虑补仓;更深一档的补仓按系统自决处理(自决关时需人工确认)。';
|
||||||
return '先拿着,观察。';
|
return '持有观察。';
|
||||||
}
|
}
|
||||||
|
|
||||||
async function promptNum(msg, title) {
|
async function promptNum(msg, title) {
|
||||||
|
|
@ -3217,7 +3217,7 @@ createApp({
|
||||||
async function actTargetPrice(row) {
|
async function actTargetPrice(row) {
|
||||||
const v = await promptNum(curSet(row.user_target_price)
|
const v = await promptNum(curSet(row.user_target_price)
|
||||||
+ '现价 ' + row.price + ',成本 ' + (row.avg_cost || '—')
|
+ '现价 ' + row.price + ',成本 ' + (row.avg_cost || '—')
|
||||||
+ '。目标价(元):到价后生成一条清仓提议放进「等我拍板」,卖不卖由你点头,'
|
+ '。目标价(元):到价后生成一条清仓提议放进「待确认提议」,卖不卖由你点头,'
|
||||||
+ '系统不自动卖。不想要了就再设一次、填 0 即取消。', '设目标价 ' + nm(row.ts_code));
|
+ '系统不自动卖。不想要了就再设一次、填 0 即取消。', '设目标价 ' + nm(row.ts_code));
|
||||||
if (v != null) quickCmd('SET_TARGET_PRICE', { ts_code: row.ts_code, price: v });
|
if (v != null) quickCmd('SET_TARGET_PRICE', { ts_code: row.ts_code, price: v });
|
||||||
}
|
}
|
||||||
|
|
@ -3236,7 +3236,7 @@ createApp({
|
||||||
// 组合
|
// 组合
|
||||||
function pHaltBuy() { quickCmd('HALT_BUY', {}, '暂停一切新增买入并撤销在途买入指令?卖出与止损不受影响。'); }
|
function pHaltBuy() { quickCmd('HALT_BUY', {}, '暂停一切新增买入并撤销在途买入指令?卖出与止损不受影响。'); }
|
||||||
function pResumeBuy() { quickCmd('RESUME_BUY', {}); }
|
function pResumeBuy() { quickCmd('RESUME_BUY', {}); }
|
||||||
function pHaltAll() { quickCmd('HALT_ALL', {}, '进入休假模式:所有自动动作暂停,只保留对账与日报?'); }
|
function pHaltAll() { quickCmd('HALT_ALL', {}, '暂停执行:所有自动动作暂停,只保留对账与日报?'); }
|
||||||
function pResumeAll() { quickCmd('RESUME_ALL', {}); }
|
function pResumeAll() { quickCmd('RESUME_ALL', {}); }
|
||||||
function pLiquidate() { quickCmd('LIQUIDATE_ALL', { confirm: 'YES' }, '一键清仓:全部持仓按最快速度卖出,不再挑时间点。确定要这么做吗?'); }
|
function pLiquidate() { quickCmd('LIQUIDATE_ALL', { confirm: 'YES' }, '一键清仓:全部持仓按最快速度卖出,不再挑时间点。确定要这么做吗?'); }
|
||||||
async function pReduce() { const v = await promptNum('降低敞口(占总规模,%)', '降仓'); if (v != null) quickCmd('REDUCE_EXPOSURE', { pct: v + '%' }); }
|
async function pReduce() { const v = await promptNum('降低敞口(占总规模,%)', '降仓'); if (v != null) quickCmd('REDUCE_EXPOSURE', { pct: v + '%' }); }
|
||||||
|
|
@ -3312,7 +3312,7 @@ createApp({
|
||||||
});
|
});
|
||||||
const sumAutonomy = computed(() => ({
|
const sumAutonomy = computed(() => ({
|
||||||
full: '全自动 —— 系统自己提议,检查通过就直接下单。',
|
full: '全自动 —— 系统自己提议,检查通过就直接下单。',
|
||||||
propose_only: '只提议 —— 系统给建议,等你在「等我拍板」里点头。',
|
propose_only: '只提议 —— 系统给建议,等你在「待确认提议」里点头。',
|
||||||
off: '暂停 —— 系统不自己提议。'
|
off: '暂停 —— 系统不自己提议。'
|
||||||
}[pval('PMS_AUTONOMY')] || (pval('PMS_AUTONOMY') || '—')));
|
}[pval('PMS_AUTONOMY')] || (pval('PMS_AUTONOMY') || '—')));
|
||||||
// ===================== /交易员视图 =====================
|
// ===================== /交易员视图 =====================
|
||||||
|
|
@ -4083,7 +4083,7 @@ createApp({
|
||||||
{ key:'all', label:'全部' },
|
{ key:'all', label:'全部' },
|
||||||
{ key:'alert', label:'行情告警' },
|
{ key:'alert', label:'行情告警' },
|
||||||
{ key:'order', label:'系统下单' },
|
{ key:'order', label:'系统下单' },
|
||||||
{ key:'prop', label:'等我拍板' },
|
{ key:'prop', label:'待确认提议' },
|
||||||
{ key:'strat', label:'策略与风控' },
|
{ key:'strat', label:'策略与风控' },
|
||||||
];
|
];
|
||||||
const KIND_LABEL = { alert:'告警', order:'下单', prop:'待办', strat:'风控' };
|
const KIND_LABEL = { alert:'告警', order:'下单', prop:'待办', strat:'风控' };
|
||||||
|
|
@ -4121,11 +4121,11 @@ createApp({
|
||||||
cat:tx('action', r.action) + ' · ' + tx('insStatus', r.status),
|
cat:tx('action', r.action) + ' · ' + tx('insStatus', r.status),
|
||||||
strong:true, why:insWhy(r), goLabel:'看在办', go:'ins' });
|
strong:true, why:insWhy(r), goLabel:'看在办', go:'ins' });
|
||||||
}
|
}
|
||||||
// 3) 等我拍板 —— 自主档位是「只提议」时, 新提议在这里也提醒一次
|
// 3) 待确认提议 —— 自主档位是「只提议」时, 新提议在这里也提醒一次
|
||||||
for (const p of (proposals.value || [])) {
|
for (const p of (proposals.value || [])) {
|
||||||
const t = _at(p, ['created_at', 'at', 'decided_at']);
|
const t = _at(p, ['created_at', 'at', 'decided_at']);
|
||||||
out.push({ kind:'prop', time:_hm(t), sort:_hms(t) || '999999', ts_code:p.ts_code,
|
out.push({ kind:'prop', time:_hm(t), sort:_hms(t) || '999999', ts_code:p.ts_code,
|
||||||
cat:tx('action', p.action) + ' · 等你拍板', strong:true,
|
cat:tx('action', p.action) + ' · 待确认', strong:true,
|
||||||
why:(p.judge_reason || propWhy(p)), goLabel:'去处理', go:'prop' });
|
why:(p.judge_reason || propWhy(p)), goLabel:'去处理', go:'prop' });
|
||||||
}
|
}
|
||||||
// 4) 策略与风控动向
|
// 4) 策略与风控动向
|
||||||
|
|
@ -4137,7 +4137,7 @@ createApp({
|
||||||
why:'检查 ' + (sc.checked || 0) + ' 只,新挂 ' + (c.attached || 0)
|
why:'检查 ' + (sc.checked || 0) + ' 只,新挂 ' + (c.attached || 0)
|
||||||
+ ',换方案 ' + (c.handoffs || 0) + ',停买 ' + (c.paused || 0)
|
+ ',换方案 ' + (c.handoffs || 0) + ',停买 ' + (c.paused || 0)
|
||||||
+ ',被挡 ' + (c.blocked || 0) + ',跳过 ' + (c.skipped || 0),
|
+ ',被挡 ' + (c.blocked || 0) + ',跳过 ' + (c.skipped || 0),
|
||||||
goLabel:'去我的策略', go:'strat' });
|
goLabel:'去交易方案', go:'strat' });
|
||||||
}
|
}
|
||||||
for (const s of (strategies.value || [])) {
|
for (const s of (strategies.value || [])) {
|
||||||
if (!s.buy_paused) continue;
|
if (!s.buy_paused) continue;
|
||||||
|
|
@ -4146,7 +4146,7 @@ createApp({
|
||||||
cat:'这只票改成只卖不买', strong:true,
|
cat:'这只票改成只卖不买', strong:true,
|
||||||
why:'决策系统提示' + (s.buy_paused.reason || '资金异动')
|
why:'决策系统提示' + (s.buy_paused.reason || '资金异动')
|
||||||
+ ',已经停掉这个方案的买入。卖出、以及把当天做T买的那部分卖回去,都不受影响。',
|
+ ',已经停掉这个方案的买入。卖出、以及把当天做T买的那部分卖回去,都不受影响。',
|
||||||
goLabel:'去我的策略', go:'strat' });
|
goLabel:'去交易方案', go:'strat' });
|
||||||
}
|
}
|
||||||
for (const r of (SIG.value.sell || [])) {
|
for (const r of (SIG.value.sell || [])) {
|
||||||
if (r.today === false) continue; // 昨天的风控卖出不再冒充今天的消息 (2026-08-28)
|
if (r.today === false) continue; // 昨天的风控卖出不再冒充今天的消息 (2026-08-28)
|
||||||
|
|
@ -4157,7 +4157,7 @@ createApp({
|
||||||
strong:true, level:'DANGER', why:r.reason || '', goLabel:'去处理', go:'strat' });
|
strong:true, level:'DANGER', why:r.reason || '', goLabel:'去处理', go:'strat' });
|
||||||
}
|
}
|
||||||
for (const o of (opLog.value || [])) {
|
for (const o of (opLog.value || [])) {
|
||||||
if (o.result === 'OK') continue; // 顺利做完的不占消息栏, 在「你做过的操作」里查
|
if (o.result === 'OK') continue; // 顺利做完的不占消息栏, 在「操作日志」里查
|
||||||
out.push({ kind:'strat', time:_hm(o.at), sort:_hms(o.at), ts_code:o.ts_code || '',
|
out.push({ kind:'strat', time:_hm(o.at), sort:_hms(o.at), ts_code:o.ts_code || '',
|
||||||
cat:opCn(o) + ' ' + (o.result === 'BLOCKED' ? '被合规检查挡下' : ('结果 ' + o.result)),
|
cat:opCn(o) + ' ' + (o.result === 'BLOCKED' ? '被合规检查挡下' : ('结果 ' + o.result)),
|
||||||
strong:true, why:o.reason || '', goLabel:'看记录', go:'ins' });
|
strong:true, why:o.reason || '', goLabel:'看记录', go:'ins' });
|
||||||
|
|
|
||||||
|
|
@ -107,6 +107,9 @@
|
||||||
(off 不管、放行强采标准仓、低把握转试探仓、只有一方表态与异议只在技术面按 TRIAL
|
(off 不管、放行强采标准仓、低把握转试探仓、只有一方表态与异议只在技术面按 TRIAL
|
||||||
开关、四类交人、逻辑存疑放弃、深亏补仓三分支、目标价直接采纳、每日上限、record/decline
|
开关、四类交人、逻辑存疑放弃、深亏补仓三分支、目标价直接采纳、每日上限、record/decline
|
||||||
降级)/mark_decide 优先级/eval_dca 深档打 decide_kind/三参数登记与范围/两个 repo 查询 (22 例)
|
降级)/mark_decide 优先级/eval_dca 深档打 decide_kind/三参数登记与范围/两个 repo 查询 (22 例)
|
||||||
|
test_page_copy_guard.py 页面文案守卫·口语化 (2026-09-14 页面专业化包): 禁用词清单
|
||||||
|
(第一二人称/拟人口语/生造动词) 扫显示文本, 剥三种注释、成对豁免、
|
||||||
|
网址不误截、自检样例 (1 例)
|
||||||
test_page_enum_guard.py 页面文案守卫 (静态扫描, 不连库不起浏览器): 枚举字段不许
|
test_page_enum_guard.py 页面文案守卫 (静态扫描, 不连库不起浏览器): 枚举字段不许
|
||||||
直接印到页面上 / 判据码显示前必须剥前缀 / 不许把整个对象
|
直接印到页面上 / 判据码显示前必须剥前缀 / 不许把整个对象
|
||||||
打给交易员看 / 翻译兜底不许让英文码单独当句子 (1 例)
|
打给交易员看 / 翻译兜底不许让英文码单独当句子 (1 例)
|
||||||
|
|
@ -118,7 +121,7 @@
|
||||||
减持与研究理由两键各一例, 到 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; 加固包给第二十八批加三例、第三十批加三例到 846; 评审修订第三十一批加一例到 847; 09-14 盘中确认包第三十二批十九例到 866; 页面收尾包第三十三批五例到 871;
|
09-14 观察读数包第三十一批二十例到 840; 加固包给第二十八批加三例、第三十批加三例到 846; 评审修订第三十一批加一例到 847; 09-14 盘中确认包第三十二批十九例到 866; 页面收尾包第三十三批五例到 871;
|
||||||
09-14 系统自决包第三十四批二十二例、test_wiring 目标价自动清仓加一例到 894;
|
09-14 系统自决包第三十四批二十二例、test_wiring 目标价自动清仓加一例到 894;
|
||||||
09-14 选股打分包 §5.3 来源字段给第七批加两例, 现为 896)
|
09-14 选股打分包 §5.3 来源字段给第七批加两例到 896; 页面专业化包加文案守卫一例, 现为 897)
|
||||||
任一子集失败即整体失败 (退出码 1)。
|
任一子集失败即整体失败 (退出码 1)。
|
||||||
|
|
||||||
哨兵位置清单 (2026-09-03 抄录; 改了对应的东西就得来这些地方改断言, 断言不动就是漏了):
|
哨兵位置清单 (2026-09-03 抄录; 改了对应的东西就得来这些地方改断言, 断言不动就是漏了):
|
||||||
|
|
@ -176,6 +179,7 @@ SUITES = ["test_core_units.py", "test_batch2_units.py", "test_batch3_units.py",
|
||||||
"test_batch32_units.py",
|
"test_batch32_units.py",
|
||||||
"test_batch33_units.py",
|
"test_batch33_units.py",
|
||||||
"test_batch34_units.py",
|
"test_batch34_units.py",
|
||||||
|
"test_page_copy_guard.py",
|
||||||
"test_page_enum_guard.py",
|
"test_page_enum_guard.py",
|
||||||
"test_page_wiring_guard.py",
|
"test_page_wiring_guard.py",
|
||||||
"test_wiring.py"]
|
"test_wiring.py"]
|
||||||
|
|
|
||||||
|
|
@ -680,7 +680,7 @@ def _():
|
||||||
param_store.get_bool = lambda k, d=False: {"PMS_STRATEGY_ENABLED": True,
|
param_store.get_bool = lambda k, d=False: {"PMS_STRATEGY_ENABLED": True,
|
||||||
"PMS_GLOBAL_EXEC_HALT": True}.get(k, d)
|
"PMS_GLOBAL_EXEC_HALT": True}.get(k, d)
|
||||||
out3 = adv.scan(dry_run=False)
|
out3 = adv.scan(dry_run=False)
|
||||||
assert any("休假" in s["why"] for s in out3["skipped"]), out3
|
assert any("暂停执行" in s["why"] for s in out3["skipped"]), out3
|
||||||
finally:
|
finally:
|
||||||
param_store.get_bool, adv._params = orig, orig_p
|
param_store.get_bool, adv._params = orig, orig_p
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -264,7 +264,7 @@ def test_page_static():
|
||||||
assert "csLoadFull" not in seg, fn + " 里不许调回扫"
|
assert "csLoadFull" not in seg, fn + " 里不许调回扫"
|
||||||
# 读不到时不许说"今天没有"
|
# 读不到时不许说"今天没有"
|
||||||
assert html.count("srcFail(") >= 8, "各路空文案都要给失败态让位"
|
assert html.count("srcFail(") >= 8, "各路空文案都要给失败态让位"
|
||||||
assert "别当成平安无事" in html
|
assert "不要当成一切正常" in html # 取不到数≠没有信号 (2026-09-14 文案改专业化)
|
||||||
|
|
||||||
|
|
||||||
@case("路由清单收了新接口")
|
@case("路由清单收了新接口")
|
||||||
|
|
|
||||||
|
|
@ -110,7 +110,7 @@ def _():
|
||||||
def _():
|
def _():
|
||||||
c = ae.eval_dca(pos(price=8.4, cushion_pct=-0.16, market_value=50_400), PARAMS, mkt())
|
c = ae.eval_dca(pos(price=8.4, cushion_pct=-0.16, market_value=50_400), PARAMS, mkt())
|
||||||
assert c and c["needs_user_confirm"] is True, c
|
assert c and c["needs_user_confirm"] is True, c
|
||||||
assert c["hard_numbers"]["stage"] == 2 and "必须用户确认" in c["reason"], c
|
assert c["hard_numbers"]["stage"] == 2 and "需人工确认" in c["reason"], c
|
||||||
|
|
||||||
|
|
||||||
@case("补仓·各档只评估一次 / 终身只执行一次 / 浮盈不评估")
|
@case("补仓·各档只评估一次 / 终身只执行一次 / 浮盈不评估")
|
||||||
|
|
|
||||||
|
|
@ -0,0 +1,148 @@
|
||||||
|
# -*- coding: utf-8 -*-
|
||||||
|
"""页面文案守卫·口语化词 (2026-09-14 页面专业化包, 台账 015)。静态扫描, 不连库、不起浏览器。
|
||||||
|
|
||||||
|
## 为什么要有它
|
||||||
|
|
||||||
|
用户的第一条意见是「页面文案口语化,不像交易系统」。板块标题写成「等我拍板」「我的持仓」,
|
||||||
|
处置词写成「出手」「休假」,第一二人称满页都是。改完之后要有一道机器检查, 免得日后又飘回口语。
|
||||||
|
|
||||||
|
## 它检查什么
|
||||||
|
|
||||||
|
一份禁用词清单, 分三类: 第一二人称、拟人与口语、生造动词。**只查会显示给人看的文本** ——
|
||||||
|
先剥掉三种注释 (HTML 注释、JS 块注释、JS 行注释), 注释里为解释代码用到这些词不算。命中就打
|
||||||
|
行号、词、片段, 退出码 1。
|
||||||
|
|
||||||
|
剥行注释时跳过字符串内容与网址里的双斜线 (`https://`), 免得把字符串里的 `//` 当注释起点。
|
||||||
|
|
||||||
|
## 豁免
|
||||||
|
|
||||||
|
确实要在显示文本里用某个词 (极少), 用成对标记圈起来并写原因:
|
||||||
|
模板里: <!-- copy-guard:off 原因... --> ... <!-- copy-guard:on -->
|
||||||
|
脚本里: // copy-guard:off 原因... ... // copy-guard:on
|
||||||
|
缺原因或不成对, 判红 (不许无声豁免)。
|
||||||
|
|
||||||
|
跑法: python3 scripts/test_page_copy_guard.py
|
||||||
|
"""
|
||||||
|
import os
|
||||||
|
import re
|
||||||
|
import sys
|
||||||
|
|
||||||
|
HERE = os.path.dirname(os.path.abspath(__file__))
|
||||||
|
PAGE = os.path.join(os.path.dirname(HERE), "app", "web", "static", "index.html")
|
||||||
|
|
||||||
|
# 禁用词。命中即报 (在显示文本里)。分三类只为可读, 检查时一视同仁。
|
||||||
|
BANNED = [
|
||||||
|
# 第一二人称
|
||||||
|
"我的", "等我", "等你", "要你", "给你", "替你", "让你", "问过你", "你自己",
|
||||||
|
"你设", "你改", "你点", "你来", "你现在", "你还没", "你做过", "你来定", "交你定",
|
||||||
|
# 拟人与口语
|
||||||
|
"拍板", "点头", "盯着", "在盯", "盯的", "拿不准", "记一笔", "挤掉", "平安无事",
|
||||||
|
# 生造动词与口语动词
|
||||||
|
"出手", "休假", "一跳", "发生了什么",
|
||||||
|
]
|
||||||
|
|
||||||
|
OFF, ON = "copy-guard:off", "copy-guard:on"
|
||||||
|
|
||||||
|
|
||||||
|
def _strip_line_comment(line: str) -> str:
|
||||||
|
"""去掉一行里的 JS 行注释 (// 到行尾), 但跳过字符串内的 // 与网址里的 ://。
|
||||||
|
走一遍字符, 记录当前是否在 ' " ` 里; 不在字符串里遇到 // 且前一个字符不是 : 就截断。"""
|
||||||
|
q = None # 当前字符串引号, None 表示不在字符串里
|
||||||
|
i, n = 0, len(line)
|
||||||
|
while i < n:
|
||||||
|
ch = line[i]
|
||||||
|
if q:
|
||||||
|
if ch == "\\":
|
||||||
|
i += 2
|
||||||
|
continue
|
||||||
|
if ch == q:
|
||||||
|
q = None
|
||||||
|
else:
|
||||||
|
if ch in "'\"`":
|
||||||
|
q = ch
|
||||||
|
elif ch == "/" and i + 1 < n and line[i + 1] == "/":
|
||||||
|
if i > 0 and line[i - 1] == ":": # 网址 :// 不是注释
|
||||||
|
i += 2
|
||||||
|
continue
|
||||||
|
return line[:i]
|
||||||
|
i += 1
|
||||||
|
return line
|
||||||
|
|
||||||
|
|
||||||
|
def _exempt_ranges(raw: str):
|
||||||
|
"""在原始文本上找成对豁免区。返回 (豁免行号集合, 报错列表)。缺原因或不成对判红。"""
|
||||||
|
bad = []
|
||||||
|
lines = raw.split("\n")
|
||||||
|
ranges = set()
|
||||||
|
stack = []
|
||||||
|
for i, line in enumerate(lines, 1):
|
||||||
|
if OFF in line:
|
||||||
|
after = line.split(OFF, 1)[1]
|
||||||
|
reason = re.sub(r"[-#>*/\s]+$", "", after).strip(" -#>*/")
|
||||||
|
if not reason:
|
||||||
|
bad.append(f"第 {i} 行:copy-guard:off 缺原因")
|
||||||
|
stack.append(i)
|
||||||
|
if ON in line:
|
||||||
|
if not stack:
|
||||||
|
bad.append(f"第 {i} 行:copy-guard:on 没有对应的 off")
|
||||||
|
else:
|
||||||
|
start = stack.pop()
|
||||||
|
for k in range(start, i + 1):
|
||||||
|
ranges.add(k)
|
||||||
|
for s in stack:
|
||||||
|
bad.append(f"第 {s} 行:copy-guard:off 没有对应的 on")
|
||||||
|
return ranges, bad
|
||||||
|
|
||||||
|
|
||||||
|
def main():
|
||||||
|
raw = open(PAGE, encoding="utf-8").read()
|
||||||
|
exempt, bad = _exempt_ranges(raw)
|
||||||
|
|
||||||
|
# 剥注释 (保行号)
|
||||||
|
def _blank(m):
|
||||||
|
return "\n" * m.group(0).count("\n")
|
||||||
|
src = re.sub(r"<!--.*?-->", _blank, raw, flags=re.S)
|
||||||
|
src = re.sub(r"/\*.*?\*/", _blank, src, flags=re.S)
|
||||||
|
lines = src.split("\n")
|
||||||
|
|
||||||
|
for i, line in enumerate(lines, 1):
|
||||||
|
if i in exempt:
|
||||||
|
continue
|
||||||
|
text = _strip_line_comment(line)
|
||||||
|
for w in BANNED:
|
||||||
|
idx = text.find(w)
|
||||||
|
if idx >= 0:
|
||||||
|
seg = text.strip()[:100]
|
||||||
|
bad.append(f"第 {i} 行:口语化词「{w}」出现在显示文本里。\n {seg}")
|
||||||
|
|
||||||
|
# 内置自检: 只在注释里出现不算, 在字符串里出现要算
|
||||||
|
_self_ok(bad)
|
||||||
|
|
||||||
|
if bad:
|
||||||
|
print(f"页面文案守卫·口语化:发现 {len(bad)} 处")
|
||||||
|
for m in bad:
|
||||||
|
print(" FAIL", m)
|
||||||
|
sys.exit(1)
|
||||||
|
print(f"ALL OK — 页面文案守卫·口语化:{len(BANNED)} 个禁用词、注释剥离、成对豁免 全部通过")
|
||||||
|
|
||||||
|
|
||||||
|
def _self_ok(bad):
|
||||||
|
"""自检样例: 注释里的禁用词不该报, 字符串里的该报。任一不符往 bad 里加一条。"""
|
||||||
|
only_comment = " // 这里解释一下等我拍板的逻辑\n const x = 1;"
|
||||||
|
in_string = " const t = '等我拍板';"
|
||||||
|
# 注释行剥掉后不含禁用词
|
||||||
|
c = _strip_line_comment(only_comment.split("\n")[0])
|
||||||
|
if any(w in c for w in BANNED):
|
||||||
|
bad.append("自检失败:行注释里的禁用词没有被剥掉")
|
||||||
|
# 字符串行剥不掉 (没有 //), 仍含禁用词
|
||||||
|
s = _strip_line_comment(in_string)
|
||||||
|
if not any(w in s for w in BANNED):
|
||||||
|
bad.append("自检失败:字符串里的禁用词被误剥")
|
||||||
|
# 网址不被当注释截断
|
||||||
|
u = _strip_line_comment(" const url = 'https://a.b/c'; const y = 2;")
|
||||||
|
if "const y = 2" not in u:
|
||||||
|
bad.append("自检失败:网址里的 // 被当成注释起点")
|
||||||
|
|
||||||
|
|
||||||
|
if __name__ == "__main__":
|
||||||
|
main()
|
||||||
|
|
@ -1889,7 +1889,7 @@ def _():
|
||||||
|
|
||||||
_prop_fakes(params={"PMS_AUTONOMY": "full", "PMS_GLOBAL_EXEC_HALT": "true"})
|
_prop_fakes(params={"PMS_AUTONOMY": "full", "PMS_GLOBAL_EXEC_HALT": "true"})
|
||||||
r2 = ps.scan_and_route()
|
r2 = ps.scan_and_route()
|
||||||
assert any("休假" in s["why"] for s in r2["skipped"]), r2
|
assert any("暂停执行" in s["why"] for s in r2["skipped"]), r2
|
||||||
|
|
||||||
fake = _prop_fakes(params={"PMS_AUTONOMY": "propose_only"})
|
fake = _prop_fakes(params={"PMS_AUTONOMY": "propose_only"})
|
||||||
r3 = ps.scan_and_route(dry_run=True)
|
r3 = ps.scan_and_route(dry_run=True)
|
||||||
|
|
|
||||||
Loading…
Reference in New Issue