diff --git a/SESSION.md b/SESSION.md index cf0a770..604a01e 100644 --- a/SESSION.md +++ b/SESSION.md @@ -835,11 +835,12 @@ Automation execution-layer PXX remains closed and its known scope stays frozen. - S3/S4 Review Now mainline gate is implemented locally: added `src/services/ict_mainline/gates.py`, `docs/AI_ICT_ReviewNow主线Gate_S3S4_2026-05-21_v1.md`, `mainline_focus_gate` on Decision Assistant / Trader Cockpit, and `cockpit_promotion_gate` on Strategy Backtest. `Review Now` now requires active Trading Plan + primary draw + invalidation + no blocking plan condition + passed Strategy Backtest quality gate + candidate link to the active plan; otherwise candidates are downgraded to watch / observe and Trader Cockpit remains `Observe Only` or lower. - S5/S6 Cockpit chain sentence and feedback loop is implemented locally: added `src/services/ict_mainline/feedback.py`, `docs/AI_ICT_Cockpit链路句与反馈错误分类_S5S6_2026-05-21_v1.md`, `trader_mode_summary.trader_readout.ict_chain_sentence`, `ict_chain_steps`, and book-rule feedback classifications for operator feedback, Journal, weekly review, and study-note drafts. Observation review, paper review, and execution-derived review are now counted separately so observation samples cannot be mistaken for execution evidence. - S7/S8 read-only reliability and trader SOP is implemented, deployed, and remote-verified: added `docs/AI_ICT_只读实盘可靠性与交易员SOP_S7S8_2026-05-21_v1.md`, `maintenance_summary.readonly_live_reliability`, and `trader_mode_summary.ops_duty_panel.trader_safe_health`. Trader Cockpit now shows only a trader-safe read-only use conclusion plus recovery action, while System Ops exposes proxy / market freshness / runtime / review packet / REST fallback / stream / news-account diagnostics. Warning states remain analysis-use guidance only and are not trade signals. Validation passed focused unit/static checks, script static checks, full unit discovery (`807` tests), deploy smoke over 11 endpoints, remote ops smoke, readonly live analysis smoke, field smoke, and gstack browser verification on `/trader-mode#system-ops` with no console errors. -- Current assisted-analysis next gate: proceed to S9 only: use real intraday feedback to further compress `Observe Only` / `Review Now` / `Do Not Use` wording without adding new features or widening beyond the ICT book mainline. Do not restart external-context/archive/digest long-tail work, do not physically delete S1 deprecated surfaces yet, and keep R2 sample collection honest. +- S9 Trader Cockpit status copy compression is implemented, deployed, and remote-verified: added `docs/AI_ICT_TraderCockpit状态话术压缩_S9_2026-05-21_v1.md`; `trader_mode_summary.trader_readout` now exposes fixed six-card copy fields `Now / Reason / Wait / Risk / Historical Edge / Boundary`, and the frontend prioritizes those compressed fields before fallback text. `Review Now` now reads as manual review only, `Observe Only` as wait/no-trade, and `Do Not Use` as restore data/runtime first. Chinese Trader Cockpit display now renders the six cards as trader-readable sentences and removes the visible S9 residues `Risk:`, `Wait for FVG`, `Trader Mode changed:`, and raw `sweep -> MSS -> FVG` on `/trader-mode`. Validation passed focused operator/static/docs tests, `node --check`, script static checks, full unit discovery (`807` tests), `git diff --check` with only LF/CRLF warnings, deploy smoke over 11 endpoints, remote ops smoke, readonly live analysis smoke, six-card field smoke, and gstack browser verification with no console errors. Evidence screenshot: `output/playwright/s9-trader-cockpit-six-card-copy-final2-1366x768.png`. +- Current assisted-analysis next gate: proceed to S10 only: validate the compressed `Now / Reason / Wait / Risk / Historical Edge / Boundary` wording against real intraday use, then adjust only existing text or hiding rules if the operator still cannot tell why not to trade or what to wait for. Do not add new product features, do not restart external-context/archive/digest long-tail work, do not physically delete S1 deprecated surfaces yet, and keep R2 sample collection honest. ## Exact Next Step -Use `docs/AI_ICT_实盘辅助分析决策助手ICT主线开发方案_2026-05-21_v1.md`, `docs/AI_ICT_ICT主线概念字典_S2_2026-05-21_v1.md`, `docs/AI_ICT_ReviewNow主线Gate_S3S4_2026-05-21_v1.md`, `docs/AI_ICT_Cockpit链路句与反馈错误分类_S5S6_2026-05-21_v1.md`, and `docs/AI_ICT_只读实盘可靠性与交易员SOP_S7S8_2026-05-21_v1.md` as the active anchors. Continue with S9: use real intraday feedback to further compress `Observe Only` / `Review Now` / `Do Not Use` wording without adding new features or widening beyond the ICT book mainline. Do not restart external-context/archive/digest long-tail work, do not create order-submission features, do not physically delete S1 deprecated surfaces yet, and do not fabricate R2 paper samples. Keep `execution_allowed`, `decision_support_ready`, `real_live_ready`, risk governor, trust bucket, and operator confirmation protocol unchanged. +Use `docs/AI_ICT_实盘辅助分析决策助手ICT主线开发方案_2026-05-21_v1.md`, `docs/AI_ICT_ICT主线概念字典_S2_2026-05-21_v1.md`, `docs/AI_ICT_ReviewNow主线Gate_S3S4_2026-05-21_v1.md`, `docs/AI_ICT_Cockpit链路句与反馈错误分类_S5S6_2026-05-21_v1.md`, `docs/AI_ICT_只读实盘可靠性与交易员SOP_S7S8_2026-05-21_v1.md`, and `docs/AI_ICT_TraderCockpit状态话术压缩_S9_2026-05-21_v1.md` as the active anchors. Continue with S10: validate the compressed `Now / Reason / Wait / Risk / Historical Edge / Boundary` wording against real intraday use, then adjust only existing text or hiding rules if the operator still cannot tell why not to trade or what to wait for. Do not restart external-context/archive/digest long-tail work, do not create order-submission features, do not physically delete S1 deprecated surfaces yet, and do not fabricate R2 paper samples. Keep `execution_allowed`, `decision_support_ready`, `real_live_ready`, risk governor, trust bucket, and operator confirmation protocol unchanged. ## Validation Commands Used Recently diff --git a/TASKS.md b/TASKS.md index 0c7eee4..b8b7f2f 100644 --- a/TASKS.md +++ b/TASKS.md @@ -939,7 +939,8 @@ - [x] S3/S4 Review Now mainline gate: Trading Plan is now the hard upstream gate, Strategy Backtest quality-gate failure blocks weak setups from live focus, and Strategy Backtest exposes `cockpit_promotion_gate` - [x] S5/S6 Cockpit chain sentence and feedback loop: Trader Cockpit now exposes `ict_chain_sentence` / `ict_chain_steps`, operator feedback / Journal / weekly review map to ICT book-rule error classes, and observation review remains separated from paper / execution-derived review - [x] S7/S8 read-only live reliability and trader SOP: Trader Cockpit now shows a trader-safe read-only use conclusion / recovery action, System Ops exposes proxy / market freshness / runtime / review packet / REST fallback / stream / news-account diagnostics, and the one-page intraday/backtest/review SOP is documented, deployed, remote-smoked, field-smoked, and gstack browser-verified with no console errors -- [ ] S9 next gate: use real intraday feedback to further compress Observe Only / Review Now / Do Not Use wording without adding new features or widening beyond the ICT book mainline +- [x] S9 Trader Cockpit status copy compression: first-screen readout now uses fixed `Now / Reason / Wait / Risk / Historical Edge / Boundary` cards, keeps `Review Now` as manual review only, `Observe Only` as wait/no-trade, and `Do Not Use` as restore data/runtime first without adding features or widening beyond the ICT book mainline; deployed and remote-verified on `/trader-mode` with Chinese six-card wording and no S9 English residue in the checked cards +- [ ] S10 next gate: validate the compressed six-card wording against real intraday use, then adjust only existing text or hiding rules if the operator still cannot tell why not to trade or what to wait for - [ ] R2 paper-ticket sample gate is waiting on a real executable candidate; do not fabricate paper tickets from blocked candidates - [ ] R2 operator feedback sample gate still needs 2 more real feedback samples (`operator_feedback_count=3/5`) - [ ] R2 weekly review visible gate still needs a real execution-derived journal/weekly review entry (`weekly_review_entry_count=0`) diff --git a/docs/AI_ICT_TraderCockpit状态话术压缩_S9_2026-05-21_v1.md b/docs/AI_ICT_TraderCockpit状态话术压缩_S9_2026-05-21_v1.md new file mode 100644 index 0000000..92dc4b5 --- /dev/null +++ b/docs/AI_ICT_TraderCockpit状态话术压缩_S9_2026-05-21_v1.md @@ -0,0 +1,58 @@ +# AI_ICT Trader Cockpit 状态话术压缩 S9 2026-05-21 v1 + +## 1. 本阶段目标 + +S9 只解决一个问题:交易员盘中看到 `Review Now / Observe Only / Do Not Use` 时,必须立刻知道现在做什么、为什么不交易、下一步等什么、最大风险是什么。 + +本阶段不新增功能,不新增交易能力,不扩大 external context / archive / workflow 长尾。 + +## 2. 强制边界 + +- `Review Now` 只能表示“人工复核唯一 focus”,不是买入或卖出信号。 +- `Observe Only` 只能表示“链路未完整或信任/回测/计划不够”,默认不交易。 +- `Do Not Use` 只能表示“数据、runtime 或只读可靠性不够”,先恢复系统再看盘。 +- 所有状态必须保持只读辅助分析,不下单,不交易,不提交订单。 + +## 3. 六项速读话术 + +Trader Cockpit 首屏固定为六项: + +1. `Now`:一句话说明当前状态。 +2. `Reason`:一句话说明 ICT 链路或数据原因。 +3. `Wait`:只等一个下一步确认。 +4. `Risk`:只说一个最大风险。 +5. `Historical Edge`:只说回测是否支持。 +6. `Boundary`:只读、不下单、不是买卖信号。 + +## 4. 状态表达规则 + +`Review Now`: + +- 示例:`Review Now: one focus; manual check only.` +- 含义:只允许人工复核唯一 focus;仍然不下单。 + +`Observe Only`: + +- 示例:`Observe Only: wait for FVG retrace; no trade.` +- 含义:链路未完成或质量门不足;继续等确认并记录 No Trade。 + +`Do Not Use`: + +- 示例:`Do Not Use: restore data/runtime before reading.` +- 含义:不要读盘面结论;先恢复行情、runtime 或复核包。 + +## 5. 验收 + +- 首屏卡片固定为 `Now / Reason / Wait / Risk / Historical Edge / Boundary`。 +- 每张卡片内容保持短句,不再把长链路、运维诊断、archive 或 workflow 明细塞进首屏。 +- 中文界面能显示这些短句,不出现关键英文残留或 `ReferenceError`。 +- `execution_allowed=false`、`real_live_ready=false`、`must_not_trade=true` 保持不变。 +- R2 paper ticket / paper execution / weekly review 样本继续真实采集,不伪造。 + +## 6. 下一步 + +S10 只允许基于真实盘中使用反馈继续改文案或隐藏复杂字段: + +- 如果交易员仍看不懂 `Reason`,优先把它压成更具体的 ICT 缺口。 +- 如果交易员仍不知道下一步,优先调整 `Wait`。 +- 如果 `Historical Edge` 仍显得像交易建议,继续降级成 Study / Pause / Review-only 语言。 diff --git a/src/services/operator/operator_workspace_service.py b/src/services/operator/operator_workspace_service.py index e4513ee..1a23ab9 100644 --- a/src/services/operator/operator_workspace_service.py +++ b/src/services/operator/operator_workspace_service.py @@ -3568,23 +3568,48 @@ def _build_trader_mode_live_readout( current_session=current_session, market_posture=market_posture, ) - headline = _build_trader_readout_headline( + now = _build_trader_readout_now( status_label=status_label, status_tone=status_tone, focus=focus, - watch_next=watch_next, + visible_reasons=visible_reasons, no_candidate_state=no_candidate_state, ) + reason = _build_trader_readout_reason( + status_tone=status_tone, + focus=focus, + visible_reasons=visible_reasons, + current_session=current_session, + market_posture=market_posture, + ict_chain_sentence=ict_chain_sentence, + ) + wait = _build_trader_readout_wait(status_tone=status_tone, focus=focus, visible_reasons=visible_reasons) + risk = _build_trader_readout_risk( + status_tone=status_tone, + focus=focus, + visible_reasons=visible_reasons, + freshness_summary=freshness_summary, + ) + historical_edge = _build_trader_readout_historical_edge(mainline_focus_gate) + boundary = "Read-only; no orders; not a buy/sell signal." + headline = now cards = [ - {"label": "Why No Trade", "value": why_no_trade, "tone": "caution"}, - {"label": "Watch Next", "value": watch_next, "tone": "focus"}, - {"label": "ICT Chain", "value": ict_chain_sentence, "tone": "focus" if focus else "muted"}, - {"label": "Data State", "value": data_state, "tone": "blocked" if status_tone == "do_not_use" else "neutral"}, - {"label": "No Candidate", "value": no_candidate_state, "tone": "muted" if focus else "caution"}, + {"label": "Now", "value": now, "tone": "blocked" if status_tone == "do_not_use" else "focus"}, + {"label": "Reason", "value": reason, "tone": "focus" if focus else "muted"}, + {"label": "Wait", "value": wait, "tone": "caution"}, + {"label": "Risk", "value": risk, "tone": "blocked" if status_tone == "do_not_use" else "risk"}, + {"label": "Historical Edge", "value": historical_edge, "tone": "edge" if "passed" in historical_edge.lower() else "caution"}, + {"label": "Boundary", "value": boundary, "tone": "boundary"}, ] return { "mode": "trader_one_screen_readout", "headline": headline, + "now": now, + "reason": reason, + "wait": wait, + "risk": risk, + "historical_edge": historical_edge, + "boundary": boundary, "why_no_trade": why_no_trade, "watch_next": watch_next, "focus_reason": ict_chain_sentence, @@ -3600,14 +3625,80 @@ def _build_trader_mode_live_readout( } -def _build_trader_readout_headline(*, status_label: str, status_tone: str, focus: dict, watch_next: str, no_candidate_state: str) -> str: +def _build_trader_readout_now( + *, + status_label: str, + status_tone: str, + focus: dict, + visible_reasons: list[str], + no_candidate_state: str, +) -> str: if status_tone == "do_not_use": - return "Do Not Use: restore freshness/readiness before reading this market." + return "Do Not Use: restore data/runtime before reading." if focus: - return f"{status_label}: {watch_next}" + if status_tone == "review": + return f"{status_label}: one focus; manual check only." + wait_for = _short_trader_wait_label(list(focus.get("missing_confirmations") or []) or visible_reasons) + return f"{status_label}: wait for {wait_for}; no trade." return f"{status_label}: {no_candidate_state}" +def _build_trader_readout_reason( + *, + status_tone: str, + focus: dict, + visible_reasons: list[str], + current_session: str, + market_posture: str, + ict_chain_sentence: str, +) -> str: + if status_tone == "do_not_use": + return "Data/readiness is not safe enough for live reading." + if focus: + structure = _short_structure_label(str(focus.get("model_code") or "")) + wait_for = _short_trader_wait_label(list(focus.get("missing_confirmations") or []) or visible_reasons) + session = str(focus.get("session_code") or current_session or "session") + return f"{session}: {structure}; {wait_for} still decides it." + if "wait sweep/MSS/FVG" in ict_chain_sentence: + return "No complete sweep -> MSS -> FVG chain yet." + return f"{current_session}: no clean ICT focus; {market_posture}." + + +def _build_trader_readout_wait(*, status_tone: str, focus: dict, visible_reasons: list[str]) -> str: + if status_tone == "do_not_use": + return "Restore data/runtime, then refresh." + if focus: + wait_for = _short_trader_wait_label(list(focus.get("missing_confirmations") or []) or visible_reasons) + return f"Wait for {wait_for}; then record Agree/Disagree/No Trade." + return "Wait for sweep -> MSS -> FVG alignment." + + +def _build_trader_readout_risk( + *, + status_tone: str, + focus: dict, + visible_reasons: list[str], + freshness_summary: dict, +) -> str: + if status_tone == "do_not_use": + reason = str(freshness_summary.get("freeze_reason") or "data/runtime blocked") + return f"Risk: {reason.replace('_', ' ')}." + if focus: + caution = _short_trader_wait_label(visible_reasons[:1]) if visible_reasons else "confirmation risk" + return f"Risk: acting before {caution} confirms." + return "Risk: forcing a setup before the ICT chain completes." + + +def _build_trader_readout_historical_edge(mainline_focus_gate: dict) -> str: + backtest_gate = dict(mainline_focus_gate.get("backtest_quality_gate") or {}) + if backtest_gate.get("passed"): + return "Backtest passed; still read-only." + reason = str(backtest_gate.get("reason") or "") + if "missing" in reason or "sample" in reason or not reason: + return "Backtest insufficient; study only." + return "Backtest failed quality gate; study only." + + def _build_trader_readout_why_no_trade( *, status_tone: str, diff --git a/src/web/i18n.js b/src/web/i18n.js index 0e1829e..65b47be 100644 --- a/src/web/i18n.js +++ b/src/web/i18n.js @@ -238,6 +238,19 @@ 'Focus Reason': '焦点原因', 'Data State': '数据状态', 'No Candidate': '无候选', + 'Do Not Use: restore data/runtime before reading.': '不要使用:先恢复数据/运行,再看盘。', + 'Review Now: one focus; manual check only.': '立即复核:只看一个焦点,只做人工检查。', + 'Data/readiness is not safe enough for live reading.': '数据/就绪状态不足,暂不适合盘中阅读。', + 'Restore data/runtime, then refresh.': '先恢复数据/运行,再刷新。', + 'Backtest passed; still read-only.': '回测通过;仍然只读。', + 'Backtest insufficient; study only.': '回测样本不足;只做学习记录。', + 'Backtest failed quality gate; study only.': '回测质量门未通过;只做学习记录。', + 'Read-only; no orders; not a buy/sell signal.': '只读;不下单;不是买卖信号。', + 'No complete sweep -> MSS -> FVG chain yet.': '还没有完整扫流动性 -> MSS -> FVG 链路。', + 'Wait for sweep -> MSS -> FVG alignment.': '等待扫流动性 -> MSS -> FVG 对齐。', + 'Risk: forcing a setup before the ICT chain completes.': '风险:ICT 链路未完成就强行看形态。', + 'No complete profile-matching ICT chain yet.': '还没有匹配当前偏好模板的完整 ICT 链路。', + 'Risk: forcing another profile into this plan.': '风险:把其他模板的形态强行套进当前计划。', 'Historical edge unavailable: check Strategy Backtest before trusting this setup.': '历史优势不可用:信任该形态前先查看策略回测。', 'Historical edge unavailable: Strategy Backtest data is not reachable.': '历史优势不可用:策略回测数据暂时不可达。', 'No historical edge yet: run Strategy Backtest before trusting this setup.': '还没有历史优势:信任该形态前先运行策略回测。', @@ -518,6 +531,9 @@ ['Range/Location', '区间/位置'], ['main Draw', '主 Draw'], ['Liquidity event', '流动性事件'], + ['sweep -> MSS -> FVG', '扫流动性 -> MSS -> FVG'], + ['Wait for', '等待'], + ['Risk:', '风险:'], ['wait for', '等待'], ['Backtest Pass', '回测通过'], ['Backtest Fail', '回测失败'], @@ -582,6 +598,14 @@ ['Historical edge unavailable', '历史优势不可用'], ['Still read-only', '仍然只读'], ['confirm current ICT context first', '先确认当前 ICT 上下文'], + ['one focus; manual check only', '只看一个焦点,只做人工检查'], + ['manual check only', '只做人工检查'], + ['no trade', '不交易'], + ['still decides it', '仍然是关键'], + ['then record Agree/Disagree/No Trade', '然后记录同意/不同意/不交易'], + ['acting before', '提前行动,早于'], + ['confirms', '确认'], + ['not a buy/sell signal', '不是买卖信号'], ['check Strategy Backtest before trusting this setup', '信任该形态前先查看策略回测'], ['run Strategy Backtest before trusting this setup', '信任该形态前先运行策略回测'], ['Strategy Backtest data is not reachable', '策略回测数据暂时不可达'], @@ -869,6 +893,13 @@ ]; const zhPatterns = [ + [/^Observe Only: wait for (.+); no trade\.$/i, '仅观察:等待 $1;不交易。'], + [/^Wait for (.+); then record Agree\/Disagree\/No Trade\.$/i, '等待 $1;然后记录同意/不同意/不交易。'], + [/^Risk: acting before (.+) confirms\.$/i, '风险:$1确认前,不要提前行动。'], + [/^([A-Z_]+): sweep -> MSS -> FVG; (.+) still decides it\.$/i, '$1:扫流动性 -> MSS -> FVG;$2仍然是关键。'], + [/^No historical edge yet: (.+)\. expectancy (.+), PF (.+), DD (.+), (.+) samples\.$/i, '还没有历史优势:$1。期望 $2,盈利因子 $3,最大回撤 $4,$5 个样本。'], + [/^Historical edge passed: expectancy (.+), PF (.+), DD (.+), (.+) samples\. Still read-only; confirm current ICT context first\.$/i, '历史优势通过:期望 $1,盈利因子 $2,最大回撤 $3,$4 个样本。仍然只读;先确认当前 ICT 上下文。'], + [/^Trader Mode changed: (.+); (.+); (.+)\.$/i, '交易员模式变化:$1;$2;$3。'], [/^Observe Only: Watch Candidate #(\d+): (.+)\.$/i, '仅观察:关注候选 #$1:$2。'], [/^Review Now: Watch Candidate #(\d+): (.+)\.$/i, '立即复核:关注候选 #$1:$2。'], [/^Watch Candidate #(\d+): (.+)\.$/i, '关注候选 #$1:$2。'], diff --git a/src/web/trader_mode.js b/src/web/trader_mode.js index cadbae7..7c583c9 100644 --- a/src/web/trader_mode.js +++ b/src/web/trader_mode.js @@ -425,9 +425,9 @@ function buildClientReadout(trader, focus, overrides = {}) { || trader.trader_readout?.boundary || 'Read-only analysis; no order submission; not a buy/sell instruction.'; const now = overrides.now || overrides.headline || trader.trader_readout?.now || trader.trader_readout?.headline || `${trader.status_label || 'Observe Only'}: ${watchNext}`; - const reason = overrides.reason || focusReason; - const wait = overrides.wait || watchNext; - const risk = overrides.risk || `${whyNoTrade} ${dataState}`.trim(); + const reason = overrides.reason || trader.trader_readout?.reason || focusReason; + const wait = overrides.wait || trader.trader_readout?.wait || watchNext; + const risk = overrides.risk || trader.trader_readout?.risk || `${whyNoTrade} ${dataState}`.trim(); const headline = now; return { mode: 'trader_one_screen_readout', @@ -523,10 +523,16 @@ function applyTraderProfile(payload, profile) { if (matchingFocus) { const nextFocus = normalizeCandidate(matchingFocus); const reviewNow = nextFocus.tier === 'review_now'; + const profileWait = (nextFocus.missing_confirmations || [])[0] + ? humanizeToken((nextFocus.missing_confirmations || [])[0]) + : 'confirmation'; const readout = currentMatches && trader.trader_readout ? trader.trader_readout : buildClientReadout(trader, nextFocus, { - headline: reviewNow ? `Review Now: watch ${nextFocus.title}, no execution.` : `Observe Only: watch ${nextFocus.title}.`, + headline: reviewNow ? 'Review Now: one focus; manual check only.' : `Observe Only: wait ${profileWait}; no trade.`, + reason: nextFocus.trader_focus_reason || buildClientFocusExplanation(nextFocus), + wait: `Wait ${profileWait}; then record Agree/Disagree/No Trade.`, + risk: nextFocus.trader_no_trade_reason || `Risk: acting before ${profileWait} confirms.`, why_no_trade: nextFocus.trader_no_trade_reason || 'No trade: wait confirmation.', watch_next: nextFocus.trader_watch_next || `Watch ${nextFocus.title}: confirmation.`, focus_reason: nextFocus.trader_focus_reason || buildClientFocusExplanation(nextFocus), @@ -568,7 +574,10 @@ function applyTraderProfile(payload, profile) { primary_conclusion: `No focus matches ${profile.label}; stay observe-only and wait for a matching setup.`, focus_quality_explanation: 'Profile filters excluded the current candidate from the fast-read view.', trader_readout: buildClientReadout(trader, {}, { - headline: `Observe Only: no ${profile.label} focus.`, + headline: `Observe Only: no ${profile.label} focus; no trade.`, + reason: 'No complete profile-matching ICT chain yet.', + wait: `Wait for a ${profile.sessions.join('/') || 'selected-session'} ${profile.setups.join('/') || 'selected-setup'} candidate.`, + risk: 'Risk: forcing another profile into this plan.', why_no_trade: `No trade: no candidate matches ${profile.label}.`, watch_next: `Wait for a ${profile.sessions.join('/') || 'selected-session'} ${profile.setups.join('/') || 'selected-setup'} candidate to enter the queue.`, focus_reason: 'No unique focus because profile filters removed the current candidate.', diff --git a/tests/unit/test_documentation_consistency.py b/tests/unit/test_documentation_consistency.py index a6cf3f4..644e986 100644 --- a/tests/unit/test_documentation_consistency.py +++ b/tests/unit/test_documentation_consistency.py @@ -25,6 +25,7 @@ ACTIVE_TOP_LEVEL_DOCS = { "AI_ICT_ReviewNow主线Gate_S3S4_2026-05-21_v1.md", "AI_ICT_Cockpit链路句与反馈错误分类_S5S6_2026-05-21_v1.md", "AI_ICT_只读实盘可靠性与交易员SOP_S7S8_2026-05-21_v1.md", + "AI_ICT_TraderCockpit状态话术压缩_S9_2026-05-21_v1.md", "AI_ICT_主线纠偏与功能裁剪方案_2026-05-20_v1.md", "AI_ICT_实盘辅助分析决策助手上线必做清单_2026-05-18_v1.md", "AI_ICT_实盘辅助分析决策助手使用说明_2026-05-18_v1.md", diff --git a/tests/unit/test_operator_workspace_service.py b/tests/unit/test_operator_workspace_service.py index 6762234..8340ee6 100644 --- a/tests/unit/test_operator_workspace_service.py +++ b/tests/unit/test_operator_workspace_service.py @@ -226,13 +226,27 @@ class OperatorWorkspaceServiceTests(unittest.TestCase): self.assertIn("plain_language_reasons", trader_mode_summary) self.assertLessEqual(len(trader_mode_summary["plain_language_reasons"]), 3) self.assertEqual(trader_mode_summary["trader_readout"]["mode"], "trader_one_screen_readout") + self.assertIn("now", trader_mode_summary["trader_readout"]) + self.assertIn("reason", trader_mode_summary["trader_readout"]) + self.assertIn("wait", trader_mode_summary["trader_readout"]) + self.assertIn("risk", trader_mode_summary["trader_readout"]) + self.assertIn("historical_edge", trader_mode_summary["trader_readout"]) + self.assertIn("boundary", trader_mode_summary["trader_readout"]) + self.assertIn("manual check only", trader_mode_summary["trader_readout"]["now"]) + self.assertIn("Read-only", trader_mode_summary["trader_readout"]["boundary"]) self.assertIn("No trade", trader_mode_summary["trader_readout"]["why_no_trade"]) self.assertIn("Watch", trader_mode_summary["trader_readout"]["watch_next"]) self.assertIn("Current chain:", trader_mode_summary["trader_readout"]["focus_reason"]) self.assertIn("ict_chain_sentence", trader_mode_summary["trader_readout"]) self.assertIn("backtest", trader_mode_summary["trader_readout"]["ict_chain_steps"]) self.assertIn("market", trader_mode_summary["trader_readout"]["data_state"]) - self.assertIn("No Candidate", [item["label"] for item in trader_mode_summary["trader_readout"]["one_screen_cards"]]) + self.assertEqual( + [item["label"] for item in trader_mode_summary["trader_readout"]["one_screen_cards"]], + ["Now", "Reason", "Wait", "Risk", "Historical Edge", "Boundary"], + ) + self.assertTrue( + all(len(str(item["value"])) <= 140 for item in trader_mode_summary["trader_readout"]["one_screen_cards"]) + ) self.assertEqual(trader_mode_summary["ict_rulebook_mapping"]["surface"], "trader_cockpit") self.assertIn("backtest_evidence", trader_mode_summary["ict_rulebook_mapping"]["required_concepts"]) self.assertIn("no_trade_conditions", trader_mode_summary["ict_rulebook_mapping"]["present_concepts"])