# dsh-tradewatcher 待开发清单(ROADMAP) 本清单回答一个问题:**对标爱盯盘(浏览器扩展)之后,我们该补什么、补到什么程度、按什么顺序补。** 对标对象:爱盯盘(Edge/Chrome/Firefox 扩展 + VS Code 版,面壁投基开发,2.36.0;招牌能力是浏览器角标、置顶置底、摸鱼主题、云同步)。 三类条目**不进清单**:① 依赖浏览器扩展形态才能做的(角标常驻、置顶置底、跨页面注入);② 数据源已被监管层停止披露的(北向实时净买额);③ 与 README「明确不做的项」重复的(再引图表库/状态管理库、CI 供应商、账号体系)。每条给:目的 / 做什么 / 数据源 / 验收 / 工作量(S ≈ 半天,M ≈ 1–2 天,L ≈ 3 天以上)。 **排序原则**:先让已有数据不说谎(口径与状态)→ 再把状态外化到一眼看得见 → 最后做加法(新市场、新面板)。加法越多,口径不清时的误导越大。 ## 前置:动手前必须先探活的数据源 以下接口来自对标对象的第三方整理,**本仓库尚未验证连通性与字段**。做对应功能前先跑通、并记录真实返回结构(不许按文档猜): | 用途 | 候选 | 条目 | 风险 | | --- | --- | --- | --- | | 场外基金估值(盘中参考) | `fundgz.1234567.com.cn/js/.js`(JSONP:GSZ/GSZZL/GZTIME) | P1-14 | 域名未验证;JSONP 需手工剥壳 | | 场外基金净值(收盘定稿) | F10 `FundMNFInfo`(NAV/NAVCHGRT) | P1-14 | 需确认 code 前缀与日期字段 | | ETF 季报持仓穿透 | `FundArchivesDatas.aspx?type=jjcc`(JS 包裹的 HTML 表格) | P2-1 | 需解析 HTML;只给前十大 | | 汇率(港股持仓折算) | 腾讯 `whHKDCNY` / 新浪 `fx_shkdcny` | P1-11 | 符号未验证;需确认中间价还是即期 | ## P0(**v0.25.0 已全部落地** —— 以下保留原始条目文本,便于逐条对照验收) > 落地情况与两处**有意偏离**见文末「已完成 → P0(v0.25.0)」。偏离都是有理由的、写明的,不是漏做。 ### P0-1 读工具「数据时刻」统一契约(S) **目的**:agent 读到的每个数都能回答"这是几点、什么源、可不可信"。 **做什么**:7 个读工具(portfolio / ledger / watchlist / quotes / calendar / rescue / models)统一返回 `asOf`(数据时刻)/ `stale`(是否降级)/ `source`(em|tencent|sina|lkg)/ `missing[]`(拿不到的字段与原因);`portfolio` 增 `fxMode` 与 `unpriced[]`;`rescue` 增 `activeWindow`(采样窗口)与 `calibratedAt`。 **数据源**:现有内部状态,无需新源。 **验收**:任一工具返回不得出现无出处的数字;`missing[]` 要能解释每个缺失字段("上游没有这份数据" ≠ "这次暂时失败")。 **注意**:`stale`(降级复用)与 `cached`(休市定稿零回源)语义不同,两个字段都要留。 ### P0-2 卡片四态标记与来源徽章(S) **目的**:一眼分辨卡片是实时 / 延迟 / 定稿复用 / 没有。 **做什么**:每张行情卡加 6px 状态点——绿=实时、黄=延迟(超过刷新间隔 3 倍)、灰=定稿复用(`cached`)、红=缺失;tooltip 给源与时刻;列表头汇总 `12/23 实时 · 9 延迟 · 2 缺失`,缺失项可点开看原因。 **数据源**:现有 `/quotes` 的 `asOf`/`stale`/`cached`/`source`。 **验收**:四态色点都要真实出现过;汇总数字与逐卡状态**必须加得上**;未开盘时不得显示绿色。 ### P0-3 侧边栏徽标(S) **目的**:不打开面板也知道发生了什么(对标它的浏览器角标)。 **做什么**:徽标取护盘等级或持仓当日涨跌——护盘 ≥「疑似护盘」显示「护」;否则显示持仓当日盈亏率(带符号);收盘后加后缀「收」;隐身视图下只显示点位、不显示金额。 **数据源**:现有 rescue 快照 + portfolio 当日盈亏。 **验收**:徽标数字与面板顶部数字必须同源(同一次取数结果,不许各算一遍)。 ### P0-4 非活跃时段显式态(S) **目的**:收盘/午休时分数位为空,必须说清是"暂停采样"而不是"坏了"。 **做什么**:非采样时段分数位显示 `—`,旁注 `采样暂停 · 下次 10-09 09:25`;`gap` 只反映当日真实失败。 **验收**:暂停时段不得出现「采样缺口」字样;断言要覆盖到 UI 层。 ### P0-5 图表数据口径条(S) **目的**:图上每个数都能被追问"哪根、什么口径、更新到几点"。 **做什么**:详情图顶部一行 `日K · 前复权 · 截至 10-09 14:31(当根未收盘,MA 已含)`;非交易时段显示 `已收盘定稿`。 **验收**:口径条字段与实际取数参数同源(不许手写死);MA 是否含未收盘当根必须写明。 ### P0-6 成本口径切换联动说明(S) **目的**:摊薄/移动加权两口径并存,切换不能数字跳变且无解释。 **做什么**:切换时 toast 报差值(`口径切换:成本 X → Y,影响浮盈 ±Z`);两口径共用同一成本函数,切换只换参数。 **验收**:任一持仓两口径的成本都能复算;切换后总额变化等于逐项差值之和。 ### P0-7 评分可解释性与阈值漂移监控(M) **目的**:护盘评分现在只给总分与六因子,缺"这一分是谁加的"。 **做什么**:因子表加贡献度列(权 × 因子分),断言"贡献度求和 == 总分 ±0.5";每次出分写一条 rescue-log(含各因子原始值与当次阈值),阈值漂移时可回溯。 **验收**:任意快照可复算总分;阈值改动后能从日志看出分差从哪天开始偏。 ### P0-8 紧凑 / 隐身视图(M) **目的**:对标"一键摸鱼"。 **做什么**:三档 `完整 / 紧凑 / 隐身`(Alt+M 轮换,prefs.viewMode 持久化);紧凑=去说明文字与脚注;隐身=金额用 `¥••••` 的模糊表示、涨跌色转灰阶;金额统一走格式化出口(一处改、全局生效)。 **注意**:隐身只影响**显示**,不许影响取数、告警与 agent 工具返回。 **验收**:三档切换不触发额外请求;隐身态下页面不出现任何绝对值金额。 ### P0-9 多设备导出 / 导入(M) **目的**:对标它的云同步,但不做账号体系,走文件。 **做什么**:导出 watch/positions/ledger/prefs 打包 JSON,带 `schemaVersion` 与内容校验和;导入前预览冲突(行数、最新时间),覆盖前写 `.bak`;**账本重放不一致直接拒绝导入**(宁可不导入,不能导入一份算不平的账)。 **验收**:导出 → 清空 → 导入后持仓视图逐项一致;损坏/半截文件必须被拒并给出具体原因。 ### P0-10 内嵌 iframe 三态降级(S) **目的**:云图面板现在"白了就是白了",分不清没网还是站点挂了。 **做什么**:懒加载 + 8s 超时占位(显示域名与最后成功时间,不用裸心跳文案);三态=加载中/已加载/超时;超时态给一次「重试」。 **注意**:iframe 源站是第三方,本仓库只做展示与降级说明,不抓取它的内容。 **验收**:断网时显示超时态与最后成功时间,而不是空白 iframe。 ### P0-11 写工具最小权限与可撤销(S) **目的**:agent 能写数据(日历),写错要能退。 **做什么**:写工具记录 `by`(会话 id);提供 undo(按 id 反向操作);同秒 3 次以上写入节流;改到不该改的文件时拒绝并说明原因。 **验收**:任一写入都能撤回到写入前状态;节流触发时返回明确错误,不静默丢弃。 ## P1(增量:口径显式化 + 共同功能补强) ### P1-1 分时缩略图叠加信息(M) 缩略图叠加均价线与开/高/低、振幅(hover 时显示);图上不加常驻文字(密集布局里加字就是噪音)。 ### P1-2 B/S 去噪与区间图例(M) 同一天多笔合并为 `B×3`;缩放区间外给出图例(净买入/净卖出/成本变化),避免"点位看着多、其实只有两笔"。 ### P1-3 板块 alpha 口径显式化(S) 板块涨跌缺失时显示 `—` 并注明原因,**禁用 0 代替**——0 会被读成"没涨没跌",那是错的信息。 ### P1-4 自选异动提示(S) 自选里突然放量/异动用呼吸高亮 + 徽标队列;同一条目静默 30 分钟,避免刷屏。 ### P1-5 可用数量与费用列(M) 持仓表加「可用数量」(A 股 T+1、ETF/港股 T+0 白名单)与「费用」列(费用合计 + 占成交额比例)。 ### P1-6 不计入总额可下钻(S) 总额旁「不含 N 项」可点开,列出被排除的持仓与原因(无价/退市/口径不符)。 ### P1-7 资金流排行口径与时刻(S) 15:00 后停更改标「定稿」;金额与占比两种排序可切;来源标注沿用现有 `flowSource`。 ### P1-8 涨跌家数历史分位(M) 收盘后每日存一条快照,给出当前值在近 N 日的分位;`N<5` 不显示分位(样本太少的分位是噪音)。 ### P1-9 概率面板统计护栏(S) `N<30` 加「样本少」标记;概率给 Wilson 95% 区间——20 个样本的 80% 与 500 个样本的 80% 不是一回事。 ### P1-10 日历「可能变更」标记与勾稽(S+S) 自动事件改期给 diff 与历史;日历与持仓/自选勾稽(三组排序,格点实心=有持仓、空心=仅自选)。 ### P1-11 持仓跨市场汇率折算(M) 总额口径行 + `fxMode` 三态(不折算/固定汇率/实时汇率);**实时汇率源待探活**。不折算时总额必须标「不含港股市值」,而不是按 1:1 悄悄加进去。 ### P1-12 南向实时 + 北向收盘口径(S) 南向(港股通)盘中披露未暂停,可做实时。**北向自 2024-05-13 起港交所停止披露盘中买入/卖出成交金额与成交总额**,改为收市后披露成交总额及总笔数、ETF 成交总额、当日前十大成交活跃证券;盘中仅当额度余额 <30% 时公布余额(≥30% 显示"额度充足")。所以北向只做「收盘后口径 + 额度状态」,任何"实时北向净买额"都不做。 ### P1-13 港/美股自选(S) 支持港美股加自选;"当日涨跌"必须按**该市场的交易日**算(美东/香港日历),时间口径要从现在的单一 `Asia/Shanghai` 扩成「市场日历 + 按北京时间显示」。 ### P1-14 场外基金净值 + 估值双口径(M) 持仓市值**只用净值**并标 `T-1`;估值只作盘中参考、单独列且明确标"参考"。两个源都**待探活**。 ## P2(观察:可做,收益有限) - **P2-1 ETF 季报持仓穿透(L)**:只给前十大,必须标披露期与合计占比,不能呈现为"完整持仓"。 - **P2-2 面板不透明度与数字模糊(S)**:隐身模式的细化(供截图/录屏场景)。 - **P2-3 图表键盘操作与 sr-only 摘要(M)**:图表可键盘平移/缩放,并提供读屏摘要。 - **P2-4 除权日持仓影响提示(M)**:除权日提示持仓数量与成本变化。 - **P2-5 自绘行业热力(M)**:色深=涨幅、面积=成交额;与现有 iframe 并存,默认自绘(自绘才能标口径)。 ## 已完成(清单吸收的既有工作) ### P0(v0.25.0,11/11 落地) 对照上文的每条 P0 条目,逐条给出**实现位置**与验收依据;三处与原文不同的处理单独标出并说明理由。 - **P0-1 读工具「数据时刻」统一契约 ✅** — `shared/model.ts` 新增 `DataProvenance` / `MissingField`;`host/em.ts` 的 `quoteProvenance()` 把行情新鲜度升级为出处契约(含 `missing[].why` 的 `no-source` / `transient` 判定,依据是"该标的有无备用源映射");7 个读工具(portfolio / ledger / watchlist / quotes / calendar / rescue / search)全部返回 `provenance`,并在 render 尾部打印【数据出处】与缺失明细。`portfolio` 增 `fxMode` 与 `unpriced[]` + `unpricedMv`,`rescue` 增 `activeWindow` / `calibratedAt` / `factorContrib` / `scoreLog`。`stale`(降级复用)与 `cached`(休市定稿)两字段并存。 - **P0-2 卡片四态标记与来源徽章 ✅** — 新增 `client/quoteState.ts`(纯函数,可单测):绿=实时 / 黄=延迟(>刷新间隔 3 倍或 `lkg`)/ 灰=定稿复用(整批 `cached`)/ 红=缺失;`TopBar` 逐卡 6px 色点 + tooltip(源与观测时刻),列表头汇总 `12/23 实时 · 9 延迟 · 2 缺失`。汇总与逐卡**共用同一个判定函数**,因此必然加得上;定稿优先于实时(未开盘不显示绿色)。`/quotes` 的 `QuoteProvenance` 增 `cached` 字段。 - **P0-3 侧栏徽标 ✅** — 新增宿主路由 `GET /tradewatcher/badge`(只读同一份 rescue 快照与持仓汇总,**不触发新采样**);`client/index.tsx` 模块级 `badgeState` + `useSyncExternalStore` 订阅(图标与主面板之间没有共同 React 祖先,这是唯一不引新依赖的通道)。护盘 ≥ 疑似显示「护」(橙,优先于个人盈亏);否则显示当日盈亏率(带符号);隐身视图只显示沪深300 点位。 - **P0-4 非活跃时段显式态 ✅** — `rescue.ts` 新增纯函数 `samplingWindow()`(与 `inTradingWindow` 同源,含午休/周末/跨日推进);快照四个分支全部带上 `activeWindow`;`RescuePanel` 在暂停时段把分数位显示为 `—`、状态行写 `采样暂停 · 下次 10-09 09:25(已收盘)`,并**在暂停时段完全不出现「采样缺口」字样**(改为"当日曾采样失败/曾中断")。 - **P0-5 图表数据口径条 ✅** — `KlineData` 增 `asOf` / `barOpen`(`fetchKline` 填本地最近一次成功取数时刻与"当根是否未收盘");`QuoteDrawer` 顶部固定一行 `日K · klt 101 · 前复权 · 截至 10-09 14:31(当根未收盘,MA/MACD 已含未收盘当根)`。复权口径取自回包 `kline.fqt`(实际生效值),**不用界面请求值**;「截至」不用"现在"顶替。 - **P0-6 成本口径切换联动说明 ✅** — `PortfolioPage.switchBasis()` 切档时弹 6 秒 toast 报差值(`口径切换:摊薄成本 → 买入均价。持仓总盈亏 X → Y(差 Z;N 只持仓逐项差值之和等于该数)`);两口径本就共用同一套成本函数(`derivePosition`),selftest 断言逐项差值之和 == 总额差值。 - **P0-7 评分可解释性与阈值漂移监控 ✅** — `scoreRescue` 返回 `timeCoef` / `rawScore`,新增 `factorContributions()`(权 × 得分 × 时点系数);`RescueFactor` 快照增 `factorContrib`;因子表加「贡献」列 + 合计行;断言"贡献求和 == 总分 ±0.5"。新增 `DayLog.scoreLog`(含各因子原始值与当次生效阈值、引擎版本),`scoreLogOf()` 可回看。 - **P0-8 紧凑 / 隐身视图 ✅** — `PortPrefs.viewMode`(`full|compact|incognito`,宿主 `setPrefs` 校验、非法值抛错);`Alt+M` 轮换(输入控件内不抢键)、toast 说明当前档位;根节点 `data-view` 驱动 CSS(紧凑隐藏说明与脚注;隐身把涨跌色转灰阶);金额遮罩做在**格式化出口内部**(`fmtAmt`/`fmtMoneySigned`/`fmtRaw` + `setMoneyMask`),新增金额展示点不会漏。只影响显示:工具层不引该模块。 - **P0-9 多设备导出 / 导入 ✅** — 新增 `host/backup.ts`(纯函数:`makeBundle`/`verifyBundle`/`describeConflicts`)与 `GET|POST /tradewatcher/backup`;`DataStore.exportFiles()`(从**磁盘**读,反映用户真正拥有的那份)与 `importFiles()`(先写 `.bak`、再原子写入、最后重读内存,不需要重启)。导入为两段式:预览(校验 + 覆盖前后差异)→ 确认覆盖。拒绝条件逐条给出原因:非对象 / 缺 `schemaVersion` / 版本过高 / 缺文件 / 流水条目无法解析 / 引用不存在的 posId·groupId / **重放出现负持仓** / 校验和不匹配。界面在持仓页「备份」按钮。 - **P0-10 内嵌 iframe 三态降级 ✅** — `CloudMap.tsx` 重写:8 秒超时 → 超时态占位(域名 + 最后成功加载时间 + "本插件无法区分是本机网络还是站点问题")+ 一次「重试」(重建 iframe 重新计时);状态徽标常驻;最后成功时间记 localStorage。仍不抓取该站内容。 - **P0-11 写工具最小权限与可撤销 ✅** — 新增 `host/writeLog.ts`(`WriteJournal`:落盘 `write-log.json`,`by` / 反向操作 / 是否已撤销,保留 500 条);`context.ts` 的 `PluginToolExec` 增 `agent.id`(会话 id,取不到记 `unknown` 不编造);`calendar_add` 过同秒 3 次节流(超限直接报错,不静默丢弃)并登记反向操作;新增 `tradewatcher_undo`(按 `id` 或 `last=N`,`listOnly` 可列可撤销项;未知反向类型明确报错而非假装成功)。 **三处有意偏离原文:** 1. **P0-7 的出分日志粒度**:原文"每次出分写一条"。实际按 **5 分钟刻度 + 等级变化时**写。理由:采样 15–30 秒一次,逐次记录在 60 天滚动窗口下会产生数万条(日志本身变成负担与性能问题),而阈值漂移的回溯分辨率不需要秒级 —— 分差不会在 5 分钟内漂。同一 5 分钟刻度内只保留最后一条。 2. **P0-3 的「收盘后缀」**:原文"收盘后加后缀「收」"。实际该后缀放在**侧栏展开态的标题/tooltip 与 `aria-label`** 里,图标内只保留最多 4 个字形。理由:图标是 18px,塞进「护收」这类两字组合两边都看不清;完整语义仍然可见(悬停即得),信息没有丢。 3. **P0-1 的 `fxMode`**:原文只要求"增 `fxMode`"。实现时发现**总额此前把港/美股按 1:1 加进汇总**(`assemblePortfolio` 对所有有价行求和)。这次一并修正为 `fxMode='none'` 时**总额只含 A股**,非 A股逐项进 `unpriced[]` 并在界面给出 `不含 N 项 ▼` 下钻。理由:按 1:1 悄悄相加比"缺一块"更危险 —— 用户不会质疑一个看起来正常的数。这是**行为变更**(港股持仓者的总市值/盈亏数字会变小、且现在会说明原因),实时汇率源落地(P1-11)之前只能如此。 **新增/变更的测试**:`src/host/p0.test.ts`(16 条:采样窗口边界与跨周末、贡献度可复算、missing 的 no-source/transient 分类、零行 asOf 必须为 null、跨市场 unpriced 与总额排除、备份校验的 7 类拒绝路径);`src/client/quoteState.test.ts`(5 条:四态阈值 2.9×/3.1×、定稿优先于实时但缺失优先于定稿、汇总可加性、遮罩只改金额不动价格与成交量)。合计测试 70 条 × 2 个时区运行。 - **dsh 0.2.x 客户端适配(v0.24.0)**:`ctx.betterSidebar`(0.1.x 的侧边栏页签服务)在新版已被移除,浏览器半区改为宿主 Slots 契约——`ctx.slots.inject('sidebar.panellist', …)` 注册侧栏图标(id `tradewatcher`)、`ctx.slots.inject('main', …)` 注册同名主面板键,自绘 SVG 图标(不引 Harness 组件库,保持零依赖);面板只在被选中时挂载,未选中即卸载,轮询自然停止。宿主半区(`webServer`/`webRuntime`/`tools`/`systemPrompt`)在 0.2.x 形状未变,无需改动。 - **K 线复权(v0.23.0,默认前复权)**:三态可切、按标的记忆、`fqSupported` 判定适用性(指数/期货禁用并给理由)、口径进缓存键(缺该口径宁可失败不拿别的顶替)、腾讯兜底混口径修正、MA/MACD/B/S 与图同源、详情头始终真实成交价。详见 README「K 线复权」。 - 详情图入口与历史数据本地缓存(v0.22.0)、护盘面板状态表述(v0.19.1)、列表排序(v0.19.0)等见 README 对应章节。 ### P1(v0.26.0 + v0.27.0 + v0.28.0:11/14 已完成) **已完成(实现位置 + 验收)** - **P1-1 分时缩略图叠加信息 ✅** — `mini.ts` 的 `MiniData` 增 `avg`/`open`/`high`/`low`/`prePrice`;`MiniTrend` 用**同一套 y 映射**画均价虚线(不同映射的"均价线"不可比,比不画更糟),并支持断点分段(缺口不连线);开/高/低/振幅走 hover title,图上不加常驻文字。均价只在整段都有值时绘制(中间缺值会造出假跳变)。 - **P1-2 B/S 去噪与区间图例 ✅** — `kline.tsx`:同一 bar 的多笔合并为一个标记(`B3`/`S2`),悬停给出该日小结(笔数 · 合计股数 · 成交均价);图下新增区间图例 `区间内 B 3 笔 / S 1 笔 · 净买入 800 股 · 买入均价 X / 卖出均价 Y`,并有 `(区间外另有 N 笔)` 提示 —— 否则"看不到"会被读成"没有交易"。 - **P1-3 板块 alpha 口径显式化 ✅** — `WatchlistPage`:板块涨幅缺失显示 `—` 并给出原因(不在本次榜单 / 接口不可用),**禁用 0 代替**;行业接口整体失败时页面顶部如实报出错误与时刻(此前 `catch(() => undefined)` 静默吞掉,表现为"这些标的本来就没有行业")。 - **P1-5 可用数量与费用列 ✅** — `store.ts` 的 `TradeState` 增 `turnover`;`portfolio.ts` 新增 `isT0Secid()`(判定集中一处:沪深场内基金 `5xxxxx`/`15–19xxxx`、港股/美股 T+0,拿不准按 T+1 —— 保守方向是"绝不说今天买的能卖"),`PositionRow` 增 `availableQty`/`t0`/`fees`/`turnover`/`feeShare`;持仓卡片增加「可用(可卖)」与「累计费用(占成交额 %)」两格。 - **P1-6 不计入总额可下钻 ✅** — 随 P0-1 一并落地:总市值旁 `不含 N 项 ▼` 可点开,逐项列出被排除的持仓与原因(无价 / 非人民币未折算)。 - **P1-7 资金流排行口径与时刻 ✅** — `MarketPage`:口径行常显(数据时刻 / 来源 / 主力净额与占比的定义);15:05 后标「定稿」;金额与占比两种排序可切。**偏离**:占比只做"本页 40 条内重排",界面明确写出这一点(见下)。 - **P1-9 概率面板统计护栏 ✅** — 新增 `shared/stats.ts`(Wilson 区间,纯函数);`BottomCalibrationView.targets` 增 `probLo/probHi/baseLo/baseHi`,并给出 `sampleSmall`/`baseSampleSmall`;护盘面板概率条显示 `+2%:61.4%[51.9–70.3] vs 基线 50.8%`,`N < 30` 标「样本少」。 - **P1-4 自选异动提示 ✅(v0.27.0)** — 新增 `host/anomaly.ts`(纯函数 `judgeAnomaly` + 批量 `detectAnomalies`)与 `GET /tradewatcher/anomaly`:量能倍数用**同时点口径**(复用护盘标定的日内进度曲线,早盘拿全天均量比会得出"每天都缩量"的假结论),阈值 2.5x;价格异动 ±5%;量价共振(≤3% 且 ≥2x)单独判。**样本 < 5 个交易日不判定**,且"没判定"(`skip`)与"判定过且无异常"严格分家 —— 前者不是结论。客户端 `client/alertRules.ts`(纯函数,可单测)+ `client/alerts.ts`(hook):同一条目 30 分钟静默窗口记在 localStorage,**静默只压制提醒、不抹掉"它正在异动"这个事实**(队列里标"静默中")。行呼吸高亮用左侧竖条 + 缓慢呼吸背景(整行变红会与"跌了"的语义冲突)。 - **P1-8 涨跌家数历史分位 ✅(v0.27.0)** — 新增 `host/breadth.ts`(`breadth.json`,60 日滚动 + 30 天余量)与 `GET /tradewatcher/breadth`:数据源与大盘页**同一处**(上证/深证行情里的涨跌平家数与成交额),不另开链路。快照只在**收盘后**(15:05 起,15:30 后标记定稿不再改写)记录 —— 盘中家数一直在变,记进去等于把横截面混进每日定稿。分位口径写死并随结果返回:**指标 = 上涨家数占比**、**基准 = 之前 N 个交易日(不含今天)**、`N < 5` 不给分位(返回 null 而不是 0)。 - **P1-10 日历「可能变更」与勾稽 ✅(v0.27.0)** — `CalEvent` 增 `changes[]`(`from→to`,最多 5 条)与 `link{held,watched}`;`withChangeHistory()`(纯函数)在每次同步时按 `autoKey` 对比上一条,日期/结束日/标题变化即留痕,未变化不留痕。勾稽在**宿主一处**判定(持仓=实心 ● / 仅自选=空心 ○),路由与 agent 工具共用同一口径。日历页格点带勾稽字形(不用颜色:颜色已被重要度占用),当天事件卡增加改期角标(悬停给出历次 `from → to` 与时刻)与「按勾稽排序 / 按重要度排序」切换;agent 工具的事件行也会打印 `⚠ 可能变更:date A→B ●持仓`。 - **P1-11 持仓跨市场汇率折算 ✅(v0.28.0,部分:定额路径)** — `PortPrefs` 增 `fxMode`/`fxRates`;`assemblePortfolio` 接 `FxOptions`,非 A股**只有在能折算时**才计入总额,折算不了的一律进 `unpriced` 并区分「口径未开」与「开了但没这个币种的汇率」;`PortfolioView.fxRates` 回读实际生效的汇率,界面总额旁常显「汇率:不折算/固定」,`FxModal` 提供两档(不折算 / 固定汇率,填了哪个币种折哪个),并明确写出"实时汇率暂不可用及原因"。汇率归一化只接受 0.01–100(填错数量级必须拒绝,否则会得到一个完全错误的人民币市值)。宿主侧对 `fxMode='live'` **显式报错说明**,而不是接受一个什么都不做的档位。 **未完成部分**:`live`(实时汇率源)仍需先验证源的连通性与字段口径(中间价还是即期)。 **未完成(3 条)及原因** - **P1-11 的 `live` 档 / P1-14 场外基金净值与估值 / P2-1 ETF 季报持仓穿透** — 三者共同卡在**同一个前置条件:源未验证**。本仓库的硬规矩是"不许按文档猜字段",而这三项要的都是新的第三方接口(场外基金估值与净值、南向盘中披露与额度、第三方 HTML 表格)。在拿到真实返回结构之前实现解析器,得到的是一段"看起来能跑、一改版就坏、且会给出看起来正常的错数据"的代码 —— 那比不做更糟。解冻方式:先用一次真实请求把结构打出来(或在可达环境里跑通),再按实际字段实现。 - **P1-12 南向实时 + 北向收盘口径** — 同上(依赖新的披露源与额度接口)。 - **P1-13 港/美股自选** — 自选本身已可加港美股;缺的是"当日涨跌按该市场交易日算"的市场日历层,这会改动全项目的时间口径(现在是单一 `Asia/Shanghai`),风险高于收益,需要单独一轮。 - **P1-14 场外基金净值 + 估值双口径** — 两个源都待探活(`fundgz` / F10 `FundMNFInfo`)。 **P1-7 的一处偏离(记录在案)**:原文是"金额与占比两种排序可切"。实现为**本页内重排**而非全市场排行,因为上游板块接口按净额(`f62`)返回前 40 条,而"净占比"字段在免费源里没有可验证的来源 —— 本机无法连通上游做字段探活,按"不许按文档猜"的规矩,不臆造一个看起来像全市场排行的东西。界面已写明"占比排序只在**本页 40 条**内重排"。待上游可达时再评估是否补 `f184` 字段。 ### P2(v0.27.0 + v0.28.0:4/5 已完成;v0.30.0 新增 P2-6) - **P2-2 面板不透明度与数字模糊 ✅(v0.27.0)** — `PortPrefs` 增 `panelOpacity`(0.35–1,越界按边界收、类型不对才拒绝)与 `blurDigits`;行情条右侧「▣」打开设置:滑杆实时预览、一键恢复默认。不透明度只挂在本面板根节点(宿主其余界面不受影响);数字模糊的目标集合来自**证据清单**(styles.ts 里所有使用 `--tw-mono` 的数字面逐条抽出),hover 所在行/卡时显形 —— 既能录进画面,又能在需要时读某一行的数,并把"只覆盖本插件渲染的数字"写进界面说明。 - **P2-3 图表键盘操作与 sr-only 摘要 ✅(v0.28.0)** — K 线图外层可聚焦(`role="img"` + aria-label 摘要),←/→ 平移(Shift 加速 10 根)、`+`/`-` 缩放、Home/End 到两端;缩放滑块的三个手柄补齐 slider 语义(`role`/`tabIndex`/`aria-valuenow`/`aria-valuetext`,方向键微调、Shift 粗调、Home/End 到底)—— 拖拽能用不代表键盘能用,只用指针的图表对键盘用户等于不可操作。另加 `.tw-sr` 读屏摘要:区间、首末收盘、区间涨跌幅、最高最低、买卖点笔数与净买卖,**与图同源**(同一份 bars/markers/range),不会出现"摘要说涨、图上在跌";用 `clip` 而不是 `display:none`(后者读屏也读不到)。 - **P2-4 除权日持仓影响提示 ✅(v0.28.0)** — 复用**已同步**的日历事件(`div:` 前缀的分红除权),不新增数据源:持仓响应带 `corporateActions`(按 6 位代码与持仓勾稽),面板级提示列出涉及的持仓与日期,持仓卡片标「N 天后除权」。所有文案都写明**本插件不自动改账** —— 真实的送转/派息到账以券商为准,自动改会把用户唯一的交易记录改成一个"看起来对但没人能核对"的状态;正确动作是用户在「调整」里按实际到账录入。 - **P2-5 自绘行业热力 ✅(v0.28.0,**v0.62.0 已下线**:自绘视图与两档段控按用户要求删除,云图页只留第三方站点;本行保留为历史记录)** — 新增 `client/treemap.ts`(squarified treemap,**纯函数**:面积与数值成正比、不越界不重叠、长宽比不退化、退化输入不产生 NaN)+ `IndustryHeatmap.tsx`:面积=成交额、色深=涨跌幅(±5% 封顶),口径与数据时刻/来源写在图上方;云图页改为「自绘 / 第三方站点」两档并**默认自绘**(自绘才标得了口径,第三方那张图的面积/颜色口径由站点决定)。无成交额的板块不参与面积分配并明确报数,避免"图铺满了但其实缺了 N 个板块"。 - **P2-1 ETF 季报持仓穿透(L)**:需要解析第三方 HTML 表格(只给前十大,必须标披露期与合计占比,不能呈现为"完整持仓")—— 与 P1-14 同一个前置条件:源未验证。 - **P2-6 云图时间轴复盘(M,v0.30.0 新增)**:竞品云图支持 09:30–15:00 逐时点回放 + 方向键切换(见 [`COMPETITOR-NOTES.md`](archive/COMPETITOR-NOTES.md))。 **做什么**:自绘热力图加时间轴;复盘需要**每日快照**(建议 5 分钟粒度、收盘后落盘),因此与 P1-8 的 `breadth.json` 同层,可共用一套快照写入与滚动清理策略。 **口径**:面积/色深口径必须与实时图**完全一致**,回放时标出"这是 T 日 HH:mm 的快照";**样本不足(当日快照不全)时不给回放**,而不是拼一张缺块的图。 **验收**:任选一个交易日能量;快照缺口处明确显示"该时段无快照";快照与实时图的色阶映射同一实现。 **成本**:快照层(新) + 时间轴控件 + 键盘。 ### 竞品对照派生(v0.30.0) 对标记录见 [`COMPETITOR-NOTES.md`](archive/COMPETITOR-NOTES.md)(含 manifest 数据源清单与三张官方截图的观察)。 - **列头点击排序(v0.30.0 引入 → v0.36.0 删除)** — 曾经在宽屏(≥1080px)点列头排序,与面板头部的段控**功能重复**(用户在真机上同时看到两排),已按用户决定删除列头这一路:`SortHeader.tsx`、`pickSortEntry`/`useSortEntry`、`.tw-sorthead*` 规则与相关断言一并移除,**排序沿用面板头部的段控**(无条件常驻),任何宽度下只有一个入口。 - **年初至今(YTD)✅(v0.30.0)** — 前复权基准,三处边界(不适用复权 / 年内上市标 `listing` / 年内第一个交易日不给数)随结果说明。 - **云图时间轴复盘 → 见 P2-6**(待做)。 - **不借**:置顶/置底、跨页面注入、角标数字(依赖扩展形态);多券商账户分级(本仓库分组已覆盖组织语义,引入会计主体等于新增一套对账口径);**行内直改成本/股数**(本仓库数量与成本全部由流水推导,直接改会破坏可审计性 —— 改动请走「买/卖/调整」)。 ### 审计轮次记录(v0.30.0) 一次分工审计(功能/口径审计 + UI/UX 审计,两名审计员只读、独立交付)的产出与处置: | 报告 | 结论 | 处置 | | --- | --- | --- | | [`AUDIT-FUNCTION.md`](archive/AUDIT-FUNCTION.md) | P0×2 / P1×10 / P2×13 | P0 全修;P1 修 8 条(成本未录入、卖出可用数量、no-source 归因、replay 坏流水);P2 未修,逐条留在报告里 | | [`AUDIT-UI.md`](archive/AUDIT-UI.md) | 必修 7 / 应修 14 / 可选 5 | 必修 7 全修;应修修 8 条(多列填充方向、断点、可聚焦缺失原因、命名统一);其余待排 | | [`AUDIT-IMPL.md`](archive/AUDIT-IMPL.md) / [`AUDIT-IMPL2.md`](archive/AUDIT-IMPL2.md) | 两批实现自述 | 见 CHANGELOG v0.30.0 | | [`AUDIT-VERIFY.md`](archive/AUDIT-VERIFY.md) | 独立验收(反例断言 + 全量门禁) | 见该报告结论 | **仍未处理(按报告编号排,不在此重复论证)**:审计 P1-1(列表真列网格)、P1-11(硬编码色与 token 脱钩)、P1-12(原生 confirm/alert)、P1-14(弹窗 dialog 语义与焦点归还);审计 P2 的 13 条(`cached` 判据分叉、延迟阈值两个来源、`asOf` 取 max、truncated 假报、搜索失败与空结果不可区分、K 线 high/low 用 close 顶替、rescue 因子归因一刀切)。 **验收发现、未阻塞提交(记录在 [`AUDIT-VERIFY.md`](archive/AUDIT-VERIFY.md) §7)**:面板级「累计已实现」缺"不含 N 项"提示(N1)、自检不变量在 `realized=null` 时会被当 0 静默通过(N5)、`tools.ts` 类型断言未随 `number|null` 对齐(N2)—— 三条已在提交前收口;N4(fx 分支 `?? 0` 与 A 股 `add()` 写法不统一,数值等价)留待后续。 **一个待做项(断点按容器而非视口,v0.30.1 记录)**:多列网格仍按 `matchMedia` 的**视口**宽度判定,而面板可用宽度小于视口(左侧栏占宽)。视口 1080–1330 一段可能出现"多列已生效、面板只有一列"(可用但不最优)。彻底解决要么按容器宽度判定(`ResizeObserver` 量 `.tw-body`),要么把宽屏规则改用 `@container` 查询(项目里已有 `@container` 用法)。当前选择视口方案的理由:**简单可验证**,且失败形态是"少一列"而不是"两个控件并存"。 **一个产品待决策项(N3)**:补录成本不回溯历史已实现(D1b)的文案里写着"补录的成本流水时点若早于这笔卖出,重放即可算对" —— 这句**在流水文件/agent 路径上成立**,但界面「调整」表单没有日期字段,纯界面用户走不到。两个选项:① 给「调整」加可选日期字段(要在 UI 上说不清"回填历史"的审计含义);② 保留文案作为能力说明、不提供表单入口(当前选择)。记录在此,避免日后被当成 bug 反复评估。 **一条限制必须写在前面**:本机没有浏览器自动化也没有 react 运行时,**渲染层从未真正执行过**。类型检查、构建片段校验、纯函数断言与宿主自检都能跑,但"段控点击是否真的换了顺序""360/1100/1600px 下多列与断点是否符合预期""焦点环是否可见"这类结论**只能装机复核**。装机验收清单见本次交付说明。 ## 明确不做(附理由,避免反复评估) - **系统托盘 / 桌面通知徽标**:宿主不常驻托盘,硬做只会制造无效通知。 - **账号体系 + 自建云同步**:明文 JSON + 单机是现在的安全模型;引入账号等于引入凭据与自建服务,收益不抵风险(替代:P0-9 文件导入导出)。 - **券商开户导流**:与工具定位无关。 - **实时北向净买额**:监管层面已停止披露,做不出来。 - **自动交易 / 下单**:本插件只读行情与记账,不承担下单责任。 - **全市场逐笔与 Level-2**:免费源没有,付费源违背本仓库定位。 - **抓取 / OCR 第三方页面**:一改版就坏,且容易触发封禁。 - **涨跌预测式「AI 荐股」**:概率面板给的是"历史同类情形的频率",任何预测话术都是越界。 - **再引入图表库或状态管理库**:零新增依赖是硬规矩,现有自绘图表已够用。