docs(data): akshare 低频 schtask plan(spec §14.5 A+B+C)
spec §14.5 akshare/index 低频 schtask 部署 plan, 待 compact 后新 session 执行: - A 三表/估值增量(sanguo-ak-eod 日频 + ak-quarter 季频, --force 全量夜间慢跑) - B 成份股月度(sanguo-index, 改 merge_constituent 可重跑 DROP/REPLACE + 3源wrapper + TDD) - C 事件类 7 种(ak-events per-date 日频 + ak-stock per-stock 周频) 实测现状: static 停 7/22 18:25(provider依赖) / events 全空(从未采集) / constituent_unified 7110 静态 / 旧 schtask 全无(方案A stop_all 清了) 关键约束: akshare marker symbol级→三表/估值必须--force更新 / per-stock全量慢+东财限流→夜间 / merge_constituent RENAME不可重跑→改幂等 plan 自包含(fresh agent 可执行), 关联 memory akshare-low-freq-plan
This commit is contained in:
@@ -0,0 +1,168 @@
|
||||
# akshare 低频任务 schtask 部署 Plan (spec §14.5)
|
||||
|
||||
> **For agentic workers:** REQUIRED SUB-SKILL: superpowers:subagent-driven-development / executing-plans。本 plan 自包含(实测现状+脚本能力+VPS访问),fresh agent 可直接执行。
|
||||
|
||||
**Goal:** 部署 spec §14.5 akshare/index 低频 schtask — A 三表/估值增量 + B 成份股月度 + C 事件类 7 种。方案A 数据层(`dbbardata`/`constituent_unified`/`valuation_baostock`)的使用层配套,补全 `LocalUnifiedProvider` 依赖的静态数据源。
|
||||
|
||||
**Architecture:** 复用 `akshare_static_download.py`(16类/marker断点/4模式)+ `baostock_constituent_download.py`;改 `merge_constituent.py`/`migrate_constituent.py` 可重跑;按 akshare per-stock 全量慢 + 东财限流,拆多 schtask(日频/季频/月频)。
|
||||
|
||||
**Tech Stack:** Python 3.10, akshare, baostock, sqlite3, Windows schtasks
|
||||
|
||||
## Global Constraints(铁律)
|
||||
|
||||
- **akshare**: 单线程 0.8s sleep / 30s 超时 / 断路器(连30 failed exit) / marker 断点 / **unset proxy** — `akshare_static_download.py` 已内置;东财限流严,**per-stock 全量慢,夜间跑**
|
||||
- **baostock**: 单进程单登录 48000/天,不并发(防黑名单)
|
||||
- **staging→验证→合并**(成份股 B,用户铁律:下载质量不可控,不直接写主库)
|
||||
- **VPS**: ssh alias = `49.232.102.198`(IP 即 alias,User Administrator,key id_ed25519);`C:\Python310\python.exe -X utf8`;schtasks `/create /ru SYSTEM /rl HIGHEST /sc daily|monthly`;Windows ssh 引号地狱 → 脚本 scp + ssh python 跑最稳
|
||||
- **dbbardata UNIQUE 不破坏**;constituent_unified 治偏差
|
||||
- 直连不走代理(schtask wrapper 开头 `unset http_proxy https_proxy all_proxy`)
|
||||
|
||||
## 实测现状(2026-07-23 probe_akshare_status.py)
|
||||
|
||||
- **static/**(`C:\sanguo_vnpy_v2\data\static\`): balance/income/cashflow/valuation/financial_abstract 各 **5530 parquet,停 2026-07-22 18:25**(sanguo-bs-akshare disabled 前最后一次);provider fundamentals 依赖,要续更
|
||||
- **events/**: **全 MISSING**(龙虎榜/北向/两融/解禁/大宗/可转债/研报从未采集)
|
||||
- **constituent_unified**: 7110 行 = baostock 2938(000016:195/000300:940/000905:1803) + akshare_cni 1172(深证 399001:702/399005:145/399006:175/399330:150) + akshare_csindex 3000(000852:1000/932000:2000);**静态,月度更新 schtask 无**
|
||||
- **schtask 状态**: sanguo-bs-akshare / sanguo-index / sanguo-index-hist **全无**(方案A `stop_all_data_schtasks.ps1` 清了)
|
||||
- **akshare_static_download.py 16 类分 5 组**:
|
||||
- PER_STOCK(5500股循环,unit=`{symbol}_{type}`): valuation/northbound/share_capital/balance/income/cashflow/financial_abstract
|
||||
- TOP_HOLDERS(per-stock×period): top_holders
|
||||
- PER_DATE(每交易日,unit=`{date}_{type}`): dragon_tiger/block_trade/margin_sse/restricted
|
||||
- PER_PERIOD(报告期,unit=`{period}_{type}`): forecast/express
|
||||
- ONE_SHOT: index_const/industry
|
||||
- marker 是 **symbol 级**(非 period 级)→ 三表/估值要更新新数据必须 `--force`(否则 marker 跳过永不更新)
|
||||
- **merge_constituent.py 不可重跑**: `ALTER TABLE bs_index_constituent RENAME TO bs_index_constituent_old` 只能一次(_old 已存在);无 DROP/REPLACE constituent_unified
|
||||
- **existing wrappers**(参考模式): `bs_eod_wrapper.ps1` / `xt_eod_wrapper.ps1`(unset proxy + log + 调 python);`register_schtasks.ps1`(schtasks /create 模板)
|
||||
|
||||
## schtask 清单(方案A §14.5 适配,按频率拆)
|
||||
|
||||
| schtask | 频率 | 时间 | 脚本 | 内容 |
|
||||
|---|---|---|---|---|
|
||||
| `sanguo-ak-eod` | daily | 19:00 | ak_eod_wrapper.ps1 | valuation + financial_abstract `--force`(日频,5500×2×0.8s≈2.2h 夜间) |
|
||||
| `sanguo-ak-quarter` | monthly(财报季 5/9/11 月+年报4月) | 周末 02:00 | ak_quarter_wrapper.ps1 | balance + income + cashflow + forecast + express `--force`(季频,5500×3×0.8≈2.2h) |
|
||||
| `sanguo-ak-events` | daily | 19:30 | ak_events_wrapper.ps1 | dragon_tiger + block_trade + margin_sse + restricted `--start today --end today`(per-date 日频,4 unit 快) |
|
||||
| `sanguo-ak-stock` | weekly | 周六 03:00 | ak_stock_wrapper.ps1 | northbound + share_capital + top_holders(per-stock 慢,周频) |
|
||||
| `sanguo-index` | monthly | 19:50 | index_monthly_wrapper.ps1 | 成份股 3 源 + merge(B) |
|
||||
|
||||
## File Structure
|
||||
|
||||
- **Modify:** `scripts/data_platform/merge_constituent.py`(可重跑: DROP/REPLACE 替代 RENAME)
|
||||
- **Modify:** `scripts/data_platform/migrate_constituent.py`(可重跑: staging 隔离 + DROP staging 重建)
|
||||
- **Create:** `scripts/data_platform/ak_eod_wrapper.ps1` / `ak_quarter_wrapper.ps1` / `ak_events_wrapper.ps1` / `ak_stock_wrapper.ps1`(4 个 akshare wrapper)
|
||||
- **Create:** `scripts/data_platform/index_monthly_wrapper.ps1`(B 成份股 3 源编排)
|
||||
- **Create:** `scripts/data_platform/register_akshare_schtasks.ps1`(注册 5 schtask)
|
||||
- **Create:** `scripts/data_platform/verify_akshare_e2e.py`(验证全部)
|
||||
- **Test:** `tests/portfolio/test_merge_constituent_rerun.py`(B 改造 TDD)
|
||||
|
||||
---
|
||||
|
||||
## Task A: 三表/估值增量(2 schtask)
|
||||
|
||||
**Files:** Create 4 akshare wrapper + register; 复用 `akshare_static_download.py`(不改)。
|
||||
|
||||
- [ ] **A1: ak_eod_wrapper.ps1**(日频估值/财务摘要)
|
||||
```powershell
|
||||
# unset proxy + 调 akshare_static_download.py --types valuation,financial_abstract --force
|
||||
$env:http_proxy=""; $env:https_proxy=""; $env:all_proxy=""
|
||||
cd C:\sanguo_vnpy_v2
|
||||
C:\Python310\python.exe -X utf8 scripts\data_platform\akshare_static_download.py `
|
||||
--types valuation,financial_abstract --force `
|
||||
*>> C:\sanguo_vnpy_v2\data\ak_eod.log
|
||||
```
|
||||
- [ ] **A2: ak_quarter_wrapper.ps1**(季频三表+预告/快报,财报季)— 同上 `--types balance,income,cashflow,forecast,express --force`
|
||||
- [ ] **A3: 验证 A** — scp wrapper + 手动跑 `--limit 5` 确认 valuation parquet 更新今日:
|
||||
```bash
|
||||
scp scripts/data_platform/ak_eod_wrapper.ps1 49.232.102.198:'C:/sanguo_vnpy_v2/scripts/data_platform/'
|
||||
ssh 49.232.102.198 'cd /d C:\sanguo_vnpy_v2 && C:\Python310\python.exe -X utf8 scripts\data_platform\akshare_static_download.py --types valuation --force --limit 3'
|
||||
# 验 static/valuation/<code>_valuation.parquet mtime = 今日
|
||||
```
|
||||
- [ ] **A4: 注册 schtask**(register_akshare_schtasks.ps1 含 sanguo-ak-eod daily 19:00 + sanguo-ak-quarter monthly)
|
||||
|
||||
---
|
||||
|
||||
## Task B: 成份股月度(sanguo-index)— 代码改造 TDD
|
||||
|
||||
**Files:** Modify `merge_constituent.py` + `migrate_constituent.py`; Create `index_monthly_wrapper.ps1`; Test `test_merge_constituent_rerun.py`。
|
||||
|
||||
- [ ] **B1: 写失败测试 — merge_constituent 可重跑**
|
||||
```python
|
||||
# tests/portfolio/test_merge_constituent_rerun.py
|
||||
def test_merge_constituent_rerun_twice(tmp_path):
|
||||
"""merge_constituent 跑两次不崩(第二次 DROP 重建,不 RENAME 已 _old 的表)。"""
|
||||
db = tmp_path / "t.db"; c = sqlite3.connect(str(db))
|
||||
# 造 constituent_unified + bs_index_constituent_old(已存在,_old 状态)
|
||||
c.execute("CREATE TABLE constituent_unified(index_code,code,source,in_current,was_removed)")
|
||||
c.execute("CREATE TABLE constituent_unified_staging(index_code,code,source,in_current,was_removed)")
|
||||
c.execute("CREATE TABLE bs_index_constituent_old(code,date)") # _old 已存在
|
||||
c.executemany("INSERT INTO constituent_unified_staging VALUES(?,?,?,?,?)",
|
||||
[("000300","600519","baostock",1,0)])
|
||||
c.commit(); c.close()
|
||||
# 跑两次
|
||||
import scripts.data_platform.merge_constituent as m # 或函数级 import
|
||||
m.merge(str(db)) # 第一次: staging→unified(DROP 旧 unified 重建)
|
||||
m.merge(str(db)) # 第二次: 不崩, unified 仍 1 行
|
||||
c = sqlite3.connect(str(db))
|
||||
assert c.execute("SELECT COUNT(*) FROM constituent_unified").fetchone()[0] == 1
|
||||
```
|
||||
|
||||
- [ ] **B2: 改 merge_constituent.py 可重跑** — 把 `ALTER TABLE bs_index_constituent RENAME TO _old`(只能一次)改为:staging→`DROP TABLE IF EXISTS constituent_unified`→`CREATE constituent_unified AS SELECT FROM staging`。bs_index_constituent_old 已存在不碰。幂等。
|
||||
|
||||
- [ ] **B3: 改 migrate_constituent.py 可重跑** — staging 表 `DROP IF EXISTS constituent_unified_staging` 重建(每次重新聚合 baostock 988 时点 + akshare cni union + csindex),不依赖上次状态。
|
||||
|
||||
- [ ] **B4: index_monthly_wrapper.ps1**(3 源编排)
|
||||
```powershell
|
||||
$env:http_proxy=""; $env:https_proxy=""; $env:all_proxy=""
|
||||
cd C:\sanguo_vnpy_v2
|
||||
# 1. baostock 300/500/50 最新快照(单进程)
|
||||
C:\Python310\python.exe -X utf8 scripts\data_platform\baostock_constituent_download.py --start 2026-01-01
|
||||
# 2. akshare cni 深证 + csindex 中证(复用 P0 脚本 index_const_hist_download 或 akshare_static_download --types index_const)
|
||||
C:\Python310\python.exe -X utf8 scripts\data_platform\akshare_constituent_download.py
|
||||
# 3. migrate + merge(可重跑版)
|
||||
C:\Python310\python.exe -X utf8 scripts\data_platform\migrate_constituent.py
|
||||
C:\Python310\python.exe -X utf8 scripts\data_platform\merge_constituent.py
|
||||
```
|
||||
(注:akshare_constituent_download.py 若不存在,从 `akshare_static_download.py --types index_const` 或 P0 的 `index_const_hist_download.py` 复用;执行 agent 确认现有脚本)
|
||||
|
||||
- [ ] **B5: 验证 B** — 手动跑 wrapper,确认 `constituent_unified` 行数 ≥ 7110,source 含 baostock/akshare_cni/akshare_csindex,跑两次不崩。
|
||||
- [ ] **B6: 注册 sanguo-index monthly 19:50**
|
||||
|
||||
---
|
||||
|
||||
## Task C: 事件类(per-date 日频 + per-stock 周频)
|
||||
|
||||
**Files:** Create `ak_events_wrapper.ps1` + `ak_stock_wrapper.ps1`(已在 A 的 register 注册)。
|
||||
|
||||
- [ ] **C1: ak_events_wrapper.ps1**(per-date 日频)— `--types dragon_tiger,block_trade,margin_sse,restricted --start {today} --end {today}`。每日 4 类×1 unit,快。落 `events/{type}/{date}_{type}.parquet`。
|
||||
- [ ] **C2: ak_stock_wrapper.ps1**(per-stock 周频慢)— `--types northbound,share_capital,top_holders --force`。5500×3 慢,周六 03:00。
|
||||
- [ ] **C3: 可转债/研报**(spec §14.2 列但 akshare_static_download.py 16 类无对应 fetcher) — **评估**:若 akshare 有 `bond_zh_hs_cov_min`/`stock_research_info_em` 接口,加 fetcher;否则 N/A 标注使用说明。执行 agent 确认 akshare 接口可用性,不可用则跳过并在 verify 标注。
|
||||
- [ ] **C4: 验证 C** — 手动跑 events wrapper `--start today --end today`,确认 `events/dragon_tiger/{today}_dragon_tiger.parquet` 生成。
|
||||
|
||||
---
|
||||
|
||||
## Task D: 注册 + E2E
|
||||
|
||||
- [ ] **D1: register_akshare_schtasks.ps1** — 注册 5 schtask(sanguo-ak-eod/ak-quarter/ak-events/ak-stock/index),`/create /ru SYSTEM /rl HIGHEST`,verify 段 `schtasks /query` 确认 Status=Ready。
|
||||
- [ ] **D2: verify_akshare_e2e.py** — 验证全部:
|
||||
- static/valuation 最新 mtime = 近日
|
||||
- events/dragon_tiger 有 parquet
|
||||
- constituent_unified ≥ 7110 + 3 source
|
||||
- [ ] **D3: 更新 memory + 使用说明** — `data-fusion-design-finalized` 的"剩余待办 akshare schtask"标完成;`docs/portfolio_local_unified_provider.md` 事件类从 N/A 更新。
|
||||
|
||||
---
|
||||
|
||||
## Self-Review
|
||||
|
||||
1. **spec §14.5 覆盖**: sanguo-bs-akshare(三表+事件)→ 拆 ak-eod/ak-quarter/ak-events/ak-stock(频率适配);sanguo-index 月度 → B。✓
|
||||
2. **provider 依赖**: valuation/balance/income/cashflow/financial_abstract(LocalUnifiedProvider.get_fundamentals_df)→ A 覆盖。✓
|
||||
3. **限流现实**: per-stock --force 全量慢,日频 valuation 2.2h / 季频三表 2.2h,夜间 + 周末 schtask。events per-date 日频快。✓
|
||||
4. **B 可重跑**: merge DROP 重建幂等,migrate staging 隔离。TDD 跑两次不崩。✓
|
||||
5. **风险**: akshare 东财封 IP(per-stock 全量)→ wrapper 内置断路器 + sleep;财报季触发 ak-quarter(`/sc monthly` 指定月或手动);可转债/研报接口待确认(C3)。
|
||||
|
||||
## Execution Handoff
|
||||
|
||||
Plan saved to `docs/superpowers/plans/2026-07-23-akshare-low-freq-schtask.md`。compact 后新 session 派 Sub Agent 执行(参考 LocalUnifiedProvider 模式:Task A→B→C→D,每 task 验证 + commit)。
|
||||
|
||||
## 关联文档/memory
|
||||
|
||||
- spec: `docs/superpowers/specs/2026-07-21-data-source-fusion-design.md` §14.5
|
||||
- memory: `data-fusion-design-finalized`(方案A)/ `local-unified-provider-complete`(使用层)/ `vps-local-data-layout` / `baostock-concurrent-blacklist` / `schtasks-system-bat-gotchas`
|
||||
- VPS 访问: ssh `49.232.102.198`,见 memory `windows-vps-access`
|
||||
Reference in New Issue
Block a user