191 lines
8.5 KiB
Markdown
191 lines
8.5 KiB
Markdown
# 程序结构设计:01-分仓管理工具
|
||
|
||
> 迭代设计 v2 的程序结构细化 | 2026-08-27
|
||
|
||
## 一、整体目录结构
|
||
|
||
```
|
||
one_divine_lot/
|
||
├── package.json # 插件元数据 + dsh.client 声明(web 平台)
|
||
├── src/ # 源码(服务端 + 客户端)
|
||
│ ├── index.js # 插件入口:apply(ctx),组装各模块
|
||
│ ├── settings.js # 设置管理:ctx.settings.register(策略 CRUD + 开关)
|
||
│ ├── storage.js # 本地存储:分仓分配 JSON 读写
|
||
│ ├── api.js # 服务端 RPC API:connection.rpc.intercept('/api') 端点
|
||
│ ├── data-source/
|
||
│ │ ├── types.js # 统一数据模型(Position / AssetSummary / DataSource 接口)
|
||
│ │ └── qmt-bridge-rest.js # QMT Bridge REST 适配器(fetch 直连)
|
||
│ ├── position/
|
||
│ │ └── manager.js # 分仓逻辑:持仓↔策略份额分配、聚合视图
|
||
│ └── client/
|
||
│ ├── index.js # 客户端插件入口:注册 conversation.view entries
|
||
│ └── views/
|
||
│ ├── StrategyTab.jsx # 策略标签 tab 组件(渲染该策略持仓)
|
||
│ └── PositionList.jsx # 持仓列表子组件
|
||
├── lib/ # 构建输出(与 src 结构对应)
|
||
│ ├── index.js
|
||
│ ├── settings.js
|
||
│ ├── storage.js
|
||
│ ├── api.js
|
||
│ ├── data-source/
|
||
│ ├── position/
|
||
│ └── client/ # 客户端 bundle(浏览器端加载)
|
||
└── docs/ # 神之一手框架文档(既有)
|
||
```
|
||
|
||
## 二、模块职责与依赖
|
||
|
||
### 1. index.js(插件入口)
|
||
```js
|
||
const name = 'one-divine-lot';
|
||
const inject = ['tools', 'settings', 'webServer']; // 依赖 DSH 服务
|
||
async function apply(ctx, config) {
|
||
const settings = registerSettings(ctx); // 设置管理
|
||
const storage = new Storage(config); // 本地存储
|
||
const dataSource = new QmtBridgeRestDataSource(config); // REST 数据源
|
||
const manager = new PositionManager({ dataSource, storage }); // 分仓逻辑
|
||
registerApi(ctx, manager); // 服务端 API
|
||
registerClient(ctx); // 客户端插件(dsh.client)
|
||
ctx.effect(() => () => { /* 清理 */ });
|
||
}
|
||
export { name, inject, apply };
|
||
```
|
||
|
||
### 2. settings.js(设置管理)
|
||
- `ctx.settings.register('one-divine-lot', schema)`
|
||
- schema:strategies: array[{ id, name, visible, order }]
|
||
- 预置:网格超市、手动做T
|
||
- 提供 getStrategies() / watchStrategies()(客户端 API 读取)
|
||
|
||
### 3. storage.js(本地存储)
|
||
- 路径:`~/.dsh/one-divine-lot/allocations.json`
|
||
- 结构:`{ [positionCode]: { [strategyId]: shares } }`
|
||
- API:load() / save() / get(code) / set(code, strategyShares)
|
||
|
||
### 4. data-source/qmt-bridge-rest.js(REST 数据源)
|
||
- baseUrl:http://192.168.3.43:8610(config 可配)
|
||
- getPositions() → GET /trade/positions → Position[]
|
||
- getAsset() → GET /trade/asset → AssetSummary
|
||
- 字段映射:语义字段优先,m_ 回退(技术约束-001/003)
|
||
|
||
### 5. position/manager.js(分仓逻辑)
|
||
|
||
> 2026-08-27 S5 讨论定稿:
|
||
> - Q1 按股数分配;Q2 允许未分配余额(sum ≤ 总持仓);Q3 策略仅为人为分组(无策略逻辑);
|
||
> - Q4 不做统计(无市值/盈亏/占比聚合);Q5 UI = 策略 tab 内编辑(添加默认0起填 + 指定数量移出)。
|
||
|
||
- getAllPositions():全量持仓(QMT,真实数据)
|
||
- getPosition(code):单只持仓
|
||
- getStrategyPositions(strategyId):某策略下的持仓(读分配份额,过滤出份额>0的标的)
|
||
- getUnallocated():未分配持仓(总持仓 - 各策略份额之和 > 0 的标的)
|
||
- addToStrategy(code, strategyId, shares):添加到策略(**份额从0起填**,更新该策略份额,并刷新已分配)
|
||
- removeFromStrategy(code, strategyId, shares):从策略移出(**指定数量**,份额减少,回到未分配)
|
||
- getSummary():返回 { 全部持仓, 各策略份额, 已分配合计, 未分配 }(仅份额,无统计)
|
||
- 校验:单策略份额 ≤ 总持仓;已分配合计 ≤ 总持仓(允许未分配余量)
|
||
|
||
### 6. api.js(服务端 RPC API —— DSH 原生通道)
|
||
|
||
> **修正(2026-08-27)**:使用 DSH 原生 RPC(connection.rpc.intercept('/api')),不自开 HTTP 路由。
|
||
|
||
```js
|
||
ctx.inject(['connection'], (connectionCtx) => {
|
||
connectionCtx.connection.rpc.intercept('/api', matcher, handler, { authority: 'trusted-host' })
|
||
})
|
||
```
|
||
|
||
- 端点(endpoint 命名,channel 相对路径):
|
||
- `one-divine-lot/strategies` → 策略列表(含 visible)
|
||
- `one-divine-lot/positions` → 全量持仓(QMT)
|
||
- `one-divine-lot/allocations` → 分仓分配
|
||
- `one-divine-lot/summary` → 分仓聚合视图
|
||
- `one-divine-lot/strategy/:id/positions` → 某策略持仓
|
||
- 职责:接收 DSH RPC 请求 → 调 manager/data-source(统一接口)→ 返回数据
|
||
- **不是请求本身**:真正发 HTTP 到 QMT 的是 data-source/qmt-bridge-rest.js
|
||
|
||
### 7. client/index.js(客户端插件)
|
||
- package.json dsh.client 声明(web 平台)→ DSH clientModules 自动加载
|
||
- **tab 结构(2026-08-27 Q5 确认)**:
|
||
- 全部持仓:显示全量真实持仓,已分配部分标注(网格 X / 做T Y / 未分配 Z)
|
||
- 网格策略持仓:初始为空,可从全部持仓添加
|
||
- 做T持仓:初始为空,可从全部持仓添加
|
||
- 注册 conversation.view entries(在对话/轨迹/上下文之后):
|
||
```js
|
||
ctx.slots.register('conversation.view', {
|
||
id: 'all-positions',
|
||
label: '全部持仓',
|
||
component: AllPositionsTab,
|
||
})
|
||
ctx.slots.register('conversation.view', {
|
||
id: 'strategy-grid',
|
||
label: '网格策略持仓',
|
||
component: StrategyTab,
|
||
})
|
||
ctx.slots.register('conversation.view', {
|
||
id: 'strategy-manual-t',
|
||
label: '做T持仓',
|
||
component: StrategyTab,
|
||
})
|
||
```
|
||
- 策略持仓 tab 内编辑:添加(默认0起填)+ 移出(指定数量)
|
||
|
||
### 8. client/views/StrategyTab.jsx
|
||
- 挂载后从服务端 API 拉取该策略持仓
|
||
- 渲染:策略下持仓列表(代码/名称/份额/市值/盈亏)
|
||
- 展示未分配持仓区域
|
||
- 数据刷新:切换 tab 时拉取(本期)
|
||
|
||
## 三、职责边界(api.js vs qmt-bridge-rest.js)
|
||
|
||
> 2026-08-27 老师提问澄清,明确两层职责:
|
||
|
||
| 层 | 文件 | 职责 | 交互对象 |
|
||
|---|---|---|---|
|
||
| RPC 封装层 | api.js | 接收 DSH 请求,转发给业务层 | 浏览器客户端(DSH RPC 通道) |
|
||
| 业务逻辑层 | position/manager.js | 分仓逻辑(份额分配/聚合) | 上层(api.js) |
|
||
| 数据源适配层 | data-source/qmt-bridge-rest.js | **真正发 HTTP 请求到 QMT** + 字段映射 + 统一接口 | QMT Bridge REST |
|
||
|
||
**调用链**:api.js → manager.js → qmt-bridge-rest.js → fetch → QMT REST
|
||
**换数据源**:只替换 qmt-bridge-rest.js(统一接口不变,上层无感)—— 技术约束-001
|
||
|
||
## 四、数据流
|
||
|
||
```
|
||
用户点击「网格策略持仓」tab
|
||
→ 客户端 StrategyTab 挂载
|
||
→ call('/api', 'one-divine-lot/strategy/grid-supermarket/positions') // RPC 通道
|
||
→ api.js handler 调 manager.getStrategyPositions(id)
|
||
→ storage 读分配份额 + dataSource 读全量持仓
|
||
→ 过滤出该策略份额>0的持仓
|
||
→ RpcResult 返回 → 客户端渲染持仓列表
|
||
|
||
用户添加份额(在网格 tab 操作)
|
||
→ call('/api', 'one-divine-lot/allocations/add', { code, strategyId, shares })
|
||
→ manager.addToStrategy(code, strategyId, shares)
|
||
→ storage.set 更新份额(校验 ≤ 总持仓)
|
||
→ 返回更新后的分配 → 客户端刷新
|
||
```
|
||
|
||
## 五、关键接口签名(待实现时对照类型确认)
|
||
|
||
- `ctx.settings.register(ns, schema)` → SettingsScope(get/watch/update)
|
||
- `webServer.register({kind:'http'|'upgrade', path, handler})` → disposer
|
||
- `ctx.slots.register('conversation.view', {id, label, component})`(客户端,具体签名实现时读 slots.d.ts)
|
||
- `dsh.client` 声明:`{ "dsh": { "client": { "platform": "web", "inject": [...] } } }`
|
||
|
||
## 六、开发顺序(编码)
|
||
|
||
1. package.json + 骨架目录
|
||
2. data-source(REST 适配 + 类型)
|
||
3. settings(策略注册)
|
||
4. storage(分仓 JSON)
|
||
5. position/manager(分仓逻辑)
|
||
6. api(服务端路由)
|
||
7. client(tab 注册 + 组件)
|
||
8. 宿主挂载 + 联调验收
|
||
|
||
## 七、非目标(本期不做)
|
||
|
||
- 不做份额编辑 UI(二期)
|
||
- 不做策略参数的完整管理(网格区间等,后续迭代)
|
||
- 不做做T 记录
|
||
- 不做风控引擎 |