Validate trader cockpit copy with feedback
This commit is contained in:
+3
-2
@@ -836,11 +836,12 @@ Automation execution-layer PXX remains closed and its known scope stays frozen.
|
||||
- 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.
|
||||
- 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.
|
||||
- S10 real intraday feedback validation is implemented, deployed, and remote-verified: added `docs/AI_ICT_TraderCockpit真实反馈验证_S10_2026-05-21_v1.md`; used real remote evidence (`operator_feedback_count=3`, recent Candidate `4610` observation with `operator_agreement=unclear`, `paper_ticket_count=0`, `paper_execution_count=0`, `weekly_review_entry_count=0`) to tighten the six-card copy without adding features. `Observe Only` now says the explicit gap (`FVG retrace is not confirmed yet; no trade`), `Wait` says the operator should wait for `trigger-chart FVG retrace confirmation`, `Risk` warns against treating the watch as a signal, and `Historical Edge` is reduced to Study / Review-only language without first-screen PF/DD/expectancy detail. 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/s10-trader-cockpit-real-feedback-six-card-1366x768.png`.
|
||||
- Current assisted-analysis next gate: proceed to S11 only: continue real intraday use on `/trader-mode`; if `unclear` appears again, adjust only existing `Reason` / `Wait` / `Risk` wording or hide overly detailed text. 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. Paper ticket / paper execution / weekly review gates still wait for a real executable candidate and cannot be backfilled from blocked candidates.
|
||||
|
||||
## 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`, `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.
|
||||
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`, `docs/AI_ICT_TraderCockpit状态话术压缩_S9_2026-05-21_v1.md`, and `docs/AI_ICT_TraderCockpit真实反馈验证_S10_2026-05-21_v1.md` as the active anchors. Continue with S11: use `/trader-mode` in the real intraday loop and collect the next real operator feedback. If the next feedback is still `unclear` or `disagree`, adjust only existing text/hiding rules around `Reason`, `Wait`, and `Risk`; if feedback is clear, stop tuning and wait for a real executable candidate before paper ticket / paper execution evidence. 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
|
||||
|
||||
|
||||
@@ -940,7 +940,8 @@
|
||||
- [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
|
||||
- [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
|
||||
- [x] S10 real intraday feedback validation: used real `operator_agreement=unclear` feedback on Candidate `4610` to tighten the six-card copy; `Observe Only` now names the missing FVG confirmation, `Wait` points to trigger-chart FVG retrace confirmation, `Risk` warns not to treat the watch as a signal, and `Historical Edge` is Study / Review-only without first-screen PF/DD/expectancy detail; deployed and remote/browser verified
|
||||
- [ ] S11 next gate: continue real `/trader-mode` use and collect the next real operator feedback; if `unclear`/`disagree` recurs, tune only existing `Reason` / `Wait` / `Risk` text or hiding rules, otherwise stop tuning and wait for a real executable candidate before paper ticket / paper execution evidence
|
||||
- [ ] 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`)
|
||||
|
||||
@@ -0,0 +1,60 @@
|
||||
# AI_ICT Trader Cockpit 真实反馈验证 S10 2026-05-21 v1
|
||||
|
||||
## 1. 本阶段目标
|
||||
|
||||
S10 只验证一件事:交易员盘中看 `Now / Reason / Wait / Risk / Historical Edge / Boundary` 六卡片时,是否能一眼知道为什么现在不交易、下一步等什么。
|
||||
|
||||
本阶段不新增产品功能,不新增交易能力,不扩大 archive / workflow / external context 长尾。
|
||||
|
||||
## 2. 使用的真实反馈
|
||||
|
||||
远端真实样本显示:
|
||||
|
||||
- `operator_feedback_count=3`
|
||||
- `paper_ticket_count=0`
|
||||
- `paper_execution_count=0`
|
||||
- `weekly_review_entry_count=0`
|
||||
- 最近 observation 中 Candidate `4610` 曾记录为 `operator_agreement=unclear`
|
||||
- 当前仍没有真实 executable candidate,因此不能创建 paper ticket 或 paper execution
|
||||
|
||||
结论:S10 可以使用 `unclear` 反馈来优化首屏表达,但不能把 R2 标为完成,也不能伪造 paper / execution / weekly review 样本。
|
||||
|
||||
## 3. 本轮调整
|
||||
|
||||
原 S9 表达:
|
||||
|
||||
- `Observe Only: wait for FVG retrace; no trade.`
|
||||
- `Wait for FVG retrace; then record Agree/Disagree/No Trade.`
|
||||
|
||||
S10 改为更明确的盘中语言:
|
||||
|
||||
- `Observe Only: FVG retrace is not confirmed yet; no trade.`
|
||||
- `Wait for trigger-chart FVG retrace confirmation; then record Agree/Disagree/No Trade.`
|
||||
|
||||
`Reason` 也从“链路存在”改成“链路只是观察对象,缺什么确认”:
|
||||
|
||||
- `LONDON: sweep -> MSS -> FVG is only a watch; FVG retrace is not confirmed yet.`
|
||||
|
||||
`Historical Edge` 不再在首屏展示期望、PF、DD 等指标长句,只保留:
|
||||
|
||||
- `Study only: ...; no historical edge.`
|
||||
- `Review-only: backtest gate passed; still not a signal.`
|
||||
|
||||
指标细节继续留在 Strategy Backtest 页面,不塞进 Trader Cockpit 首屏。
|
||||
|
||||
## 4. 验收
|
||||
|
||||
- 六卡片仍固定为 `Now / Reason / Wait / Risk / Historical Edge / Boundary`。
|
||||
- `Now` 必须直接给出“不交易”的缺口原因。
|
||||
- `Wait` 必须指向一个可等待的确认,而不是抽象名词。
|
||||
- `Historical Edge` 只能表达 Study / Review-only,不得像交易建议。
|
||||
- 中文界面不得出现本轮检查到的关键英文残留。
|
||||
- `execution_allowed=false`、`real_live_ready=false`、`must_not_trade=true` 保持不变。
|
||||
|
||||
## 5. 下一步
|
||||
|
||||
继续收集真实盘中反馈:
|
||||
|
||||
- 如果还有 `unclear`,优先压缩 `Reason` 和 `Wait`。
|
||||
- 如果出现 `disagree`,优先检查 `Risk` 是否遗漏了交易员看到的关键阻塞。
|
||||
- 只有出现真实 executable candidate,才能进入 paper ticket / paper execution 样本采集。
|
||||
@@ -3638,8 +3638,8 @@ def _build_trader_readout_now(
|
||||
if focus:
|
||||
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."
|
||||
wait_gap = _trader_wait_gap_sentence(list(focus.get("missing_confirmations") or []) or visible_reasons)
|
||||
return f"{status_label}: {wait_gap}; no trade."
|
||||
return f"{status_label}: {no_candidate_state}"
|
||||
|
||||
|
||||
@@ -3656,9 +3656,11 @@ def _build_trader_readout_reason(
|
||||
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 status_tone == "review":
|
||||
return f"{session}: {structure} is the one focus; manual review still decides it."
|
||||
wait_gap = _trader_wait_gap_sentence(list(focus.get("missing_confirmations") or []) or visible_reasons)
|
||||
return f"{session}: {structure} is only a watch; {wait_gap}."
|
||||
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}."
|
||||
@@ -3668,9 +3670,11 @@ def _build_trader_readout_wait(*, status_tone: str, focus: dict, visible_reasons
|
||||
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)
|
||||
if status_tone == "review":
|
||||
return "Review the focus manually; then record Agree/Disagree/No Trade."
|
||||
wait_for = _trader_wait_action_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."
|
||||
return "Wait for a complete sweep -> MSS -> FVG chain."
|
||||
|
||||
|
||||
def _build_trader_readout_risk(
|
||||
@@ -3683,20 +3687,22 @@ def _build_trader_readout_risk(
|
||||
if status_tone == "do_not_use":
|
||||
reason = str(freshness_summary.get("freeze_reason") or "data/runtime blocked")
|
||||
return f"Risk: {reason.replace('_', ' ')}."
|
||||
if status_tone == "review":
|
||||
return "Risk: treating Review Now as a trade signal."
|
||||
if focus:
|
||||
caution = _short_trader_wait_label(visible_reasons[:1]) if visible_reasons else "confirmation risk"
|
||||
return f"Risk: acting before {caution} confirms."
|
||||
caution = _trader_wait_action_label(visible_reasons[:1]) if visible_reasons else "confirmation"
|
||||
return f"Risk: treating this as a signal before {caution}."
|
||||
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."
|
||||
return "Review-only: backtest gate passed; still not a signal."
|
||||
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."
|
||||
return "Study only: backtest sample insufficient; no historical edge."
|
||||
return "Study only: backtest gate blocked; no historical edge."
|
||||
|
||||
|
||||
def _build_trader_readout_why_no_trade(
|
||||
@@ -4108,6 +4114,50 @@ def _short_trader_wait_label(values: list) -> str:
|
||||
return _humanize_trader_reason(tokens[0]).rstrip(".").lower()[:56]
|
||||
|
||||
|
||||
def _trader_wait_gap_sentence(values: list) -> str:
|
||||
tokens = [str(item or "").strip() for item in list(values or []) if str(item or "").strip()]
|
||||
joined = " ".join(tokens).lower()
|
||||
if not tokens:
|
||||
return "confirmation is missing"
|
||||
if "runtime_offline" in joined or "runtime" in joined:
|
||||
return "runtime is not ready"
|
||||
if "review_packet" in joined:
|
||||
return "review packet is not fresh"
|
||||
if "market" in joined or "stream" in joined or "gap" in joined:
|
||||
return "market feed is not reliable"
|
||||
if "fvg" in joined and "retrace" in joined:
|
||||
return "FVG retrace is not confirmed yet"
|
||||
if "direction_mismatch" in joined:
|
||||
return "direction alignment is missing"
|
||||
if "signal_missing" in joined or "signal" in joined:
|
||||
return "clean signal is missing"
|
||||
if "trust" in joined:
|
||||
return "trust gate is not cleared yet"
|
||||
return f"{_short_trader_wait_label(tokens)} is not confirmed yet"
|
||||
|
||||
|
||||
def _trader_wait_action_label(values: list) -> str:
|
||||
tokens = [str(item or "").strip() for item in list(values or []) if str(item or "").strip()]
|
||||
joined = " ".join(tokens).lower()
|
||||
if not tokens:
|
||||
return "one clean confirmation"
|
||||
if "runtime_offline" in joined or "runtime" in joined:
|
||||
return "runtime recovery"
|
||||
if "review_packet" in joined:
|
||||
return "a fresh review packet"
|
||||
if "market" in joined or "stream" in joined or "gap" in joined:
|
||||
return "reliable market feed"
|
||||
if "fvg" in joined and "retrace" in joined:
|
||||
return "trigger-chart FVG retrace confirmation"
|
||||
if "direction_mismatch" in joined:
|
||||
return "direction alignment"
|
||||
if "signal_missing" in joined or "signal" in joined:
|
||||
return "a clean signal"
|
||||
if "trust" in joined:
|
||||
return "trust gate clearance"
|
||||
return _short_trader_wait_label(tokens)
|
||||
|
||||
|
||||
def _humanize_caution_reason(*, visible_reasons: list[str], decision_packet: dict) -> str:
|
||||
reasons = [str(item or "") for item in visible_reasons if str(item or "")]
|
||||
rejected = list((decision_packet.get("confirmation_chain") or {}).get("rejected") or [])
|
||||
|
||||
@@ -245,6 +245,11 @@
|
||||
'Backtest passed; still read-only.': '回测通过;仍然只读。',
|
||||
'Backtest insufficient; study only.': '回测样本不足;只做学习记录。',
|
||||
'Backtest failed quality gate; study only.': '回测质量门未通过;只做学习记录。',
|
||||
'Study only: backtest data unavailable.': '只做学习:回测数据不可用。',
|
||||
'Study only: no backtest edge yet.': '只做学习:还没有回测优势。',
|
||||
'Review-only: backtest gate passed; still not a signal.': '仅复核:回测质量门通过,但仍不是信号。',
|
||||
'Review the focus manually; then record Agree/Disagree/No Trade.': '人工复核这个焦点;然后记录同意/不同意/不交易。',
|
||||
'Risk: treating Review Now as a trade signal.': '风险:把“立即复核”误当成交易信号。',
|
||||
'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 对齐。',
|
||||
@@ -534,10 +539,22 @@
|
||||
['sweep -> MSS -> FVG', '扫流动性 -> MSS -> FVG'],
|
||||
['Wait for', '等待'],
|
||||
['Risk:', '风险:'],
|
||||
['FVG retrace is not confirmed yet', 'FVG 回撤尚未确认'],
|
||||
['trigger-chart FVG retrace confirmation', '触发图 FVG 回撤确认'],
|
||||
['trust gate is not cleared yet', '信任门禁尚未放行'],
|
||||
['trust gate clearance', '信任门禁放行'],
|
||||
['is only a watch', '只是观察对象'],
|
||||
['is the one focus', '是唯一焦点'],
|
||||
['manual review still decides it', '仍由人工复核决定'],
|
||||
['treating this as a signal before', '在以下条件前把它当成信号:'],
|
||||
['not confirmed yet', '尚未确认'],
|
||||
['no historical edge', '没有历史优势'],
|
||||
['wait for', '等待'],
|
||||
['Backtest Pass', '回测通过'],
|
||||
['Backtest Fail', '回测失败'],
|
||||
['Backtest Insufficient', '回测样本不足'],
|
||||
['backtest sample insufficient', '回测样本不足'],
|
||||
['backtest gate blocked', '回测质量门阻塞'],
|
||||
['therefore Review Now', '因此人工复核'],
|
||||
['therefore Observe Only', '因此仅观察'],
|
||||
['therefore Do Not Use', '因此不要使用'],
|
||||
@@ -894,9 +911,14 @@
|
||||
|
||||
const zhPatterns = [
|
||||
[/^Observe Only: wait for (.+); no trade\.$/i, '仅观察:等待 $1;不交易。'],
|
||||
[/^Observe Only: (.+); no trade\.$/i, '仅观察:$1;不交易。'],
|
||||
[/^Wait for (.+); then record Agree\/Disagree\/No Trade\.$/i, '等待 $1;然后记录同意/不同意/不交易。'],
|
||||
[/^Risk: acting before (.+) confirms\.$/i, '风险:$1确认前,不要提前行动。'],
|
||||
[/^Risk: treating this as a signal before (.+)\.$/i, '风险:$1前,不要把它当成信号。'],
|
||||
[/^([A-Z_]+): sweep -> MSS -> FVG; (.+) still decides it\.$/i, '$1:扫流动性 -> MSS -> FVG;$2仍然是关键。'],
|
||||
[/^([A-Z_]+): sweep -> MSS -> FVG is only a watch; (.+)\.$/i, '$1:扫流动性 -> MSS -> FVG 只是观察对象;$2。'],
|
||||
[/^([A-Z_]+): sweep -> MSS -> FVG is the one focus; manual review still decides it\.$/i, '$1:扫流动性 -> MSS -> FVG 是唯一焦点;仍由人工复核决定。'],
|
||||
[/^Study only: (.+); no historical edge\.$/i, '只做学习:$1;没有历史优势。'],
|
||||
[/^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。'],
|
||||
|
||||
+7
-26
@@ -163,18 +163,6 @@ function formatDurationSeconds(value) {
|
||||
return `${(seconds / 3600).toFixed(1)}h`;
|
||||
}
|
||||
|
||||
function formatCompactNumber(value, digits = 2) {
|
||||
const number = Number(value);
|
||||
if (!Number.isFinite(number)) return 'n/a';
|
||||
return number.toFixed(digits);
|
||||
}
|
||||
|
||||
function formatCompactR(value) {
|
||||
const number = Number(value);
|
||||
if (!Number.isFinite(number)) return 'n/a';
|
||||
return `${number.toFixed(2)}R`;
|
||||
}
|
||||
|
||||
function normalizeTone(value) {
|
||||
return String(value || 'observe').toLowerCase().replace(/[^a-z0-9]+/g, '-');
|
||||
}
|
||||
@@ -462,14 +450,14 @@ function backtestLatestRun(backtests) {
|
||||
function buildHistoricalEdgeSummary(backtests) {
|
||||
if (backtests?.unavailable) {
|
||||
return {
|
||||
value: 'Historical edge unavailable: Strategy Backtest data is not reachable.',
|
||||
value: 'Study only: backtest data unavailable.',
|
||||
tone: 'blocked',
|
||||
};
|
||||
}
|
||||
const latest = backtestLatestRun(backtests);
|
||||
if (!latest) {
|
||||
return {
|
||||
value: 'No historical edge yet: run Strategy Backtest before trusting this setup.',
|
||||
value: 'Study only: no backtest edge yet.',
|
||||
tone: 'caution',
|
||||
};
|
||||
}
|
||||
@@ -477,22 +465,15 @@ function buildHistoricalEdgeSummary(backtests) {
|
||||
const qualityGate = backtests?.quality_gate || summary.quality_gate || {};
|
||||
const passed = Boolean(qualityGate.passed);
|
||||
const reasons = Array.isArray(qualityGate.reasons) ? qualityGate.reasons : [];
|
||||
const sampleCount = summary.executed_signals ?? summary.total_signals ?? summary.candidate_count ?? 0;
|
||||
const metricLine = [
|
||||
`expectancy ${formatCompactR(summary.expectancy_r)}`,
|
||||
`PF ${formatCompactNumber(summary.profit_factor_r)}`,
|
||||
`DD ${formatCompactR(summary.max_drawdown_r)}`,
|
||||
`${sampleCount} samples`,
|
||||
].join(', ');
|
||||
if (passed) {
|
||||
return {
|
||||
value: `Historical edge passed: ${metricLine}. Still read-only; confirm current ICT context first.`,
|
||||
value: 'Review-only: backtest gate passed; still not a signal.',
|
||||
tone: 'edge',
|
||||
};
|
||||
}
|
||||
const reasonText = reasons.length ? reasons.slice(0, 2).map(humanizeToken).join('; ') : 'quality gate blocked';
|
||||
return {
|
||||
value: `No historical edge yet: ${reasonText}. ${metricLine}.`,
|
||||
value: `Study only: ${reasonText}; no historical edge.`,
|
||||
tone: 'caution',
|
||||
};
|
||||
}
|
||||
@@ -529,10 +510,10 @@ function applyTraderProfile(payload, profile) {
|
||||
const readout = currentMatches && trader.trader_readout
|
||||
? trader.trader_readout
|
||||
: buildClientReadout(trader, nextFocus, {
|
||||
headline: reviewNow ? 'Review Now: one focus; manual check only.' : `Observe Only: wait ${profileWait}; no trade.`,
|
||||
headline: reviewNow ? 'Review Now: one focus; manual check only.' : `Observe Only: ${profileWait} is not confirmed yet; 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.`,
|
||||
wait: `Wait for ${profileWait}; then record Agree/Disagree/No Trade.`,
|
||||
risk: nextFocus.trader_no_trade_reason || `Risk: treating this as a signal before ${profileWait}.`,
|
||||
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),
|
||||
|
||||
@@ -26,6 +26,7 @@ ACTIVE_TOP_LEVEL_DOCS = {
|
||||
"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_TraderCockpit真实反馈验证_S10_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",
|
||||
|
||||
@@ -233,6 +233,8 @@ class OperatorWorkspaceServiceTests(unittest.TestCase):
|
||||
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("Review the focus manually", trader_mode_summary["trader_readout"]["wait"])
|
||||
self.assertIn("trade signal", trader_mode_summary["trader_readout"]["risk"])
|
||||
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"])
|
||||
@@ -930,6 +932,12 @@ class OperatorWorkspaceServiceTests(unittest.TestCase):
|
||||
self.assertEqual(summary["candidate_watchlist_tiers"]["review_now"], [])
|
||||
self.assertEqual(summary["trader_mode_summary"]["status"], "observe_only")
|
||||
self.assertIn("sample_size_too_small", summary["trader_mode_summary"]["key_reasons"])
|
||||
readout = summary["trader_mode_summary"]["trader_readout"]
|
||||
self.assertIn("not cleared", readout["now"])
|
||||
self.assertIn("only a watch", readout["reason"])
|
||||
self.assertIn("trust gate clearance", readout["wait"])
|
||||
self.assertIn("Study only", readout["historical_edge"])
|
||||
self.assertIn("no historical edge", readout["historical_edge"])
|
||||
|
||||
def test_decision_assistant_trader_mode_freezes_stale_payload(self) -> None:
|
||||
service = DecisionAssistantSummaryService(
|
||||
|
||||
Reference in New Issue
Block a user