# 数据存储设计(JSON Data Store Schema) > 策略数据集 + 行情缓存的 JSON data store 设计(R-006 及其扩展,2026-08-31 / 2026-09-01 定) > 本文件是数据存储领域的**设计约束文档**,后续数据存储相关迭代以此为依据。 ## 1. 设计目标 1. **数据快速展现**:DataStore 的核心目标是「数据快速展现」——份额分配、行情价格持久化,重启/刷新后首屏即有数据,不依赖每次实盘查询。 2. **标准化描述**:为每个策略定义标准化的字段描述(schema),在描述之上挂数据集。 3. **统一 JSON 操作**:数据量很少(无需数据库/Circe),用 JSON 文件 + 类 JSON Schema 统一读写与校验。 ## 2. 总体架构 ``` ~/.dsh/one-divine-lot/ ├── store.schema.json # 数据集 schema(类 JSON Schema,随代码发布,自动生成) ├── store.json # 份额分配数据(策略为中心,原子写) └── store.market.json # 行情快照缓存(code → 最新行情,原子写) ``` **分层**: - **策略定义**(id/name/visible/order):存 DSH settings(~/.dsh/settings.yaml),设置页交互不变 - **份额分配**(策略 → 股票 → 股数):存 store.json(策略为中心) - **行情快照**(code → lastPrice 等):存 store.market.json(持久化缓存) ## 3. 数据文件结构与 Schema ### 3.1 store.schema.json(数据集 schema) ```json { "version": 1, "kind": "one-divine-lot-data-store", "strategies": { "description": "策略数据集:每个策略一个 dataset(份额分配)", "type": "array", "items": { "strategyId": { "type": "string", "required": true, "description": "策略 id(与 settings 中的策略一致)" }, "dataset": { "type": "array", "description": "该策略的持仓数据集(份额分配)", "items": { "code": { "type": "string", "required": true, "description": "证券代码(含后缀,如 600719.SH)" }, "shares": { "type": "number", "required": true, "description": "份额(股数,>0)" } } } } } } ``` ### 3.2 store.json(份额分配,策略为中心) ```json { "version": 1, "strategies": [ { "strategyId": "grid-supermarket", "dataset": [ { "code": "600719.SH", "shares": 800 }, { "code": "300057.SZ", "shares": 1000 } ] }, { "strategyId": "manual-t", "dataset": [ { "code": "600719.SH", "shares": 1000 } ] } ] } ``` **数据视角**:按策略查持仓(策略为中心),比旧 code 为中心(allocations.json)更贴合业务。 ### 3.3 store.market.json(行情快照缓存) ```json { "version": 1, "savedAt": 1788193491108, "quotes": { "600719.SH": { "time": 1788159604000, "timetag": "20260831 15:00:04", "lastPrice": 7.16, "open": 7.19, "high": 7.24, "low": 7.02, "lastClose": 7.22, "volume": 63523, "amount": 45341300 } } } ``` **用途**:宿主重启后首屏快速展示上次价格(不依赖实盘订阅);实盘推送/轮询做增量更新并定期写回。 ## 4. 数据流 ``` 启动时: DataStore.load() → 读 store.json(份额分配) DataStore.loadMarket() → 读 store.market.json(行情缓存,首屏有价) MarketFeed._primePositions() → 主动拉持仓盘口 → 写缓存(服务端启动即预热最新价) 实盘中: MarketFeed WS 推送 / REST 定时刷新 → MarketDataHub.ingest() → 内存缓存 MarketDataHub 防抖 3s → DataStore.setMarketQuotes() → store.market.json 查询时(market-snapshot): MarketDataHub.getByCodes() → 内存 → 磁盘 → REST 补拉(三级命中) ``` ## 5. 关键设计决策 | 决策 | 结论 | 理由 | |---|---|---| | 存储介质 | JSON 文件(不引入数据库) | 数据量很少,JSON + schema 足够 | | 策略定义位置 | DSH settings(不迁 store) | 设置页交互不变,查询简单 | | 份额分配结构 | 策略为中心(strategies[].dataset) | 贴合业务视角,schema 统一描述 | | dataset 格式 | [{code, shares}] 数组 | 可扩展字段(未来加成本价/备注) | | 行情缓存 | 持久化(store.market.json) | DataStore 为数据快速展现存在,重启不丢价 | | 写入 | 原子写(临时文件 + rename) | 防损坏,多次写安全 | | 测试隔离 | ODL_TEST_DATA_DIR 环境变量或 dataDir 参数 | 防误删真实数据(技术约束-011) | ## 6. 迁移 - 旧 allocations.json(code 为中心)→ 新 store.json(策略为中心):**启动时自动迁移**,旧文件备份为 allocations.json.bak - 迁移逻辑在 DataStore._migrateFromLegacy():code 为中心 → 策略为中心聚合 ## 7. 对应实现 | 模块 | 职责 | 文件 | |---|---|---| | DataStore | 份额分配 + 行情持久化读写、迁移、schema | src/component/DataStore.js | | PositionManager | 份额业务逻辑(依赖 DataStore) | src/component/PositionManager.js | | MarketDataHub | 行情缓存(内存 + 磁盘持久化 + 查询) | src/component/MarketDataHub.js | | MarketFeed | 行情获取(WS + REST 定时刷新 + 启动 prime) | src/component/MarketFeed.js | ## 8. 约束条目(引用) - 技术约束-011:测试/回归脚本禁止在真实数据上执行写操作(独立数据目录) - 本文件为新领域设计约束,后续数据存储变更以本文件为最终依据