feat(data): 数据源融合 P0 补全 + 每日增量脚本
采集层(多源各下): - baostock: 日线全字段全量(baostock_daily_fullmarket) + 15min全市场 + 静态(基础/复权/分红/季频/三表) + 成份股 - akshare: 静态(估值/龙虎榜/大宗/融资融券/北向/指数成分/行业/股本/解禁/业绩预告) - xtdata(miniQMT): build_daily_from_xtdata + daily_update_xtdata 数据补全 P0: - ETF全市场: universe 扩展 沪深A股∪ETF∪基金(7414), dividend_type='front' 前复权 - 历史成份股(治幸存者偏差): index_const_hist_download 深证/国证 adjust_cni 4指数 + 中证1000/2000快照 + 新浪交叉校验 - 退市K线: baostock_delisted_download + import_delisted_to_db(实证 Day1 fetch_all_stocks 已含退市) 灌库: - import_baostock_to_db: daily_baostock_full(5537股/1826万行,18字段)+ bs_index_constituent + bs_adjust_factor - INSERT OR REPLACE 幂等, WAL+busy_timeout, dbbardata 不碰 每日增量 #7(用户决策A: VPS直跑): - daily_update_static: login探针防黑名单graceful skip + LOOKBACK7 + query_stock_basic含退市 + INSERT OR REPLACE + QUERY_COUNT守48000/天 设计文档: spec(13节三层融合) + P0 plan + 数据gap设计
This commit is contained in:
@@ -0,0 +1,82 @@
|
||||
# LocalParquetProvider V1 数据缺口记录
|
||||
|
||||
> V1 已通过 VPS 真实数据验证(2026-07-21):
|
||||
> fundamentals 字段值合理(茅台市值 18433亿/PE 23.6/ROE 0.19/净利率 0.51)、
|
||||
> get_price/get_index_stocks/trade_days/all_securities 全通、B_mean 趋势信号正常、
|
||||
> 回测出完整 JSON(117 交易日, 0.4s/月, 无 baostock 卡死)。
|
||||
>
|
||||
> **0 交易根因(非 provider bug)**: `_pick_big_universe` 选股 target=[]
|
||||
> = `big` filter 8 条件 AND 过严 + `roic_big` 用 roic(V1 NaN) + bm market_cap 100-900亿
|
||||
> 不匹配 hs300 大盘 + B_mean<0 时兜底海外 ETF(无 K 线)。补 roic + 调 filter 阈值即出交易。
|
||||
>
|
||||
> 以下缺口不阻塞 MVP 链路验证,但全市场正式回测前需补齐。
|
||||
|
||||
## 缺口 1: 历史成分股(治幸存者偏差,重要 ⚠️)
|
||||
|
||||
**现状**: VPS `static/index_const/index_const.parquet` 仅 **2026-07-17 最新一期**快照。
|
||||
`get_index_stocks(index, date)` 的 `date` 参数当前被忽略(无历史数据可读)。
|
||||
|
||||
**影响**: 回测 2020 年选股池 = "现在还在 hs300/zz500 里的股票" → 幸存者偏差(结果虚高)。
|
||||
`max_pool` 小范围验证影响相对小(只取前 N 只),**全市场轮动回测前必须补**。
|
||||
|
||||
**补齐方案**(任选,不用 baostock online):
|
||||
- akshare `index_stock_cons_csindex(symbol="000300")` 按调仓日拉历史成分(csindex 源)
|
||||
- 中证指数官网 csindex.com.cn 历史成分下载
|
||||
- 用户侧(数据补全 session)补到 `static/index_const_history/` 多期 parquet, provider 加日期过滤
|
||||
|
||||
## 缺口 2: gross_profit_margin(V1 NaN)
|
||||
|
||||
**现状**: akshare income 表无明确"营业成本(COGS)"列(有 OPERATE_INCOME 营收、OPERATE_EXPENSE 营业总成本,但非纯 COGS)。
|
||||
V1 `gross_profit_margin` 置 NaN,策略 filter 该阈值失效(不过滤毛利率)。
|
||||
|
||||
**补齐方案**: 从 `static/financial_abstract/{code}_*.parquet` 读现成"销售毛利率"
|
||||
(宽表 指标×季度,含 1990-2026)。解析:找指标行"销售毛利率",取最新季度列。
|
||||
|
||||
## 缺口 3: roic(V1 NaN)
|
||||
|
||||
**现状**: ROIC = NOPAT / (权益 + 有息负债 - 现金),需有息负债拆分。
|
||||
V1 置 NaN。balance 表有 BORROW_FUND/BOND_PAYABLE 等字段可算。
|
||||
|
||||
**补齐方案**: balance 读 BORROW_FUND(短期借款) + BOND_PAYABLE(应付债券) + SUBBOND_PAYABLE
|
||||
+ 现金(CASH_DEPOSIT_PBC 附近字段),算 roic。NOPAT = 营业利润 ×(1 - 税率)。
|
||||
|
||||
## V1 单位口径备忘(VPS 实测验证合理 ✅)
|
||||
|
||||
| 字段 | VPS 源单位 | 转换 | 验证值(2024-06) |
|
||||
|---|---|---|---|
|
||||
| market_cap | 总市值(元) | /1e8 转亿 | 茅台 18433 亿 ✅ |
|
||||
| circulating_market_cap | 流通市值(元) | /1e8 | ✅ |
|
||||
| pe_ratio | PE(TTM) 数值 | 直接 | 茅台 23.6 ✅ |
|
||||
| pb_ratio | 市净率 数值 | 直接 | 茅台 7.69 ✅ |
|
||||
| ps_ratio/pcf_ratio | 市销率/市现率 | 直接 | ✅ |
|
||||
| eps | BASIC_EPS 元 | 直接 | 茅台 33.19 ✅ |
|
||||
| roe | 归母净利润/归母权益 | 小数(单期非TTM) | 茅台 0.19 ✅ |
|
||||
| roa | 净利润/总资产 | 小数 | ✅ |
|
||||
| net_profit_margin | 归母净利润/营收 | 小数 | 茅台 0.51 ✅ |
|
||||
| inc_revenue_yoy | OPERATE_INCOME_YOY 百分数 | /100 | 茅台 +0.18 ✅ |
|
||||
| total_liability | TOTAL_LIABILITIES 元 | /1e8 | 浦发 85000 亿 ✅ |
|
||||
| total_sheet_owner_equities | TOTAL_PARENT_EQUITY 元 | /1e8 | ✅ |
|
||||
| retained_profit | SURPLUS_RESERVE+UNASSIGN_RPOFIT | /1e8 | ✅ |
|
||||
|
||||
## VPS 两种代码格式(已适配,备忘)
|
||||
|
||||
VPS `data/` 下代码格式**不统一**:
|
||||
- **K 线** `qfq/{年}/` `raw/{年}/`: baostock 风格 `sh600000_daily.parquet`(sh/sz 前缀无点)
|
||||
→ `jq_to_kline_code("600000.XSHG") = "sh600000"`
|
||||
- **三表/估值** `static/{table}/`: jq 后缀 `000001.SZ_balance.parquet`
|
||||
→ `jq_to_file_code("000001.XSHE") = "000001.SZ"`
|
||||
|
||||
`get_price` 用 `jq_to_kline_code`,`get_fundamentals_df` 用 `jq_to_file_code`。
|
||||
|
||||
## V1 已验证可用的接口
|
||||
|
||||
| 方法 | 状态 | 备注 |
|
||||
|---|---|---|
|
||||
| get_price | ✅ | qfq 日线, 单股 index=date / 多股 panel |
|
||||
| get_fundamentals_df | ✅ | 19 列对齐 _FUNDAMENTAL_COLUMNS, 字段值合理 |
|
||||
| get_security_info | ✅ | valuation 最新行 |
|
||||
| get_trade_days | ✅ | sh600000 K 线 date 列 |
|
||||
| get_all_securities | ✅ | 5528 股 |
|
||||
| get_index_stocks | ⚠️ | 仅当前快照(缺口 1) |
|
||||
| get_current_tick | ✅ | valuation 推算 close + 涨跌停(主板±10%) |
|
||||
| get_split_dividend | ✅ | 占位返空(qfq 已复权) |
|
||||
@@ -0,0 +1,105 @@
|
||||
# A股静态数据全量缓存到 VPS — 设计与采集计划
|
||||
|
||||
> 2026-07-19 立。目标:全市场 A股静态/基本面/参考数据全量缓存到 VPS 本地(parquet),作选股(基本面)与回测数据源。
|
||||
|
||||
## 原则(用户钦定)
|
||||
1. **尽量多缓存**——能下的全下,避免限流/网络依赖。
|
||||
2. **串行可,等待长可接受**——不追求并发速度,稳定性优先。
|
||||
3. **准确性第一**——下错不如不下,每类数据必须验证。
|
||||
4. **建立每日自动更新**——历史一次灌满 + 每日增量。
|
||||
5. 用途:基本面选股 + 回测。盘中实时当日数据(实盘)未来再做。
|
||||
|
||||
## 为什么缓存优于实时取(背景)
|
||||
历史静态数据(含"日频"的历史部分)永不改变。本地缓存:秒级读盘/零网络依赖/可复现快照/不触发限流。实时取历史:慢/不稳/不可复现/反复触发封 IP。**唯一非静态是"今天未收盘/未公布"的部分,日终收盘后即变静态。**
|
||||
|
||||
## 范围(全量 ~2GB)
|
||||
| 组 | 类别 | 频率 | 源 | 估算 |
|
||||
|---|---|---|---|---|
|
||||
| A 基础元数据 | 基础信息(代码/名称/交易所/板块/上市退市/状态) | 静态 | baostock | 1MB |
|
||||
| | 行业分类(申万/中信/概念) | 静态 | akshare | 30MB |
|
||||
| | 指数成分+权重(300/500/1000/国证2000) | 月 | akshare | 50MB |
|
||||
| B 财务 | 三大报表(资产/利润/现金流) | 季 | baostock | 150MB |
|
||||
| | 季频衍生指标(ROE/EPS/毛利率/净利增速/负债率/杜邦) | 季 | baostock | 200MB |
|
||||
| | 业绩预告/快报 | 季事件 | akshare | 30MB |
|
||||
| C 股本/公司行为 | 股本结构变动 | 事件 | akshare | 50MB |
|
||||
| | 十大股东+十大流通股东 | 季 | akshare | 250MB |
|
||||
| | 分红送转配股 | 事件 | baostock | 40MB |
|
||||
| | 限售解禁 | 事件 | akshare | 20MB |
|
||||
| D 估值/复权(日频) | 估值快照(PE/PB/PS/PF/股息率/市值/流通市值) | 日 | akshare | 300MB |
|
||||
| | 复权因子(qfq/hfq) | 日 | baostock | 150MB |
|
||||
| E 市场参考(日频,可选) | 龙虎榜/大宗交易/融资融券/北向/ST停复牌 | 日 | akshare | ~360MB |
|
||||
|
||||
## 存储格式与目录
|
||||
**parquet(每类一个目录)**,VPS `C:\sanguo_vnpy_v2\data\static\<type>\`。匹配现有 15min/daily parquet 模式,回测 pandas 直读,增量 append/overwrite 幂等。
|
||||
```
|
||||
data/static/
|
||||
basic/ 基础信息(全量刷新,1文件 or per-stock)
|
||||
industry/ 行业分类
|
||||
index_const/ 指数成分
|
||||
balance/ 资产负债表(per-stock parquet)
|
||||
income/ 利润表
|
||||
cashflow/ 现金流量表
|
||||
indicator/ 季频财务指标(ROE/EPS/...)
|
||||
forecast/ 业绩预告/快报
|
||||
share_capital/ 股本结构
|
||||
top_holders/ 十大股东
|
||||
dividend/ 分红送转
|
||||
lockup_release/ 限售解禁
|
||||
valuation/ 估值日频(PE/PB/市值)
|
||||
adjust_factor/ 复权因子
|
||||
dragon_tiger/ 龙虎榜
|
||||
block_trade/ 大宗交易
|
||||
margin/ 融资融券
|
||||
northbound/ 北向资金
|
||||
```
|
||||
|
||||
## 数据源映射 + 串行约束(关键)
|
||||
| 源 | 数据 | 并发约束 |
|
||||
|---|---|---|
|
||||
| **baostock** | 基础信息/复权因子/分红/季频指标/三表 | **单登录串行,跟15min共用登录→必须等15min跑完才能开**(并发=IP封6-24h) |
|
||||
| **akshare** | 估值/龙虎榜/大宗/融资融券/北向/指数成分/行业/股本/十大股东/解禁/业绩预告 | 不同源,可与baostock错峰;东财源要限速防反爬 |
|
||||
|
||||
## 准确性协议(每类数据强制)
|
||||
1. **断点续传 marker + 失败/空数据区分**(复用 15min 的 empty-vs-failed 修复)
|
||||
2. **下载后抽样验证(≥10只)**:字段完整 / 日期覆盖(回溯到2020) / 值合理性(PE>0、ROE合理区间、volume≥0、OHLC 自洽)
|
||||
3. **行数 + 覆盖率统计**写入日志
|
||||
4. **幂等写入**(per-stock parquet overwrite;INSERT OR REPLACE 若入 DB),staging 隔离→验证→合并(用户铁律:绝不直写主库/主目录未验)
|
||||
5. (可选)跨源抽检:baostock 季频财务 vs miniQMT PershareIndex 抽几只对一对
|
||||
|
||||
## 每日自动更新机制
|
||||
Windows schtask `sanguo-static-daily`,每日盘后 **16:30** 跑 `daily_update_static.py`:
|
||||
- **日频类(估值/龙虎榜/大宗/融资融券/北向)**:追加当日(或近N日补漏)
|
||||
- **小表全量刷新**:基础信息/行业/指数成分/分红(事件少,全量省得算增量)
|
||||
- **财报季(4/8/10月底后)**:追加新季报(三表/季频/十大股东)
|
||||
- **复权因子**:每日刷新(除权事件会改累计因子)
|
||||
- 失败告警 + 断点续传 + **绝不破坏既有数据**(只 append/replace 单日单股)
|
||||
- 串行 baostock 部分 + 限速 akshare 部分,单进程跑完
|
||||
|
||||
## 执行阶段
|
||||
- **Phase 0(进行中)**:15min baostock,ETA 07-19 ~22:30
|
||||
- **Phase 1**:baostock 静态下载脚本(基础/复权/分红/季频/三表)— **构建 now,运行须等15min完**
|
||||
- **Phase 2**:akshare 静态下载脚本(估值/龙虎榜/大宗/融资融券/北向/指数成分/行业/股本/十大股东/解禁/业绩预告)— **构建+可now起**(不同源)
|
||||
- **Phase 3**:每类抽样验证 → 修问题
|
||||
- **Phase 4**:每日更新 schtask + 验证增量
|
||||
|
||||
## akshare 调查结果(2026-07-19 确认,15类端点实证)
|
||||
**15类中 11 类端点直接可用,4 类有替代。财务类全部"单股一次拉全历史"(完美绕开 baostock per-quarter 百万调用)。全量 ~2.5-3GB,单线程 17-22h(可挂机,不同源可与 baostock 并行)。**
|
||||
|
||||
确认端点(4种模式):
|
||||
- **per-stock(5500股×1调用)**:估值`stock_value_em` / 北向`stock_hsgt_individual_em` / 股本`stock_share_change_cninfo` / 十大流通`stock_gdfx_free_top_10_em`(×20报告期) / **三大报表`stock_balance/profit/cash_flow_sheet_by_report_em`(319/203/254列全历史)** / 财务摘要`stock_financial_abstract`
|
||||
- **per-date(交易日×1调用)**:龙虎榜`stock_lhb_detail_em` / 大宗`stock_dzjy_mrmx(symbol="A股")` / 融资融券沪`stock_margin_detail_sse` / 解禁`stock_restricted_release_detail_em`
|
||||
- **per-period(报告期×1调用)**:业绩预告`stock_yjyg_em` / 业绩快报`stock_yjkb_em`
|
||||
- **one-shot**:指数成分`index_stock_cons_csindex`(300/500/1000) / 行业`sw_index_first_info`(申万,东财`stock_board_industry_name_em`ConnectionError 弃用)
|
||||
|
||||
**有问题/替代**:`stock_margin_detail_szse`(深融资融券)超时频繁→先跳过;`stock_gdfx_holding_detail_em(date)`按日全市场超时→改个股循环;`stock_a_indicator_lg`新版删→用`stock_value_em`。
|
||||
|
||||
**大小明细**:估值150MB / 三大报表1.2GB / 财务摘要300MB / 北向120MB / 融资融券500MB / 十大流通60MB / 其余<100MB各。**总~2.5-3GB**。
|
||||
|
||||
**优先级**:P0 三大报表+财务摘要(~1.5GB,~10h,核心财务)→ P1 估值+北向+融资融券(~770MB,~3h)→ P2 龙虎榜/大宗/解禁/业绩预告/股本/指数/行业(~300MB,~1h)。
|
||||
|
||||
## 部署架构(自愈链,2026-07-19)
|
||||
- **15min baostock**:schtask `sanguo-bs15min` + 自愈.bat(ping-sleep 30min重试)。2026-07-19 12:00 baostock全球故障(Mac+VPS同挂10002007),自愈中,恢复即续 from marker 2847。
|
||||
- **baostock静态**:schtask `sanguo-bs-static` + 自愈.bat(**wait15**等15min "ALL DONE" → 自动接力 → ping-sleep自愈)。脚本`baostock_static_download.py`已部署(basic/adjust_factor/dividend,rs.fields动态取字段)。
|
||||
- **akshare静态**:schtask `sanguo-bs-akshare`(待建)+ 自愈.bat。脚本构建中。**不同源,可与baostock并行**。
|
||||
- 监控:cron 779cbb71 每30min probe_all + 异常自修 + 完成报告。caffeinate防睡眠。
|
||||
- **.bat sleep 用 `ping -n N 127.0.0.1`**(timeout.exe在SYSTEM schtask下失效,见 memory schtasks-system-bat-gotchas)。
|
||||
@@ -0,0 +1,46 @@
|
||||
# 静态数据 3 个真缺口 — 补充设计(2026-07-19 记录,待当前下载完再动手)
|
||||
|
||||
> 当前下载(15min Mac补全 + akshare static + baostock static)跑完后再继续本设计。先记录存底。
|
||||
|
||||
## 数据现状实测(VPS quant_trading.db + data/ 目录,非推理)
|
||||
- **DB dbbardata**: 15m(2025-07~2026-07,1年,xt_tacitdata源)/5m(1年)/**d日线(2010~2026,16年,5205 symbols,OHLCV+amount)**。dbbardata schema 有 turnover(=成交额amount),**无换手率/涨跌幅列**。
|
||||
- **parquet data/raw + data/qfq**: 各 59816,O H L C V 6列,16年(xtdata建,build_daily_from_xtdata)。
|
||||
- **data/static/**: akshare balance 跑着(4300+ parquet)。
|
||||
- **实测缺口**:
|
||||
1. 日线**缺换手率+涨跌幅**(amount已在DB 16年)
|
||||
2. **ETF不在daily universe**(5205 symbols大概率纯股票,518880等海外ETF缺)
|
||||
3. **指数成分历史(含被踢)完全无**(全项目无脚本,akshare只当前快照)
|
||||
|
||||
## 3 缺口设计(派生方案,避开数据混乱+接口限流)
|
||||
|
||||
### 缺口1:日线换手/涨跌 → 派生,不新下载
|
||||
- **pct_chg** = (close今 - close昨)/close昨,**从 qfq close 算**(16年,避免除权跳空)。
|
||||
- **换手率** = volume / 流通股本。volume在DB;**流通股本在 akshare valuation(stock_value_em,排队P1,8.5年)**。
|
||||
- amount:DB已有(16年)。
|
||||
- **不开 daily_extra 新目录**,读取层派生 或 DB加列 → 不加剧"四套口径分裂" + **零新东财负载**。
|
||||
|
||||
### 缺口2:ETF日线 → 加进现有 xtdata universe
|
||||
- ETF清单:海外 518880(黄金)/513100(纳指)/513030(德国)/164824(石油)/159866(有色) + 主 510300/510500/159915等,maintain成config。
|
||||
- 加进 `build_daily_from_xtdata` 的 universe → 走现有 xtdata 本地管线(miniQMT),**不碰东财,无限流**。
|
||||
|
||||
### 缺口3:指数成分历史(含被踢) → csindex 抓取,先 spike
|
||||
- 范围:hs300(000300)/zz500(000905)/zz50(000016)/中小综指(399101)/创业板指(399006)。
|
||||
- **先派 agent spike 调研源**:akshare 有无历史成分API(index_stock_cons_weight_csindex带日期?fund_portfolio_hold_em?)、csindex.cn 历史成分xlsx URL规律+反爬、深证399101/399006 巨潮/szse 源。
|
||||
- **B档(务实,先行)**:抓全部历史调仓成分→并集(曾经入选集),消灭幸存者偏差。output `data/index_const_hist/<indexcode>.parquet`。
|
||||
- A档(精确,后做):时间序列(指数,生效日,成分,加/剔)。
|
||||
- csindex 独立源,串行单线程抓,**限流风险低**。
|
||||
|
||||
## 风险结论(为何用派生方案)
|
||||
- **原设计(akshare daily_extra)** 有双风险:① 数据混乱——日线口径第四处(DB/parquet raw/parquet qfq/daily_extra),回测不知读哪;② 接口限流——daily_fields_akshare 并发 akshare_static = 第二股东财流量→东财封(同 baostock 黑名单原理)。
|
||||
- **派生方案**:缺口1 两风险全消(不下载/不开新目录);ETF走xtdata无限流;csindex独立源串行低风险。
|
||||
- 唯一仍调外部API:缺口3(csindex)+ 已排队的 akshare static 本身——保持串行+限速,不新增并发。
|
||||
|
||||
## 决策与顺序(下载完后)
|
||||
1. **派生换手/涨跌**(读取层工具 或 DB加列)——缺口1
|
||||
2. **ETF 入 xtdata daily universe**——缺口2
|
||||
3. **csindex spike 调研** → 定 B档抓取脚本——缺口3
|
||||
4. (远期)A档精确成分时间序列
|
||||
|
||||
## 关联
|
||||
- 主计划:`docs/static_data_cache_plan.md`
|
||||
- 现状memory:`baostock-15min-vps-deploy-plan` / `db-primary-parquet-fallback` / `data-download-architecture`
|
||||
@@ -0,0 +1,221 @@
|
||||
# P0 数据补全实现计划(历史成份股 + ETF 全市场 + 退市 K 线)
|
||||
|
||||
> **For agentic workers:** 用 superpowers:subagent-driven-development 或 executing-plans 执行。Steps 用 `[ ]` 跟踪。
|
||||
|
||||
**Goal:** 补齐治幸存者偏差 + 策略核心缺口三类数据,落到 VPS 本地。
|
||||
|
||||
**Architecture:** 各源采集脚本 → staging parquet → 验证探针 → 合并主库;baostock 单登录守 48000/天;dbbardata 不动。
|
||||
|
||||
**Tech Stack:** python3.10 / akshare / baostock / xtquant(xtdata)/ pandas / pyarrow / sqlite3
|
||||
|
||||
---
|
||||
|
||||
## Global Constraints(所有 task 隐含)
|
||||
|
||||
- **baostock 单进程单登录**,不并发(防黑名单,日 ≤48000 query)
|
||||
- **直连不走代理**:`$env:http_proxy=''; $env:https_proxy=''; $env:all_proxy=''`
|
||||
- **dbbardata 不破坏**:只 INSERT OR REPLACE `daily_baostock_full` / 新表,不动 dbbardata 既有行
|
||||
- **优先 baostock + miniQMT(xtdata)**
|
||||
- **staging → 验证探针 → 合并主库**(用户铁律,不直接写主库)
|
||||
- Windows VPS 49.232.102.198,`C:\Python310\python.exe -X utf8`,schtasks `/ru SYSTEM`
|
||||
- 输出根:`C:\sanguo_vnpy_v2\data\`
|
||||
|
||||
---
|
||||
|
||||
## File Structure
|
||||
|
||||
| 文件 | 责任 |
|
||||
|---|---|
|
||||
| `scripts/data_platform/index_const_hist_download.py`(新) | 历史成份股采集(akshare 国证 + 新浪 + baostock 补时点) |
|
||||
| `scripts/data_platform/build_daily_from_xtdata.py`(改 :40) | ETF universe 扩展(一次性全量) |
|
||||
| `scripts/data_platform/daily_update_xtdata.py`(改 :114) | ETF 每日增量 universe |
|
||||
| `scripts/data_platform/baostock_delisted_download.py`(新) | 退市股列表 + K 线采集 |
|
||||
| `scripts/data_platform/import_delisted_to_db.py`(新) | 退市 K 线灌 `daily_baostock_full` |
|
||||
| 各 `*_wrapper.ps1` + schtask | 部署 |
|
||||
|
||||
---
|
||||
|
||||
## Task 1: 历史成份股采集(治幸存者偏差)
|
||||
|
||||
**Files:** Create `scripts/data_platform/index_const_hist_download.py`;Output `data/index_const_hist/{code}.parquet`
|
||||
|
||||
**Interfaces:**
|
||||
- Consumes: akshare `index_detail_hist_cni(symbol)` + `index_detail_hist_adjust_cni(symbol)`(国证源);新浪 `vII_HistoryComponent`(pandas.read_html, gb2312);baostock `query_hs300/zz500/sz50_stocks(date)`
|
||||
- Produces: `data/index_const_hist/{code}.parquet`(列:`updateDate/index_code/code/code_name/adjust_type`);并集 = 曾经入选集
|
||||
|
||||
**指数清单:**
|
||||
- 深证/国证(akshare 国证源):399001 / 399006 / 399101 / 399005 / 399330
|
||||
- 中证(新浪):000852(中证1000)/ 932000(中证2000)/ 000300(交叉校验)/ 000016(上证50)
|
||||
- baostock 已有(300/500/50 在 `bs_index_constituent`):Task1 补时点序列到同 schema
|
||||
|
||||
- [ ] **1.1 探针:akshare 国证源 hist 版**
|
||||
```python
|
||||
import akshare as ak
|
||||
df = ak.index_detail_hist_cni(symbol="399101") # 历史样本(日期/样本代码/权重)
|
||||
print(df.columns.tolist(), len(df), df.head(3))
|
||||
adj = ak.index_detail_hist_adjust_cni(symbol="399101") # 调样记录(调整类型 OLD/+/-)
|
||||
print(adj.columns.tolist(), len(adj))
|
||||
```
|
||||
预期:hist 有日期+样本+权重;adjust 有调整类型。**陷阱:必须 hist 版**(`index_detail_cni` 非 hist 版 2025-11-25 起只近期);`ak.index_stock_hist` 已下线别用。
|
||||
|
||||
- [ ] **1.2 探针:新浪中证历史成份**
|
||||
```python
|
||||
import pandas as pd
|
||||
url = "http://vip.stock.finance.sina.com.cn/corp/go.php/vII_HistoryComponent/indexid/000852.phtml"
|
||||
df = pd.read_html(url, encoding="gb2312")[0]
|
||||
print(df.columns.tolist(), len(df), df.head(3))
|
||||
```
|
||||
预期:品种代码/品种名称/纳入日期/剔除日期(空=至今在列),含 *ST/退市股。
|
||||
|
||||
- [ ] **1.3 实现 `index_const_hist_download.py`**:三路采集 → 统一 schema(`updateDate/index_code/code/code_name/adjust_type`)→ 写 `data/index_const_hist/{code}.parquet`。串行 `time.sleep(0.8)`(akshare/新浪防封),单进程。环境变量 `BS_INDEX_HIST_OUT_DIR` 覆盖默认 Mac 路径(同 Day1 wrapper 模式)。
|
||||
|
||||
- [ ] **1.4 验证探针**:每指数 parquet 行数 + 抽样 3 行;**幸存者偏差校验** = 并集 `distinct code` 数 > 当前成份股数(证明含被踢股,例如 399101 并集 > 958 当前)。
|
||||
|
||||
- [ ] **1.5 wrapper + schtask**:`index_const_hist_wrapper.ps1`(设 OUT_DIR + utf8 + unset proxy + log);schtask `sanguo-index-hist` `/sc monthly /mo 2`(半年度调样后,6/12 月)`/ru SYSTEM`。
|
||||
|
||||
- [ ] **1.6 commit**:`git add scripts/data_platform/index_const_hist_download.py scripts/data_platform/index_const_hist_wrapper.ps1 && git commit -m "feat(data): 历史成份股采集(治幸存者偏差,国证+新浪+baostock)"`
|
||||
|
||||
---
|
||||
|
||||
## Task 2: ETF 全市场日线
|
||||
|
||||
**Files:** Modify `scripts/data_platform/build_daily_from_xtdata.py:40` + `daily_update_xtdata.py:114`
|
||||
|
||||
**Interfaces:**
|
||||
- Consumes: xtdata `get_stock_list_in_sector('沪深A股'/'沪深ETF'/'沪深基金')` + `get_market_data_ex(dividend_type='front')`
|
||||
- Produces: 全市场 ETF(~1000 只)日线**前复权**,落 parquet/dbbardata(复用现有 xtdata 管线)
|
||||
|
||||
- [ ] **2.1 探针:ETF universe + 1 只 K 线**
|
||||
```python
|
||||
from xtquant import xtdata as xd
|
||||
etf = xd.get_stock_list_in_sector('沪深ETF') or []
|
||||
fund = xd.get_stock_list_in_sector('沪深基金') or []
|
||||
a = xd.get_stock_list_in_sector('沪深A股') or []
|
||||
u = list(set(a + etf + fund))
|
||||
print(f"A={len(a)} ETF={len(etf)} fund={len(fund)} union={len(u)}")
|
||||
r = xd.get_market_data_ex([], ['510300.SH'], period='1d',
|
||||
start_time='20240101', end_time='20260721', dividend_type='front')
|
||||
df = r.get('510300.SH')
|
||||
print('510300 bars:', 0 if df is None else len(df), '| tail close:', None if df is None else df['close'].iloc[-1])
|
||||
```
|
||||
预期:ETF ~1000,union > A 股数;510300 前复权日线有值,close 非 NaN。
|
||||
|
||||
- [ ] **2.2 改 universe**:`build_daily_from_xtdata.py:40` 和 `daily_update_xtdata.py:114` 把
|
||||
```python
|
||||
u = xd.get_stock_list_in_sector("沪深A股") or []
|
||||
```
|
||||
改为
|
||||
```python
|
||||
u = list(set(
|
||||
(xd.get_stock_list_in_sector("沪深A股") or []) +
|
||||
(xd.get_stock_list_in_sector("沪深ETF") or []) +
|
||||
(xd.get_stock_list_in_sector("沪深基金") or [])
|
||||
))
|
||||
```
|
||||
保留 `dividend_type='front'`(前复权,§13 默认)。
|
||||
|
||||
- [ ] **2.3 全量下载 ETF**:跑改后的 `build_daily_from_xtdata.py`(走现有 xtdata 管线,**无限流**)→ parquet。
|
||||
|
||||
- [ ] **2.4 验证**:ETF 数 + 抽样(510300/513050/159919)+ 前复权 close 非 NaN + 日期范围。
|
||||
|
||||
- [ ] **2.5 schtask**:复用 `sanguo-daily-update`(universe 扩展后自动含 ETF,无需新 schtask)。
|
||||
|
||||
- [ ] **2.6 commit**:`git commit -m "feat(data): ETF 全市场日线(xtdata universe 扩展+前复权)"`
|
||||
|
||||
---
|
||||
|
||||
## Task 3: 退市股 K 线(反幸存者偏差核心)
|
||||
|
||||
**Files:** Create `scripts/data_platform/baostock_delisted_download.py` + `import_delisted_to_db.py`;Output → `daily_baostock_full`
|
||||
|
||||
**Interfaces:**
|
||||
- Consumes: baostock `query_all_stock(day)` + `query_stock_basic(code)`(status + 退市日期)+ `query_history_k_data_plus(code, fields, adjustflag=3)`
|
||||
- Produces: 退市股 K 线 INSERT OR REPLACE `daily_baostock_full`(18 列,复用 `parse_baostock_code`)
|
||||
|
||||
**范围:** 近 5 年退市(退市日期 ≥ 2021;守 48000/天;退市股分天跑)
|
||||
|
||||
- [ ] **3.1 探针:退市股列表字段**
|
||||
```python
|
||||
import baostock as bs, pandas as pd
|
||||
bs.login()
|
||||
rs = bs.query_all_stock(day="2026-07-18")
|
||||
rows = []
|
||||
while (rs.error_code == '0') & rs.next():
|
||||
rows.append(rs.get_row_data())
|
||||
df = pd.DataFrame(rows, columns=rs.fields)
|
||||
print('query_all_stock fields:', rs.fields, '| rows:', len(df))
|
||||
rs2 = bs.query_stock_basic(code="sh.600000")
|
||||
b = []
|
||||
while (rs2.error_code == '0') & rs2.next():
|
||||
b.append(rs2.get_row_data())
|
||||
print('query_stock_basic fields:', rs2.fields, '| sample:', b[0] if b else None)
|
||||
bs.logout()
|
||||
```
|
||||
预期:`query_stock_basic` 含 `type`(1股)/`status`(1上市 0退市)/`outDate`(退市日期)。筛 `status=0 & outDate>='2021-01-01'`。
|
||||
|
||||
- [ ] **3.2 实现 `baostock_delisted_download.py`**:
|
||||
- 遍历全 code(或 `query_all_stock` 多日并集)→ `query_stock_basic` 筛 `status=0 & outDate>='2021-01-01'` → 退市股列表
|
||||
- 逐只 `query_history_k_data_plus(code, start_date='1990-01-01', end_date=outDate, fields=18字段, adjustflag=3)` → staging `data/delisted_kline/{code}.parquet`
|
||||
- 单进程单登录,`time.sleep` 守预算,marker 断点续传(复用 Day1 模板),DAILY_LIMIT 计数器
|
||||
|
||||
- [ ] **3.3 `import_delisted_to_db.py`**:staging → INSERT OR REPLACE `daily_baostock_full`(复用 `parse_baostock_code` sh.600000→600000+SH + `executemany`,WAL + busy_timeout=60000,同 `import_baostock_to_db.py`)。**dbbardata 不碰**。
|
||||
|
||||
- [ ] **3.4 验证探针**:退市股数 + 抽样(某退市股 K 线行数 + max(date) ≤ 退市日)+ `daily_baostock_full` 行数增量 + distinct symbol 增量。
|
||||
|
||||
- [ ] **3.5 wrapper + schtask**:`baostock_delisted_wrapper.ps1`;schtask `sanguo-delisted` `/sc monthly /ru SYSTEM`(月度,守 48000,错开 day2b 02:00 + bs-daily-increment 17:00)。
|
||||
|
||||
- [ ] **3.6 commit**:`git commit -m "feat(data): 退市股 K 线采集(baostock,反幸存者偏差)"`
|
||||
|
||||
---
|
||||
|
||||
## Task 4: baostock 日增量 → daily_baostock_full(#7 daily_update_static)
|
||||
|
||||
> **串行约束**:本 task 与 Task3 都用 baostock 长会话,**必须串行**(Task3 probe → Task3 执行 → Task4),不可并发(防黑名单)。
|
||||
|
||||
**Files:** Create `scripts/data_platform/daily_update_static.py` + `daily_update_static_wrapper.ps1`
|
||||
|
||||
**背景:** 现有 `daily_update_xtdata.py` 只产 parquet 不灌 `daily_baostock_full`(已知 gap,memory `db-primary-parquet-fallback` 记录)。本 task 补 baostock 日线的**每日增量灌库**。
|
||||
|
||||
**Interfaces:**
|
||||
- Consumes: baostock `query_stock_basic`(全 A,type=1 含退市,复用 `baostock_daily_fullmarket_download.py:fetch_all_stocks`)+ `query_history_k_data_plus`(LOOKBACK 窗口,adjustflag=3 raw,18 字段同 `BS_FIELDS`)
|
||||
- Produces: staging `data/daily_baostock_increment/{YYYYMMDD}/{code}.{exc}_daily.parquet`(审计)→ 同进程 INSERT OR REPLACE `daily_baostock_full`(复用 `parse_baostock_code`+executemany+WAL+busy_timeout,同 `import_baostock_to_db.py`)
|
||||
|
||||
**设计(LOOKBACK 窗口 + 幂等,不同于全量 marker 模式):**
|
||||
- **不用 marker 断点续传**(全量才需要;增量每日全量重拉最近 N 天)
|
||||
- `LOOKBACK_DAYS=7`(覆盖周末/节假日;baostock 日终更新,17:00 跑时当日 bar 已就绪)
|
||||
- 每只 1 query → 5537 query/run ≪ 48000/天 ✅(留足余量给 day2b/Task3)
|
||||
- `sleep 0.4s × 5537 ≈ 37min`(17:00 schtask 可接受)
|
||||
- `QUERY_COUNT` 计数器 + `DAILY_LIMIT=40000` 防御(复用全量脚本模式)
|
||||
- **一脚本贯通**:download LOOKBACK → staging parquet(审计)→ in-memory df → executemany INSERT OR REPLACE(幂等,重复跑同一天安全,`drop_duplicates keep last` 不需要因 PK+OR REPLACE 天然去重)
|
||||
|
||||
**Steps:**
|
||||
- [ ] **4.1 探针(可选,Day1 已实证 query_history_k_data_plus 可用)**:ssh VPS 跑 1 只近 7 天确认接口 + 当日 bar 就绪
|
||||
- [ ] **4.2 写 `daily_update_static.py`**:自包含,结构
|
||||
- `unset proxy` + `socket.setdefaulttimeout(30)`(同全量脚本,baostock 坑)
|
||||
- `_login_once`/`_relogin`/`fetch_all_stocks`/`fetch_one_daily`/`parse_baostock_code` 复用(可 import 或复制;优先 from `baostock_daily_fullmarket_download import ...`,注意 `QUERY_COUNT` global 需在同进程)
|
||||
- `LOOKBACK` 窗口:`start=today-7, end=today`
|
||||
- 主循环:逐只 `fetch_one_daily` → staging parquet → 累积 df → 每 100 只 `executemany INSERT OR REPLACE`(WAL+busy_timeout=60000)
|
||||
- `QUERY_COUNT`/`DAILY_LIMIT`/断路器/定期重登 复用
|
||||
- 结束 verify:抽样 3 只 `max(date) ≈ today`、当日新增行数
|
||||
- 环境变量 `BS_INCREMENT_OUT_DIR`/`DB_PATH` 覆盖默认(同 Day1 wrapper 模式适配 Win)
|
||||
- [ ] **4.3 小样本**:`--limit 10` 跑 10 只,确认 staging 有行 + DB 抽样 max(date)≈today
|
||||
- [ ] **4.4 全量跑**:5537 只,守预算
|
||||
- [ ] **4.5 wrapper + schtask**:`daily_update_static_wrapper.ps1`(unset proxy+utf8+OUT_DIR+log);schtask `sanguo-bs-daily-increment` `/sc daily /st 17:00 /ru SYSTEM`(错开 daily-update 16:30 + day2b 02:00 + Task3 月度)
|
||||
- [ ] **4.6 commit**:`git commit -m "feat(data): baostock 日增量灌库 daily_update_static(#7 gap 补)"`
|
||||
|
||||
---
|
||||
|
||||
## Self-Review
|
||||
|
||||
- **Spec 覆盖**:Task1→spec §4 成份股行 + §8 P0.1;Task2→§4 ETF 行 + §8 P0.2;Task3→§4 退市行 + §8 P0.3 ✅
|
||||
- **Placeholder 扫描**:无 TBD/TODO;采集脚本给接口+探针+schema,实现者按骨架写完整(采集脚本完整代码由执行 agent 基于 接口/schema/陷阱 产出)✅
|
||||
- **类型一致**:`index_const_hist` schema 各源统一;`daily_baostock_full` 18 列复用 `import_baostock_to_db.py` 的 `parse_baostock_code`+executemany ✅
|
||||
- **陷阱纳入**:`ak.index_stock_hist` 下线(1.1 标注)/ csindex SPA 无历史(用国证+新浪)/ 新浪 gb2312(1.2)/ hist 版必须(1.1)✅
|
||||
|
||||
---
|
||||
|
||||
## Execution Handoff
|
||||
|
||||
计划存 `docs/superpowers/plans/2026-07-21-data-fusion-p0.md`。执行方式:
|
||||
1. **Subagent-Driven**(推荐):每 Task 派 fresh agent + task 间 review
|
||||
2. **Inline**:本 session 批量执行 + checkpoint
|
||||
@@ -0,0 +1,243 @@
|
||||
# A 股多数据源融合层设计
|
||||
|
||||
> 日期:2026-07-21 | 基于 brainstorming + 4 源全能力调查(akshare / baostock / miniQMT / csindex)
|
||||
> 状态:设计草案,待用户评审 → writing-plans
|
||||
|
||||
---
|
||||
|
||||
## 1. 背景与痛点
|
||||
|
||||
多数据源(akshare / baostock / xtdata / miniQMT)各不全,整合时格式有偏差:
|
||||
- **symbol 格式**:`600519` vs `sh.600000` vs `600519.SH`
|
||||
- **exchange 命名**:`SSE` vs `SH`
|
||||
- **复权口径**:raw vs qfq
|
||||
- **volume 单位**:xtdata÷100 vs 原值
|
||||
- **价格源间漂移**:同股同日不同源 close 微差
|
||||
|
||||
### 用户约束(明确)
|
||||
- ❌ 不要查询时网络源切换(源变化/限流不可控)
|
||||
- ✅ VPS 本地一份稳定数据,日常只读本地
|
||||
- ✅ 网络源只用于「采集时拼凑完整本地」
|
||||
- ✅ 使用层无感(本地缺才网络兜底,罕见)
|
||||
- ✅ 优先 miniQMT + baostock
|
||||
|
||||
---
|
||||
|
||||
## 2. 设计原则(三层)
|
||||
|
||||
| 层 | 职责 | 原则 |
|
||||
|---|---|---|
|
||||
| **采集层** | 多网络源 → 拼凑完整本地 | 各源 adapter + 定时 schtask;源不可控隔离在采集(失败重试,不影响使用层) |
|
||||
| **数据层** | 整理(重叠定权威,特定保留) | **不强合物理表**(vnpy 回归风险);每类定权威源(优先 baostock+miniqmt) |
|
||||
| **使用层** | `LocalUnifiedProvider` 逻辑融合 | 读权威表 + 归一化 + 本地缺网络兜底;策略无感 |
|
||||
|
||||
**核心**:网络源不稳的风险只影响采集层(定时跑、可重试),使用层永远读本地 —— 风险隔离。
|
||||
|
||||
---
|
||||
|
||||
## 3. 数据源全能力盘点(综合调查)
|
||||
|
||||
### 3.1 已下(稳定源)
|
||||
| 表/源 | 内容 | 范围 | 增量 |
|
||||
|---|---|---|---|
|
||||
| `dbbardata` | 日线+5m/15m(xtdata/akshare/baostock) | 日线2010+/分钟2020+ | ✅ 16:30 |
|
||||
| `daily_baostock_full` | 日线 18 字段(pe/pb/turn/pctChg) | 1990–2026 | 待 #7 |
|
||||
| `bs_index_constituent` | 成份股 300/500/50(含退市) | 2006+ | 待增量 |
|
||||
| `bs_adjust_factor` | 复权因子 | 全史 | 待增量 |
|
||||
| akshare 静态表 | 三表/估值/北向/龙虎榜/融资融券/股本/解禁/业绩预告 | - | 部分增量 |
|
||||
| miniQMT xtdata | 日线+5m/15m 全周期(实时 T+0,零漂移) | 16年 | 按需 |
|
||||
| miniQMT PershareIndex | ROE/毛利率/EPS | 季频 | 按需 |
|
||||
| parquet | data/raw、daily_baostock、minute_5/15、static/index_const、qfq+raw ETF(5只) | - | 部分 |
|
||||
|
||||
### 3.2 重叠(5 处)
|
||||
1. 日线 OHLCV:dbbardata ∩ daily_baostock_full ∩ parquet raw(三处)
|
||||
2. 估值 PE/PB:akshare valuation ∩ daily_baostock_full(peTTM/pbMRQ)
|
||||
3. 15min:dbbardata ∩ parquet minute_15
|
||||
4. 基本面:akshare 三表 ∩ miniQMT PershareIndex ∩ baostock 季频
|
||||
5. 成份股:bs_index_constituent ∩ parquet index_const
|
||||
|
||||
### 3.3 新发现缺口(本次调查)
|
||||
- **ETF 全市场日线**(现仅 5 只,策略资产类别缺口)
|
||||
- **深证/中证历史成份股**(治幸存者偏差,当前仅最新快照 = latent bug)
|
||||
- **退市股 K 线**(baostock 有,从未提取;反幸存者偏差核心)
|
||||
- 申万行业 SW1/2/3 + 历史变动
|
||||
- 龙虎榜 / 合约信息(涨跌停/ST)/ 可转债 / 研报一致预期 / 股东户数 / 除权明细
|
||||
|
||||
---
|
||||
|
||||
## 4. 权威源地图(数据层整理)
|
||||
|
||||
| 数据类 | 权威源 | 物理存储 | 重叠处理 / 备注 |
|
||||
|---|---|---|---|
|
||||
| 日线 OHLCV(个股,历史) | **baostock** | `daily_baostock_full` | dbbardata 日线保留(vnpy 回测硬依赖);parquet raw 冗余 |
|
||||
| 日线(盘中实时) | **miniQMT xtdata** | xtdata API | 独有(T+0 实时) |
|
||||
| **日线 ETF(全市场)** | **miniQMT xtdata** | parquet/dbbardata | universe 加 `沪深ETF∪沪深基金`,`dividend_type='front'` 自动复权 |
|
||||
| 估值 PE/PB/turn | **baostock** | `daily_baostock_full` | akshare valuation 兜底/校验 |
|
||||
| 基本面指标 ROE/毛利率 | **miniQMT PershareIndex** | miniQMT API | akshare 三表补原始报表 |
|
||||
| 基本面三表(原始) | akshare | akshare 表 | miniQMT Balance/Income/CashFlow + baostock 季频交叉 |
|
||||
| 15min | **baostock** | `dbbardata` | parquet minute_15 冗余可清 |
|
||||
| 成份股 300/500/50(含退市) | **baostock** | `bs_index_constituent` | `query_*_stocks(date)` 任意时点 |
|
||||
| **成份股 深证/国证(399xxx)** | **akshare(国证源)** | parquet | `index_detail_hist_cni` + `index_detail_hist_adjust_cni` |
|
||||
| **成份股 中证1000/2000(000852/932000)** | **新浪** | parquet | `vII_HistoryComponent`(gb2312,含退市) |
|
||||
| **退市股 K 线** | **baostock** | `daily_baostock_full` | `query_all_stock` status=0 + `query_stock_basic` 退市日期 + 逐只 K |
|
||||
| 复权因子 | **baostock** | `bs_adjust_factor` | 独有 |
|
||||
| 申万行业 SW1/2/3 | **miniQMT xtdata** | parquet | `get_sector_list` + `get_stock_list_in_sector`;akshare 补历史变动 |
|
||||
| 龙虎榜 | **miniQMT xtdata** | parquet | `get_longhubang`;akshare 兜底 |
|
||||
| 合约信息(涨跌停/ST/上市日) | **miniQMT xtdata** | parquet | `get_instrument_detail` 全 A 一入库 |
|
||||
| 可转债 | akshare | parquet | `bond_zh_hs_cov_min` + `bond_cb_adj_logs_jsl`(转股价) |
|
||||
| 研报/一致预期 EPS | akshare | parquet | `stock_research_info_em` |
|
||||
| 股东户数 | miniQMT/akshare | parquet | `Holdernum` 表 / `stock_zh_a_gdhs_detail` |
|
||||
| 除权明细 | miniQMT xtdata | parquet | `get_divid_factors` |
|
||||
| 龙虎榜/北向/融资融券/解禁 | akshare | akshare 表 | 独有(保留) |
|
||||
|
||||
---
|
||||
|
||||
## 5. 归一化规则
|
||||
|
||||
| 维度 | 统一标准 | 源映射 |
|
||||
|---|---|---|
|
||||
| symbol | `600519.SH`(数字+交易所后缀) | baostock `sh.600000`→`600000.SH`;dbbardata 纯数字+exchange 字段 |
|
||||
| exchange | `SH`/`SZ` | dbbardata `SSE`/`SZSE` → `SH`/`SZ` |
|
||||
| 日期 | ISO `2026-07-21` | 各源统一 |
|
||||
| 复权 | raw 存储 + `factor` 字段(QLib:`factor=adj/raw`) | 查询时按需 qfq(`$close/$factor`);治 raw/qfq 冲突 |
|
||||
| volume | 原值(股) | xtdata ÷100 还原 |
|
||||
| 停牌 | OHLCV 全 NaN | QLib 约定 |
|
||||
| 溯源 | `source` 字段 | 每行标来源 + 主源/补丁标记 |
|
||||
|
||||
---
|
||||
|
||||
## 6. 使用层:`LocalUnifiedProvider`(逻辑融合)
|
||||
|
||||
**接口**:
|
||||
```python
|
||||
get_daily(symbol, start, end, adjust='raw') # 个股日线
|
||||
get_etf_daily(symbol, ...) # ETF 日线
|
||||
get_fundamentals(symbol, fields, date) # 财务指标/三表
|
||||
get_constituent(index, date) # 成份股(含历史,治幸存者偏差)
|
||||
get_industry(symbol, date) # 申万行业(含历史变动)
|
||||
get_longhubang(symbol, start, end) # 龙虎榜
|
||||
get_instrument_detail(symbol) # 涨跌停/ST
|
||||
get_delisted_kline(...) # 退市股 K 线
|
||||
```
|
||||
|
||||
**职责**:
|
||||
- 按数据类路由到权威表(§4 地图)
|
||||
- 归一化(§5 规则):源格式 → 统一 vt_symbol/exchange/复权/单位
|
||||
- 本地缺 → `network_fetcher` 透明兜底(罕见,如新股未及采集)→ 写本地 → 返回
|
||||
- `source` 字段溯源;可选多源交叉校验
|
||||
- 使用层 API 不变,不知数据来自哪个源
|
||||
|
||||
**实现**:扩展现有 `sanguo_portfolio/providers/` 的 `DataProvider` 接口(BaostockProvider/LocalParquetProvider 已有)。
|
||||
|
||||
---
|
||||
|
||||
## 7. 增量 schtask 清单
|
||||
|
||||
| schtask | 数据 | 源 | 时间 |
|
||||
|---|---|---|---|
|
||||
| `sanguo-daily-update`(已有) | 日线+分钟→dbbardata | xtdata | 16:30 |
|
||||
| `sanguo-bs-daily-increment`(#7 待建) | 日线→daily_baostock_full | baostock | 17:00 |
|
||||
| `sanguo-etf-daily-increment`(新) | ETF 全市场日线 | xtdata(universe 沪深ETF) | 17:30 |
|
||||
| `sanguo-akshare-static-increment`(新) | 估值/龙虎榜/三表/北向 | akshare | 18:00 |
|
||||
| `sanguo-index-hist`(新,半年度) | 历史成份股调样 | akshare 国证 + 新浪 | 调样后(6/12 月) |
|
||||
| `sanguo-delisted`(新,月度) | 退市股列表+K 线 | baostock | 月初 |
|
||||
|
||||
**query 预算守 48000/天/IP**(baostock 硬限):各 baostock schtask 错开 + 日计数器。
|
||||
|
||||
---
|
||||
|
||||
## 8. 分阶段实现
|
||||
|
||||
### P0 — 治幸存者偏差 + 策略核心缺口
|
||||
1. **历史成份股**:深证/国证(`index_detail_hist_cni`)+ 中证1000/2000(新浪 `vII_HistoryComponent`)+ 300/500/50(baostock 已有)
|
||||
2. **ETF 全市场日线**:xtdata universe 改 `沪深A股∪沪深ETF∪沪深基金` + `dividend_type='front'`(改 `build_daily_from_xtdata.py:40` + `daily_update_xtdata.py:114`)
|
||||
3. **退市股 K 线**:baostock `query_all_stock` 筛 status=0 + `query_stock_basic` 退市日期 + 逐只 K → `daily_baostock_full`
|
||||
|
||||
### P1 — 策略增强
|
||||
4. 申万行业 SW1/2/3(xtdata `get_sector_list`)+ 历史变动
|
||||
5. 龙虎榜(xtdata `get_longhubang`)
|
||||
6. 合约信息涨跌停/ST(xtdata `get_instrument_detail`)全 A 入库
|
||||
7. 可转债(akshare `bond_zh_hs_cov_min` + 转股价调整)
|
||||
8. 研报/一致预期 EPS(akshare `stock_research_info_em`)
|
||||
|
||||
### P2 — 按需
|
||||
9. 股东户数 / 除权明细 / 业绩快报 / 大宗交易 / 宏观 / 期货
|
||||
|
||||
### 融合层(贯穿)
|
||||
10. 归一化库(vt_symbol/exchange/factor/volume 映射)
|
||||
11. `LocalUnifiedProvider`(读权威表 + 归一化 + 网络兜底)
|
||||
12. 完整度监控报表(每类覆盖率/缺口,源退化早发现)
|
||||
|
||||
---
|
||||
|
||||
## 9. 陷阱清单(实证)
|
||||
|
||||
- `ak.index_stock_hist` **已下线**(akshare 1.10.37,2024 初)—— 别抄 2022-23 旧博客
|
||||
- `ak.index_stock_cons` 的"纳入日期"字段有迷惑性 —— 只是当前 300 只各自最初纳入日,不含被剔除,治不了幸存者偏差
|
||||
- csindex.com.cn 是 Vue SPA —— `requests.get` 拿空壳,官网只当前 Excel 无历史
|
||||
- 新浪 `fund_etf_hist_sina` **不复权** —— 不适合回测;ETF 复权走 xtdata `dividend_type='front'`
|
||||
- 东财 `fund_etf_hist_em` **封 IP** —— 单线程限速或避用
|
||||
- baostock `query_all_stock` 不列退市日期 —— 配合 `query_stock_basic`
|
||||
- 申万历史板块有变更 —— 回测用当时分类
|
||||
- `ak.index_detail_cni`(非 hist 版)2025-11-25 起只近期 —— 必须用 hist 版
|
||||
|
||||
---
|
||||
|
||||
## 10. YAGNI(不做)
|
||||
|
||||
- ❌ 不强合物理表(冲突解决/历史一致性/vnpy 回归风险,代价大)
|
||||
- ❌ 不引 QLib/OpenBB 框架(几百 MB,只摘模式:factor/source/归一)
|
||||
- ❌ CS 截面归一(中期按需,先解决不全+格式)
|
||||
- ❌ 实时 tick/盘口(非日终策略才需)
|
||||
|
||||
---
|
||||
|
||||
## 11. 风险与对策
|
||||
|
||||
| 风险 | 对策 |
|
||||
|---|---|
|
||||
| vnpy 回测硬读 dbbardata | 不动 dbbardata,provider 层 SSE↔SH 映射 |
|
||||
| baostock query 预算 48000/天 | 增量 schtask 错开 + 日计数器(#7+day2b 同天 44296<48000) |
|
||||
| 东财封 IP | 避用东财,优先 baostock/xtdata/新浪/国证 |
|
||||
| 接口下线(如 index_stock_hist) | 调查实证,不抄旧文 |
|
||||
| 数据源漂移/幽灵尖峰 | source 溯源 + 涨跌停/量异常校验 |
|
||||
| 历史成份股缺口致回测幸存者偏差 | P0 优先补(深证+中证1000/2000+退市) |
|
||||
|
||||
---
|
||||
|
||||
## 12. 实现路径(writing-plans 拆)
|
||||
|
||||
- **Phase 1(P0 数据补全)**:历史成份股 + ETF 全市场 + 退市 K 线(3 个采集脚本 + 灌库)
|
||||
- **Phase 2(融合层)**:归一化库 + `LocalUnifiedProvider`(读现有+新表)
|
||||
- **Phase 3(P1 增强)**:板块/龙虎榜/合约/可转债/研报
|
||||
- **Phase 4(运维)**:增量 schtask 全套 + 完整度监控
|
||||
|
||||
---
|
||||
|
||||
## 13. 开放问题与默认决策(自行决策,你可推翻)
|
||||
|
||||
| # | 问题 | 默认(我定) | 备选 | 理由 |
|
||||
|---|---|---|---|---|
|
||||
| 1 | exchange 统一格式 | **SH/SZ**(provider 映射 dbbardata SSE→SH) | 保留 SSE/SZSE | baostock/xtdata 都用 SH/SZ,主流;vnpy SSE 在 provider 层映射 |
|
||||
| 2 | P0 三项优先级 | **全做**(历史成份股 + ETF + 退市 K 线) | 先 ETF(策略即用) | 三项都治幸存者偏差/核心缺口,并行不冲突 |
|
||||
| 3 | ETF 复权方式 | **xtdata `dividend_type='front'`**(前复权) | raw + factor(精确还原) | 前复权够策略用;raw+factor 中期按需 |
|
||||
| 4 | 退市股 K 线范围 | **近 5 年退市**(守 baostock 48000/天预算) | 全退市(几千只,慢) | 近 5 年覆盖绝大多数回测;全量可后补 |
|
||||
| 5 | `LocalUnifiedProvider` 接口 | §6 签名(get_daily/get_etf/get_fundamentals/get_constituent/get_industry/...) | 精简 | 覆盖全天候 + CTA 需求 |
|
||||
| 6 | 增量 schtask 时间 | 17:00(baostock 日线)/ 17:30(ETF)/ 18:00(akshare 静态) | 调整 | 错开 sanguo-daily-update 16:30 + day2b 02:00 |
|
||||
| 7 | 物理表 | **不新建统一表**,provider 读现有(dbbardata/daily_baostock_full/各 parquet) | 建 daily_unified | 避免 vnpy 回归 + 数据迁移(§10 YAGNI) |
|
||||
|
||||
**默认推进路径**:按以上默认 → spec 定稿 → 转 writing-plans(P0 拆 3 个采集脚本实现计划)。你审 spec 时可推翻任一项,我改。
|
||||
|
||||
---
|
||||
|
||||
## 参考(调查来源)
|
||||
|
||||
- xtdata 官方:https://dict.thinktrader.net/nativeApi/xtdata.html
|
||||
- akshare 指数:https://akshare.akfamily.xyz/data/index/index.html
|
||||
- akshare 基金:https://akshare.akfamily.xyz/data/fund/fund_public.html
|
||||
- akshare 债券:https://akshare.akfamily.xyz/data/bond/bond.html
|
||||
- baostock API:https://www.baostock.com/mainContent?file=pythonAPI.md
|
||||
- 国证指数网(深证历史):http://www.cnindex.com.cn/module/index-detail.html?indexCode=399001
|
||||
- 新浪历史成份:http://vip.stock.finance.sina.com.cn/corp/go.php/vII_HistoryComponent/indexid/000852.phtml
|
||||
- QLib 数据层:https://qlib.readthedocs.io/en/latest/component/data.html
|
||||
- 现有缺口设计:`docs/static_data_gaps_design.md`
|
||||
Reference in New Issue
Block a user