Files
kyugao 329ac67a71 docs(迭代11+归档): R-013 归档完成 + 迭代 11 复盘
- 迭代 11 复盘:实施事实(R-013 自定义字段 + 列显隐 + 单元格编辑 + unit)+
  经验教训(JSX 未导入白屏 / z.dict dts / SQLite 关键字列 / map this 丢失 / 列宽稳定)
- R-013 归档至 已完成/(归档头:日期/状态/实现迭代/讨论记录)
- 索引 R-013 → 已实现(已归档);PLAN-012 → 已完成
2026-09-02 17:34:51 +08:00

13 KiB
Raw Permalink Blame History

R-013 策略自定义字段配置(定义随策略,值落库) · 已完成

归档日期:2026-09-02 | 需求状态:已完成 原索引:docs/05-需求池/需求池索引.md(主索引保留 R-013 条目,指向本归档) 实现迭代:11-策略自定义字段配置(验收通过,迭代复盘见 docs/04-迭代记录/11-策略自定义字段配置/迭代复盘.md) 讨论记录:2026-09-02 定稿(Q1-Q4 + D6);迭代 11 实施中扩展列显隐/单元格编辑/unit(老师确认);实现见迭代 11 技术实现方案/程序结构设计/复盘

状态:已定稿2026-09-02,老师确认 Q1-Q4)| 登记日期:2026-09-02 来源:老师提出(2026-09-02,策略可自定义 / 可扩展讨论;T-009 转正) 优先级:P1(策略管理能力扩展,无数据风险) 关联:T-003(策略配置文件骨架,起草 —— 本需求吸收其「策略数据结构」部分)、T-006(数据池 + 策略表格动态字段配置,起草 —— 边界:本需求=策略自定义字段,T-006=表格列配置)

需求描述

策略要可自定义、可扩展:每个策略可定义自己需要的一组自定义字段(带类型:文本 / 数字 / 布尔 / 枚举),该策略下的每个持仓(strategy_holdings 行)按策略定义存一份键值对值JSON,可任意扩展)。字段定义随策略走(不落库),值落库strategy_holdings 新增 values JSON 列)。

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

现状
策略定义 settings.strategies = [{ id, name }]R-011 收窄),存 DSH settings
持仓存储 strategy_holdingsholding_id / strategy_id / code / shares / created_at / closed_at),无自定义字段列
定义位置 设置页「策略分组」子 tab:策略行内 CRUD(新增 / 重命名 / 删除),无字段配置能力
持仓渲染 策略持仓 tab 固定列(代码/名称/份额/现价/盈亏/操作),R-010 已支持持仓行展开看交易记录

讨论结论(2026-09-02 老师确认 Q1-Q4

  • D1(第二轮)字段定义不存数据库:随策略定义走,扩展 settings.strategiesconfigSchema 数组);数据库只存值。
  • D2(第三轮 + Q1)值 = 持仓级 JSON 键值对strategy_holdings 新增一列 values TEXT(JSON);每行(一个策略 × 一只股票)一套值;某持仓需要哪些字段 = 看所属策略的 configSchema。
  • D3Q2/Q3)字段类型化:文本 / 数字 / 布尔 / 枚举(预设选项下拉,有用,采纳)。定义含:key / label / type / enum 选项 / 默认值。
  • D4(第三轮)配置入口 = 设置页「策略分组」子 tab:策略行做成可展开,展开区添加 / 配置字段(名称 + 类型 + 枚举选项 + 默认值)。
  • D5Q4)兼容:旧策略无 configSchema(缺省空数组)→ 持仓行不展示自定义字段区,行为与现在完全一致;旧行 values 列缺省 NULL(空);无迁移历史值。
  • D62026-09-02 老师确认,2026-09-02 迭代 11 演进为列化)值编辑入口原定为持仓行展开区;随「自定义字段列化 + 单元格点击编辑」演进(老师选 A):字段直接作为策略持仓表展示,点击字段单元格进入内联编辑(text/number 输入框、boolean 开关、enum 下拉),编辑即调 API 写回该行 values。展开区(交易明细)不再显示自定义字段。

目标数据模型(定稿)

// settings.strategies(扩展 configSchema;不落库)
strategies: [{
  id: 'grid-supermarket',
  name: '网格超市',
  configSchema: [
    { key: 'gridGap',    label: '网格间距', type: 'text',    def: '3%' },
    { key: 'gridAmount', label: '单格金额', type: 'number',  def: 10000 },
    { key: 'hasStop',    label: '设置止损', type: 'boolean', def: false },
    { key: 'riskLevel',  label: '风险等级', type: 'enum', enum: ['低','中','高'], def: '中' },
  ],
}]
-- strategy_holdings 新增列(幂等 ALTER,沿用 _ensureTradeAttributionColumns 模式)
ALTER TABLE strategy_holdings ADD COLUMN values TEXT;  -- JSON 键值对
// 持仓行 values 示例(key 与 configSchema.key 对齐;未定义的额外键允许存在=可扩展)
{ "gridGap": "3%", "gridAmount": 10000, "hasStop": true, "riskLevel": "中" }

语义要点

  • key 为稳健标识(英文 slug),label 为 UI 展示名(可中文);值 JSON 以 key 存;
  • 枚举字段值校验 ∈ enum 选项;数字字段 UI 数字输入;布尔渲染开关;文本自由输入——校验在服务端写回时执行;
  • 定义变更对已有值数据无破坏(旧值保留,新增字段缺省用 def,未配置字段不渲染)。

端到端示例(Sample,供后续推进参考)

场景:现有三个策略 —— 网格超市(已配自定义字段)、手动做T(未配,旧策略)、长线持有(新增策略,配另一组字段)。沿用项目现有个股:601117.SH(中国交建)、300057.SZ(万顺新材)、600719.SH(大连热电)。

1. 策略定义(settings.strategies,不落库)

// 完整值(新增/编辑后持久化于 DSH settings
strategies: [
  {
    id: 'grid-supermarket',
    name: '网格超市',
    configSchema: [
      { key: 'gridGap',    label: '网格间距', type: 'text',    def: '3%' },
      { key: 'gridAmount', label: '单格金额', type: 'number',  def: 10000 },
      { key: 'hasStop',    label: '设置止损', type: 'boolean', def: false },
      { key: 'riskLevel',  label: '风险等级', type: 'enum', enum: ['低','中','高'], def: '中' },
    ],
  },
  { id: 'manual-t', name: '手动做T' },                 // 旧策略:无 configSchema(兼容,缺省 []
  {
    id: 'long-term-hold',
    name: '长线持有',
    configSchema: [
      { key: 'targetPrice', label: '目标价',     type: 'number', def: null },
      { key: 'notes',       label: '持仓备注',   type: 'text',   def: '' },
    ],
  },
]

2. strategy_holdings 表数据(含 values 列)

-- ALTER 幂等补列后,既有行 values 自动为 NULL(不迁移)
ALTER TABLE strategy_holdings ADD COLUMN values TEXT;

SELECT holding_id, strategy_id, code, shares, values FROM strategy_holdings WHERE closed_at IS NULL;
-- 结果示例:
-- 1 | grid-supermarket | 601117.SH | 600  | {"gridGap":"3%","gridAmount":10000,"hasStop":true,"riskLevel":"高"}
-- 2 | grid-supermarket | 300057.SZ | 1000 | {"gridGap":"5%","gridAmount":8000,"hasStop":false,"riskLevel":"低"}
-- 8 | manual-t          | 600719.SH | 1000 | NULL(手动做T 未配字段 → 无值)
-- 9 | long-term-hold    | 601117.SH | 200  | {"targetPrice":7.5,"notes":"周线突破再加仓"}

同策略多行各存各的值(600719 与 300057 的网格间距不同);不同策略可有完全不同的字段(long-term-hold 无 gridGap)。

3. API 交互示例(/odl/api 端点,RPC 通道)

// ① 读策略(含 configSchema
call('one-divine-lot/strategies', {})
// 响应 value: 见 §1 完整数组(策略行自带 configSchema

// ② 配字段:整表更新 strategies(改 configSchema 随整表提交)
call('one-divine-lot/strategies/update', { strategies: [ /* §1 完整数组 */ ] })

// ③ 读某策略持仓(每行附 values)
call('one-divine-lot/strategy-positions', { args: { strategyId: 'grid-supermarket' } })
// 响应 value: [
//   { holdingId: 1, code: '601117.SH', shares: 600, /* 既有列 */ values: { gridGap: '3%', gridAmount: 10000, hasStop: true, riskLevel: '高' } },
//   { holdingId: 2, code: '300057.SZ', shares: 1000, /* ... */ values: { gridGap: '5%', gridAmount: 8000, hasStop: false, riskLevel: '低' } },
// ]

// ④ 写某持仓行的值(无 values → 传全量;已有 → 合并更新)
call('one-divine-lot/holdings/values-update', { args: { holdingId: 1, values: { gridGap: '4%', gridAmount: 10000, hasStop: true, riskLevel: '高' } } })
// 响应 value: { holdingId: 1, values: { gridGap: '4%', gridAmount: 10000, hasStop: true, riskLevel: '高' } }

4. UI 形态

设置页 →「策略分组」子 tab(配置字段定义)

[▸] 网格超市        [重命名] [删除]
     自定义字段:
     展示名      | key         | 类型    | 选项(枚举) | 默认值  | 操作
     网格间距    | gridGap     | 文本    | -          | 3%     | [删除]
     单格金额    | gridAmount  | 数字    | -          | 10000  | [删除]
     设置止损    | hasStop     | 布尔    | -          | 关     | [删除]
     风险等级    | riskLevel   | 枚举    | 低/中/高   | 中     | [删除]
     [+ 添加字段](名称/类型/选项/默认值 一行表单)           [保存]
[▸] 手动做T        ...(无字段区,保持现状)
[▸] 长线持有       ...(目标价 / 持仓备注)

策略持仓 tab(迭代 11 演进:自定义字段直接为表格列,点击单元格内联编辑)

代码      名称     现价  涨幅    网格间距  单格金额  设置止损  风险等级  策略份额  操作
601117.SH 中国交建  7.20 +0.56%   4%      10000      ✓        高       600     [移出]
300057.SZ 万顺新材  5.10 -1.2%   5%      8000       ✗        低      1000     [移出]
          (点击「网格间距/单格金额/风险等级」单元格 → 输入框/下拉编辑,Enter 保存;
            点击「设置止损」单元格 → 开关直接切换;空值显示 — / 默认值 def)

表格顶部另有「列设置」按钮:每策略独立配置列显隐与顺序(基础数据列 + 自定义字段列;代码/名称/操作固定)。

5. 渲染 / 校验逻辑参考

// 渲染(策略持仓 tab):configSchema 决定字段集合,values 提供值,缺省回退 def
const schema = strategy.configSchema ?? [];                // 无定义 → 不渲染字段区
const merged = schema.map((f) => ({ ...f, value: (row.values ?? {})[f.key] ?? f.def }));

// 校验(服务端 holdings/values-update 写回时)
function validateField(f, v) {
  if (v === undefined || v === null || v === '') return null;      // 允许空
  if (f.type === 'number' && !Number.isFinite(Number(v))) return f.label + ' 需为数字';
  if (f.type === 'enum' && !f.enum.includes(v)) return f.label + ' 需在选项内: ' + f.enum.join('/');
  if (f.type === 'boolean' && typeof v !== 'boolean') return f.label + ' 需为布尔';
  return null;
}

涉及改动面(技术方案,定稿)

  1. src/settings.jsstrategySchema 的 strategies 项扩展 configSchemaschemastery array of objecttype union text|number|boolean|enum);读取归一化(旧项缺省 []);strategies/update 沿用整表更新语义。
  2. src/storage/SqliteStore.jsinit() 增加 _ensureHoldingValuesColumnPRAGMA table_info → ALTER,幂等,只读容忍同 trade_orders 先例);openHolding / addShares / reduceShares / closeHolding 保持既有列行为(values 不随份额操作变动);新增 readValues(holdingId) / writeValues(holdingId, values)。
  3. src/api/strategies.jsstrategy-positions 返回项附加 values;新增端点 holdings/values-update {holdingId, values} 写回;strategies/update 已可携带 configSchema(整表更新天然支持)。
  4. src/client/views/SettingsSection.jsx:「策略分组」子 tab 策略行加展开手柄:展开区 = 字段列表(label/key/type/enum/def+ 添加/删除/编辑字段表单;保存走 strategies/update。
  5. src/client/views/StrategyTab.jsx:持仓行展开区(R-010 基础)增加「自定义字段」区块:按该策略 configSchema 渲染输入控件(文本/数字/布尔/枚举),值来自该行 values,编辑保存调 values-update;行内表格可选加字段列。
  6. 回归脚本:设置字段定义 → 批量写/改持仓行 values → 校验存储与读取(独立数据目录,技术约束-011)。

定稿记录(Q1-Q42026-09-02 老师确认)

  • Q1 字段定义 = 策略级(某策略下每笔持仓需要的字段由该策略定义决定);值 = 持仓级(每行一套键值对)→ 确认。
  • Q2 字段加类型(文本 / 数字 / 布尔 / 枚举)→ 确认。
  • Q3 支持枚举字段(预设选项下拉)→ 确认(有用)。
  • Q4 旧策略无自定义字段:沿用现有字段定义(不迁移、不补默认),值列缺省空 → 确认。
  • D6(值编辑入口):持仓行展开区编辑(老师确认 2026-09-02)→ 迭代 11 演进为表格列 + 单元格点击编辑(老师选 A2026-09-02)。

三要素满足(边界清楚 / 核心逻辑明确 / 老师确认 Q1-Q4),已定稿,可进入计划范围。