Files
one_divine_lot/docs/05-需求池/已完成/R-009.md
T
kyugao 2aacd8a25d docs(迭代07/08): 交易记录本地存储 + 策略持仓展开全量文档 + 需求归档(R-009/R-010)
- PLAN-008/PLAN-009 计划(已完成)
- 迭代 07/08 四件套(目标/技术方案/验收标准/复盘)
- 设计约束:数据存储设计 §10/§10.1、技术约束-012/013、产品约束-008
- R-009/R-010 需求归档(含二次定稿:手动归属、Q4 成本价/成交价)
- 需求池索引同步
2026-09-02 01:21:10 +08:00

10 KiB
Raw Blame History

R-009 交易记录本地存储(SQLite)+ 策略关联 · 已完成

归档日期:2026-09-01 | 需求状态:已完成 原索引:docs/05-需求池/需求池索引.md(主索引保留 R-009 条目,指向本归档) 实现迭代:07-交易记录本地存储SQLite与策略关联(验收通过,迭代复盘见 docs/04-迭代记录/07-交易记录本地存储SQLite与策略关联/迭代复盘.md 关联计划:PLAN-008docs/02-计划/计划-交易记录本地存储SQLite与策略关联.md) 讨论记录:Q1-Q8 定稿(2026-09-01),Q3 关联方式修正(外键链 + 两表零冗余)


R-009 交易记录本地存储(SQLite)+ 策略关联

状态:已定稿2026-09-01,老师确认)| 登记日期:2026-09-01 来源:老师指令(2026-09-01 优先级:P1 关联:R-007(交易记录接入)、R-008SQLite 存储)、docs/03-设计约束/数据存储设计.md §9(holding_id 关联锚点预留)、迭代 06 复盘遗留项 2

需求描述

在 SQLite 中新增交易记录表(委托 + 成交),将 QMT Bridge 当日交易数据本地持久化(跨日积累,形成本地历史库),并建立与插件策略体系的关联,支持按策略过滤 / 复盘交易。归属由用户在交易记录 tab 手动设置(冗余存 strategy_id + holding_id),UPSERT 不覆盖归属列。

  • 承接 R-007 的 Q5(本地持久化,本期不做)与 Q10(按策略过滤,后续迭代);
  • 落实迭代 06 复盘遗留项 2:「strategy_holdings.holding_id 已就绪,可作为 trades 表关联锚点(R-007 实现时建 trades 表)」;
  • 顺带解决 R-007 历史范围「接口开发中」占位问题(本地积累后历史可查)。

现状(代码审查 2026-09-01

现状
交易展示 TradeRecordsTab 接 QMT 当日委托(/trade/orders+ 当日成交(/trade/trades),单表合并展示,仅实时、不落盘
历史范围 非今日范围前端占位「历史数据接口开发中」(QMT Bridge 无历史接口,get_trade_detail_data 读客户端缓存仅当日)
SQLite 迭代 06 落地:strategy_holdings(持仓生命周期:holding_id 自增 + created_at/closed_at+ market_quotes_cache 两表;holding_id 预留为交易关联锚点
策略 策略=标签(settings),持仓份额存 strategy_holdings;同码可跨多个策略(部分唯一索引仅约束同策略同码一笔当前持仓)

待讨论点(Q1-Q8,AI 建议见各条)

Q1 存储粒度与表结构(AI 建议:委托 + 成交两表)

-- 交易委托(委托主行;order_id 唯一,UPSERT 幂等;零冗余:不存 strategy_id/holding_id
CREATE TABLE IF NOT EXISTS trade_orders (
  order_id       TEXT PRIMARY KEY,   -- m_strOrderSysID
  trade_date     TEXT NOT NULL,      -- 交易日 YYYYMMDDm_strInsertDate
  code           TEXT NOT NULL,
  name           TEXT NOT NULL DEFAULT '',
  exchange       TEXT NOT NULL DEFAULT '',
  direction      TEXT NOT NULL DEFAULT '',   -- buy / sell
  direction_code INTEGER,
  opt_name       TEXT NOT NULL DEFAULT '',
  status         INTEGER,
  order_volume   REAL NOT NULL DEFAULT 0,
  traded_volume  REAL NOT NULL DEFAULT 0,
  limit_price    REAL NOT NULL DEFAULT 0,
  traded_price   REAL NOT NULL DEFAULT 0,
  amount         REAL NOT NULL DEFAULT 0,
  insert_date    TEXT NOT NULL DEFAULT '',
  insert_time    TEXT NOT NULL DEFAULT '',
  insert_ts      INTEGER NOT NULL,   -- 派生列:insert_date+insert_time 合成毫秒时间戳(join 持仓窗口用)
  cancel_info    TEXT NOT NULL DEFAULT '',
  error_msg      TEXT NOT NULL DEFAULT '',
  fetched_at     INTEGER NOT NULL    -- 同步时间戳
);

-- 交易成交(成交明细;trade_id 唯一,order_id 外键关联委托;零冗余:不存 strategy_id/holding_id
CREATE TABLE IF NOT EXISTS trade_fills (
  trade_id            TEXT PRIMARY KEY, -- m_strTradeID
  order_id            TEXT NOT NULL,    -- → trade_orders.order_id
  trade_date          TEXT NOT NULL,
  code                TEXT NOT NULL,
  name                TEXT NOT NULL DEFAULT '',
  exchange            TEXT NOT NULL DEFAULT '',
  direction           TEXT NOT NULL DEFAULT '',
  direction_code      INTEGER,
  opt_name            TEXT NOT NULL DEFAULT '',
  price               REAL NOT NULL DEFAULT 0,
  volume              REAL NOT NULL DEFAULT 0,
  amount              REAL NOT NULL DEFAULT 0,
  commission_rate_wan REAL NOT NULL DEFAULT 0,
  trade_time          TEXT NOT NULL DEFAULT '',
  fetched_at          INTEGER NOT NULL
);
CREATE INDEX IF NOT EXISTS idx_trade_orders_date  ON trade_orders (trade_date);
CREATE INDEX IF NOT EXISTS idx_trade_orders_ts    ON trade_orders (insert_ts);
CREATE INDEX IF NOT EXISTS idx_trade_fills_order  ON trade_fills (order_id);
CREATE INDEX IF NOT EXISTS idx_trade_fills_date   ON trade_fills (trade_date);
  • 理由:委托 1:N 成交,两表忠实 R-007「单表合并」模型(前端 mergeOrdersAndTrades 可复用);两表能保留无成交委托(待报/已撤/废单),复盘完整;避免 JSON 压扁。
  • 2026-09-01 老师修正:两表都不冗余 strategy_id / holding_id(外键链推导,见 Q3);trade_orders 增加派生列 insert_tsinsert_date+insert_time 合成,join 持仓窗口用)。

Q2 存储时机 / 同步机制(AI 建议:服务端定时同步 + API 写穿)

  • 服务端 TradeSync 模块定时(建议 60s)拉取今日 orders+trades → UPSERT 落库(幂等);
  • 前端今日轮询命中 orders/trades 端点时同步写穿(机会式);
  • 插件启动时同步一次(预热今日数据);
  • 理由:不依赖前端开 tab 也持续积累;UPSERT 幂等不重复。

Q3 策略关联方式(2026-09-01 二次修正定稿:手动设置归属)

最终定稿(老师拍板):关联链 = 成交(fill) → 委托(order) → 策略(strategy) → holding(持仓 ID),但归属由用户手动设置,不靠算法推导

  • 信息源 = 人:交易记录 tab 加载每日交易记录,用户手动设置每笔委托的归属(下拉候选 = 该 code 的当前持仓策略 + 未关联);符合「人机合一」目标-007(AI 给候选,人做最终决定);
  • trade_orders 冗余存 strategy_id + holding_id(Q1 老师确认:加冗余字段,便于过滤/展示/复盘);
  • UPSERT 不覆盖归属列(Q2 老师确认):归属是手动指定的、是插件逻辑,TradeSync 定时同步只更新 QMT 原始字段,保留已有归属;
  • 全手动选(Q3 老师确认):不自动预填候选值,用户明确选择后才落库;未设置前归属为空(未关联);
  • 可修改(Q4 老师确认):已设置的归属可随时改,以最终修改为准;
  • 成交明细跟随委托归属(不单独设置);
  • 归属推导逻辑(方案 A 时间窗 + 份额最大)降级为候选列表来源:一票一策略时给出该策略候选、一票多策略时给出全部候选让用户选、无持仓时仅「未关联」。

Q4 历史查询能力(AI 建议:新增本地查询端点,策略过滤走 FK 链 join)

  • 新增端点(如 trades/history):按 { start, end, code, strategyId, direction } 查本地 SQLite,返回 { orders, fills }
  • strategyId 过滤 = trade_orders JOIN strategy_holdings(生命周期窗口含委托时间,取份额最大持仓)筛选 strategy_id
  • 前端历史范围从「占位」切换为查本地库(数据=插件运行期间逐日积累;插件停运期间无数据,诚实记录不可补);
  • 今日仍走 QMT 实时(更新鲜),同步写穿本地;QMT 不可用时可用本地兜底(可选)。

Q5 同步范围与数据积累(AI 建议)

  • 只同步「当日」数据(QMT 能力所限,无法回溯补历史);
  • 数据逐日积累 = 本地历史库;不做自动清理(复盘需要历史),导出/清理后续再说。

Q6 UI 扩展(AI 建议:策略过滤)

  • TradeRecordsTab 加策略过滤下拉(全部 / 各策略 / 未关联);
  • 历史范围展示本地数据(不再占位);按策略过滤对今日(实时)与历史(本地)均生效。

Q7 委托状态更新(AI 建议:UPSERT 覆盖)

  • 同一 order_id 再次同步时更新 status / 成交量等(当日委托状态流转:已报→部成→已成);
  • 历史日状态 = 最后一次同步快照(当日收市时基本已终态,可接受)。

Q8 文档与约束(后续执行)

  • 数据存储设计.md 增补交易记录表设计(§10);技术方案约束新增条目;产品约束按需(策略过滤 UI)。

边界(初拟,待讨论收敛)

:两表落地(trade_orders + trade_fills,零冗余 strategy_id/holding_id+ 定时同步落库(幂等)+ 本地历史查询端点(策略过滤 = 委托时间 join 持仓生命周期窗口)+ tab 策略过滤 + 历史范围本地展示。 不做(本期):手动修正策略关联入口(后续);导出/清理管理界面(后续);QMT 历史接口接入(老师完善后再接);下单/撤单操作(目标-007 另议)。

讨论与修正记录

  • 2026-09-01 老师提出需求,AI 登记(讨论中),提出 Q1-Q8 建议待确认。
  • 2026-09-01 老师反馈(Q3 关联方式修正):关联链 = 成交 → 委托 → 策略 → holding。成交只与委托相关(order_id 外键即可);委托与策略相关;策略对应具体 holding_id。两表均不冗余 strategy_id + holding_id —— 采用零冗余 + 查询时按持仓生命周期时间窗 join 推导(Q3 定稿)。
  • 2026-09-01 老师确认 Q1-Q8 全部定稿:两表零冗余 + 外键链关联(Q3)+ 定时同步 UPSERT + 本地历史查询 + tab 策略过滤 + 只同步当日;R-009 转已定稿,进入迭代 07 实施
  • 2026-09-01 老师二次定稿(Q1-Q4):归属由用户在交易记录 tab 手动设置——trade_orders 冗余存 strategy_id + holding_idQ1);UPSERT 不覆盖归属列Q2,手动指定为插件逻辑);全手动选Q3,不自动预填候选值);可随时改、以最终为准Q4);算法推导降级为候选列表来源。