迭代22+23: 策略tab静态化 + 计算字段(数字/判定型)+ 网格超市信号字段
- R-027 静态化:策略恒为内置两项(不可增删改名,保留显隐/排序);设置页移除「策略分组」; 字段配置入口迁到策略 tab 内「列设置」旁(FieldConfigDialog + schema-update 单策略写入) - R-026 计算字段:自写公式引擎(中文变量、四则/括号/round·abs·min·max、缺值短路→—)+ 变量目录(行情/合约/持仓/自定义字段)+ strategy-positions 逐行现算(只读内存缓存,不落库、列只读) - R-028 判定型:比较运算 + and/or + inferResultKind + 结果类型一致性校验 + 判定列 ✓/— 渲染; 网格超市落地 可下空单=涨停价>基准值+网格大小、可下多单=跌停价<基准值-网格大小 - 变量选择改标签平铺(老师反馈);公式手册 docs/99-其他材料/计算字段公式说明.md - 约束同步:产品约束-002/003/004/009/013/014、技术约束-014/015/022/023/024、UI约束-002/003/005/008 - 回归:新增 test-r026/test-r027,更新 r011/r013/r017,17 个脚本全绿;typecheck/build 通过
This commit is contained in:
@@ -10,13 +10,16 @@
|
||||
| 编号 | 约束说明 | 添加日期 | 生效状态 | 失效日期 | 最后一次变更描述 |
|
||||
|---|---|---|---|---|---|
|
||||
| UI约束-001 | 会话头部 QMT 连接切换 chip:紧凑形态 `QMT: <激活配置名> ▾`,与 PTC 模式标签并排;下拉菜单列出全部配置(当前激活项勾选标记),点选即激活;操作结果用顶部轻提示反馈(成功绿/失败红,约 3s 消失,沿用现有 Toast 体系) | 2026-08-29 | 生效 | - | R-004 定稿(2026-08-29):Q9/Q10 确认的头部控件形态 |
|
||||
| UI约束-002 | 设置页「QMT 连接配置」子 tab:与「Tab 设置 / 策略分组」并列的第三个子 tab;配置以**卡片形式**展示(自适应网格 minmax(260px,1fr),激活卡片绿色描边):卡片含名称 + 激活/默认徽标 + HTTP 地址 + 行内操作(激活 / 设为默认 / 编辑 / 测试连接 / 删除)+ 新增/编辑表单(纵排两字段);删除沿用现有确认弹窗模式,删除最后一条时给出禁止提示;超时不设输入框(Q5);长文本(地址/错误信息)单行省略 + 悬停看全文,组件设 minWidth:0 防撑行 | 2026-08-29 | 生效 | - | 变更(2026-08-29 老师反馈):列表形式改卡片形式,约束组件宽度、长文本省略不换行;原定稿:设置页子 tab 形态沿用现有设置交互体系;变更(2026-09-02 R-011 定稿):「通用设置」更名为「Tab 设置」 |
|
||||
| UI约束-003 | 设置页「Tab 设置」子 tab:表格列出**全部会话 tab(系统内置 + 策略分组)混排**,内置行带「内置」徽标、策略行带「策略」徽标;每行 = 拖动手柄(原生 HTML5 Drag & Drop)+ 名称 + 显示/隐藏开关;**任何行均无重命名/删除按钮**;拖动落点确认后**立即持久化**(每次 drop 提交整表,沿用「刷新页面后生效」提示机制);「策略分组」子 tab 只保留 新增/重命名/删除,移除排序箭头与显隐开关,并提示「顺序与显示请在「Tab 设置」中调整」 | 2026-09-02 | 生效 | - | R-011 定稿(2026-09-02 Q1/Q4/Q5 确认):拖动排序 + 显隐开关,禁重命名/删除 |
|
||||
| UI约束-002 | 设置页「QMT 连接配置」子 tab:与「Tab 设置」并列的子 tab(R-027 后设置页仅此两个子 tab);配置以**卡片形式**展示(自适应网格 minmax(260px,1fr),激活卡片绿色描边):卡片含名称 + 激活/默认徽标 + HTTP 地址 + 行内操作(激活 / 设为默认 / 编辑 / 测试连接 / 删除)+ 新增/编辑表单(纵排两字段);删除沿用现有确认弹窗模式,删除最后一条时给出禁止提示;超时不设输入框(Q5);长文本(地址/错误信息)单行省略 + 悬停看全文,组件设 minWidth:0 防撑行 | 2026-08-29 | 生效 | - | 变更(2026-08-29 老师反馈):列表形式改卡片形式,约束组件宽度、长文本省略不换行;原定稿:设置页子 tab 形态沿用现有设置交互体系;变更(2026-09-02 R-011 定稿):「通用设置」更名为「Tab 设置」;**变更(2026-09-10 R-027)**:移除「策略分组」子 tab,设置页子 tab 由三变二 |
|
||||
| UI约束-003 | 设置页「Tab 设置」子 tab:表格列出**全部会话 tab(系统内置 + 策略分组)混排**,内置行带「内置」徽标、策略行带「策略」徽标;每行 = 拖动手柄(原生 HTML5 Drag & Drop)+ 名称 + 显示/隐藏开关;**任何行均无重命名/删除按钮**;拖动落点确认后**立即持久化**(每次 drop 提交整表,沿用「刷新页面后生效」提示机制);**落点指示为行间间隙高亮线**(迭代 21,与列设置弹层一致,见 UI约束-007);「策略分组」子 tab 已移除(R-027 策略静态化),设置页不再有任何策略增删改名入口 | 2026-09-02 | 生效 | - | R-011 定稿(2026-09-02 Q1/Q4/Q5 确认):拖动排序 + 显隐开关,禁重命名/删除;变更(2026-09-10 R-025 老师指令):落点从整行高亮改行间间隙高亮线;**变更(2026-09-10 R-027)**:策略 tab 静态化、策略分组子 tab 移除 |
|
||||
|
||||
| UI约束-005 | 策略自定义字段配置 UI(R-013,2026-09-02):设置页「策略分组」子 tab 策略行**可展开** → 展开区 = 字段列表(label/key/type/enum/默认值)+ 添加/编辑/删除字段表单(名称/类型/枚举选项/默认值)+ 保存(走 strategies/update 整表);策略持仓 tab 持仓行展开区(R-010 基础上)增加「自定义字段」区块:按该策略 configSchema 渲染输入控件(文本=输入框、数字=数字输入、布尔=开关、枚举=下拉),值来自该行 values,编辑即保存调 holdings/values-update,Toast 反馈;未配置字段定义的策略持仓行不渲染字段区 | 2026-09-02 | 生效 | - | 新增(2026-09-02 R-013 定稿 D6 老师确认:值编辑入口 = 持仓行展开区) |
|
||||
| UI约束-005 | 策略自定义字段配置 UI(R-013 → **R-027 入口迁移 + 现状更正**):**入口** = 策略 tab 顶部「列设置」**旁**的「字段配置」按钮 → 独立弹层(约 520px 宽、70vh 高、内部滚动,锚定弹层视觉同列设置):字段列表(展示名 / key / 类型 / 公式或默认值 / 单位 / 操作)+ 添加/编辑/删除字段表单(名称/类型/枚举选项/默认值/单位)+ 底部 [关闭] + [保存字段](保存走 strategies/schema-update 单策略写入);有未保存改动时关闭需二次确认;**现状更正**:字段**值**不再是「持仓行展开区编辑」,而是**作为持仓表列展示 + 单元格点击内联编辑**(R-013 §4 演进:文本=输入框、数字=数字输入、布尔=开关、枚举=下拉,编辑即保存调 holdings/values-update + Toast);列显隐/顺序由「列设置」弹层管理(每策略独立);未配置字段定义的策略该列不存在 | 2026-09-02 | 生效 | - | 新增(2026-09-02 R-013 定稿 D6);**变更(2026-09-10 R-027/R-026 实施)**:入口由设置页「策略分组」迁至策略 tab 内「字段配置」弹层;同时更正文案到现状(值编辑 = 表格列内联编辑,展开区仅保留交易明细) |
|
||||
| UI约束-004 | 神之一手 UI 适配 DSH 主题:所有颜色引用宿主 `--dsw-*` token(带 fallback `var(--dsw-alias-xxx, #原色)`),随 DSH 浅色 / 深色 / 跟随系统自动切换,不自行维护主题偏好(跟随宿主);语义映射:表面底→bg-layer-1、次级面→bg-layer-2、hover→interactive-bg-hover、主/次/弱文字→label-primary/secondary/tertiary、边框→border-l1..l4、红(涨/删/错)→state-error-primary、绿(跌/成/激活)→state-success-primary、蓝(信息/业务)→state-business-primary、实心按钮字→button-contrast-fill、遮罩→bg-mask-1、阴影→shadow-lv3;淡底用 color-mix(in srgb, var(--语义色) 10-12%, transparent);禁止硬编码色值(#hex/rgb)进运行代码 | 2026-09-02 | 生效 | - | R-012 定稿(2026-09-02,暂定跟随系统)+ 迭代 10 实施:141 处硬编码色 token 化,宿主机制实证(body[data-ds-dark-theme] + alias token 双值定义) |
|
||||
|
||||
| UI约束-006 | 展开/收起箭头全站统一:使用共享组件 `ExpandChevron`(src/client/views/ExpandChevron.jsx)——14×14 chevron-right、描边圆角、主题 success 绿、展开时旋转 90°(0.15s 过渡);适用:策略持仓 tab / 交易记录 tab 表格行、设置页「策略分组」行首箭头(原 ▸/▾ 文字箭头废弃);不得再散落内联 SVG 或文字符号实现 | 2026-09-03 | 生效 | - | 新增(2026-09-03 老师反馈:设置页策略分组箭头与策略 tab 表格行展开图标一致;顺带消除两处内联 SVG 重复) |
|
||||
| UI约束-007 | 列表/弹层排序交互:优先**原生 HTML5 拖拽排序**(draggable 行 + ≡ 拖拽手柄 + drop 重排 + 落点立即持久化,行之间允许 ↑↓ 箭头兜底微调);**落点指示用间隙高亮线**(两行之间的主色线,指示精确插入位置/分清前与后,不做整行高亮)——策略 tab 列设置弹层(R-024)与设置页 Tab 设置(R-025)均已采用;不做表头直拖列排序(兼容成本高,需处理排序点击/固定列冲突) | 2026-09-10 | 生效 | - | 新增(2026-09-10 R-024 定稿:策略 tab 列设置弹层从 ↑↓ 升级为拖拽排序,复用 Tab 设置已验证模式,去掉「保存」按钮改立即持久化);变更(2026-09-10 老师反馈:整行高亮无法判断插入前/后,落点改行间间隙高亮线);变更(2026-09-10 R-025 老师指令:设置页「Tab 设置」落点统一为同款间隙线,整行高亮全站废止) |
|
||||
|
||||
| UI约束-008 | 计算字段 UI(R-026):字段类型下拉含「计算」——选中后**隐藏默认值**,显示公式输入框(等宽字体)+ 变量**标签平铺**(行情 / 合约 / 持仓 / 自定义字段 分组成行,每组 = 灰色组名 + 胶囊形 tag,点选即插入光标处;不自弹下拉)+「试算」按钮(用该策略一行真实持仓数据算出结果行内展示:`试算:平安银行 000001.SZ → 6.67 %`;无持仓提示「暂无可试算的持仓数据」)+ **结果类型下拉(数字 / 判定,R-028)**:选「判定」时**隐藏小数位**、公式框 placeholder 切换为判定示例(如 `涨停价 > 基准值 + 网格大小`),公式框下方常驻运算符提示(`运算:+ - × ÷ 括号;判定:> < >= <= == != ,组合:and / or`)+ 判定型试算显示 `→ ✓ 满足 / — 不满足`;数字型仍为小数位输入(0-4,默认 2);保存失败时公式框**描红**(state-error)+ 显示服务端提示,表单不关闭、改动保留;持仓表中计算列**只读**:表头列名前缀 `ƒ`(tertiary 色,title="计算字段(只读)")、列头与单元格 title 显示公式原文(`= <公式>`)、**判定型(R-028)**:满足 → `✓ <字段展示名>`(success 绿),不满足或数据缺失 → `—`(tertiary 灰,两者同形以免把「缺数据」误读为「不可以」);数字型按小数位 + 单位格式化(如 `6.67 %`、`700.00 元`)、缺数据/计算失败显示 `—`(tertiary 色)、点击不进内联编辑;历史持仓行显示 `—`;列设置弹层中该列名同样带 `ƒ` 前缀 | 2026-09-10 | 生效 | - | 新增(2026-09-10 R-026 定稿 + D-1~D-7 老师拍板:中文变量名 + 选择器插入、表头 ƒ 前缀 + 悬停看公式、本期做试算、已删变量 ⚠ 标记、小数位 0-4 默认 2、不做实时高亮);**变更(2026-09-10 R-028)**:新增「结果类型(数字/判定)」下拉与判定列渲染(`✓ 字段名` / `—`)+ 判定试算;**变更(2026-09-10 老师反馈)**:变量选择从「+ 插入变量 ▾」下拉改为**标签平铺**(分组成行、点选插入),弃用下拉交互;判定型隐藏小数位 |
|
||||
|
||||
<!-- 示例条目(确认格式后删除):
|
||||
| UI约束-001 | 示例:求签页面必须保持单屏完整,不出现滚动 | 2026-08-26 | 生效 | - | 讨论确认:移动端优先,避免滚动打断仪式感 |
|
||||
|
||||
@@ -11,17 +11,20 @@
|
||||
|---|---|---|---|---|---|
|
||||
| 产品约束-001 | 分仓管理以「全量持仓」为数据基础:系统必须完整展示账户全部持仓信息(代码/名称/数量/可用/均价/现价/市值/盈亏),不遗漏、不裁剪 | 2026-08-27 | 生效 | - | 讨论确认(R-002):老师要求「首先肯定支持全部持仓信息」 |
|
||||
| 产品约束-002 | 分仓通过「持仓标签」体系实现:在全量持仓之上,用不同标签对持仓进行逻辑分组,每个标签展示其下的仓位汇总(数量/市值/盈亏/占比) | 2026-08-27 | 生效 | - | 讨论确认(R-002):老师要求「分不同的持仓标签,各自有多少仓位」 |
|
||||
| 产品约束-004 | 删除持仓策略时,该策略下已分配的份额自动回到「未分配」,数据不丢失(删除前需弹窗确认) | 2026-08-28 | 生效 | - | R-003 O3 定稿(2026-08-28):删除策略 = 份额回未分配 + 弹窗确认 |
|
||||
| 产品约束-003 | 持仓标签可自定义、可增删改;标签维度候选包括策略/用途/风险等级等(具体维度待讨论确认) | 2026-08-27 | 生效 | - | 讨论确认(R-002):标签体系需支持自定义,维度待细化 |
|
||||
| 产品约束-004 | 删除持仓策略时,该策略下已分配的份额自动回到「未分配」,数据不丢失(删除前需弹窗确认) | 2026-08-28 | **失效** | 2026-09-10 | **失效(2026-09-10 R-027 策略静态化)**:策略不可删除,本条款无适用场景,随之退役(原定稿:删除策略 = 份额回未分配 + 弹窗确认) |
|
||||
| 产品约束-003 | 持仓标签(策略)**集合与名称固定**为插件内置两项(网格超市 / 手动做T):不可新增、不可删除、不可重命名(R-027 策略静态化);「可自定义」收窄为**标签下的字段定义可自定义**(见 产品约束-010 / 产品约束-014);策略 tab 仍支持显隐与拖拽排序(见 产品约束-009) | 2026-08-27 | 生效 | - | **变更(2026-09-10 R-027)**:原「标签可自定义、可增删改」(维度候选策略/用途/风险等级)收窄为「固定两标签 + 字段自定义」;老师重构指令「策略 tab 做成静态的,和全部持仓、交易记录一样是插件内置 tab」 |
|
||||
| 产品约束-005 | QMT 连接配置多配置管理:设置页「QMT 连接配置」子 tab 维护多个连接配置(名称 + HTTP 地址,增/删/改/查);一次只能激活一个,激活配置为当前生效连接;默认 = 插件启动时自动激活的配置(运行中手动激活其他配置立即生效,重启回到默认);测试连接为手动操作(请求 /health 返回可达性与延迟,激活不强制先测);删除边界:删除激活配置自动切换到默认,删除默认配置则默认标记转移到列表第一条并激活之,禁止删除最后一条 | 2026-08-29 | 生效 | - | R-004 定稿(2026-08-29):Q1-Q6/Q8 逐条确认(不迁移宿主配置、超时不纳入表单) |
|
||||
| 产品约束-007 | 3 个监控表格(全部持仓 / 手动做T / 网格超市)展示**盘中现价**:现价列显示 lastPrice,红涨绿跌着色(对比昨收),价格变化轻微高亮;本期仅现价一项实时数据 | 2026-08-31 | 生效 | - | R-005 定稿(2026-08-31):老师确认现价列 + 涨跌色 + 变化高亮 |
|
||||
| 产品约束-006 | QMT 连接会话头部快捷切换:会话窗口顶栏(PTC 模式标签旁)常驻下拉控件(chip 显示 `QMT: <激活配置名>`),点开列出全部配置(激活项勾选),点选即激活并轻提示反馈;头部控件仅做切换,配置管理(增删改/测试连接/默认标记)仍在设置页子 tab | 2026-08-29 | 生效 | - | R-004 定稿(2026-08-29):Q9/Q10 确认(入口=会话头部 PTC 旁,菜单仅切换激活) |
|
||||
|
||||
| 产品约束-008 | 交易记录支持**按策略过滤**:交易记录 tab 提供策略过滤下拉(全部 / 各策略 / 未关联),对今日(QMT 实时)与历史(本地 SQLite)均生效;策略归属 = 委托时间 join 持仓生命周期窗口推导(一码多策略取份额最大,未命中=未关联);历史范围展示本地积累数据(不再「接口开发中」占位) | 2026-09-01 | 生效 | - | 新增(2026-09-01 R-009 定稿 + 迭代 07 实施):策略过滤 + 历史本地展示 |
|
||||
| 产品约束-010 | 策略自定义字段配置:每个策略可在设置页「策略分组」子 tab 配置自定义字段定义(字段名 / 类型文本·数字·布尔·枚举 / 枚举选项 / 默认值),定义随策略存 settings(不落库);该策略下每个持仓(strategy_holdings 行)按所属策略的定义存取一份字段值(values JSON 列,持仓级键值对,key 对齐定义、允许扩展额外键);旧策略无定义时行为与现状一致(不渲染字段区、不迁移历史值) | 2026-09-02 | 生效 | - | 新增(2026-09-02 R-013 定稿 Q1-Q4 + D6):定义随策略走、值随持仓行,类型化(含枚举),旧策略兼容 |
|
||||
| 产品约束-009 | 会话 tab 统一由「Tab 设置」管理:设置页「Tab 设置」子 tab 是**所有会话 tab(系统内置 + 策略分组)的唯一顺序与显隐入口**,两类 tab 混排;每行 = 拖动排序 + 显示/隐藏开关;**任何 tab 均不支持重命名与删除**(内置 tab 名称只读,策略命名/删除仍在「策略分组」子 tab);策略改名后 Tab 设置中的名称自动跟随(只存引用);新增策略默认追加到列表末尾;删除策略联动删除 Tab 设置中对应条目;顺序与显隐唯一数据源 = settings.tabs 有序数组 | 2026-09-02 | 生效 | - | R-011 定稿(2026-09-02 Q1-Q5 确认):Tab 设置 = 显示/隐藏 + 拖动排序(落点立即持久化),全 tab 禁重命名/删除 |
|
||||
| 产品约束-010 | 策略自定义字段配置:每个策略在**策略 tab 顶部「列设置」旁的「字段配置」按钮(独立弹层)**中配置本策略的字段定义(字段名 / 类型文本·数字·布尔·枚举·**计算** / 枚举选项 / 默认值 / 单位 / 计算字段的公式与小数位),定义存 settings(不落库);该策略下每个持仓(strategy_holdings 行)按所属策略的定义存取一份字段值(values JSON 列,持仓级键值对,key 对齐定义、允许扩展额外键);旧策略无定义时行为与现状一致(不渲染字段区、不迁移历史值) | 2026-09-02 | 生效 | - | **变更(2026-09-10 R-027/R-026)**:入口由设置页「策略分组」迁到策略 tab 内「字段配置」弹层;类型新增「计算」(见 产品约束-014);定义真相源迁 settings.strategyFields(原定稿:设置页策略分组配置、四类型、定义随策略存 settings) |
|
||||
| 产品约束-009 | 会话 tab 统一由「Tab 设置」管理:设置页「Tab 设置」子 tab 是**所有会话 tab(系统内置 + 策略)的唯一顺序与显隐入口**,两类 tab 同权混排;每行 = 拖动排序(间隙高亮线)+ 显示/隐藏开关;**任何 tab 均不支持重命名与删除**;R-027 静态化后策略 tab 恒存在(不随任何 CRUD 增删),故「策略改名跟随 / 新增追加末尾 / 删除联动」等条款失效;顺序与显隐唯一数据源 = settings.tabs(由内置常量序列 + 存量偏好归一化得出) | 2026-09-02 | 生效 | - | **变更(2026-09-10 R-027 策略静态化)**:策略 tab 与内置 tab 完全同权且恒存在;原「策略命名/删除在策略分组子 tab」及联动增删条款失效(设置页「策略分组」子 tab 已移除) |
|
||||
| 产品约束-011 | 持仓页数据(策略持仓 / 全部持仓 / 未分配)以服务端 10s 内存快照为准,**接受最多 10s 滞后**(同步时间前端不显示,老师拍板);QMT 抖动/掉线时持仓页面显示**最后一次快照**而非空白;**幽灵自动清仓退役(变更 1,2026-09-08 R-018 数据域分界)**:同步机制不再把本地策略持仓自动转历史——策略持仓份额只由「交易关联(归属=账本写操作)」与「手动份额操作」驱动,账本转历史唯一途径 = 卖出单关联份额减至 0;对账单码消失仅表现为快照无此行 + **漏关联软提示**(只读提示去关联/移出,不自动写账本);部分减持仅表现为「未分配为负」,不做自动修正 | 2026-09-02 | 生效 | - | 新增(2026-09-02 R-014 定稿);**变更 1(2026-09-08 R-018/迭代 16 拍板)**:幽灵自动清仓退役(同步机制不写账本),策略持仓由交易关联 + 手动份额操作驱动;漏关联软提示替代自动归档 |
|
||||
| 产品约束-012 | 行情数据服务形态(R-015):现价/昨收/涨停/跌停统一由盘口内存快照提供(≤5s 更新),价格单一入口;QMT 抖动时页面价格保持旧值不空白;会话头部(QMT 健康灯旁)提供**持仓/盘口同步指示灯**——绿=同步正常(持仓 30s/盘口 15s 内)、黄=同步失败中快照陈旧、灰=从未同步;悬停显示同步时间/快照量/失败数/错误摘要;点击灯 = 立即触发该域同步;不做个股停牌标识(另议) | 2026-09-02 | 生效 | - | 新增(2026-09-02 R-015 定稿):老师提出指示灯,采纳 AI 推荐三态/点击即同步/取消 PriceCell 灰点 |
|
||||
| 产品约束-013 | 策略 tab 静态化(R-027):会话 tab 中的策略 tab 恒为**插件内置两项**(网格超市 / 手动做T),与「全部持仓 / 交易记录」同类的内置 tab —— **不可新增、不可删除、不可重命名**;仍支持 Tab 设置中的显隐与拖拽排序;设置页不再有「策略分组」子 tab(设置页 = Tab 设置 / QMT 连接配置);策略字段配置入口 = 策略 tab 顶部「列设置」**旁**的「字段配置」按钮(独立弹层),每个策略 tab 各管本策略字段 | 2026-09-10 | 生效 | - | 新增(2026-09-10 R-027 定稿 Q1-Q4/Q6 老师拍板):静态化程度=固定不可增删改名但保留显隐/排序;入口形态=列设置旁独立按钮;范围=每策略管自己的字段 |
|
||||
| 产品约束-014 | 策略计算字段(R-026):字段类型新增「计算(formula)」——用户在字段配置中写**公式**(**中文变量名**,如 `(现价 - 成本价) × 份额`),公式引用**服务端缓存数据集**(行情盘口 / 合约信息(涨停跌停)/ 持仓账本(份额·成本价·最后成交价)/ 本行手填字段值),由服务端按**持仓行实时计算**得出列值;计算字段**不落库**(不写 values 列)、在持仓表中**只读**(不可内联编辑);变量**只能引用非计算字段**(零循环依赖);缺数据 / 除零 / 无法计算 → 该格显示 `—`(不显示 NaN、不报错、不炸表);历史持仓行显示 `—`(不做历史时点计算);**结果类型两种(R-028 扩展)**:`resultKind = number | boolean` —— **数字**(默认,按小数位+单位格式化)与**判定**(比较运算结果:满足显示 `✓ <字段名>`(success 绿)、不满足或数据缺失显示 `—`),结果类型在字段表单**显式声明**且必须与公式形态一致(服务端校验);公式能力边界 = 四则运算 + 括号 + 比较(`> < >= <= == !=`)+ 逻辑组合(`and / or`,含 `&& ||` 与全角 `≥ ≤ ≠`)+ round/abs/min/max,禁止任意代码执行 | 2026-09-10 | 生效 | - | 新增(2026-09-10 R-026 定稿 Q1-Q3 + Q4-Q10 + D-1~D-7 老师拍板):数据集首批三组、中文变量名、仅行级、服务端现算、只读列、缺值 —;**变更(2026-09-10 R-028)**:原「首批结果只做数字」扩展为「数字 / 判定」双类型 + 逻辑组合,首个落地用例 = 网格超市「可下空单 / 可下多单」 |
|
||||
|
||||
<!-- 示例条目(确认格式后删除):
|
||||
| 产品约束-001 | 示例:求签功能必须保证抽取结果的不可预测性 | 2026-08-26 | 生效 | - | 讨论确认:为保证公平性,抽取必须不可预测 |
|
||||
-->
|
||||
@@ -23,13 +23,19 @@
|
||||
| 技术约束-009 | 会话头部快捷切换控件挂载 DSH 开放 slot `conversation.session.header.actions`(多实例挂载点,按 order 排序多插件共存):客户端插件以独立 id 并排注册(DSH 内置 PTC 标签 order=-10,本控件 order=-9),不改动 DSH 宿主;控件经 ConnectionProvider 包装复用现有 RPC 通道与 /odl/api/* 端点 | 2026-08-29 | 生效 | - | R-004 定稿(2026-08-29):Q9 确认;宿主代码审查核实 slot 机制与内置插件注册方式 |
|
||||
|
||||
| 技术约束-013 | 交易记录本地存储(R-009):QMT 当日交易数据(委托/成交)由服务端 TradeSync 定时同步落 SQLite(启动预热 + 60s 定时 + UPSERT 幂等,只同步当日);trade_orders(委托主行,order_id 主键 + insert_ts 派生时间列 + **strategy_id/holding_id 手动归属列**)+ trade_fills(成交明细,trade_id 主键、order_id 外键)两表;**委托归属由用户在交易记录 tab 手动设置**(候选 = 该 code 当前持仓策略 + 未关联,全手动选、可随时改、以最终为准);**UPSERT 不覆盖归属列**(手动指定为插件逻辑);本地历史查询走 trades/history 端点(策略过滤 = 用户设置的归属);今日实时仍走 QMT Bridge;QMT 委托/成交 code 无后缀、持仓带后缀 —— 数据源映射层统一 normalizeInstrumentCode 归一化;委托交易日 = insertDate(tradeDate 兜底) | 2026-09-01 | 生效 | - | 新增(2026-09-01 R-009 定稿 + 迭代 07 实施):两表 + 定时同步 + 本地历史查询;变更 1(2026-09-01):+code 归一化 + tradeDate 兜底;变更 2(2026-09-01 老师二次定稿):归属改**手动设置**(trade_orders 冗余 strategy_id+holding_id,UPSERT 不覆盖归属列),弃算法推导 |
|
||||
| 技术约束-015 | 策略自定义字段存储(R-013,2026-09-02):字段定义随策略定义存 settings.strategies 扩展 configSchema([{key,label,type,enum?,def}],type ∈ text|number|boolean|enum,旧项缺省空数组);字段值落 strategy_holdings 新增 values TEXT(JSON 键值对,key 对齐 configSchema.key,允许额外键=可扩展,NULL=未配置);补列用幂等 ALTER(沿用 _ensureTradeAttributionColumns 模式,只读连接容忍);API:strategy-positions 每行附 values,新增 holdings/values-update {holdingId, values} 写回,服务端按 configSchema 校验(number=有限数、enum=在选项内、boolean=布尔),空值/缺省可写入;持仓生命周期操作(openHolding/addShares/reduceShares/closeHolding)不碰 values 列 | 2026-09-02 | 生效 | - | 新增(2026-09-02 R-013 定稿 + PLAN-012):定义 settings + 值 SQLite 列 + 幂等补列 + 类型校验 |
|
||||
| 技术约束-014 | 会话 tab 注册与顺序显隐(R-011):客户端注册统一读 **settings.tabs 有序数组**(唯一顺序与显隐来源,内置条目 refKey + 策略条目 refId),按 order 排序、过滤 visible 后注册(builtin 走内置 render、strategy 走 StrategyTab),移除硬编码 order 间隔(原内置 10/11/12、策略 13+);settings.tabs 从布尔对象升级为有序数组,读取时对旧格式(布尔对象 + 策略自带 order/visible)静默归一化迁移(旧隐藏策略迁移后显示),写入即落库;策略定义表收窄为 {id,name}(去除 visible/order);tabs/update 语义改为整表更新(顺序 + 显隐),strategies/add 联动追加 tab 条目(末尾),strategies/remove 联动删除对应 tab 条目,废弃 strategies/move | 2026-09-02 | 生效 | - | R-011 定稿(2026-09-02 Q1-Q5 确认):统一 tabs 有序数组 + 自动迁移 + 联动增删 |
|
||||
| 技术约束-015 | 策略自定义字段存储(R-013 → **R-027 真相源迁移**):字段定义存 **settings.strategyFields**(`{ [strategyId]: [{ key, label, type, enum?, def?, unit?, formula?, decimals? }] }`,type ∈ text|number|boolean|enum|**formula**);`settings.strategies` 收窄为**内置身份表**(仅兼容读取旧 `configSchema`:strategyFields 缺失时按内置 id 回填视图,首次保存即落新键,无迁移脚本、不动库数据);写路径唯一 = `strategies/schema-update { strategyId, configSchema }`(单策略整份覆盖,未知 strategyId 抛 strategy-not-found,校验见 技术约束-024);`normalizeFields` 归一化(类型白名单回退 text、decimals 夹取 0-4、formula 字段不带 def);字段值落 strategy_holdings 新增 values TEXT(JSON 键值对,key 对齐 configSchema.key,允许额外键=可扩展,NULL=未配置);补列用幂等 ALTER(沿用 _ensureTradeAttributionColumns 模式,只读连接容忍);API:strategy-positions 每行附 values,新增 holdings/values-update {holdingId, values} 写回,服务端按 configSchema 校验(number=有限数、enum=在选项内、boolean=布尔),空值/缺省可写入;持仓生命周期操作(openHolding/addShares/reduceShares/closeHolding)不碰 values 列 | 2026-09-02 | 生效 | - | 新增(2026-09-02 R-013 定稿 + PLAN-012):定义 settings + 值 SQLite 列 + 幂等补列 + 类型校验;**变更(2026-09-10 R-027/R-026)**:定义真相源迁 strategyFields(strategies 只读兼容)、类型白名单加计算类型、写路径改单策略端点 |
|
||||
| 技术约束-014 | 会话 tab 注册与顺序显隐(R-011 → **R-027 静态化**):客户端注册统一读 **settings.tabs**(唯一顺序与显隐来源,内置条目 refKey + 策略条目 refId),按 order 排序、过滤 visible 后注册(builtin 走内置 render、strategy 走 StrategyTab);**R-027 后**:tab 序列由**内置常量派生**(3 内置 tab + 2 内置策略 tab),`normalizeTabs` 只保留常量表内条目的 visible/order 偏好、**丢弃**未知条目(自建策略 tab / 已退役 tab)、补齐缺失条目;`updateTabs` 只接受内置 id(未知条目过滤),不再有联动增删(`appendStrategyTab/removeStrategyTab` 退役);旧布尔对象格式(迭代 02)继续兼容 | 2026-09-02 | 生效 | - | R-011 定稿(2026-09-02 Q1-Q5 确认):统一 tabs 有序数组 + 自动迁移 + 联动增删;**变更(2026-09-10 R-027)**:策略静态化,tabs 由常量派生、联动增删与 strategies CRUD 一并退役 |
|
||||
| 技术约束-016 | src 目录按功能域归类(2026-09-02 结构优化):服务端代码**禁止平铺**,按职责域分目录 —— src/data-source/(QmtBridgeRestDataSource + data-source-types + QmtHealthMonitor,数据源与连接健康)、src/storage/(SqliteStore + DataStore,存储层)、src/position/(PositionManager,分仓逻辑)、src/market/(MarketDataHub + MarketFeed,行情)、src/trades/(TradeSync,交易同步);api/ 按领域拆分子文件(positions/strategies/qmt-connections/market/trades),client/ 仅放 UI(views/ 组件 + market/ provider);文件命名 = 类名(PascalCase)+ .js/.jsx;新增服务端模块必须先落对应域目录,无合适域时先讨论补域,不得回退平铺 | 2026-09-02 | 生效 | - | 新增(2026-09-02 结构审查 + 优化落地):component/ 平铺还原为语义分域,删除死代码 AllocationStorage、DataStore.setDataset/removeDataset |
|
||||
| 技术约束-017 | 持仓内存快照(R-014,2026-09-02;**变更 1:2026-09-08 R-018 数据域分界**):全量实盘持仓由服务端 PositionSync **进程内内存快照**管理(启动预热 + 10s 定时全量拉 /trade/positions → 校验 → 整体替换),**不落库**(账本与对账单分离,holding_id 交易锚点不掺易变快照);PositionManager.getAllPositions 以快照为准(strategy-positions / unallocated / summary 三接口不再请求时穿透 QMT),快照为空读穿透兜底;同步失败保留上次快照;空快照双重确认(/health 可用 + getAsset 账户身份可识别)才接受为真清仓;syncNow 允许手动调用。**幽灵自动清仓**(R-014 原条款:快照连续 3 轮消失 → 本地全部策略当前持仓 closeHolding 转历史)**退役**——同步机制只作用于对账单域,不再写 strategy_holdings 账本(R-018 老师拍板:幽灵清仓本就是一个同步机制,不可以让幽灵把爪子伸太长);账本行转历史唯一途径 = 卖出单关联减至 0 closeHolding(R-018);对账单域「码消失」仅表现为快照无此行 + 漏关联软提示(positions/orphan-hints 只读提示) | 2026-09-02 | 生效 | - | 新增(2026-09-02 R-014 定稿 + 迭代 12 实施);**变更 1(2026-09-08 R-018/迭代 16 拍板)**:幽灵自动清仓退役(不再 closeHolding 本地账本),PositionSync 只同步对账单快照;漏关联由只读软提示承担 |
|
||||
| 技术约束-018 | 盘口内存快照(R-015,2026-09-02):行情数据由 QuoteSync(取数:启动 prime + 5s REST 定时刷 watch 集合 + 涨停跌停经 /data/instrument 按**交易日**内存缓存)与 QuoteHub(存查:内存快照 Map + watchCodes Set + 读穿透走 dataSource.getTicks + getQuote/getByCodes 对外)管理,**替换并删除 MarketFeed/MarketDataHub**(方案 A,不留兼容壳);**WS 数据通路移除**(ingest 入口带 source 标签留回归口子);**market_quotes_cache 表退役 DROP**(幂等),价格单一入口 = QuoteHub,DataStore 行情方法(loadMarket/getMarketQuote(s)/setMarketQuotes)删除;同步失败保留内存旧值;watchCodes 维持只进不出无上限;涨停/跌停/昨收不落库 | 2026-09-02 | 生效 | - | 新增(2026-09-02 R-015 定稿 + 迭代 13 实施):老师五拍板(替换/WS 移除/纯内存 DROP/watch 现状/指示灯);warmup bug 复现(loadMarket return this 残迹)为不落库关键证据 |
|
||||
|
||||
| 技术约束-019 | 历史持仓查询(R-016,2026-09-07):策略 tab 历史持仓展示走**本地库只读查询**(SqliteStore.getHoldingsHistory:strategy_id + closed_at IS NOT NULL + closed_at ≥ sinceMs,closed_at DESC),经独立端点 strategy-holdings/history 暴露;**当前持仓路径(strategy-positions / PositionSync 快照语义)不掺历史数据**(两份结果前端合并渲染);closeHolding 置 shares=0 语义维持不变(Q2 老师拍板:历史行份额显示 0,重点在追溯该持仓的历史操作而非清仓时份额);范围换算服务端做(week=7d/month=30d/quarter=90d/halfYear=182d/year=365d 自然日近似)——**变更 1(2026-09-07 二轮补充 Q8-Q9 老师拍板)**:range 新增 'today' = **本地自然日 00:00 起**(特判零点,不落回溯毫秒档),供前端「今日已清仓默认层」(恒显示、不受历史持仓开关控制);历史范围层语义收窄为**今天之前**;其余不变 | 2026-09-07 | 生效 | - | 变更 1(2026-09-07 R-016 二轮补充 Q8-Q9 老师拍板):+range=today 自然日边界,历史开关只控今天之前;首轮新增(Q1-Q7):历史行=追溯操作锚点,不动存储写路径 |
|
||||
| 技术约束-020 | QMT 连接健康自适应探测(R-022,2026-09-09):QmtHealthMonitor 探测节奏由固定 5 分钟改为**结果驱动自适应**——健康 → intervalMs(默认 5 分钟,稳态低开销);异常/未知(含从未探测成功或 baseUrl 解析失败)→ retryMs(默认 10 秒)快速重试,与前端 sync-status 10s 轮询对齐,QMT Bridge 启动/恢复后指示灯 ≤20s 自动回绿(原固定 5 分钟导致恢复感知滞后,老师反馈「自动检查没生效」);间隔经构造参数 { intervalMs, retryMs } 注入(测试用短间隔);setTimeout 自适应链 + _inFlight 防并发,手动探测/配置热切换共用 probe() 并重排下轮节奏;stop() 即停;resolveActiveBaseUrl 失败也落 healthy:false 缓存走快速重试;前端读缓存秒回与 sync-status/qmt-health 端点语义不变 | 2026-09-09 | 生效 | - | 新增(2026-09-09 R-022 讨论 + 迭代 19 实施):健康灯恢复感知修复;稳态间隔维持原 5 分钟设计不变 |
|
||||
| 技术约束-021 | MCP 状态自适应探测(R-023,2026-09-09):QmtMcpManager 状态缓存增加**结果驱动自适应探测**(原无定时回路,只在挂载/切换/手动探测时刷新)——connected → intervalMs(默认 5 分钟,稳态低开销);非 connected → retryMs(默认 10 秒)快速重试,MCP 服务(激活连接 baseUrl + /mcp)恢复后状态灯 ≤20s 自动回绿;dsh-mcp-client 自带 reconnect 只恢复真实连接、不刷新状态缓存,本类缓存必须自带刷新回路;无激活连接(url 为空)不探测不调度,dispose()/unmount() 停止调度;探测走 SDK 独立只读握手(Client connect + initialize + tools/list),与 dsh-mcp-client 自管连接互不干扰;间隔经构造参数 { intervalMs, retryMs } 注入(测试用短间隔);_probing 防并发与手动/热切换探测共用 probe() 重排节奏;前端 getStatus 读缓存与 mcp-status/sync-status 端点语义不变 | 2026-09-09 | 生效 | - | 新增(2026-09-09 R-023 讨论 + 迭代 19 实施):MCP 状态灯恢复感知修复(与 R-022 同模式);挂载/重连逻辑零改动 |
|
||||
| 技术约束-022 | 公式引擎与变量目录(R-026,2026-09-10):`src/formula/evaluator.js` **自写小型表达式引擎**(零依赖,**禁止 `eval/Function`**):词法支持 Unicode 标识符(**中文变量名**)与全角归一(()+-×÷),语法 = 四则 + 括号 + 一元负 + 函数 round/abs/min/max(递归下降 + 深度≤32 + 长度≤500 上限);**R-028 扩展**:比较 `> < >= <= == !=` 与逻辑 `and / or`(含 `&& ||`、全角 `≥ ≤ ≠ > < =`),优先级 `or < and < 比较 < 加减 < 乘除`,判定结果缺值短路;新增 `inferResultKind(ast)` 静态推断结果类型(顶层比较/逻辑 → boolean,`neg` 下钻);`validateFormula(expr, allowedVars)` 返回 { ok, vars } 或 { ok:false, error:{ code: empty|syntax|unknown-var|unknown-func|arity, message, position } };求值语义 = **缺值短路**(任一变量 null/undefined/NaN → 整式 null)、除零/非有限 → null、**最终结果浮点归一**(|v|<1e12 时四舍五入 10 位,消除 0.7000000000000002 类噪声,中间步骤不归一再保精度);`src/formula/variables.js` 为**变量目录单一入口**(行情 7 项 / 合约 2 项 / 持仓账本 3 项 + 运行时按策略非 formula 字段展示名展开「自定义字段」组),`allowedVarNames` 排除 formula 字段(零循环依赖) | 2026-09-10 | 生效 | - | 新增(2026-09-10 R-026 定稿 + 技术方案 §2.2/§2.3):中文标识符需自有词法(第三方库均不支持中文变量),该层即 T-006 数据池的表达式层;**变更(2026-09-10 R-028)**:新增比较与逻辑运算 + `inferResultKind`(判定型计算字段);**复核(2026-09-10 老师提问「有没有现成的库」)**:对比 expr-eval(CVE-2025-12735 变量对象注入 → RCE)、mathjs(标识符不含 CJK)、Jexl(ASCII 标识符)、CEL、解析器工具包后**维持自写引擎**,论证见 迭代 23 技术方案 §4.5 |
|
||||
| 技术约束-023 | 计算字段现算口径(R-026,2026-09-10):`src/formula/FormulaService.js#computeRows` 在 **API 层**为 `strategy-positions` 返回行附加 `computed: { [fieldKey]: number|null }`(不改 PositionManager 份额语义);取数**只读 QuoteHub 内存缓存**(`quotes` / `instruments`,与 api/market.js#projectQuote 同口径),**不做读穿透**(高频读路径零网络;miss → 相关变量 null → 该格 `—`,等 QuoteSync 下轮 ≤5s 覆盖);**无 formula 字段的策略直接返回原数组(零开销)**;值**不落库**(公式存 settings,值每次现算);`strategy-holdings/history` 不计算(历史行前端显示 `—`) | 2026-09-10 | 生效 | - | 新增(2026-09-10 R-026 技术方案 §2.4):计算位置与缓存口径由 AI 定并写入方案,老师验收覆盖缺数据边界 |
|
||||
| 技术约束-024 | 字段定义 API 与校验(R-027/R-026,2026-09-10):写路径唯一 `strategies/schema-update { strategyId, configSchema }`(返回该策略对象);校验顺序 = 结构(展示名/key 非空、类型白名单、枚举选项非空)→ 唯一性(key 唯一、展示名唯一)→ **内置变量名冲突**(字段展示名不得等于现价/份额/…等内置变量名)→ 公式(`validateFormula`,变量集合 = 内置名 ∪ 本策略非 formula 字段展示名);失败统一抛 `code='field-validation'`(前端 Toast + 公式框描红);`formula/variables { strategyId? }` 提供变量目录、`formula/trial { strategyId, formula }` 试算(取份额>0 首行,返回 { code, name, value, reason: no-holding|no-data|null });**退役端点** `strategies/add|remove|update`(调用返回 not-found) | 2026-09-10 | 生效 | - | 新增(2026-09-10 R-027/R-026 定稿 + 技术方案 §1.2/§2.5):单策略写入避开整表覆盖,校验规则服务端唯一;**变更(2026-09-10 R-028)**:公式字段增加「结果类型一致性」校验——`resultKind`(默认 number)必须等于 `inferResultKind(compileFormula(formula).ast)`,不一致抛 `field-validation`(双向提示)|
|
||||
|
||||
<!-- 示例条目(确认格式后删除):
|
||||
| 技术约束-001 | 示例:技术栈以 Node.js / TypeScript 为准,不引入未讨论的新框架 | 2026-08-26 | 生效 | - | 讨论确认:优先复用 DSH 既有能力,新框架需论证 |
|
||||
-->
|
||||
Reference in New Issue
Block a user