docs(data): 补齐数据脚本文档(v1调研5文档复制+11脚本清单表)
This commit is contained in:
@@ -0,0 +1,311 @@
|
||||
# 需求规格文档:本地数据源体系建设
|
||||
|
||||
**任务ID**: data-platform-20260502
|
||||
**节点**: pangtong_requirements
|
||||
**作者**: 庞统(副军师)
|
||||
**日期**: 2026-05-02
|
||||
|
||||
---
|
||||
|
||||
## 一、项目背景与核心问题
|
||||
|
||||
### 1.1 现状
|
||||
|
||||
| 资产 | 状态 | 位置 |
|
||||
|------|------|------|
|
||||
| NAS日线Parquet | ✅ 2010-2026年全市场,按年分目录 | `/Volumes/stock/A股数据/日线数据/daily/{year}/sh{code}_daily.parquet` |
|
||||
| NAS分钟线Parquet | ⚠️ 仅84只15分钟线 | `/Volumes/stock/minute_kline/15min/sz{code}_15min.parquet` |
|
||||
| vnpy quant_trading.db | ❌ **空库(8KB,0张表)** | `/Volumes/stock/sanguo_vnpy/data/quant_trading.db` |
|
||||
| 回测服务 | ✅ 运行中(http://192.168.2.154:8088) | Docker容器 |
|
||||
| 本地数据适配器 | ⚠️ 已有但路径硬编码Mac本地 | `vnpy_local_data_adapter.py`(指向`/Users/chufeng/nas/stock/...`) |
|
||||
|
||||
### 1.2 核心问题
|
||||
|
||||
**vnpy回测服务的数据库是空的**,回测引擎 `engine.load_data()` 从数据库读取数据 → 无数据 → 所有回测任务必然失败。
|
||||
|
||||
回测服务executor.py关键代码(L171-175):
|
||||
```python
|
||||
engine.load_data() # 从vnpy SQLite数据库加载
|
||||
```
|
||||
如果没有数据,直接抛出 `ValueError("无法加载历史数据")`。
|
||||
|
||||
### 1.3 目标
|
||||
|
||||
打通 **NAS Parquet → vnpy SQLite DB → 回测引擎** 的数据通路,让回测服务可以正常执行回测任务。
|
||||
|
||||
---
|
||||
|
||||
## 二、功能需求
|
||||
|
||||
### P1:打通vnpy数据通路
|
||||
|
||||
#### P1-1:确认Docker volume映射路径
|
||||
|
||||
| 项 | 说明 |
|
||||
|-----|------|
|
||||
| 需求 | 确认Mac写入的文件,Docker容器内能读到 |
|
||||
| 输入 | NAS目录结构、Docker容器配置 |
|
||||
| 输出 | 明确的映射关系文档:Mac路径 ↔ 容器内路径 |
|
||||
| 验证 | 在Mac写入测试文件,容器内能读到;反之亦然 |
|
||||
|
||||
**关键证据**:
|
||||
- 回测服务配置 `base_dir = "/app/backtest_jobs"`
|
||||
- 数据目录 `data_dir = settings.base_dir.replace("backtest_jobs", "data")` → `/app/data`
|
||||
- quant_trading.db 位于 `/Volumes/stock/sanguo_vnpy/data/`
|
||||
- 需确认Docker容器启动时是否挂载了 `/Volumes/stock/sanguo_vnpy/data` → `/app/data`
|
||||
|
||||
#### P1-2:编写vnpy DB导入脚本
|
||||
|
||||
| 项 | 说明 |
|
||||
|-----|------|
|
||||
| 需求 | 将NAS日线Parquet数据批量导入vnpy SQLite数据库 |
|
||||
| 输入 | `/Volumes/stock/A股数据/日线数据/daily/{year}/sh{code}_daily.parquet` |
|
||||
| 输出 | quant_trading.db 中有完整的日线bar数据 |
|
||||
| 验证 | 回测引擎 `load_data()` 能读出数据 |
|
||||
| 约束 | 幂等操作(INSERT OR REPLACE),可重复执行 |
|
||||
|
||||
**vnpy DB Schema要求**(待姜维确认):
|
||||
- vnpy 4.x的BacktestingEngine通过 `MainEngine` + `BaseDataManager` 加载数据
|
||||
- 数据表名和字段名由vnpy内部定义
|
||||
- 必须先搞清楚vnpy 4.x期望的数据库结构,再写导入脚本
|
||||
|
||||
**Parquet字段**:
|
||||
```
|
||||
date, open, high, low, close, volume, amount, outstanding_share, turnover, year
|
||||
```
|
||||
|
||||
**导入脚本功能要求**:
|
||||
1. 扫描 `/Volumes/stock/A股数据/日线数据/daily/` 下所有年份目录
|
||||
2. 每个Parquet文件解析股票代码(从文件名提取,如 `sh600000` → `600000.SSE`)
|
||||
3. 转换为vnpy DB格式并批量写入
|
||||
4. 支持增量导入(只导入新增数据)
|
||||
5. 支持断点续传(中断后可继续)
|
||||
6. 记录导入日志(成功/失败数、耗时)
|
||||
|
||||
#### P1-3:全量导入日线
|
||||
|
||||
| 项 | 说明 |
|
||||
|-----|------|
|
||||
| 需求 | 运行导入脚本,将全市场2010-2026年日线数据全部导入 |
|
||||
| 输入 | P1-2的导入脚本 + NAS日线Parquet |
|
||||
| 输出 | quant_trading.db 填满日线数据 |
|
||||
| 验证 | 统计导入记录数,抽查几只股票确认数据完整 |
|
||||
| 风险 | 导入耗时长(预估2-4小时),需支持断点续传 |
|
||||
|
||||
#### P1-4:验证回测服务可用
|
||||
|
||||
| 项 | 说明 |
|
||||
|-----|------|
|
||||
| 需求 | 提交一个简单回测任务,确认回测引擎能加载数据并完成回测 |
|
||||
| 输入 | 回测服务API + 简单策略代码 |
|
||||
| 输出 | 回测成功返回统计结果 |
|
||||
| 验证 | total_trades > 0 或 total_days > 0 |
|
||||
|
||||
---
|
||||
|
||||
### P2:数据基础设施
|
||||
|
||||
#### P2-1:多源降级管理器 `fallback.py`
|
||||
|
||||
| 项 | 说明 |
|
||||
|-----|------|
|
||||
| 需求 | 统一数据获取入口,支持多数据源顺序降级 |
|
||||
| 降级链(日线) | akshare `stock_zh_a_hist` → 腾讯K线API |
|
||||
| 降级链(实时) | 新浪实时 → 东方财富 → 腾讯 |
|
||||
| 接口 | `get_daily(symbol, start, end)` / `get_realtime(symbol)` |
|
||||
| 行为 | 第一个源失败自动切下一个,记录使用的源 |
|
||||
| 产出 | ~150行 |
|
||||
|
||||
#### P2-2:数据校验层 `validator.py`
|
||||
|
||||
| 项 | 说明 |
|
||||
|-----|------|
|
||||
| 需求 | 入库前校验数据质量,fatal级拒绝入库 |
|
||||
| V1规则(7条fatal) | D1: close/open/high/low > 0;D2: OHLC一致性(high≥max(open,close), low≤min(open,close));D3: volume ≥ 0;D6: 同股同日不重复;D7: date ≤ 今天;R1: 实时价格 > 0;R7: 必须携带source+fetched_at |
|
||||
| 接口 | `validate(df) → (passed: bool, errors: List[str])` |
|
||||
| 产出 | ~150行 |
|
||||
|
||||
#### P2-3:实时行情三源降级 `realtime.py`
|
||||
|
||||
| 项 | 说明 |
|
||||
|-----|------|
|
||||
| 需求 | 获取实时行情,支持3个源降级 |
|
||||
| 降级链 | 新浪实时 → 东方财富 → 腾讯 |
|
||||
| 接口 | `get_realtime_quote(symbol) → dict` |
|
||||
| 产出 | ~200行 |
|
||||
|
||||
#### P2-4:增量更新 `updater.py`
|
||||
|
||||
| 项 | 说明 |
|
||||
|-----|------|
|
||||
| 需求 | 每日增量更新,Parquet+vnpy DB双写 |
|
||||
| 流程 | 1.获取最新日期 2.拉取增量数据 3.校验 4.写Parquet(原子:临时文件+rename) 5.写vnpy DB(INSERT OR REPLACE幂等) 6.一致性校验 |
|
||||
| 约束 | Parquet是真相源;vnpy DB失败不影响Parquet |
|
||||
| 接口 | `update_daily() → UpdateResult` |
|
||||
| 产出 | ~150行 |
|
||||
|
||||
#### P2-5:cron定时任务
|
||||
|
||||
| 项 | 说明 |
|
||||
|-----|------|
|
||||
| 需求 | 每交易日15:30自动执行增量更新 |
|
||||
| 配置 | Mac crontab(Mac已确认永不休眠) |
|
||||
| 验证 | 下一个交易日检查是否自动执行 |
|
||||
|
||||
---
|
||||
|
||||
### P3:分钟线数据
|
||||
|
||||
#### P3-1:P0限频验证
|
||||
|
||||
| 项 | 说明 |
|
||||
|-----|------|
|
||||
| 需求 | 验证腾讯API限频阈值 |
|
||||
| 测试1 | 100只股票15分钟线连续下载,是否成功 |
|
||||
| 测试2 | 连续1小时请求,记录每分钟成功次数、封禁恢复时间 |
|
||||
| 输出 | 限频验证报告(每分钟最大请求数、封禁时长、恢复策略) |
|
||||
| 决策 | 报告决定P3-2/P3-3的实现策略(分批间隔、每批数量) |
|
||||
|
||||
#### P3-2/P3-3:分钟线全量下载
|
||||
|
||||
| 项 | 说明 |
|
||||
|-----|------|
|
||||
| 需求 | 下载HS300/全市场15分钟线 |
|
||||
| 前置 | P3-1限频验证通过 |
|
||||
| 数据源 | 腾讯mkline API(唯一可用源,akshare分钟线已失效) |
|
||||
| 存储路径 | `/Volumes/stock/minute_kline/15min/` |
|
||||
| 约束 | 15分钟线优先,1分钟线暂缓 |
|
||||
|
||||
#### P3-4:分钟线导入vnpy DB
|
||||
|
||||
| 项 | 说明 |
|
||||
|-----|------|
|
||||
| 需求 | 将分钟线Parquet导入vnpy DB |
|
||||
| 前置 | P1-2已确认vnpy DB Schema + 分钟线Parquet已下载 |
|
||||
| 不确定项 | vnpy 4.x如何区分不同周期(15min vs 1min)的分钟线 |
|
||||
|
||||
---
|
||||
|
||||
### P4:配套skill与自动化
|
||||
|
||||
#### P4-1/P4-2:更新skill文档
|
||||
|
||||
更新 `data-acquisition` 和 `quant-backtest` SKILL.md,补充vnpy数据通路说明。
|
||||
|
||||
#### P4-3:全量校验脚本
|
||||
|
||||
关羽设计的V2规则(14条),用于定期全量扫描。
|
||||
|
||||
#### P4-4:周维护cron
|
||||
|
||||
每周校验Parquet与vnpy DB一致性。
|
||||
|
||||
---
|
||||
|
||||
## 三、交付物清单
|
||||
|
||||
### 代码文件(放到 `~/.openclaw/sanguo_projects/sanguo_vnpy/data_platform/`)
|
||||
|
||||
| 文件 | 功能 | 阶段 | 预估行数 |
|
||||
|------|------|------|---------|
|
||||
| `import_vnpy.py` | Parquet → vnpy DB 导入 | P1 | ~200 |
|
||||
| `fallback.py` | 多源降级管理器 | P2 | ~150 |
|
||||
| `validator.py` | 数据校验(V1 7条fatal) | P2 | ~150 |
|
||||
| `realtime.py` | 实时行情三源降级 | P2 | ~200 |
|
||||
| `updater.py` | 增量更新(双写) | P2 | ~150 |
|
||||
| `validate_full.py` | 全量校验(V2 14条) | P4 | ~100 |
|
||||
|
||||
### 文档文件
|
||||
|
||||
| 文件 | 内容 | 位置 |
|
||||
|------|------|------|
|
||||
| 需求规格文档 | 本文档 | `docs/data-platform/01-requirements.md` |
|
||||
| 设计方案文档 | 接口设计、数据流、Schema映射 | `docs/data-platform/02-design.md` |
|
||||
| 验证报告 | 限频验证、导入验证、回测验证 | `docs/data-platform/reports/` |
|
||||
|
||||
### 配置文件
|
||||
|
||||
| 文件 | 内容 |
|
||||
|------|------|
|
||||
| crontab配置 | 每日15:30增量更新 |
|
||||
| vnpy DB路径映射 | Mac ↔ Docker |
|
||||
|
||||
---
|
||||
|
||||
## 四、假设与不确定项
|
||||
|
||||
| # | 假设/不确定项 | 影响范围 | 验证人 | 验证时机 |
|
||||
|---|-------------|---------|--------|---------|
|
||||
| 1 | **Docker volume映射**:Mac写入NAS的文件Docker容器能读到 | P1全部 | 姜维 | P1开始前 |
|
||||
| 2 | **vnpy 4.x DB Schema**:回测引擎load_data()期望的表结构和字段 | P1-2, P3-4 | 姜维 | P1开始前 |
|
||||
| 3 | **vnpy分钟线周期区分**:vnpy如何存储/区分不同粒度分钟线 | P3-4 | 姜维 | P3开始前 |
|
||||
| 4 | **腾讯API限频**:连续请求的频率上限和封禁恢复时间 | P3全部 | 赵云 | P3开始前 |
|
||||
| 5 | **全量导入耗时**:5500只×17年数据的导入时间 | P1-3 | 张飞/赵云 | P1-3执行时 |
|
||||
| 6 | **SQLite并发**:cron写入+回测读取是否冲突 | P2-5 | 姜维 | P2-5配置时 |
|
||||
| 7 | NAS存储空间充足(1.5TB可用,只需28GB) | 全局 | 已确认 | - |
|
||||
| 8 | Mac永不休眠(cron可靠执行) | P2-5 | 已确认 | - |
|
||||
| 9 | 不引新依赖(只用akshare+urllib+已有库) | 全局 | 约束 | - |
|
||||
|
||||
**关键阻塞项**:#1和#2如果不明确,P1无法开始。**建议姜维先验证这两项。**
|
||||
|
||||
---
|
||||
|
||||
## 五、约束
|
||||
|
||||
1. 所有产出放到 `~/.openclaw/sanguo_projects/sanguo_vnpy/` 目录下
|
||||
2. 不引新依赖(只用akshare + urllib + 已有的库)
|
||||
3. 不改Docker/NAS配置,数据通过volume映射
|
||||
4. Parquet是唯一真相源,vnpy DB是可重建的派生缓存
|
||||
5. 双写顺序:先Parquet(原子写入)→ 再vnpy DB(幂等写入)
|
||||
6. 腾讯API是唯一可用的分钟线源
|
||||
7. 15分钟线优先,1分钟线暂缓
|
||||
8. 不确定项遇到阻塞时,用最大尝试轮数限制,不无限重试
|
||||
9. 每个阶段先输出需求和设计方案,经评审再编码
|
||||
|
||||
---
|
||||
|
||||
## 六、成功标准
|
||||
|
||||
| # | 标准 | 验证方法 |
|
||||
|---|------|---------|
|
||||
| 1 | vnpy DB有全市场日线数据 | `SELECT count(*) FROM ...` > 0 |
|
||||
| 2 | 回测服务能完成一次完整回测 | 提交回测任务返回成功 |
|
||||
| 3 | 增量更新可自动执行 | crontab触发后日志显示成功 |
|
||||
| 4 | 数据校验拦截bad data | 构造异常数据,校验返回fatal |
|
||||
| 5 | 多源降级正常工作 | 关掉主源,自动切到备用源 |
|
||||
| 6 | 分钟线P0验证有结论 | 限频报告有明确数字 |
|
||||
|
||||
---
|
||||
|
||||
## 七、数据流架构
|
||||
|
||||
```
|
||||
Layer 1: 远程数据源
|
||||
┌──────────────┐ ┌──────────────┐ ┌──────────────┐
|
||||
│ akshare │ │ 新浪实时 │ │ 腾讯API │
|
||||
│ (日线主源) │ │ (实时主源) │ │ (分钟线唯一源)│
|
||||
└──────┬───────┘ └──────┬───────┘ └──────┬───────┘
|
||||
│ │ │
|
||||
└────────┬────────┴────────┬────────┘
|
||||
│ fallback.py │
|
||||
│ 降级管理 │
|
||||
▼ │
|
||||
Layer 2: 校验层 │ │
|
||||
validator.py │
|
||||
(7条fatal规则) │
|
||||
│ │
|
||||
▼ ▼
|
||||
Layer 3: NAS持久层 (唯一真相源)
|
||||
/Volumes/stock/A股数据/日线数据/daily/{year}/{code}_daily.parquet
|
||||
/Volumes/stock/minute_kline/15min/{code}_15min.parquet
|
||||
│
|
||||
│ import_vnpy.py / updater.py
|
||||
▼
|
||||
Layer 4: vnpy SQLite DB (派生缓存)
|
||||
/Volumes/stock/sanguo_vnpy/data/quant_trading.db
|
||||
│
|
||||
│ engine.load_data()
|
||||
▼
|
||||
Layer 5: 回测引擎
|
||||
BacktestingEngine → 回测结果
|
||||
```
|
||||
@@ -0,0 +1,265 @@
|
||||
# P2 需求规格文档:数据基础设施建设
|
||||
|
||||
**任务ID**: data-platform-p2-20260502
|
||||
**节点**: pangtong_requirements
|
||||
**作者**: 庞统(副军师)
|
||||
**日期**: 2026-05-02
|
||||
|
||||
---
|
||||
|
||||
## 一、背景
|
||||
|
||||
### 1.1 P1已完成的基础
|
||||
|
||||
| 项 | 状态 | 详情 |
|
||||
|----|------|------|
|
||||
| vnpy DB日线数据 | ✅ | 5191只,1281万行,2010~2026-03-27 |
|
||||
| 回测服务可用 | ✅ | 端到端验证通过 |
|
||||
| 导入脚本 | ✅ | `import_vnpy_daily_fast.py`(126行,pandas向量化) |
|
||||
| DB路径 | ✅ | `/Volumes/stock/sanguo_vnpy/data/quant_trading.db`(1.4GB) |
|
||||
| 已有适配器 | ⚠️ | `vnpy_local_data_adapter.py`(路径硬编码Mac本地,仅日线) |
|
||||
|
||||
### 1.2 当前数据缺口
|
||||
|
||||
- NAS日线数据停在 **2026-03-27**,需补约 **25个交易日**(至2026-05-02)
|
||||
- 无增量更新机制(每次需手动全量导入)
|
||||
- 无数据校验(异常数据入库无拦截)
|
||||
- 无多源降级(akshare挂了无备用)
|
||||
- 无实时行情能力
|
||||
- 无自动定时任务
|
||||
|
||||
### 1.3 关键设计决策(P1已确认)
|
||||
|
||||
| 决策 | 结论 |
|
||||
|------|------|
|
||||
| Source of Truth | NAS Parquet是唯一真相源 |
|
||||
| vnpy DB定位 | 可重建的派生缓存 |
|
||||
| 双写顺序 | 先Parquet(原子写入:临时文件+rename)→ 再vnpy DB(INSERT OR REPLACE幂等) |
|
||||
| SMB写入策略 | SQLite写本地/tmp,完成后复制到NAS(避免SMB锁库) |
|
||||
|
||||
---
|
||||
|
||||
## 二、功能需求
|
||||
|
||||
### P2-1:多源降级管理器 `fallback.py`
|
||||
|
||||
| 项 | 说明 |
|
||||
|-----|------|
|
||||
| 需求 | 统一数据获取入口,支持多数据源顺序降级 |
|
||||
| 产出 | `~/.openclaw/sanguo_projects/sanguo_vnpy/data_platform/fallback.py` |
|
||||
|
||||
**日线降级链**:
|
||||
1. akshare `stock_zh_a_hist()` → 成功则返回
|
||||
2. 腾讯K线API → 成功则返回
|
||||
3. 全部失败 → 抛异常
|
||||
|
||||
**接口设计**:
|
||||
```python
|
||||
class FallbackManager:
|
||||
def get_daily(self, symbol: str, start_date: str, end_date: str) -> pd.DataFrame
|
||||
def get_realtime(self, symbol: str) -> dict
|
||||
def get_source_used(self) -> str # 返回实际使用的数据源名称
|
||||
```
|
||||
|
||||
**行为要求**:
|
||||
- 第一个源失败自动切下一个
|
||||
- 记录使用的源(写入返回数据的metadata)
|
||||
- 每个源的超时控制(单次请求10秒超时)
|
||||
- 日志记录降级事件(哪个源失败、切到哪个、耗时)
|
||||
|
||||
**预估行数**:~150行
|
||||
|
||||
### P2-2:数据校验层 `validator.py`
|
||||
|
||||
| 项 | 说明 |
|
||||
|-----|------|
|
||||
| 需求 | 入库前校验数据质量,fatal级拒绝入库 |
|
||||
| 产出 | `~/.openclaw/sanguo_projects/sanguo_vnpy/data_platform/validator.py` |
|
||||
|
||||
**V1规则(7条fatal)**:
|
||||
|
||||
| 规则ID | 检查逻辑 | 级别 |
|
||||
|--------|---------|------|
|
||||
| D1 | close/open/high/low > 0 | fatal |
|
||||
| D2 | high ≥ max(open,close),low ≤ min(open,close) | fatal |
|
||||
| D3 | volume >= 0 | fatal |
|
||||
| D6 | 同股同日不能两条记录 | fatal |
|
||||
| D7 | date <= 当前日期 | fatal |
|
||||
| R1 | 实时价格 current > 0, prev_close > 0 | fatal |
|
||||
| R7 | 必须携带 source + fetched_at 字段 | fatal |
|
||||
|
||||
**接口设计**:
|
||||
```python
|
||||
class DataValidator:
|
||||
def validate(self, df: pd.DataFrame, data_type: str = "daily") -> ValidationResult
|
||||
|
||||
class ValidationResult:
|
||||
passed: bool
|
||||
fatal_errors: List[str] # 阻断入库
|
||||
warnings: List[str] # 标记但不阻断
|
||||
checked_rows: int
|
||||
failed_rows: int
|
||||
```
|
||||
|
||||
**行为要求**:
|
||||
- fatal错误 → 拒绝整批入库,返回具体失败行号和原因
|
||||
- warning → 标记但允许入库(数据中附加warning字段)
|
||||
- 校验报告可序列化为JSON
|
||||
|
||||
**预估行数**:~150行
|
||||
|
||||
### P2-3:实时行情三源降级 `realtime.py`
|
||||
|
||||
| 项 | 说明 |
|
||||
|-----|------|
|
||||
| 需求 | 获取实时行情,支持3个源降级 |
|
||||
| 产出 | `~/.openclaw/sanguo_projects/sanguo_vnpy/data_platform/realtime.py` |
|
||||
|
||||
**降级链**:
|
||||
1. 新浪实时接口 → 成功则返回
|
||||
2. 东方财富接口 → 成功则返回
|
||||
3. 腾讯实时接口 → 成功则返回
|
||||
4. 全部失败 → 抛异常
|
||||
|
||||
**接口设计**:
|
||||
```python
|
||||
def get_realtime_quote(symbol: str) -> dict
|
||||
# 返回: {symbol, name, current, prev_close, open, high, low, volume, amount,
|
||||
# bid1_price, ask1_price, timestamp, source, fetched_at}
|
||||
```
|
||||
|
||||
**行为要求**:
|
||||
- 返回标准化的字段(不同数据源字段名不同,需统一映射)
|
||||
- 每个源10秒超时
|
||||
- 记录实际使用的数据源
|
||||
|
||||
**预估行数**:~200行
|
||||
|
||||
### P2-4:增量更新 `updater.py`
|
||||
|
||||
| 项 | 说明 |
|
||||
|-----|------|
|
||||
| 需求 | 每日增量更新,Parquet+vnpy DB双写 |
|
||||
| 产出 | `~/.openclaw/sanguo_projects/sanguo_vnpy/data_platform/updater.py` |
|
||||
| 当前缺口 | 数据停在2026-03-27,需补约25个交易日 |
|
||||
|
||||
**流程**:
|
||||
```
|
||||
1. 扫描NAS Parquet获取每只股票最后日期
|
||||
2. 对比今天,确定需要更新的日期范围
|
||||
3. 调用 fallback.py 获取增量数据
|
||||
4. 调用 validator.py 校验
|
||||
5. 写Parquet(原子写入:临时文件+rename)
|
||||
6. 写vnpy DB(INSERT OR REPLACE,复用P1的批量导入逻辑)
|
||||
7. 一致性校验(Parquet条数 vs DB条数)
|
||||
8. 输出更新报告
|
||||
```
|
||||
|
||||
**接口设计**:
|
||||
```python
|
||||
class DailyUpdater:
|
||||
def update_all(self) -> UpdateReport
|
||||
def update_symbol(self, symbol: str) -> SymbolUpdateResult
|
||||
|
||||
class UpdateReport:
|
||||
total_symbols: int
|
||||
updated: int
|
||||
skipped: int # 已是最新
|
||||
failed: int
|
||||
new_records: int
|
||||
parquet_size: str
|
||||
db_size: str
|
||||
consistency_ok: bool
|
||||
```
|
||||
|
||||
**关键约束**:
|
||||
- Parquet写入必须是原子的(临时文件+os.rename)
|
||||
- vnpy DB写入失败不影响Parquet
|
||||
- 复用 `import_vnpy_daily_fast.py` 的批量INSERT逻辑
|
||||
- SMB锁库:DB操作先在/tmp完成再复制
|
||||
|
||||
**首次执行**:需补2026-03-28~2026-05-02约25天数据
|
||||
|
||||
**预估行数**:~200行
|
||||
|
||||
### P2-5:cron定时任务
|
||||
|
||||
| 项 | 说明 |
|
||||
|-----|------|
|
||||
| 需求 | 每交易日15:30自动执行增量更新 |
|
||||
| 配置 | Mac crontab(Mac永不休眠已确认) |
|
||||
| 验证 | 下一个交易日检查是否自动执行 |
|
||||
|
||||
**crontab配置**:
|
||||
```
|
||||
30 15 * * 1-5 cd ~/.openclaw/sanguo_projects/sanguo_vnpy && python3 data_platform/updater.py >> data_platform/logs/update.log 2>&1
|
||||
```
|
||||
|
||||
**配套**:
|
||||
- 日志目录:`~/.openclaw/sanguo_projects/sanguo_vnpy/data_platform/logs/`
|
||||
- 失败通知:更新失败时写日志(后续可接入三国mail通知)
|
||||
|
||||
---
|
||||
|
||||
## 三、交付物清单
|
||||
|
||||
### 代码文件(`~/.openclaw/sanguo_projects/sanguo_vnpy/data_platform/`)
|
||||
|
||||
| 文件 | 功能 | 预估行数 |
|
||||
|------|------|---------|
|
||||
| `fallback.py` | 多源降级管理器 | ~150 |
|
||||
| `validator.py` | 数据校验(7条fatal) | ~150 |
|
||||
| `realtime.py` | 实时行情三源降级 | ~200 |
|
||||
| `updater.py` | 增量更新(双写) | ~200 |
|
||||
|
||||
### 配置文件
|
||||
|
||||
| 文件 | 内容 |
|
||||
|------|------|
|
||||
| crontab条目 | 每交易日15:30自动更新 |
|
||||
| logs目录 | 更新日志 |
|
||||
|
||||
### 文档
|
||||
|
||||
| 文件 | 内容 |
|
||||
|------|------|
|
||||
| 本需求文档 | `~/.openclaw/sanguo_projects/sanguo_vnpy/docs/data-platform/02-p2-requirements.md` |
|
||||
|
||||
---
|
||||
|
||||
## 四、假设与不确定项
|
||||
|
||||
| # | 不确定项 | 影响 | 验证方式 |
|
||||
|---|---------|------|---------|
|
||||
| 1 | akshare `stock_zh_a_hist()` 当前是否可用 | 降级链主源 | 赵云编码时测试 |
|
||||
| 2 | 腾讯K线API的请求格式(备用日线源) | 降级链备源 | 赵云编码时测试 |
|
||||
| 3 | 新浪/东财/腾讯实时接口的当前可用性 | 实时行情 | 赵云编码时测试 |
|
||||
| 4 | 增量更新数据量(25天×5191只)的耗时 | cron窗口 | 首次执行时实测 |
|
||||
| 5 | vnpy DB导入增量数据的SMB性能 | 更新耗时 | 首次执行时实测 |
|
||||
| 6 | crontab执行时NAS是否已挂载 | cron可用性 | 配置时验证 |
|
||||
|
||||
---
|
||||
|
||||
## 五、约束
|
||||
|
||||
1. 所有产出放到 `~/.openclaw/sanguo_projects/sanguo_vnpy/data_platform/`
|
||||
2. 不引新依赖(只用akshare + urllib + 已有库)
|
||||
3. Parquet是唯一真相源,vnpy DB是可重建的派生缓存
|
||||
4. 双写顺序:先Parquet(原子写入)→ 再vnpy DB(幂等写入)
|
||||
5. SMB锁库:DB操作先在/tmp完成再复制
|
||||
6. 遇阻塞用最大尝试轮数限制
|
||||
7. 先输出设计方案经评审再编码
|
||||
8. 复用P1已有代码(`import_vnpy_daily_fast.py`的批量INSERT逻辑)
|
||||
|
||||
---
|
||||
|
||||
## 六、成功标准
|
||||
|
||||
| # | 标准 | 验证方法 |
|
||||
|---|------|---------|
|
||||
| 1 | 降级管理器可用:关掉主源自动切备源 | 手动测试 |
|
||||
| 2 | 校验层拦截bad data:构造异常数据返回fatal | 单元测试 |
|
||||
| 3 | 实时行情可获取:输入股票代码返回实时报价 | 手动测试 |
|
||||
| 4 | 增量更新可执行:补齐25天数据 | 执行updater后检查数据日期 |
|
||||
| 5 | Parquet+vnpy DB一致性 | 比对条数 |
|
||||
| 6 | cron可触发 | 配置后下个交易日检查日志 |
|
||||
@@ -0,0 +1,169 @@
|
||||
# P3 需求规格文档:分钟线数据下载与导入
|
||||
|
||||
**任务ID**: data-platform-p3-20260502
|
||||
**节点**: pangtong_requirements
|
||||
**作者**: 庞统(副军师)
|
||||
**日期**: 2026-05-02
|
||||
|
||||
---
|
||||
|
||||
## 一、背景
|
||||
|
||||
### 1.1 已完成的前置工作
|
||||
|
||||
| 项 | 状态 | 证据 |
|
||||
|----|------|------|
|
||||
| P1 vnpy数据通路 | ✅ 完成 | 5191只日线,1281万行,回测验证通过 |
|
||||
| P0 腾讯限频验证 | ✅ 通过 | 100只100%成功,0.19秒/请求,无封禁 |
|
||||
| vnpy DB Schema | ✅ 已知 | DbBarData表,interval字段:d=日线,1m=1分钟 |
|
||||
| 已有分钟线数据 | ⚠️ 84只 | `/Volumes/stock/minute_kline/15min/sz{code}_15min.parquet` |
|
||||
|
||||
### 1.2 已有分钟线数据格式
|
||||
|
||||
**文件名**:`sz000001_15min.parquet`
|
||||
**字段**:day, open, high, low, close, volume, amount(7列)
|
||||
**日期范围**:2025-09-17 ~ 2026-03-27(约1970条/只)
|
||||
**字段类型**:day=object, open/high/low/close=float64, volume/amount=object
|
||||
|
||||
### 1.3 vnpy DB分钟线interval值
|
||||
|
||||
根据P1赵云确认:`1m` = 1分钟线。**15分钟线的interval值需在编码阶段确认**(可能是 `15m` 或其他值)。
|
||||
|
||||
### 1.4 腾讯mkline API
|
||||
|
||||
唯一可用的分钟线数据源(akshare `stock_zh_a_minute()` 已失效)。
|
||||
- 限频:100只连续请求无限制,全市场预估17分钟
|
||||
- 需确认API的请求格式、返回格式、单次返回的历史数据长度
|
||||
|
||||
---
|
||||
|
||||
## 二、功能需求
|
||||
|
||||
### P3-1:下载脚本 `download_minute.py`
|
||||
|
||||
| 项 | 说明 |
|
||||
|-----|------|
|
||||
| 需求 | 从腾讯mkline API下载15分钟线数据 |
|
||||
| 数据源 | 腾讯财经mkline API(唯一可用源) |
|
||||
| 存储格式 | Parquet,与已有84只保持一致(day,open,high,low,close,volume,amount) |
|
||||
| 存储路径 | `/Volumes/stock/minute_kline/15min/{code}_15min.parquet` |
|
||||
| 文件名格式 | `sz000001_15min.parquet` 或 `sh600000_15min.parquet` |
|
||||
|
||||
**功能要求**:
|
||||
1. 支持指定股票列表(HS300 / 全市场)
|
||||
2. 支持增量下载(已有数据只追加新部分)
|
||||
3. 断点续传(记录已下载到哪只)
|
||||
4. 限频保护(如遇封禁自动等待重试,最大重试次数限制)
|
||||
5. 下载日志(成功/失败/跳过/耗时)
|
||||
6. 对已有84只文件做增量更新而非覆盖
|
||||
|
||||
**输出**:
|
||||
- 下载报告(成功数、失败数、总耗时、总数据量)
|
||||
|
||||
### P3-2:HS300 15分钟线全量下载
|
||||
|
||||
| 项 | 说明 |
|
||||
|-----|------|
|
||||
| 需求 | 下载HS300成分股的15分钟线 |
|
||||
| 股票数 | ~300只 |
|
||||
| 预估耗时 | ~1分钟(基于P0验证:0.19秒/只) |
|
||||
| 预估存储 | ~1.2GB(300只 × ~4MB/只) |
|
||||
|
||||
### P3-3:全市场15分钟线下载
|
||||
|
||||
| 项 | 说明 |
|
||||
|-----|------|
|
||||
| 需求 | 下载全市场A股15分钟线 |
|
||||
| 股票数 | ~5500只 |
|
||||
| 预估耗时 | ~17分钟 |
|
||||
| 预估存储 | ~22GB |
|
||||
| 前置 | P3-2验证无问题 |
|
||||
|
||||
### P3-4:分钟线导入vnpy DB `import_vnpy_minute.py`
|
||||
|
||||
| 项 | 说明 |
|
||||
|-----|------|
|
||||
| 需求 | 将15分钟线Parquet导入vnpy SQLite DB |
|
||||
| 输入 | `/Volumes/stock/minute_kline/15min/{code}_15min.parquet` |
|
||||
| 输出 | quant_trading.db 新增分钟线数据(interval ≠ 'd') |
|
||||
| 约束 | 复用P1的导入逻辑(pandas向量化+批量INSERT OR REPLACE) |
|
||||
|
||||
**关键映射**:
|
||||
|
||||
| Parquet字段 | DB字段 | 转换规则 |
|
||||
|------------|--------|---------|
|
||||
| day | datetime | 直接使用(已是 "YYYY-MM-DD HH:MM:SS" 格式) |
|
||||
| open | open_price | 直接映射 |
|
||||
| high | high_price | 直接映射 |
|
||||
| low | low_price | 直接映射 |
|
||||
| close | close_price | 直接映射 |
|
||||
| volume | volume | float转换 |
|
||||
| amount | turnover | float转换 |
|
||||
| 文件名前缀 | symbol+exchange | sz→SZSE, sh→SSE |
|
||||
| 固定值 | interval | **待确认**(可能为 "15m") |
|
||||
| 固定值 | open_interest | 0.0 |
|
||||
|
||||
**SMB锁库问题**:同P1,先写 `/tmp/` 再复制到NAS。或在本地操作DB后整体替换。
|
||||
|
||||
---
|
||||
|
||||
## 三、交付物清单
|
||||
|
||||
### 代码文件(`~/.openclaw/sanguo_projects/sanguo_vnpy/data_platform/`)
|
||||
|
||||
| 文件 | 功能 | 预估行数 |
|
||||
|------|------|---------|
|
||||
| `download_minute.py` | 腾讯mkline下载+增量+断点续传 | ~200 |
|
||||
| `import_vnpy_minute.py` | Parquet→vnpy DB导入 | ~150(复用P1逻辑) |
|
||||
|
||||
### 数据文件
|
||||
|
||||
| 产物 | 位置 | 预估大小 |
|
||||
|------|------|---------|
|
||||
| HS300 15分钟线Parquet | `/Volumes/stock/minute_kline/15min/` | ~1.2GB |
|
||||
| 全市场15分钟线Parquet | `/Volumes/stock/minute_kline/15min/` | ~22GB |
|
||||
| vnpy DB(增量) | `/Volumes/stock/sanguo_vnpy/data/quant_trading.db` | 增加~2GB |
|
||||
|
||||
### 报告
|
||||
|
||||
| 文件 | 内容 |
|
||||
|------|------|
|
||||
| 下载报告 | 成功/失败/耗时统计 |
|
||||
| 导入报告 | 记录数/字段校验结果 |
|
||||
|
||||
---
|
||||
|
||||
## 四、假设与不确定项
|
||||
|
||||
| # | 不确定项 | 影响 | 验证方式 |
|
||||
|---|---------|------|---------|
|
||||
| 1 | 腾讯mkline API的具体请求/返回格式 | 下载脚本实现 | 赵云编码时实测 |
|
||||
| 2 | vnpy 15分钟线的interval值 | 导入脚本实现 | 查vnpy源码或实测 |
|
||||
| 3 | 腾讯API单次返回的历史数据长度(是否支持获取全量历史) | 全量下载策略 | P3-1实测 |
|
||||
| 4 | SMB写入大量小文件的性能 | 下载耗时 | 实测 |
|
||||
| 5 | DB导入分钟线后的总大小和对查询性能影响 | 回测性能 | P3-4后验证 |
|
||||
| 6 | 已有84只Parquet的字段格式与新下载是否一致 | 数据一致性 | 编码时对比 |
|
||||
|
||||
---
|
||||
|
||||
## 五、约束
|
||||
|
||||
1. 产出放到 `~/.openclaw/sanguo_projects/sanguo_vnpy/data_platform/`
|
||||
2. 不引新依赖
|
||||
3. 15分钟线优先,1分钟线暂缓
|
||||
4. 腾讯API是唯一数据源
|
||||
5. 遇阻塞用最大尝试轮数限制
|
||||
6. 先输出设计方案经评审再编码
|
||||
7. 与已有84只Parquet格式保持一致
|
||||
|
||||
---
|
||||
|
||||
## 六、成功标准
|
||||
|
||||
| # | 标准 | 验证方法 |
|
||||
|---|------|---------|
|
||||
| 1 | HS300 300只15分钟线下载完成 | 检查文件数和数据完整性 |
|
||||
| 2 | 全市场5500只下载完成 | 检查文件数和总大小 |
|
||||
| 3 | 分钟线成功导入vnpy DB | DB中有interval≠'d'的记录 |
|
||||
| 4 | 已有84只数据增量更新无覆盖 | 对比更新前后首条记录 |
|
||||
| 5 | 断点续传有效 | 中断后重启继续 |
|
||||
@@ -0,0 +1,776 @@
|
||||
# 数据平台每日增量更新 — 详细设计文档
|
||||
|
||||
**项目**: sanguo_vnpy 数据平台
|
||||
**作者**: 赵云(数据总管)
|
||||
**日期**: 2026-05-06
|
||||
**版本**: v2.0
|
||||
**状态**: 待评审(重大架构变更)
|
||||
|
||||
---
|
||||
|
||||
## 一、背景与目标
|
||||
|
||||
### 1.1 现状
|
||||
|
||||
经过 P1(日线导入)和 P3(15分钟线下载导入),数据平台已建成:
|
||||
|
||||
| 数据类型 | 存储 | 覆盖范围 | 数据量 |
|
||||
|---------|------|---------|--------|
|
||||
| 日线行情 | NAS Parquet (`/Volumes/stock/A股数据/日线数据/daily/{year}/`) | 2010~2026 全市场 | ~5000只/年 |
|
||||
| 15min分钟线 | NAS Parquet (`/Volumes/stock/minute_kline/15min/`) | 2025-09~2026-04 全市场 | 5193只 |
|
||||
| vnpy主库 | NAS SQLite (`/Volumes/stock/sanguo_vnpy/data/quant_trading.db`) | 同上 | 1.4GB, 1281万行 |
|
||||
| vnpy DB备份 | NAS (`.bak`) | 2026-05-02 | 330MB |
|
||||
|
||||
**问题**:数据是静态快照,没有自动更新机制。每次更新需手动执行脚本。
|
||||
|
||||
### 1.2 目标
|
||||
|
||||
1. **每日自动增量更新**:交易日收盘后自动更新日线+15min数据
|
||||
2. **多数据源整合**:保留所有数据源访问方式,取各源最优数据合并
|
||||
3. **数据最大化**:历史数据尽量完整,增量数据每日累积
|
||||
4. **部署集成**:最终整合到 sanguo_vnpy 项目统一部署(待实现)
|
||||
|
||||
---
|
||||
|
||||
## 二、数据源调研
|
||||
|
||||
### 2.1 已验证的数据源
|
||||
|
||||
| 源 | 接口 | 可用性 | 历史深度 | 限频 | 适用场景 |
|
||||
|---|---|---|---|---|---|
|
||||
| **新浪财经** | `quotes.sina.cn/.../getKLineData` | ✅ Mac可用 | 15min: 800条(~3个月), 日线: 800条(~3年), 60min: 800条(~10月) | 0.3s/请求无封禁 | 15min增量、日线增量 |
|
||||
| **腾讯财经** | `web.ifzq.gtimg.cn/.../fqkline` | ⚠️ 偶尔连接重置 | 日线: 按日期范围查询,可获取多年 | 无明显限制 | 日线增量(主源) |
|
||||
| **东方财富** | `push2his.eastmoney.com/.../kline` | ❌ Mac直连被拒 | 理论上可指定任意日期范围 | 未知 | 历史回补(需Windows环境) |
|
||||
| **akshare** | `stock_zh_a_hist_min_em` / `stock_zh_a_hist` | ⚠️ 走东方财富,受代理影响 | 理论完整 | 有代理污染问题 | 备用(需网络正常时) |
|
||||
| **腾讯 minute/query** | `web.ifzq.gtimg.cn/.../minute/query` | ⚠️ 仅当天1min数据 | 仅当天 | 未知 | 当天1min→聚合15min(备源) |
|
||||
|
||||
### 2.2 数据源限制详情
|
||||
|
||||
**新浪财经 K线API**:
|
||||
- URL: `https://quotes.sina.cn/cn/api/jsonp_v2.php/var%20=min15_{symbol}=/CN_MarketDataService.getKLineData?symbol={symbol}&scale={period}&ma=no&datalen={count}`
|
||||
- `datalen` 参数最大有效值: **800**(超过返回null)
|
||||
- `scale` 支持: 5, 15, 30, 60, 240(日线)
|
||||
- 字段: day, open, high, low, close, volume, amount
|
||||
- amount为真实成交额
|
||||
- 时间戳为end-of-bar格式
|
||||
- 返回JSONP,需正则提取JSON数组
|
||||
|
||||
**腾讯财经 fqkline API**:
|
||||
- URL: `https://web.ifzq.gtimg.cn/appstock/app/fqkline/get?param={symbol},{period},{start},,{days},`
|
||||
- 支持按日期范围查询
|
||||
- 返回格式: `[date, open, close, high, low, volume]` 或 7列含amount
|
||||
- amount有时为0(不完整)
|
||||
|
||||
**东方财富 K线API**:
|
||||
- URL: `http://push2his.eastmoney.com/api/qt/stock/kline/get?secid={market}.{code}&klt={period}&fqt=1&beg={start}&end={end}`
|
||||
- Mac环境直连被拒绝(Connection reset / 502)
|
||||
- 可能与IP/地区/UA有关
|
||||
- **Windows Node(192.168.2.33)待验证**:Node当前离线
|
||||
|
||||
### 2.3 多数据源策略
|
||||
|
||||
```
|
||||
数据源选择优先级(按数据质量排序):
|
||||
|
||||
日线增量更新:
|
||||
主源: 腾讯 fqkline(支持日期范围,amount有时为0)
|
||||
备源: 新浪 getKLineData scale=240(固定800条,amount真实)
|
||||
|
||||
15min增量更新:
|
||||
主源: 新浪 getKLineData scale=15(固定800条,amount真实,稳定可靠)
|
||||
备源: 腾讯 minute/query → 聚合15min(仅当天数据)
|
||||
|
||||
历史回补(15min更早的历史):
|
||||
首选: 东方财富(需Windows环境,可指定日期范围)
|
||||
备选: akshare stock_zh_a_hist_min_em(依赖东方财富,需网络正常)
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## 三、系统设计
|
||||
|
||||
### 3.1 整体架构
|
||||
|
||||
```
|
||||
┌──────────────────────────────────────────────────────┐
|
||||
│ 定时调度层 (OpenClaw Cron) │
|
||||
│ 每交易日 15:35 触发 daily_update_all.sh │
|
||||
└───────────────────┬──────────────────────────────────┘
|
||||
│
|
||||
▼
|
||||
┌──────────────────────────────────────────────────────┐
|
||||
│ daily_all_update.py (主脚本) │
|
||||
│ │
|
||||
│ ┌─────────────────┐ ┌──────────────────────┐ │
|
||||
│ │ 日线增量更新 │ │ 15min增量更新 │ │
|
||||
│ │ 腾讯fqkline(主) │ │ 新浪API(主) │ │
|
||||
│ │ 新浪(备) │ │ 腾讯聚合(备) │ │
|
||||
│ └────────┬────────┘ └──────────┬───────────┘ │
|
||||
│ │ │ │
|
||||
│ ▼ ▼ │
|
||||
│ ┌─────────────────────────────────────────────┐ │
|
||||
│ │ 数据校验层 │ │
|
||||
│ │ 价格>0 | OHLC一致性 | 去重 | 类型兼容 │ │
|
||||
│ └─────────────────────┬───────────────────────┘ │
|
||||
│ │ │
|
||||
│ ┌────────────┴────────────┐ │
|
||||
│ ▼ ▼ │
|
||||
│ ┌─────────────────┐ ┌──────────────────────┐ │
|
||||
│ │ Parquet写入 │ │ vnpy DB写入 │ │
|
||||
│ │ 原子写入(.tmp) │ │ 本地tmp→ATTACH导入 │ │
|
||||
│ │ 增量合并 │ │ NAS SQLite │ │
|
||||
│ └─────────────────┘ └──────────────────────┘ │
|
||||
└──────────────────────────────────────────────────────┘
|
||||
```
|
||||
|
||||
### 3.2 文件结构
|
||||
|
||||
```
|
||||
~/.openclaw/sanguo_projects/sanguo_vnpy/data_platform/
|
||||
├── daily_all_update.py # 主脚本:全市场增量更新(日线+15min)
|
||||
├── daily_update_all.sh # Shell wrapper,由cron调用
|
||||
├── download_minute.py # 15min全量/批量下载脚本(保留)
|
||||
├── import_vnpy_minute.py # 分钟线导入vnpy DB(保留)
|
||||
├── import_vnpy_daily_fast.py # 日线全量导入脚本(保留,首次用)
|
||||
├── updater.py # 旧版日线更新脚本(保留)
|
||||
├── daily_update.sh # 旧版wrapper(保留)
|
||||
├── fallback.py # 降级工具(保留)
|
||||
├── validator.py # 数据验证工具(保留)
|
||||
└── logs/ # 日志目录
|
||||
```
|
||||
|
||||
### 3.3 核心流程
|
||||
|
||||
#### 3.3.1 日线增量更新
|
||||
|
||||
```
|
||||
1. 扫描全市场股票列表(从 stock_basic_info CSV)
|
||||
2. 对每只股票:
|
||||
a. 获取Parquet中最后日期
|
||||
b. 计算需要补充的日期范围(last_date+1 ~ today)
|
||||
c. 如果已是最新,跳过
|
||||
d. 调用腾讯fqkline API获取增量数据
|
||||
e. 数据校验(价格>0, 类型一致)
|
||||
f. 增量合并到年度Parquet文件(原子写入)
|
||||
g. 收集vnpy DB写入数据
|
||||
3. 批量写入vnpy DB(本地tmp → ATTACH导入NAS DB)
|
||||
```
|
||||
|
||||
#### 3.3.2 15分钟线增量更新
|
||||
|
||||
```
|
||||
1. 扫描全市场股票列表
|
||||
2. 对每只股票:
|
||||
a. 调用新浪API获取最近800条15min数据
|
||||
b. 数据校验(价格>0, OHLC一致性)
|
||||
c. 与已有Parquet增量合并(drop_duplicates keep='last')
|
||||
d. 原子写入Parquet
|
||||
e. 计算新增行数,收集vnpy DB写入数据
|
||||
3. 批量写入vnpy DB
|
||||
```
|
||||
|
||||
#### 3.3.3 vnpy DB写入策略(解决SMB性能问题)
|
||||
|
||||
**问题**:NAS通过SMB挂载在Mac上,直接对1.4GB SQLite文件进行频繁读写:
|
||||
- 查询超时(>20秒无响应)
|
||||
- 写入可能触发SIGKILL(进程被系统终止)
|
||||
- SMB文件锁与SQLite锁冲突风险
|
||||
|
||||
**方案:本地临时DB → ATTACH导入**
|
||||
|
||||
```
|
||||
1. 在 /tmp/ 创建本地SQLite DB,写入增量数据
|
||||
2. ATTACH NAS DB
|
||||
3. INSERT OR REPLACE ... SELECT 从本地导入NAS DB
|
||||
4. 更新 dbbaroverview 表
|
||||
5. DETACH,删除本地临时文件
|
||||
```
|
||||
|
||||
**优点**:
|
||||
- 增量数据先在本地SSD写完,与NAS交互只有一次批量INSERT
|
||||
- 减少SMB文件操作次数
|
||||
- INSERT OR REPLACE保证幂等性
|
||||
|
||||
**风险与缓解**:
|
||||
| 风险 | 缓解措施 |
|
||||
|------|---------|
|
||||
| ATTACH时NAS DB被锁定 | timeout=120秒等待 |
|
||||
| 中途失败导致DB不一致 | WAL模式 + INSERT OR REPLACE幂等 |
|
||||
| overview表更新慢 | 只在全部数据导入后执行一次 |
|
||||
|
||||
### 3.4 数据校验规则
|
||||
|
||||
| 规则 | 说明 | 实现 |
|
||||
|------|------|------|
|
||||
| 价格>0 | close/open ≤ 0 的行丢弃 | `(df[["close","open"]] <= 0).any(axis=1)` |
|
||||
| OHLC一致性 | high < max(open,close) 或 low > min(open,close) 的行丢弃 | 逐行比较 |
|
||||
| 去重 | 相同day/date保留最新 | `drop_duplicates(subset=["day"], keep="last")` |
|
||||
| 类型兼容 | volume/amount保持object与已有Parquet一致 | `.astype(str)` |
|
||||
| NaN处理 | 价格NaN行丢弃,volume/amount NaN填0 | `fillna(0)` + `dropna` |
|
||||
| 日期格式 | 日线: YYYY-MM-DD(str), 15min: YYYY-MM-DD HH:MM:SS(str) | 统一astype(str)避免混合类型 |
|
||||
|
||||
### 3.5 断点续传
|
||||
|
||||
- 15min更新:进度文件 `/Volumes/stock/logs/daily_update/progress/15min_progress.json`
|
||||
- 记录已完成的股票代码列表
|
||||
- 中断后重启自动跳过已完成的
|
||||
- 日线更新:通过检查Parquet最后日期判断,天然幂等
|
||||
- 日志:`/Volumes/stock/logs/daily_update/update_{timestamp}.log`
|
||||
- 报告:`/Volumes/stock/logs/daily_update/report_{date}.json`
|
||||
|
||||
### 3.6 限频与容错
|
||||
|
||||
| 参数 | 值 | 说明 |
|
||||
|------|-----|------|
|
||||
| 请求间隔 | 0.3秒 | 避免触发源站限频 |
|
||||
| 单股重试 | 3次 | 失败后重试,间隔1秒 |
|
||||
| 连续失败暂停 | 10次连续失败后暂停60秒 | 防止批量封禁 |
|
||||
| 超时 | 15秒/请求 | 单次请求超时 |
|
||||
| DB写入超时 | 120秒 | SMB写入等待 |
|
||||
|
||||
---
|
||||
|
||||
## 四、vnpy DB Schema 参考
|
||||
|
||||
```sql
|
||||
-- 主数据表
|
||||
CREATE TABLE dbbardata (
|
||||
symbol VARCHAR(32),
|
||||
exchange VARCHAR(32),
|
||||
datetime VARCHAR(64),
|
||||
interval VARCHAR(8),
|
||||
volume FLOAT,
|
||||
turnover FLOAT,
|
||||
open_interest FLOAT,
|
||||
open_price FLOAT,
|
||||
high_price FLOAT,
|
||||
low_price FLOAT,
|
||||
close_price FLOAT,
|
||||
PRIMARY KEY (symbol, exchange, interval, datetime)
|
||||
);
|
||||
|
||||
-- 概览表
|
||||
CREATE TABLE dbbaroverview (
|
||||
symbol VARCHAR(32),
|
||||
exchange VARCHAR(32),
|
||||
interval VARCHAR(8),
|
||||
count INT,
|
||||
start VARCHAR(64),
|
||||
end VARCHAR(64),
|
||||
PRIMARY KEY (symbol, exchange, interval)
|
||||
);
|
||||
```
|
||||
|
||||
**interval值说明**:
|
||||
- `d` = 日线
|
||||
- `15m` = 15分钟线(v1.1修正:与司马懿确认,采用方案B)
|
||||
|
||||
**方案B实现**(2026-05-03 司马懿评审确认):
|
||||
1. vnpy Interval枚举加 `MINUTE_15 = "15m"`(monkey patch方式注入,不依赖vnpy版本)
|
||||
2. executor INTERVAL_MAP 改 `"15m" = Interval.MINUTE_15`
|
||||
3. DB迁移:`UPDATE dbbardata SET interval="15m" WHERE interval="1m" AND ...`
|
||||
4. DB中现有"1m"数据需要一次迁移(迁移前备份)
|
||||
|
||||
---
|
||||
|
||||
## 五、多数据源保留策略
|
||||
|
||||
### 5.1 当前实现
|
||||
|
||||
| 数据源 | 代码文件 | 状态 |
|
||||
|--------|---------|------|
|
||||
| 新浪财经 | `daily_all_update.py` 中的 `try_sina_15min()` | ✅ 在用 |
|
||||
| 腾讯fqkline | `daily_all_update.py` 中的 `fetch_tencent_daily()` | ✅ 在用 |
|
||||
| 腾讯minute/query | `download_minute.py` 中的 `try_minute_query_aggregate()` | ✅ 已实现,作为备源 |
|
||||
| 东方财富 | 未实现 | ❌ 待开发(需Windows环境) |
|
||||
| akshare | `daily_all_update.py` 外部依赖 | ⚠️ 受代理影响 |
|
||||
|
||||
### 5.2 设计原则
|
||||
|
||||
1. **所有数据源接口统一保留**,不删除任何已有的数据源访问代码
|
||||
2. **数据合并策略**:同一股票同一周期从多个源获取时,按优先级选择:
|
||||
- amount(成交额):优先有真实值的源(新浪 > 腾讯)
|
||||
- 数据长度:优先历史更长的源
|
||||
- 数据时效:优先更新的源
|
||||
3. **源降级链**:主源失败自动尝试备源,不丢数据
|
||||
4. **源标记**:Parquet文件可选增加 `_source` 列标记数据来源(待讨论)
|
||||
|
||||
### 5.3 未来扩展点
|
||||
|
||||
- 东方财富API集成(需Windows Node)
|
||||
- akshare作为备用日线源(网络恢复后)
|
||||
- 1min/5min/30min/60min等其他周期
|
||||
- 北交所920xxx数据(需新数据源)
|
||||
|
||||
---
|
||||
|
||||
## 六、SMB/NAS 性能问题与方案
|
||||
|
||||
### 6.1 已知问题
|
||||
|
||||
| 问题 | 现象 | 影响 |
|
||||
|------|------|------|
|
||||
| SMB读大文件慢 | 1.4GB SQLite查询超时(>20s) | 无法直接在Mac上操作NAS DB |
|
||||
| SMB写大文件卡死 | 进程被SIGKILL | 全量导入必须用本地中转 |
|
||||
| SMB文件锁冲突 | SQLite WAL模式可能异常 | 并发写入风险 |
|
||||
| Parquet小文件延迟 | 5300个parquet文件,SMB逐个读写 | 全量更新约30分钟 |
|
||||
|
||||
### 6.2 当前方案
|
||||
|
||||
```
|
||||
写入流程(NAS DB):
|
||||
本地/tmp写SQLite → ATTACH NAS DB → INSERT OR REPLACE → DETACH → 删除临时文件
|
||||
|
||||
写入流程(Parquet):
|
||||
内存中合并 → 写本地.tmp → rename到NAS路径
|
||||
```
|
||||
|
||||
### 6.3 待讨论:是否直接在NAS本地执行
|
||||
|
||||
NAS (192.168.2.154) 上运行的是 Linux,如果能SSH执行Python脚本:
|
||||
- SQLite直接本地读写,无SMB延迟
|
||||
- Parquet直接本地写入
|
||||
- 速度提升10倍以上
|
||||
|
||||
**方案A(当前)**:Mac上跑脚本,SMB读写NAS
|
||||
- 优点:无需SSH,利用Mac环境
|
||||
- 缺点:SMB性能瓶颈
|
||||
|
||||
**方案B(建议)**:NAS上直接跑脚本(需姜维配合SSH/容器环境)
|
||||
- 优点:无SMB瓶颈,速度快
|
||||
- 缺点:需要NAS上有Python环境
|
||||
|
||||
**方案C(折中)**:Parquet写NAS(小文件SMB可接受),SQLite写Docker容器内(通过HTTP API)
|
||||
- 优点:各取所长
|
||||
- 缺点:需要开发写入API
|
||||
|
||||
> 📌 **待与司马懿讨论**:NAS性能问题的最终解决方案
|
||||
|
||||
---
|
||||
|
||||
## 七、定时任务配置
|
||||
|
||||
### 7.1 当前方案(OpenClaw Cron)
|
||||
|
||||
| 配置项 | 值 |
|
||||
|--------|-----|
|
||||
| 调度 | 每交易日(周一到周五)15:35 |
|
||||
| 时区 | Asia/Shanghai |
|
||||
| 执行方式 | isolated session(不消耗主session token) |
|
||||
| 超时 | 3600秒(1小时) |
|
||||
| 通知 | 完成后飞书通知 |
|
||||
|
||||
### 7.2 Cron表达式
|
||||
|
||||
```
|
||||
35 15 * * 1-5 # 周一到周五 15:35
|
||||
```
|
||||
|
||||
### 7.3 注意事项
|
||||
|
||||
- 非交易日也会触发,但脚本会检测无新数据后快速退出(所有股票都skipped)
|
||||
- 未来可增加交易日历判断(如使用akshare获取交易日历)
|
||||
|
||||
---
|
||||
|
||||
## 八、部署方案(待实现)
|
||||
|
||||
### 8.1 当前部署状态
|
||||
|
||||
- 脚本路径:`~/.openclaw/sanguo_projects/sanguo_vnpy/data_platform/`
|
||||
- 运行环境:Mac mini(楚锋的Mac mini),Python 3.9
|
||||
- 调度:OpenClaw Cron
|
||||
- 数据存储:NAS SMB挂载 `/Volumes/stock/`
|
||||
|
||||
### 8.2 目标部署(整合到sanguo_vnpy项目)
|
||||
|
||||
**待实现,设计如下**:
|
||||
|
||||
```
|
||||
sanguo_vnpy/
|
||||
├── deploy/
|
||||
│ ├── docker-compose.yml # 包含数据更新服务
|
||||
│ └── data-updater/
|
||||
│ ├── Dockerfile # 数据更新容器
|
||||
│ ├── crontab # 容器内crontab
|
||||
│ └── entrypoint.sh
|
||||
├── src/
|
||||
│ └── data_platform/ # 数据平台代码(从data_platform/迁移)
|
||||
│ ├── daily_all_update.py
|
||||
│ ├── download_minute.py
|
||||
│ ├── import_vnpy_daily_fast.py
|
||||
│ ├── import_vnpy_minute.py
|
||||
│ └── ...
|
||||
├── docs/
|
||||
│ └── data-platform/
|
||||
│ └── daily-update-design.md # 本文档
|
||||
└── config/
|
||||
└── data_platform.yaml # 配置文件(路径、限频参数等)
|
||||
```
|
||||
|
||||
### 8.3 部署步骤(草案)
|
||||
|
||||
1. 代码从 `~/.openclaw/sanguo_projects/` 迁移到 `sanguo_vnpy/src/data_platform/`
|
||||
2. 配置外置为YAML文件
|
||||
3. Docker容器内置crontab + Python脚本
|
||||
4. 容器挂载NAS数据目录
|
||||
5. 与现有vnpy回测服务docker-compose整合
|
||||
|
||||
---
|
||||
|
||||
## 九、测试
|
||||
|
||||
### 9.1 已完成的测试
|
||||
|
||||
| 测试项 | 结果 | 日期 |
|
||||
|--------|------|------|
|
||||
| 日线增量更新3只(000001/600519/300750) | ✅ 3只skipped(已是最新) | 2026-05-03 |
|
||||
| 15min增量更新3只 | ✅ 3只ok,0 failed | 2026-05-03 |
|
||||
| 全市场15min下载(5193只) | ✅ 完成,107只北交所失败(源不支持) | 2026-05-02 |
|
||||
| vnpy DB日线全量导入(1281万行) | ✅ 回测验证通过 | 2026-05-02 |
|
||||
| vnpy DB 15min导入(单只验证) | ✅ 1970行,16个时间点正确 | 2026-05-02 |
|
||||
|
||||
### 9.2 待测试项
|
||||
|
||||
| 测试项 | 方法 | 优先级 |
|
||||
|--------|------|------|
|
||||
| 全市场增量更新完整流程 | cron触发后检查report | P0 |
|
||||
| NAS离线时脚本行为 | umount后运行,验证优雅退出 | P0 |
|
||||
| DB写入并发安全 | 两个脚本同时写DB | P1 |
|
||||
| 东方财富API(Windows) | Windows Node上线后测试 | P2 |
|
||||
| 非交易日执行 | 周末运行,验证全部skipped | P1 |
|
||||
| 30天连续运行稳定性 | 观察一个月的report | P1 |
|
||||
|
||||
---
|
||||
|
||||
## 十、Q&A — 讨论过的问题汇总
|
||||
|
||||
### Q1: Parquet双写是什么意思?还需要吗?
|
||||
|
||||
**讨论**:原TODO #4提到Parquet作为真相源(source of truth)与vnpy DB双写。
|
||||
**结论**:当前架构中 Parquet 是下载的**原始产出**,vnpy DB 是**导入产物**。Parquet本身就是备份。不需要额外的双写机制。真正需要的是**vnpy DB的定时备份**(当前.bak只备份一次)。
|
||||
|
||||
### Q2: 新浪API只能拿800条,怎么获取更长的历史?
|
||||
|
||||
**讨论**:新浪 `datalen=800` 是硬限制,超过800返回null。实测15min=3个月,日线=3年。
|
||||
**结论**:
|
||||
- 增量更新场景:每日800条足够覆盖最新数据,历史在Parquet中累积
|
||||
- 历史回补:需要东方财富API(可指定日期范围),但Mac被拒,需Windows环境
|
||||
- 另一条路:如果之前有更长的CSV数据(如84只深市老数据有1970行),合并进Parquet
|
||||
|
||||
### Q3: vnpy DB的interval为什么是"1m"而不是"15m"?
|
||||
|
||||
**讨论**:vnpy 4.x的Interval枚举只有 `MINUTE="1m"`,没有 `MINUTE_15`。Docker用的是原始vnpy。
|
||||
**结论**:DB中15分钟线用 `interval="1m"` 存储,与BacktestingEngine `load_data(interval="1m")` 匹配。如果未来引入真正的1分钟线,需要重新设计interval值。
|
||||
|
||||
### Q4: 北交所107只股票怎么办?
|
||||
|
||||
**讨论**:新浪行情源不支持920xxx代码。
|
||||
**结论**:当前不影响(HS300无北交所),后续如需支持需引入新数据源(如东方财富)。
|
||||
|
||||
### Q5: 为什么不直接在NAS上跑脚本?
|
||||
|
||||
**讨论**:Mac通过SMB访问NAS,大文件操作慢且不稳定(SIGKILL)。
|
||||
**结论**:当前用本地tmp中转方案缓解。长期建议在NAS本地执行(需SSH/容器环境),或通过Docker容器HTTP API写入。
|
||||
|
||||
### Q6: amount(成交额)数据准确性?
|
||||
|
||||
**讨论**:腾讯fqkline的amount有时返回0,新浪API的amount是真实值。
|
||||
**结论**:
|
||||
- 15min:用新浪(amount真实)
|
||||
- 日线:用腾讯(amount可能为0,但支持日期范围查询更重要)
|
||||
- 未来可考虑用新浪的amount覆盖腾讯的0值
|
||||
|
||||
### Q7: 每日增量更新多长时间?
|
||||
|
||||
**预估**:
|
||||
- 日线:5300只 × 0.3s ≈ 26分钟(大部分skipped更快)
|
||||
- 15min:5300只 × 0.3s ≈ 26分钟
|
||||
- DB写入:取决于增量数据量,通常几百条
|
||||
- **总计约30-50分钟**
|
||||
|
||||
### Q8: 如何处理节假日/非交易日?
|
||||
|
||||
**当前方案**:非交易日执行时,所有股票都检测到"已是最新"被skipped,快速退出(<1分钟)。
|
||||
**改进方向**:可增加交易日历判断,非交易日直接不执行(节省一次扫描)。
|
||||
|
||||
### Q9: 数据更新和回测服务会冲突吗?
|
||||
|
||||
**风险**:更新脚本和回测服务同时读写同一个vnpy DB。
|
||||
**缓解**:回测服务在Docker容器内操作自己的DB副本(`/home/vnpy/.vntrader/database.db`),与NAS上的DB是不同文件。NAS DB更新后需要同步到Docker(目前手动wget)。
|
||||
**待改进**:自动化DB同步机制(cron或文件监控)。
|
||||
|
||||
### Q10: 代码部署为什么要和sanguo_vnpy整合?
|
||||
|
||||
**理由**:
|
||||
1. 数据平台是为vnpy回测服务的,放一起管理方便
|
||||
2. Docker统一部署,减少环境依赖
|
||||
3. 配置集中管理(NAS路径、限频参数等)
|
||||
|
||||
---
|
||||
|
||||
## 十一、文件清单
|
||||
|
||||
| 文件 | 路径 | 说明 |
|
||||
|------|------|------|
|
||||
| `daily_all_update.py` | `sanguo_vnpy/data_platform/` | 主脚本:全市场增量更新 |
|
||||
| `daily_update_all.sh` | `sanguo_vnpy/data_platform/` | Shell wrapper |
|
||||
| `download_minute.py` | `sanguo_vnpy/data_platform/` | 15min全量下载(保留) |
|
||||
| `import_vnpy_minute.py` | `sanguo_vnpy/data_platform/` | 分钟线导入DB(保留) |
|
||||
| `import_vnpy_daily_fast.py` | `sanguo_vnpy/data_platform/` | 日线全量导入(保留) |
|
||||
| `updater.py` | `sanguo_vnpy/data_platform/` | 旧版日线更新(保留) |
|
||||
| `daily_update.sh` | `sanguo_vnpy/data_platform/` | 旧版wrapper(保留) |
|
||||
|
||||
---
|
||||
|
||||
## 十二、变更记录
|
||||
|
||||
| 日期 | 版本 | 变更 | 作者 |
|
||||
|------|------|------|------|
|
||||
| 2026-05-03 | v1.0-draft | 初始版本 | 赵云 |
|
||||
| 2026-05-03 | v1.1 | 司马懿评审后修改:interval→15m, 严格增量追加, 日线进度文件, 全局源检测, DB轮转备份, 失败率告警 | 赵云 |
|
||||
| 2026-05-05 | v1.2 | 东方财富集成:日线主源切换为东方财富(amount真实,反爬策略4s/请求+随机抖动), 腾讯降为备源 | 赵云 |
|
||||
| 2026-05-06 | v2.0 | **重大架构变更**:BaoStock替代所有主源(无反爬、全量历史、amount真实);15min interval改为1m;vnpy DB写入改为本地构建+rsync;新浪API已挂移除;多源fallback机制重构 | 赵云 |
|
||||
|
||||
---
|
||||
|
||||
## 十三、评审结果(2026-05-03 司马懿评审)
|
||||
|
||||
### v1.1 评审结论:有条件通过(已完成)
|
||||
|
||||
**2个阻塞项(已解决)**:
|
||||
1. ✅ interval="1m" → "15m":采用方案B(vnpy加MINUTE_15枚举 + monkey patch)
|
||||
2. ✅ 15min增量合并:改为严格按日期追加,不再用drop_duplicates keep=last
|
||||
|
||||
**4个硬伤(已修复)**:
|
||||
1. ✅ 日线增加进度文件
|
||||
2. ✅ 全局源不可用检测(连续30只首次失败→终止)
|
||||
3. ✅ DB轮转备份(保留7天,`quant_trading_{YYYYMMDD}.db.bak`)
|
||||
4. ✅ 失败率>5%告警标记 + 源终止告警
|
||||
|
||||
---
|
||||
|
||||
## 十四、v2.0 重大架构变更(2026-05-06)
|
||||
|
||||
### 14.1 变更背景
|
||||
|
||||
v1.2运行暴露了5个根本性问题:
|
||||
|
||||
| # | 问题 | 根因 | 影响 |
|
||||
|---|------|------|------|
|
||||
| 1 | vnpy DB写入报`no such table: dbbardata` | SMB文件锁与SQLite ATTACH不兼容 | 日线+15min数据不入DB |
|
||||
| 2 | 新浪15min API已失效 | 返回Error 0,所有请求失败 | 15min无法增量更新 |
|
||||
| 3 | BaoStock 15min回补已完成但未入库 | 原脚本interval=`15m`与vnpy不兼容 | 5193只×8992条数据闲置 |
|
||||
| 4 | 日线跨年写入bug | `year=datetime.now().year`硬编码 | 年初数据会写错目录 |
|
||||
| 5 | overview全表聚合 | 1.4G DB上GROUP BY全表扫描 | NAS上可能超时/锁死 |
|
||||
|
||||
### 14.2 数据源重新调研
|
||||
|
||||
#### 数据源实测对比
|
||||
|
||||
| 数据源 | 15min可获取量 | 日线可获取量 | amount | 反爬 | 频率 | 当前状态 |
|
||||
|--------|-------------|-------------|--------|------|------|----------|
|
||||
| **BaoStock** | 无限制(按日期) | 无限制(按日期) | 真实(5.54亿) | **无** | 0.12s/只, 100只0错误 | ✅ 稳定 |
|
||||
| **东方财富** | ~496条(7周) | 多年(~1046条) | 真实(5.54亿) | 4-5s/请求+UA+Referer | 中 | ✅ 可用 |
|
||||
| **腾讯** | Connection reset | 按日期范围 | 有时为0 | 无 | 快 | ⚠️ 不稳定 |
|
||||
| **新浪** | Error/2条 | Error/2条 | - | - | - | ❌ 已挂 |
|
||||
|
||||
**关键结论**:BaoStock在所有维度都最优(无反爬、全量历史、amount真实、速度快),应作为首选源。
|
||||
|
||||
#### v1.2 BaoStock压力测试
|
||||
|
||||
```
|
||||
15min: 100只连续请求, 总耗时11.9s, 平均0.12s/只, 0错误
|
||||
日线: 10只连续请求, 总耗时1.6s, 平均0.16s/只
|
||||
全历史: sh.600000 2010-2026日线 3963条, 0.68s
|
||||
```
|
||||
|
||||
#### v1.2 SQLite本地写入性能
|
||||
|
||||
```
|
||||
100万条INSERT OR REPLACE: 2.0s
|
||||
预估4600万条(15min全量): ~91s ≈ 1.5分钟
|
||||
```
|
||||
|
||||
### 14.3 v2.0 核心架构变更
|
||||
|
||||
#### 变更1:数据源降级链重构
|
||||
|
||||
**设计原则**:按数据质量排序,质量最好的源排第一。每个源封装独立函数,统一返回DataFrame。主循环挨个尝试,成功即用,失败试下一个。
|
||||
|
||||
```
|
||||
v1.x(旧):
|
||||
日线: 腾讯(主) → 新浪(备)
|
||||
15min: 新浪(主) → 无备源
|
||||
|
||||
v2.0(新):
|
||||
日线: BaoStock(主) → 东方财富(备) → 腾讯(三备)
|
||||
15min: BaoStock(主) → 东方财富(备) → 新浪(三备,当前已挂)
|
||||
```
|
||||
|
||||
**Fallback机制**:
|
||||
```python
|
||||
SOURCES_DAILY = [
|
||||
("baostock", fetch_baostock_daily), # 最优:全量历史+无反爬+amount真实
|
||||
("eastmoney", fetch_eastmoney_daily), # 备用:多年历史+amount真实+4s限频
|
||||
("tencent", fetch_tencent_daily), # 三备:amount有时为0
|
||||
]
|
||||
SOURCES_15MIN = [
|
||||
("baostock", fetch_baostock_15min), # 最优
|
||||
("eastmoney", fetch_eastmoney_15min), # 备用:7周
|
||||
("sina", try_sina_15min), # 三备:当前已挂
|
||||
]
|
||||
|
||||
def fetch_with_fallback(sources, code, start, end):
|
||||
for name, fetch_fn in sources:
|
||||
try:
|
||||
data = fetch_fn(code, start, end)
|
||||
if data is not None and len(data) > 0:
|
||||
return data, name
|
||||
except Exception:
|
||||
continue
|
||||
return None, None
|
||||
```
|
||||
|
||||
#### 变更2:vnpy DB写入策略改为本地构建+rsync
|
||||
|
||||
**v1.x方案(ATTACH via SMB)**:直接在Mac上ATTACH NAS DB → SMB文件锁导致失败
|
||||
|
||||
**v2.0方案(本地构建+rsync)**:
|
||||
1. 每日更新时,从NAS cp当前DB到本地`/tmp/`
|
||||
2. 所有增量数据写入本地DB
|
||||
3. 验证完整性后,rsync覆盖NAS DB
|
||||
4. 备份旧DB(轮转7天)
|
||||
|
||||
```python
|
||||
def sync_db_to_nas():
|
||||
# 备份
|
||||
backup = f"quant_trading_{today}.db.bak"
|
||||
shutil.copy2(str(VNPY_DB_PATH), str(VNPY_DB_PATH.parent / backup))
|
||||
|
||||
# rsync本地→NAS
|
||||
os.system(f"rsync -av --progress {LOCAL_DB_PATH} {VNPY_DB_PATH}")
|
||||
```
|
||||
|
||||
**性能预估**:
|
||||
- cp NAS DB到本地:~15秒(1.4G)
|
||||
- 增量写入本地DB:<1秒(日线)
|
||||
- rsync覆盖NAS:~15秒
|
||||
- 全量15min导入(首次):~1.5分钟(4600万条)
|
||||
|
||||
#### 变更3:15min interval统一用`1m`
|
||||
|
||||
**v1.x**:interval=`15m`(与vnpy 4.x不兼容)
|
||||
**v2.0**:interval=`1m`(姜维确认vnpy 4.x Interval.MINUTE.value=`1m`)
|
||||
|
||||
**数据格式(姜维确认)**:
|
||||
- symbol: `000001`(纯代码)
|
||||
- exchange: `SSE` / `SZSE`
|
||||
- interval日线: `d`
|
||||
- interval分钟线: `1m`
|
||||
|
||||
#### 变更4:日线跨年写入修复
|
||||
|
||||
**v1.x bug**:`year = datetime.now().year`,年初数据写错目录
|
||||
**v2.0**:按数据日期分目录
|
||||
|
||||
```python
|
||||
def update_daily_parquet(code, new_data):
|
||||
for yr in new_data["date"].str[:4].unique():
|
||||
year_data = new_data[new_data["date"].str[:4] == yr]
|
||||
parquet_path = DAILY_DIR / yr / f"{prefix}{clean}_daily.parquet"
|
||||
# 合并写入...
|
||||
```
|
||||
|
||||
#### 变更5:overview增量更新
|
||||
|
||||
**v1.x**:`SELECT ... FROM dbbardata GROUP BY` 全表扫描(1.4G DB上很慢)
|
||||
**v2.0**:只更新本次涉及的symbol
|
||||
|
||||
```python
|
||||
for sym, exc, ivl in affected_keys:
|
||||
c.execute("""INSERT OR REPLACE INTO dbbaroverview
|
||||
SELECT ?,?,?,COUNT(*),MIN(datetime),MAX(datetime)
|
||||
FROM dbbardata WHERE symbol=? AND exchange=? AND interval=?""",
|
||||
(sym, exc, ivl, sym, exc, ivl))
|
||||
```
|
||||
|
||||
#### 变更6:进度文件加日期
|
||||
|
||||
**v1.x**:进度文件不区分日期,跨天可能跳过
|
||||
**v2.0**:`daily_20260506_progress.json`,每次运行独立进度
|
||||
|
||||
#### 变更7:Cron fallback模型
|
||||
|
||||
**v1.x**:只用默认模型,配额用完则任务失败
|
||||
**v2.0**:设置fallback模型(zhipu/glm-5.1),配额不足时自动降级
|
||||
|
||||
### 14.4 执行计划
|
||||
|
||||
#### 第1步:灌入现有数据到本地vnpy DB
|
||||
|
||||
```
|
||||
1. cp NAS quant_trading.db → /tmp/quant_trading_import.db
|
||||
2. import_vnpy_daily_fast.py --start-year 2026 # 补3/28~今天的日线增量
|
||||
3. import_vnpy_minute.py --scope all # 全量导入5193只15min
|
||||
4. 验证数据完整性
|
||||
5. rsync本地DB → NAS
|
||||
```
|
||||
|
||||
#### 第2步:重构daily_all_update.py
|
||||
|
||||
按14.3的7个变更点重构代码。
|
||||
|
||||
#### 第3步:Cron更新+测试
|
||||
|
||||
- 更新cron任务配置
|
||||
- 手动触发一次全量更新验证
|
||||
- 确认日志无错误
|
||||
|
||||
### 14.5 与v1.x的兼容性
|
||||
|
||||
| 变更 | 向后兼容 | 影响 |
|
||||
|------|---------|------|
|
||||
| interval 15m→1m | ❌ 需要DB迁移 | 现有15m数据需UPDATE为1m |
|
||||
| DB写入策略 | ✅ 无影响 | Parquet不受影响 |
|
||||
| 数据源顺序 | ✅ 无影响 | 只是重试顺序变化 |
|
||||
| 跨年写入 | ✅ 修正bug | 未来数据不再错 |
|
||||
| overview增量 | ✅ 无影响 | 只是优化 |
|
||||
|
||||
> ⚠️ **DB迁移注意**:v1.x如果有`interval='15m'`的记录,需要一次性UPDATE为`'1m'`。当前DB中实际无15min数据(v1.x的写入全部失败),所以无需迁移。
|
||||
|
||||
---
|
||||
|
||||
## 十五、v2.0 评审待确认项
|
||||
|
||||
| # | 问题 | 建议方案 | 待确认 |
|
||||
|---|------|---------|--------|
|
||||
| 1 | BaoStock作为全主源是否合适? | 无反爬+全量+amount真实,建议通过 | 司马懿 |
|
||||
| 2 | 本地构建+rsync替代ATTACH | 姜维确认推荐,比ATTACH稳定 | 司马懿 |
|
||||
| 3 | interval=1m而非15m | 姜维确认vnpy 4.x规范 | 司马懿 |
|
||||
| 4 | 是否需要DB迁移脚本? | 当前无15m数据,无需迁移 | 司马懿 |
|
||||
| 5 | Fallback顺序是否合理? | BaoStock→东方财富→腾讯/新浪 | 司马懿 |
|
||||
| 6 | 日常更新全市场耗时预估? | BaoStock: ~10min(15min)+~8min(日线)+rsync | 司马懿 |
|
||||
| 7 | 是否需要额外反爬措施? | BaoStock无需,备源保留原有措施 | 司马懿 |
|
||||
|
||||
### 15.6 v2.0 评审结论(2026-05-06 司马懿)
|
||||
|
||||
**结论:全部通过,可以部署**
|
||||
|
||||
C1 interval=1m:姜维翻源码确认vnpy硬约束,接受。**附加前提:代码里所有写interval='1m'的地方必须加注释,说明这是vnpy 4.x Interval.MINUTE硬约束,实际存储15分钟线。**
|
||||
|
||||
C2 rsync原子性:改为写新文件+mv原子重命名。
|
||||
|
||||
M1 BaoStock T+1延迟:已验证确认。日常增量改为东方财富(当天实时) → BaoStock(T+1补全) → 腾讯。
|
||||
|
||||
M2 失败暂停:改为失败率检测(最近100只>80%切换源)。
|
||||
|
||||
**最终Fallback顺序(含T+1调整):**
|
||||
```
|
||||
日常增量(当天15:35触发):
|
||||
日线:东方财富(实时) → BaoStock(T+1) → 腾讯
|
||||
15min:东方财富(实时7周) → BaoStock(T+1) → 新浪
|
||||
|
||||
历史回补:
|
||||
日线+15min:BaoStock(全量历史,无反爬)
|
||||
```
|
||||
@@ -0,0 +1,141 @@
|
||||
# 数据源体系建设 - 项目汇总报告
|
||||
|
||||
**任务ID**: data-platform-20260502
|
||||
**汇总人**: 庞统(副军师)
|
||||
**日期**: 2026-05-02
|
||||
**状态**: P1完成,P2-P4待后续任务
|
||||
|
||||
---
|
||||
|
||||
## 一、项目目标
|
||||
|
||||
打通从数据获取到vnpy回测的完整数据通路:**NAS Parquet → vnpy SQLite DB → 回测引擎**
|
||||
|
||||
核心问题:vnpy回测服务的 quant_trading.db 是空的(8KB),所有回测任务必然失败。
|
||||
|
||||
---
|
||||
|
||||
## 二、各节点产出汇总
|
||||
|
||||
| 节点 | 负责人 | 核心产出 | 结论 |
|
||||
|------|--------|---------|------|
|
||||
| pangtong_requirements | 庞统 | 需求规格文档(7个维度、4个阶段、9项不确定项) | ✅ 通过 |
|
||||
| zhaoyun_acquire | 赵云 | vnpy DB Schema确认 + 全量日线导入(1281万行)+ P0限频验证 | ✅ 通过 |
|
||||
| jiangwei_storage | 姜维 | Docker数据通路打通 + executor bug修复 + 端到端回测验证 | ✅ 通过 |
|
||||
| simayi_verify | 司马懿 | 数据完整性/正确性/回测可用性逐项验证 | ✅ 通过 |
|
||||
|
||||
---
|
||||
|
||||
## 三、P1 完成成果
|
||||
|
||||
### 3.1 数据导入
|
||||
|
||||
| 指标 | 数值 |
|
||||
|------|------|
|
||||
| 总行数 | **12,811,513** |
|
||||
| 股票数 | **5,191** |
|
||||
| 日期范围 | 2010-01-04 ~ 2026-03-27 |
|
||||
| DB文件大小 | 1.4 GB(NAS)/ 1.51 GB(Docker内) |
|
||||
| 导入耗时 | ~45 分钟 |
|
||||
|
||||
### 3.2 回测验证
|
||||
|
||||
| 验证项 | 结果 |
|
||||
|--------|------|
|
||||
| vnpy load_data() | ✅ 加载237根日K线(000001.SZSE 2025年) |
|
||||
| 回测服务API | ✅ 提交→执行→返回统计 |
|
||||
| 回测统计 | total_days=237, return=1.30%, sharpe=0.857 |
|
||||
| 数据质量 | 6条异常(占比0.00005%),源自原始Parquet |
|
||||
|
||||
### 3.3 解决的关键问题
|
||||
|
||||
1. **vnpy DB Schema确认**:DbBarData表11个字段,唯一索引(symbol,exchange,interval,datetime)
|
||||
2. **SMB写入SQLite锁库**:先写本地/tmp,完成后复制到NAS
|
||||
3. **Docker未挂载数据目录**:通过Mac HTTP服务从Docker内wget DB文件到~/.vntrader/
|
||||
4. **executor date→datetime bug**:修补版executor.py,字符串转datetime后再传给vnpy
|
||||
|
||||
---
|
||||
|
||||
## 四、产出的文件清单
|
||||
|
||||
### 代码文件(sanguo_vnpy/data_platform/)
|
||||
|
||||
| 文件 | 说明 | 行数 |
|
||||
|------|------|------|
|
||||
| import_vnpy_daily_fast.py | 全量日线导入脚本(pandas向量化) | 126 |
|
||||
|
||||
### 数据文件
|
||||
|
||||
| 文件 | 大小 | 路径 |
|
||||
|------|------|------|
|
||||
| quant_trading.db | 1.4 GB | /Volumes/stock/sanguo_vnpy/data/ |
|
||||
| quant_trading.db.bak | 8 KB | /Volumes/stock/sanguo_vnpy/data/(原始空库备份) |
|
||||
| database.db(Docker内) | 1.51 GB | /home/vnpy/.vntrader/ |
|
||||
|
||||
### 修复文件
|
||||
|
||||
| 文件 | 说明 |
|
||||
|------|------|
|
||||
| executor_patched.py | executor.py date→datetime 修复版 |
|
||||
| restore_backtest_service.sh | 容器重启后恢复脚本 |
|
||||
| start_backtest.sh | Docker内回测服务启动脚本 |
|
||||
|
||||
### 文档文件
|
||||
|
||||
| 文件 | 路径 |
|
||||
|------|------|
|
||||
| 01-requirements.md | ~/.openclaw/sanguo_projects/sanguo_vnpy/docs/data-platform/ |
|
||||
|
||||
---
|
||||
|
||||
## 五、P0 腾讯API限频验证结果
|
||||
|
||||
| 指标 | 数值 |
|
||||
|------|------|
|
||||
| 测试规模 | 100只股票15分钟线 |
|
||||
| 成功率 | **100%** |
|
||||
| 平均响应时间 | 0.19秒/请求 |
|
||||
| 封禁 | **无** |
|
||||
| 预估全市场下载 | ~17分钟(5500只) |
|
||||
|
||||
**结论**:腾讯API限频不构成阻塞,P3分钟线可执行。
|
||||
|
||||
---
|
||||
|
||||
## 六、遗留问题(不阻塞P1)
|
||||
|
||||
| # | 问题 | 影响 | 建议处理 |
|
||||
|---|------|------|---------|
|
||||
| 1 | **容器重启需手动恢复回测服务** | 回测不自动启动 | 修改Docker entrypoint或Synology配置 |
|
||||
| 2 | NAS数据停在2026-03-27 | 缺34天日线 | P2增量更新 |
|
||||
| 3 | 6条异常数据(原始Parquet) | 影响极小 | P4全量校验 |
|
||||
| 4 | DB导入非全自动(/tmp手动复制) | 运维不便 | 优化导入脚本 |
|
||||
|
||||
---
|
||||
|
||||
## 七、P2-P4 待后续任务推进
|
||||
|
||||
| 阶段 | 内容 | 状态 |
|
||||
|------|------|------|
|
||||
| P2: 数据基础设施 | 降级管理器+校验层+实时行情+增量更新+cron | 待创建任务 |
|
||||
| P3: 分钟线数据 | 限频已验证通过,下载+导入 | 待创建任务 |
|
||||
| P4: 配套skill | skill更新+全量校验+周维护 | 待创建任务 |
|
||||
|
||||
---
|
||||
|
||||
## 八、数据流架构(当前状态)
|
||||
|
||||
```
|
||||
NAS Parquet (5191只×17年)
|
||||
↓ import_vnpy_daily_fast.py
|
||||
SQLite DB (1281万行, 1.4GB)
|
||||
↓ Mac HTTP → Docker wget
|
||||
Docker ~/.vntrader/database.db (1.51GB)
|
||||
↓ engine.load_data()
|
||||
vnpy BacktestingEngine → 回测结果 ✅
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
*汇总完成:2026-05-02*
|
||||
*庞统(副军师)🐦*
|
||||
@@ -28,7 +28,23 @@ NAS /volume1/stock(日线/15min parquet + vnpy DB)
|
||||
- `STOCK_LIMIT` env:限制股票数(前 N,**验证用**,默认 0=全市场)
|
||||
- **`run_daily_update.sh`** — 包装(rsync 拉 + v1 + 推),SSH 模式入口
|
||||
- env:`STOCK_LIMIT=N` / `SKIP_PULL=1`(验证跳过拉)
|
||||
- 其他 v1 脚本:`backfill_15min_baostock.py`(15min 回补)/ `realtime.py`(实时三源)/ `validator.py`(校验)等
|
||||
### 完整脚本清单(`v2/scripts/data_platform/`)
|
||||
|
||||
| 脚本 | 用途 | 用法 |
|
||||
|------|------|------|
|
||||
| `run_daily_update.sh` | **SSH 模式入口**(rsync 拉+v1+推)| `./run_daily_update.sh [--skip-daily\|--skip-15min]` |
|
||||
| `daily_all_update.py` | **全市场每日增量**(日线+15min,多源 fallback+熔断)| `python3 daily_all_update.py`(`STOCK_MOUNT`/`STOCK_LIMIT` env)|
|
||||
| `backfill_15min_baostock.py` | BaoStock 全量重建 15min 历史(`adjustflag=3` **raw**)| 手动回补,按需 |
|
||||
| `download_minute.py` | 15min 下载(HS300 子集等)| 按需 |
|
||||
| `fallback.py` | 多源降级管理器(日线 akshare→腾讯 / 实时 新浪→东财→腾讯)| 被主脚本调用 |
|
||||
| `realtime.py` | 实时行情三源降级 | 盘中按需 |
|
||||
| `updater.py` | vnpy DB 增量更新(腾讯主源)| 被主脚本调用 |
|
||||
| `validator.py` | 数据校验(7 条 fatal 规则)| 校验按需 |
|
||||
| `import_vnpy_daily.py` / `import_vnpy_daily_fast.py` | vnpy DB 日线导入 | 迁移按需 |
|
||||
| `import_vnpy_minute.py` | vnpy DB 分钟导入 | 迁移按需 |
|
||||
|
||||
> **主入口**:`run_daily_update.sh`(定时/手动)。其他脚本是组件或按需工具。
|
||||
> v1 调研文档(需求/设计/总结)已复制到 `v2/docs/data-platform/` 供参考。
|
||||
|
||||
## 定时(launchd,替代 crontab)
|
||||
|
||||
|
||||
Reference in New Issue
Block a user