6f74ed4a79
归类重构(技术约束-016)落地后同步活动文档旧路径: - 数据存储设计.md: 三处对应实现表 12 处 src/component/* → storage/position/market/trades - R-013 / PLAN-012 / 迭代11技术实现方案: SqliteStore/PositionManager 路径更新 - 迭代11程序结构设计: 补后续动作记录(历史档案按追加式原则不改写)
194 lines
11 KiB
Markdown
194 lines
11 KiB
Markdown
# R-013 策略自定义字段配置(定义随策略,值落库)
|
||
|
||
> 状态:**已定稿**(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_holdings(holding_id / strategy_id / code / shares / created_at / closed_at),无自定义字段列 |
|
||
| 定义位置 | 设置页「策略分组」子 tab:策略行内 CRUD(新增 / 重命名 / 删除),无字段配置能力 |
|
||
| 持仓渲染 | 策略持仓 tab 固定列(代码/名称/份额/现价/盈亏/操作),R-010 已支持持仓行展开看交易记录 |
|
||
|
||
## 讨论结论(2026-09-02 老师确认 Q1-Q4)
|
||
|
||
- **D1**(第二轮)字段**定义不存数据库**:随策略定义走,扩展 settings.strategies(configSchema 数组);数据库只存值。
|
||
- **D2**(第三轮 + Q1)值 = **持仓级 JSON 键值对**:strategy_holdings 新增一列 values TEXT(JSON);每行(一个策略 × 一只股票)一套值;某持仓需要哪些字段 = 看所属策略的 configSchema。
|
||
- **D3**(Q2/Q3)字段**类型化**:文本 / 数字 / 布尔 / **枚举**(预设选项下拉,有用,采纳)。定义含:key / label / type / enum 选项 / 默认值。
|
||
- **D4**(第三轮)配置入口 = **设置页「策略分组」子 tab**:策略行做成**可展开**,展开区添加 / 配置字段(名称 + 类型 + 枚举选项 + 默认值)。
|
||
- **D5**(Q4)兼容:旧策略无 configSchema(缺省空数组)→ 持仓行不展示自定义字段区,行为与现在完全一致;旧行 values 列缺省 NULL(空);无迁移历史值。
|
||
- **D6**(2026-09-02 老师确认)值编辑入口 = **策略持仓 tab 持仓行展开区**(沿 R-010 展开能力),展开后按 configSchema 渲染各字段输入控件,编辑即调 API 写回该行 values。
|
||
|
||
## 目标数据模型(定稿)
|
||
|
||
```js
|
||
// 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: '中' },
|
||
],
|
||
}]
|
||
```
|
||
|
||
```sql
|
||
-- strategy_holdings 新增列(幂等 ALTER,沿用 _ensureTradeAttributionColumns 模式)
|
||
ALTER TABLE strategy_holdings ADD COLUMN values TEXT; -- JSON 键值对
|
||
```
|
||
|
||
```js
|
||
// 持仓行 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,不落库)
|
||
|
||
```js
|
||
// 完整值(新增/编辑后持久化于 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 列)
|
||
|
||
```sql
|
||
-- 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 通道)
|
||
|
||
```js
|
||
// ① 读策略(含 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 → 持仓行展开区(编辑值,R-010 展开能力之上)**:
|
||
|
||
```
|
||
601117.SH 中国交建 600股 现价 7.20 +0.56% [展开]
|
||
└ 交易记录(R-010 已有)
|
||
└ 自定义字段:
|
||
网格间距 [ 4% ]
|
||
单格金额 [ 10000 ]
|
||
设置止损 [ ✓ ]
|
||
风险等级 [ 高 ▼ ]
|
||
[保存]
|
||
```
|
||
|
||
### 5. 渲染 / 校验逻辑参考
|
||
|
||
```js
|
||
// 渲染(策略持仓 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.js**:strategySchema 的 strategies 项扩展 configSchema(schemastery array of object,type union text|number|boolean|enum);读取归一化(旧项缺省 []);strategies/update 沿用整表更新语义。
|
||
2. **src/storage/SqliteStore.js**:init() 增加 _ensureHoldingValuesColumn(PRAGMA table_info → ALTER,幂等,只读容忍同 trade_orders 先例);openHolding / addShares / reduceShares / closeHolding 保持既有列行为(values 不随份额操作变动);新增 readValues(holdingId) / writeValues(holdingId, values)。
|
||
3. **src/api/strategies.js**:strategy-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-Q4,2026-09-02 老师确认)
|
||
|
||
- **Q1** 字段定义 = 策略级(某策略下每笔持仓需要的字段由该策略定义决定);值 = 持仓级(每行一套键值对)→ 确认。
|
||
- **Q2** 字段加类型(文本 / 数字 / 布尔 / 枚举)→ 确认。
|
||
- **Q3** 支持枚举字段(预设选项下拉)→ 确认(有用)。
|
||
- **Q4** 旧策略无自定义字段:沿用现有字段定义(不迁移、不补默认),值列缺省空 → 确认。
|
||
- **D6(值编辑入口)**:持仓行展开区编辑 —— 老师确认(2026-09-02)。
|
||
|
||
> 三要素满足(边界清楚 / 核心逻辑明确 / 老师确认 Q1-Q4),**已定稿**,可进入计划范围。 |