ec02a791ee
- 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 通过
8.9 KiB
8.9 KiB
迭代复盘:22-策略计算字段与策略 tab 静态化
结论
实现完成(两阶段全范围),待老师人工验收(尚未宣告验收通过)。事实:策略 tab 静态化 + 字段配置入口迁移(R-027)
与计算字段(R-026)均已落地;pnpm typecheck 0 错、pnpm build 通过、17 个回归脚本全绿(含新增 2 个)。
事实记录
阶段一 · 策略 tab 静态化与入口迁移(R-027)
- src/settings.js:新增
BUILTIN_STRATEGIES(内置两项:网格超市 grid-supermarket / 手动做T manual-t)、isBuiltinStrategy / assertBuiltinStrategy / normalizeFields;字段定义真相源迁到strategyFields(getStrategies恒返回内置两项、缺失时兼容读取旧strategies[].configSchema;getStrategyFields/updateStrategyFields为唯一读写入口);normalizeTabs改为常量派生(3 内置 tab + 2 策略 tab, 只保留存量 visible/order 偏好、丢弃未知条目、补齐缺失条目),updateTabs只接受内置 id;normalizeStrategyColumns的字段列来源改读getStrategyFields;新增scopeValue容错读取 (测试桩无get不炸);退役addStrategy / removeStrategy / renameStrategy / updateStrategies / appendStrategyTab / removeStrategyTab / DEFAULT_STRATEGIES。 - src/api/strategies.js:退役
strategies/add | strategies/remove | strategies/update(调用返回 not-found); 新增strategies/schema-update { strategyId, configSchema }(唯一写路径)+validateConfigSchema(结构 / key 唯一 / 展示名唯一 / 内置变量名冲突 / 枚举选项 / 公式校验,失败统一code='field-validation'); 新增formula/variables(变量目录)与formula/trial(试算)。 - 设置页(SettingsSection.jsx):「策略分组」子 tab 整体移除(含 CRUD 与删除确认)→ 设置页仅 「Tab 设置 / QMT 连接配置」。
- 字段配置弹层(FieldConfigDialog.jsx 新建 + StrategyFieldsEditor.jsx 改造):策略 tab 顶栏
「字段配置」(列设置左侧)→ 锚定弹层(520px / 70vh / 内部滚动);字段列表 + 添加/编辑/删除 + 底部
[关闭]/[保存字段];dirty 时关闭/外点/Esc 二次确认;保存走
strategies/schema-update。
阶段二 · 计算字段(R-026)
- src/formula/evaluator.js(自写零依赖引擎,不用
eval/Function):词法支持中文标识符 + 全角归一 (()+-×÷),递归下降解析(四则 / 括号 / 一元负 / round·abs·min·max;长度 ≤500、深度 ≤32);validateFormula返回{ok,vars}或{ok:false,error:{code:empty|syntax|unknown-var|unknown-func|arity,message,position}}; 求值缺值短路(任一变量 null → 整式 null)、除零/非有限 → null、最终结果浮点归一(|v|<1e12 四舍五入 10 位, 消除0.7000000000000002类噪声;中间步骤不归一再保精度——首版曾因每步归一导致6.66666667精度劣化,已改)。 - src/formula/variables.js:变量目录单一入口(行情 7 项 / 合约 2 项 / 持仓账本 3 项 + 运行时按策略
非 formula 字段展示名展开「自定义字段」组);
allowedVarNames排除 formula 字段(零循环依赖);buildContext取数(昨收兜底合约 preClose、布尔 → 0/1、空值 → null)。 - src/formula/FormulaService.js:
computeRows为strategy-positions行附computed;行情/合约 只读 QuoteHub 内存缓存(不读穿透);无 formula 字段直接返回原数组(零开销);值不落库。 - 前端:字段表单类型「计算」分支(等宽公式框 + 「+ 插入变量」分组菜单 + 试算 + 小数位 0-4 默认 2;
保存失败公式框描红 + 服务端文案);持仓表计算列只读(表头
ƒ+ title 公式原文、按toFixed(decimals)+ 单位 格式化如6.67 %/700.00 元、缺数据—、点击不进内联编辑);历史持仓行—;列设置弹层列名带ƒ。
实机数据核实(实施前)
~/.dsh/settings.yaml:策略仅两个内置(网格超市 5 字段 / 手动做T 1 字段);~/.dsh/one-divine-lot/store.db:strategy_holdings13 / 3 行,交易与归属数据只涉及这两个策略 → 无自建策略,静态化零数据迁移。
验证
pnpm typecheck→ 0 错;pnpm build→ 通过(lib 32 文件 + client bundle 195545 bytes,scripts/wrap-client.mjs正常);- 回归脚本 17 / 17 全绿:
- 新增
scripts/test-r027-static-strategies.mjs35/35(策略常量 / 旧定义兼容 / 单策略写入 / tabs 归一化 / 端点退役); - 新增
scripts/test-r026-formula-fields.mjs48/48(引擎 / 目录 / 上下文 / 现算 / 保存校验 / 试算); - 更新
test-r011-tabs22/0、test-r011-api17/0、test-r013-custom-fields23/0、test-r013-columns18/0(期望值按 R-015 行情列 + R-027 真相源更新)、test-r017-candidates12/0(策略 id 改用内置); - 其余(r009 系列 / r016 / r018 / quote-sync / position-sync / health / mcp)零改动全绿。
- 新增
偏差、顺带修复与发现
generateStrategyId/PINYIN_MAP保留(方案原文列入退役):QMT 连接 id 生成generateQmtConnectionId仍依赖,本轮不动(已回写技术方案)。- 真 bug(实施中发现并修复):
normalizeStrategyColumns仍读旧strategies[].configSchema, 字段定义迁到strategyFields后策略列会丢失字段列 → 改为getStrategyFields(scope, strategyId)。 教训:真相源迁移必须全量搜「读点」而不只是「写点」。 - 兼容修复:
strategy-positions新增 settings 依赖后,测试里无get的 settings 桩会抛错 (test-r016/test-r017 暴露)→ 新增scopeValue容错读取。 - 顺带修复迭代 20 复盘遗留第 1 项:
test-r013-columns期望过期(R-015 增加 4 个默认隐藏行情列后应为 12 列)——本次随实现更新期望并补「strategyFields 优先」用例。 test-r017-candidates策略 id 迁移:attribution-targets经getStrategies取策略表, 静态化后仅内置两项 → 测试改用manual-t / grid-supermarket(R-027 的必然结果,非缺陷)。- 交互边界修复:字段配置弹层的「外点关闭」原以弹层面板为界,点「字段配置」按钮自身会触发放弃确认 (dirty 时甚至静默卸载)→ 改为以锚点容器(含两个按钮)为界。
- 工作区背景(非本轮产出):仓库工作区另含迭代 19/20/21(R-022/R-023/R-024/R-025)的未提交改动, 本轮未触碰其逻辑,仅在其上叠加;建议老师确认后一并提交。
遗留与风险
- 行情 miss 显示
—:计算列取数只读内存缓存、不读穿透(技术约束-023);某 code 无快照时该格为空, 等 QuoteSync 下轮(≤5s)覆盖。若老师更希望「宁可慢也要立刻有价」,可改判为读穿透。 - 需要重载插件才能人工验收:
~/.dsh/profiles/web/node_modules/one-divine-lot是指向本工作区的软链, lib 已构建完成;老师侧需重载插件(或重启 dsh web)后在页面验收(未由我启动任何服务)。 - 旧
strategies[].configSchema仅只读兼容:本机字段定义在其首次保存后落入strategyFields(不写迁移脚本、不动库数据)。 - 自建策略若未来出现:读取忽略、tab 丢弃,其
strategy_holdings数据保留在库(本机无此情况)。
待老师确认事项(人工验收)
按 验收标准.md:A1–A11(重构)+ B1–B12(计算字段),重点四条:
- 设置页无「策略分组」;策略 tab 恒两个、不可增删改名,但 Tab 设置仍可显隐/拖拽排序;
- 策略 tab 顶栏「字段配置 / 列设置」可用,字段增删保存后表格列跟随、重启后定义仍在;
- 计算字段可用:中文变量公式 + 变量选择器 + 试算 → 持仓列只读显示结果(
ƒ标记 + 悬停看公式); - 边界:非法公式/未知变量/名称冲突被拒 + 描红;缺数据与历史行显示
—;strategy_holdings.values不含计算字段键。
经验沉淀(候选)
- 并行委派的契约必须在开工前冻结:本轮前端由子代理并行实现,期间服务端
settings.js仍在演进 (子代理观察到 mtime 变化、并在报告里提出「疑似工作区未冻结」)——契约冻结 + 文件集互斥是并行可行性的前提。 - 真相源迁移要全量搜读点:本次
normalizeStrategyColumns差点漏改,靠既有回归脚本的红灯暴露; 遗留的旧键兼容读取(而非直接删键)让迁移零风险、零脚本。 - 测试期望也需要跟着设计演进:
test-r013-columns的过期期望在迭代 20 复盘就被记录却未处理, 本轮顺手清偿,避免「红灯常态化」。