diff --git a/docs/superpowers/reports/2026-07-05-phase1-data-layer-completion.md b/docs/superpowers/reports/2026-07-05-phase1-data-layer-completion.md new file mode 100644 index 0000000..9f02215 --- /dev/null +++ b/docs/superpowers/reports/2026-07-05-phase1-data-layer-completion.md @@ -0,0 +1,77 @@ +# Phase 1 数据层 — 完成报告 + +**日期**:2026-07-05 +**状态**:✅ DONE(本地 16 passed + 端到端冒烟通过) +**分支**:已 merge to master(`20e2437`) + +## 1. 目标 + +把 v1(sanguo_vnpy)散落的数据资产(BaoStock parquet + vnpy SQLite db)收拢成统一的、可被 vnpy 原生调用的数据读取层;打通 NAS Docker 端到端链路。 + +**范围约束**:只做数据"读/写/调度/校验",不做回测/策略/Web(留 Phase 2+)。 + +## 2. 交付清单 + +### 代码模块(`sanguo_data/`) + +| 模块 | 职责 | 关键设计 | +|------|------|---------| +| `config.py` | 加载 `data_platform.yaml` | 统一配置入口 | +| `datareader.py` | 读日 K → `BarData` | `read_parquet_daily` + `read_db_daily`(配 vnpy SETTINGS)+ `guess_exchange`(代码前缀判 SSE/SZSE) | +| `datafeed.py` | 在线数据源抓取 | BaoStock + 东财/腾讯 fallback;`_fetch_baostock_with_timeout` 子进程隔离(修 v1 卡死坑) | +| `datafeed/` | 数据源实现子包 | 可扩展 | +| `datawriter.py` | 数据落盘 | parquet 年分区 + vnpy sqlite 原子写 | +| `database/` | vnpy 数据库适配子包 | sqlite 路径配置 | +| `scheduler.py` | `UpdateScheduler` | 增量更新 + 断点续传 + 熔断 | +| `validator.py` | 数据完整性校验 | 缺口/异常值检测 | + +### 测试(`tests/data/`,16 passed) + +`test_config` / `test_datareader` / `test_datafeed` / `test_datawriter` / `test_scheduler` / `test_validator` / `test_spike_vnpy44` + +## 3. 关键 Fix(收尾阶段) + +| Gap | commit | 说明 | +|-----|--------|------| +| read_db_daily 没配 vnpy database 路径 | `339d85a` | 默认空 database.db 读不到 NAS 真实数据 → 加 `SETTINGS["database.database"]` 配置 | +| spike 测试被全局 SETTINGS 污染 | `8829501` | read_db_daily 改全局 SETTINGS → spike 用 monkeypatch mock load_bar_data 隔离 | + +## 4. 部署产出(NAS Docker) + +- 容器 `sanguo_vnpy_v2` 重建加挂载 `/volume1/stock`(RW) +- 镜像 `sanguo_vnpy_v2:with-sqlite`(docker commit 保 vnpy_sqlite,避免 NAS 弱 CPU pip 卡死) +- 外网链路 `vnpy.mysanguo.top` 恢复正常(未改动原代理/转发设计) + +## 5. 端到端验证(真实数据) + +`scripts/smoke_e2e.py` 在临时容器内读 NAS `quant_trading.db`: + +``` +600000.SSE: 541 条 +000001.SZSE: 563 条 +300750.SZSE: 564 条 +区间: 2024-01-01 ~ 2026-06-30 +``` + +## 6. 风险与兜底 + +- **v1 db 被容器改**:当前 `quant_trading.db` 1.66→2.13GB(容器 RW 下 peewee create_tables 副作用)。 +- **兜底**:`/volume1/stock/sanguo_vnpy/data/` 含多个历史备份 `quant_trading_*.db.bak`(1.5G 各,20260519~20260522 + pre_interval_migration),原始可恢复。数据完整性已验证。 + +## 7. 已知 Tech Debt(留后续 phase) + +| 项 | 影响 | +|----|------| +| `_load_stock_list` NotImplementedError | 部署前 copy v1,后续需补实现 | +| datawriter 一致性回滚 | final triage 待定 | +| validator row-by-row 全量校验性能 | 增量场景 OK,海量回测时需优化 | +| peewee 版本冲突告警 | empyrical-reloaded 要 `peewee<3.17.4`,vnpy_sqlite 装 4.1.1 — **可能影响 Phase 2 因子层** | + +## 8. Phase 2 衔接点 + +Phase 1 提供的下游可用接口: +- `read_db_daily(symbol, start, end, cfg) → list[BarData]` — vnpy 原生 BarData,可直接喂 vnpy.alpha / ctabacktester +- `read_parquet_daily(...)` — parquet 快速读取路径 +- `UpdateScheduler` — 增量数据更新 + +Phase 2(因子/回测层)应基于这些接口构建,遵循 PRD ADR:vnpy.alpha 集成、因子层可插拔、multiprocessing+Ray 编排、不引 Qlib。 diff --git a/scripts/smoke_e2e.py b/scripts/smoke_e2e.py new file mode 100644 index 0000000..dc5f598 --- /dev/null +++ b/scripts/smoke_e2e.py @@ -0,0 +1,41 @@ +"""端到端冒烟:验证 read_db_daily 能读 NAS quant_trading.db(真实数据)。 + +在临时容器内跑(docker run --rm,挂载 /volume1/stock + /app)。 +探测 vnpy 4.4.0 database 配置 + 验证数据读取。 +""" +import sys +sys.path.insert(0, "/app") + +print("=== Step 1: 探测 vnpy 4.4.0 database 配置 ===") +from vnpy.trader.setting import SETTINGS +db_keys = {k: v for k, v in SETTINGS.items() if "database" in k.lower() or "sql" in k.lower()} +print("database 相关 SETTINGS:", db_keys) + +SETTINGS["database.driver"] = "sqlite" +SETTINGS["database.database"] = "/volume1/stock/sanguo_vnpy/data/quant_trading.db" +print(f"已设置: driver=sqlite, database={SETTINGS['database.database']}") + +print("\n=== Step 2: 加载 sanguo_data 配置 ===") +from sanguo_data.config import load_config +cfg = load_config("config/data_platform.yaml") +print(f"cfg vnpy_db: {cfg.data_paths.get('vnpy_db')}") + +print("\n=== Step 3: read_db_daily 读 600000 ===") +try: + from sanguo_data.datareader import read_db_daily + bars = read_db_daily("600000", "2026-01-01", "2026-06-30", cfg) + print(f"RESULT: 读取 {len(bars)} 条 BarData") + if bars: + b = bars[0] + print(f"FIRST: symbol={b.symbol} dt={b.datetime} close={b.close_price} vol={b.volume}") + else: + print("WARN: 0 条——可能 vnpy database 路径配置不对,或数据不在该 symbol/区间") +except Exception as e: + import traceback + traceback.print_exc() + print(f"FAIL: {type(e).__name__}: {e}") + +print("\n=== Step 4: 直接探测 NAS db 文件 ===") +import os +db_path = "/volume1/stock/sanguo_vnpy/data/quant_trading.db" +print(f"{db_path} exists: {os.path.exists(db_path)}, size: {os.path.getsize(db_path) if os.path.exists(db_path) else 'N/A'}")