245fcad95c
- PositionSync: 内存快照 + 10s 定时全量同步 + 失败保留旧快照 + 空快照双重确认 (/health + getAsset 账户身份)+ 幽灵持仓自动清仓(连续3轮消失 + 账户身份守卫防误清) - PositionManager.getAllPositions 改读快照,空则读穿透兜底;未注入 sync 时兼容旧穿透 - 回归 test-position-sync.mjs 35 项(纯内存 mock,含 kept 分支刷新 syncedAt 防灯灰) - 文档链: R-014 + PLAN-013 + 迭代三件套 + 数据存储设计 §11(内存不落库决策与三问标准) 需求: R-014(老师拍板: 内存不落库/读穿透兜底/幽灵自动清仓/不显示同步时间)
3.3 KiB
3.3 KiB
计划:持仓内存快照(服务端 10s 定时同步,请求不再穿透 QMT)(阶段航点)
编号:PLAN-013 | 粒度:阶段航点 | 创建:2026-09-02 | 状态:已实施(待验收) 派生自终极目标:目标-008(真实交易系统接入)、目标-001(DSH 插件形态) 依据需求:R-014(已定稿,2026-09-02,老师逐项拍板 4 问) —— 符合入范围门槛 设计约束:技术约束-010(服务端中转 + 缓存模式沿用)、技术约束-012(存储设计)、技术约束-016(分域目录);本次新增 技术约束-017 补充说明:本计划为「先实施后补档」——代码随架构梳理讨论当场落地(老师指令),文档链(本计划 + 迭代三件套 + 约束)事后补全,事实以迭代复盘为准
目标
服务端建立全量持仓内存快照(PositionSync,10s 定时与 QMT 同步),策略持仓 / 全部持仓 / 未分配三个接口改读快照为准,**消除「QMT 抖动 → 持仓页面白屏」**并去掉每次进 tab 的穿透 HTTP 调用;前端零改动。
范围
做:
- PositionSync(src/position/PositionSync.js):启动预热 + 10s 定时全量拉 QMT → 校验 → 整体替换内存快照;同步失败保留上次快照(不清空不报错);
- 空快照双重确认:/health 可用 + getAsset 账户身份可识别,二者兼备才接受为「真清仓」,否则视为异常保留旧快照;
- 读穿透兜底:PositionManager.getAllPositions() 快照为空 → 当场拉一次 QMT 并回填;未注入 positionSync 时保持旧穿透行为(兼容);
- 幽灵持仓自动清仓:QMT 快照连续 3 轮(约 30s)消失的 code,本地全部策略当前持仓 closeHolding 转历史;防抖护栏 = 账户身份守卫(accountId 未知当轮跳过;账户切换当轮重置计数并跳过);部分减持不触发;
- index.js 装配(start/dispose/注入 manager 与 api runtime);回归脚本(纯内存 mock,技术约束-011 不受影响)。
不做:
- 不落库(无 positions_cache 表;不建表、不改 SQLite schema)——R-014 讨论 #1 明确;
- 不复用 strategy_holdings(账本与对账单分离,holding_id 锚点语义不掺快照);
- 前端改动(零改动;同步时间显示本轮不做);
- 部分减持的自动修正(负数未分配仍由 UI 暴露,后续可另立需求);
- N+1 委托查询、api/trades.js 绕门面等既有观察点(另行登记)。
涉及文件
src/
├── position/PositionSync.js # 新增:持仓内存快照同步服务
├── position/PositionManager.js # getAllPositions 改读快照 + 读穿透兜底(兼容旧构造)
└── index.js # 装配 PositionSync + 注入
scripts/
└── test-position-sync.mjs # 回归脚本(纯内存 mock 34 项)
实现步骤
- PositionSync 模块(快照 + 定时 + 校验 + 幽灵清仓);
- PositionManager 读路径切换 + 读穿透 + 兼容分支;
- index.js 装配;
- 回归脚本(34 项)+ typecheck + build + r013 回归;
- 文档链补全(本计划 + 迭代三件套 + 技术约束-017 + 数据存储设计 §11)。
验收要点
- 见
docs/04-迭代记录/12-持仓内存快照/验收标准.md。