Files
sanguo_vnpy_v2/docs/data/15min-data-design.md
T
claude_dev cf4b3a2cb0 feat(data): 15min数据补全—路径参数化+cron入口+回填+设计文档
- download_minute.py: STOCK_ROOT环境变量参数化(Mac/NAS兼容); 修tencent备源amount聚合 last→sum
- refresh_15min_daily.py: 新建cron入口(交易日判断baostock/直连pop代理/日志)
- 回填470缺口(170 SH/SZ成功, 300 BSE源不支持)
- docs/data/15min-data-design.md: 15min数据层设计文档(源/覆盖/脚本/刷新/约束/路径映射)
2026-07-12 08:26:30 +08:00

196 lines
9.1 KiB
Markdown
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
# 15min 数据层设计文档
**项目**: sanguo_vnpy_v2 数据层
**日期**: 2026-07-122026-07-08~10 审计+回填后落地)
**范围**: A 股 15 分钟 K 线数据层(数据源、覆盖现状、脚本、刷新机制、硬约束、已知问题)
**相关文档**: [`docs/data-platform/daily-update-design.md`](../data-platform/daily-update-design.md)(日线+15min+vNpy DB 早期多源架构 v1~v3,本文件聚焦 15min 最终落地的实现)
---
## 一、数据源
| 源 | 用途 | 协议 | 特点 |
|----|------|------|------|
| 新浪财经 15min API | **主源·增量刷新** | HTTP | `datalen=800`(≈2.5 个月)/ 次,有真实 `amount`,不复权,无法回填历史 |
| 腾讯 minute/query + 聚合 | 备源·仅当天 | HTTP | sina 失败时用,拉 1min 聚合成 15min |
| baostock | **历史回填** | TCP | `adjustflag=3` 不复权,2024-01-01 起,0.4s/票,单连接,**不支持 BSE** |
### 1.1 新浪 15min API(主源)
- URL: `https://quotes.sina.cn/cn/api/jsonp_v2.php/.../CN_MarketDataService.getKLineData?symbol={symbol}&scale=15&ma=no&datalen=800`
- `datalen` 最大有效值 **800**(超过返回 null),即 15min ≈ 2.5 个月
- 字段: `day, open, high, low, close, volume, amount``amount` 为真实成交额
- 时间戳为 end-of-bar 格式(09:45, 10:00 ...
- 返回 JSONP,需正则提取 JSON 数组
- 不复权,无法指定起始日期 → **只能增量刷新最近 2.5 月,不能回填更早历史**
### 1.2 腾讯 minute/query(备源)
- URL: `http://web.ifzq.gtimg.cn/appstock/app/minute/query?code={symbol}`
- 仅返回**当天** 1min 数据,聚合为 15min`_aggregate_1m_to_15m`
- 仅在 sina 主源失败时兜底
### 1.3 baostock(历史回填)
- `query_history_k_data_plus``adjustflag="3"`(不复权,与 sina 主源一致)
- 起点 2024-01-01,可按日期范围全量拉取
- 0.4s/票,单连接(并发会崩),每 400 票 relogin 防断会话
- **不支持 BSE(北交所 920xxx**
### 1.4 源降级链(15min
```
增量刷新(每日 15:30 cron:
sina 15min(主,800 条/次)→ 腾讯 minute/query(备,仅当天)
历史回填(一次性):
baostock adjustflag=3(全量历史,2024-01-01 起,不含 BSE
```
---
## 二、覆盖现状(2026-07-12 审计+回填后)
| 维度 | 数值 |
|------|------|
| 全市场 universe | **5493** |
| 主板 SH/SZ 覆盖 | **5193** = 5023 老股(≥2.5 年,sina 长期累积)+ 170 新股(baostock 回填至上市日)|
| BSE 北交所缺口 | **300**baostock + sina 均不支持,待 akshare/腾讯另接)|
| 15min 主目录文件数 | 5403 |
| 数据新鲜度 | 2026-07-08 ~ 2026-07-10 |
### 2.1 数据深度
| 股票类型 | 深度 | 起点 |
|----------|------|------|
| 成熟股(5023 只) | ≈ 2.5 年 | 2024-01-01sina 长期累积 + baostock 回填)|
| 新股(170 只) | = 上市日 | baostock 回填至各自上市日 |
| 5 年深度扩展(未来) | 2.5yr → 5yr | baostock 从 2020 起回填(大工程,未来阶段)|
### 2.2 BSE 缺口说明
- 300 只北交所股票(920xxxbaostock 和 sina 均不支持
- 多为小盘新股,多数策略可剔除
- 待后续用 akshare / 腾讯另接(东财接口有封 IP 风险,建议按需)
---
## 三、脚本清单(`scripts/data_platform/`
| 脚本 | 作用 | 关键点 |
|------|------|--------|
| `download_minute.py` | sina 增量刷新 | `STOCK_ROOT` 环境变量参数化(默认 `/Volumes/stock`NAS 用 `/volume1/stock`);0.3s/票单线程;断点续传 `download_progress.json``--scope all/hs300``--codes``--resume` |
| `backfill_15min_baostock.py` | baostock 历史回填 | 全量重建 + 备份 `backup_sina/``adjustflag=3`0.4s/票;marker 防重;每 400 票 relogin |
| `refresh_15min_daily.py`(新) | cron 入口 | pop 代理直连;交易日判断(周末短路 + baostock `query_trade_dates`);调 `download_minute --scope all --resume`;日志 `$STOCK_ROOT/logs/daily_update/` |
### 3.1 `download_minute.py` 关键参数
| 参数 | 值 / 说明 |
|------|----------|
| `STOCK_ROOT` | `os.environ.get("STOCK_ROOT", "/Volumes/stock")`line 47|
| `OUTPUT_DIR` | `$STOCK_ROOT/minute_kline/15min` |
| `REQUEST_INTERVAL` | 0.3s |
| `MAX_RETRIES` | 3 |
| 连续失败暂停 | 5 次连续失败 → 暂停 60s |
| 写入策略 | 增量合并:`concat` + `drop_duplicates(subset=["day"], keep="last")` + 原子写 `.tmp``rename` |
### 3.2 `backfill_15min_baostock.py` 关键参数
| 参数 | 值 / 说明 |
|------|----------|
| `NAS_ROOT` | 硬编码 `/Volumes/stock`line 43**待参数化**|
| `adjustflag` | `"3"`(不复权,line 131|
| `RELOGIN_EVERY` | 400 祒(line 268|
| 防重 marker | `.{stem}.baostock` 空文件(line 98/208|
| 旧数据备份 | `$MINUTE_15_DIR/backup_sina/` |
### 3.3 `refresh_15min_daily.py` 职责
1. pop 全部代理环境变量(`http_proxy/https_proxy/...`)保证直连
2. 交易日判断:周末短路;工作日用 baostock `query_trade_dates`,失败降级为"默认交易日"
3. 交易日 → `subprocess``download_minute.py --scope all --resume`,继承 `STOCK_ROOT`
4. 日志写 `$STOCK_ROOT/logs/daily_update/refresh_15min_YYYYMMDD.log`
---
## 四、刷新机制(本次新落地)
### 4.1 调度
- **NAS Synology 任务计划**,每交易日 **15:30**A 股 15:00 收盘后半小时)
- 之前无任何自动刷新(crontab / Synology / 容器 cron 全空),7-08~10 的数据新鲜是手动跑的;本次补 cron
### 4.2 执行命令
容器 `sanguo_vnpy_v2` bind-mount `/volume1/stock`,路径在容器内不变:
```bash
docker exec sanguo_vnpy_v2 bash -c "cd /app && python3 scripts/data_platform/refresh_15min_daily.py"
```
### 4.3 交易日判断
- 周末(weekday ≥ 5)→ 直接跳过
- 工作日 → baostock `query_trade_dates` 查节假日
- baostock 不可用 → 降级为"工作日默认交易日"(非交易日跑也只是全部 skip,幂等)
---
## 五、硬约束
> 来源:CLAUDE.md 全局约定 + 数据下载经验(见 MEMORY.md
| 约束 | 说明 | 实现 |
|------|------|------|
| **直连不走代理** | 避免被识别为异常流量 / akshare 代理污染 | 脚本入口 pop `http_proxy/https_proxy/...``download_minute._make_opener()``ProxyHandler({})` |
| **单线程限速,0 并发** | baostock 单连接并发会崩;新浪猛打封 IP | sina 0.3s/票,baostock 0.4s/票,无并发 |
| **间隔别太大** | baostock 长空闲断会话 | sina 0.3s / baostock 0.4s |
| **NAS 内存紧** | swap 近满,分块+断点续传,别全市场并发 | 历史踩过 macOS Jetsam 崩溃(见 MEMORY 数据下载崩溃教训)|
| **见空就停** | 连续 5 空 = 会话掉了 | `MAX_CONSECUTIVE_FAILS=5` → 暂停 60s |
---
## 六、路径映射表(Mac / NAS / 容器 三端)
| 端 | `STOCK_ROOT` | 15min 目录 |
|----|--------------|-----------|
| Mac 开发 | `/Volumes/stock`NAS 挂载) | `/Volumes/stock/minute_kline/15min` |
| NAS host | `/volume1/stock` | `/volume1/stock/minute_kline/15min` |
| 容器 `sanguo_vnpy_v2` | `/volume1/stock`bind-mount | 同 NAS |
> 对应 `config/data_platform.yaml` 路径键:`minute_15_dir: /volume1/stock/minute_kline/15min`(容器/NAS 视角)。脚本通过 `STOCK_ROOT` 环境变量切换,不写死。
---
## 七、已知问题 / 后续
| 优先级 | 问题 | 说明 | 处理 |
|--------|------|------|------|
| **HIGH bug** | `download_minute.py` `_aggregate_1m_to_15m``amount=("amount","last")` 应为 `"sum"` | 腾讯备源路径 amount 聚合错误(sina 主源不受影响) | 待修(line 147)|
| MEDIUM | BSE 920 缺口 300 只 | baostock + sina 均不支持 | 待 akshare/腾讯另接(东财有封 IP 风险,建议按需)|
| LOW | 5 年深度扩展(5023 老股 2.5yr → 5yr | baostock 从 2020 起回填,大工程 | 未来阶段 |
| LOW | `backfill_15min_baostock.py``NAS_ROOT` 仍硬编码 `/Volumes/stock` | Mac 视角写死,NAS 跑需手改 | 建议后续也参数化为 `STOCK_ROOT` |
---
## 八、相关文件索引
| 文件 | 路径 | 说明 |
|------|------|------|
| 15min 主目录 | `$STOCK_ROOT/minute_kline/15min/` | 5403 个 `_15min.parquet` 文件 |
| 断点续传进度 | `$STOCK_ROOT/minute_kline/15min/download_progress.json` | `download_minute.py --resume` 用 |
| 审计清单 | `/volume1/stock/minute_kline/15min/backfill_target.json` | 470 个回填目标 |
| 回填进度 | `/volume1/stock/minute_kline/15min/backfill_470_progress.json` | `done=170`, `bse_unsupported=300` |
| 旧文件备份 | `/volume1/stock/minute_kline/15min/backup_sina/` | baostock 全量重建前的 sina 旧数据 |
| 日刷新日志 | `$STOCK_ROOT/logs/daily_update/refresh_15min_YYYYMMDD.log` | cron 每日产出 |
| 配置 | `config/data_platform.yaml` | `minute_15_dir` 路径键 |
| 配置加载 | `sanguo_data/config.py` | `DataConfig.data_paths["minute_15_dir"]` |
---
## 变更记录
| 日期 | 变更 | 作者 |
|------|------|------|
| 2026-07-12 | 初始版本:15min 数据层落地后真实数据(5493 universe / 5193 覆盖 / 300 BSE 缺口 / cron 15:30 / sina主源+baostock回填) | 文档 Sub Agent |