Files
zhixing-system/docs/research/onechartlab-tushare-data-requirements.md
T

377 lines
39 KiB
Markdown
Raw Normal View History

# OneChartLab 复现的 Tushare 数据需求研究
> 调研日期:2026-08-28。范围:OneChartLab「板块资金雷达」与「宏观择时」的数据采集、落库、历史回填和独立重算边界。本文只讨论技术复现,不构成投资建议。
## 结论摘要与目标边界
复现工作必须先区分两个不同目标。
**目标 A:完全复刻页面消费层。** 最可靠的输入不是重新从 Tushare 推导,而是直接保存 OneChartLab 已公开的 manifest、日期分片、排名历史、宏观 `state.json`、`series.json` 和 dispersion 数据块。页面已经公开 `Ratio_Score`、`Swing_Score`、`D/C/O` 等结果,却没有公开这些结果的完整构建公式。只要保留公开 payload、schema、hash、批次和修订水位,就能精确复刻页面已经确认的排序、百分位、阈值、MA 和贡献消费逻辑。[页面/manifest 已确认:[`radar_manifest.json`](https://onechartlab.com/radar_manifest.json)、[`sector_rankings/manifest.json`](https://onechartlab.com/sector_rankings/manifest.json)、[`macro/state.json`](https://onechartlab.com/macro/state.json)]
**目标 B:独立重算未公开指标。** Tushare 能提供板块成员、个股资金流、日行情、申万分类、申万成员和指数行情等底层事实,但不能直接提供或确认 OneChartLab 的 `Ratio_Score`、3—10 日权重、`Swing_Score`、核心离散度 `D`、行业贡献 `C/O`、修订水位生成规则或原站私有版本参数。因此可以独立构造“同语义替代指标”,也可以复算公开的下游恒等式,但在获得构建公式和 point-in-time 输入快照前,不能宣称独立结果与原站等价。[页面/manifest 已确认;需实测/缺公式]
当前最小可执行策略是“双轨落库”:一轨原样保存 OneChartLab 公开发布物以保证消费层可复现,另一轨按本文接口清单保存 Tushare 原始事实,以便做抽样对账、替代公式实验和未来独立重算。两轨通过 `trade_date`、稳定代码、`source_version`、`observed_at`、响应 hash 和计算参数版本关联,不用网页展示名称作为主键。
## 证据标记
本文使用四种状态:`页面/manifest 已确认` 表示 OneChartLab 第一方公开文件明确给出;`Tushare 官方文档已确认` 表示接口名、字段、单位或限制来自 Tushare 官方接口页;`复现建议` 表示本文为采集和落库提出的工程方案;`需实测` 表示官方页没有稳定说明、实际账号权限/频次/历史覆盖或边界语义需要用已授权账号验证。官方页面上的积分和限量是 2026-08-28 的页面快照,可能调整;上线前仍应以目标账号调用结果和权限中心为准。
## 第一方公开契约
OneChartLab 的 [`sector_rankings/manifest.json`](https://onechartlab.com/sector_rankings/manifest.json) 当前明确声明以下来源和单位:
| 页面事实 | Radar | Macro | 证据状态 |
| --- | --- | --- | --- |
| 板块/行业成员 | `tushare.dc_member` | `tushare.index_member_all` | 页面/manifest 已确认 |
| 主力净额 | `tushare.moneyflow_dc.net_amount` | 同左 | 页面/manifest 已确认;输入单位万元,聚合求和后输出亿元 |
| 主买净额 | `tushare.moneyflow.net_mf_amount` | 同左 | 页面/manifest 已确认;输入单位万元,禁止由大小单金额自行相减 |
| 个股涨跌幅 | `tushare.daily.pct_chg` | 同左 | 页面/manifest 已确认;多日窗口使用 `compound_return` |
| 上市生命周期 | `tushare.stock_basic` | 同左 | 页面/manifest 已确认;有效区间为 `list_date <= trade_date <= delist_date_or_open` |
| 日停复牌 | `tushare.suspend_d` | 同左 | 页面/manifest 已确认 |
| 停牌区间 | `tushare.suspend` | 同左 | 页面/manifest 已确认其字符串;当前官方接口目录未找到对应公开页,需实测,不能替代 `suspend_d` 的官方事实 |
| 股票池 | 沪深上市 A 股,排除北交所与沪深 B 股 | 同左 | 页面/manifest 已确认 |
该 manifest 的 `public_payload` 是 `derived_top_bottom_only`,说明公开成分文件只给派生后的前后榜,不是完整逐股输入明细。2026-08-28 观测到的当前批次日期为 2026-08-27,Radar 是 `eastmoney-dc-v1`、707 个板块、窗口 `[1]`,Macro 是 `sw2021_l2_current124_*`、124 个申万二级行业、窗口 `[1,3,5,10,90]`。既有 2026-08-26 Radar 报告记录的是 791 个板块;数量变化证明 universe 必须随交易日快照保存,不得硬编码。[页面/manifest 已确认]
宏观 [`state.json`](https://onechartlab.com/macro/state.json) 在同次观测中已从既有报告的 `revision_watermark=21` 更新到 `22`。这说明相同或相邻日期的数据可被重新构建,所有公开输入都应保存 `batch_id`、`bundle_id`、`accepted_input_hash`、`revision_watermark`、响应 hash 和 `observed_at`,不能只覆盖一张“最新值”表。[页面/manifest 已确认;复现建议]
## 模块数据血缘与分级
### 板块资金雷达
| 分级 | 数据 | 用途与边界 |
| --- | --- | --- |
| 必需 | `dc_index`、`dc_member`、`moneyflow_dc`、`daily`、`trade_cal`、`stock_basic`、`suspend_d` | 建立每日东财概念/行业板块 universe、point-in-time 成员、主力净额、涨跌幅、交易日和有效样本。`moneyflow_dc.net_amount` 是公开成分 manifest 明确的主力净额来源。 |
| 必需(完整成分解释层) | `moneyflow` | 提供 `net_mf_amount` 主买净额;这是 L2 主动买卖净额,不能由大中小单简单相减。只复刻主雷达榜单而不实现成分详情时可暂缓。 |
| 推荐 | `daily_basic`、`moneyflow_ind_dc`、`namechange` | `daily_basic` 提供换手率、市值等低流动性质量特征;`moneyflow_ind_dc` 是官方“东财概念及行业板块资金流向”接口,可与逐股聚合做交叉对账;`namechange` 保留历史展示名和名称区间。三者均不是公开 manifest 对主指标公式的确认。 |
| 可选 | `adj_factor` | 用于股票复权和公司行动质检,不是单日 `daily.pct_chg` 或 `moneyflow_dc` 的必需输入,也不能单独生成前复权价格。 |
可高置信度实施的链路是 `dc_index/dc_member + stock_basic/suspend_d` 确定当日有效成员,再关联 `moneyflow_dc`、`moneyflow` 和 `daily` 生成完整逐股事实与成分前后榜。`Amount_Raw_BN` 可用 `sum(moneyflow_dc.net_amount) / 10_000` 作为待对账候选,因为万元除以 10,000 得亿元;但主雷达构建端逐股明细未公开,必须逐日逐板块与公开 raw 值抽样核对后才能固化。[页面/manifest 已确认来源与单位;复现建议聚合;需实测等价性]
`Ratio_Raw_Pct` 的概念分母由作者在[公开视频](https://www.bilibili.com/video/BV16Hur6rEq2/)中解释为当日成交额,但 Tushare 和公开页面没有确认生产公式是板块级 `moneyflow_ind_dc.net_amount_rate`、`sum(net_amount)/sum(daily.amount)`、逐股比例再聚合,还是包含截尾和低流动性过滤。`daily.amount` 可作为成交额候选,`moneyflow_dc.net_amount_rate` 和 `moneyflow_ind_dc.net_amount_rate` 可用于对照实验,不能直接指定为原站公式。[作者自述;Tushare 官方字段已确认;需实测]
### 宏观择时
| 分级 | 数据 | 用途与边界 |
| --- | --- | --- |
| 必需(独立构造主序列) | `index_classify`、`index_member_all`、`sw_daily`、`trade_cal` | 取得申万 2021 分类、二级行业及成员区间、申万行业日线和统一交易日。作者在[公开视频](https://www.bilibili.com/video/BV16Hur6rEq2/)中称核心输入为申万二级行业近 90 日涨跌幅横截面分布,但 `D` 的统计量仍未公开。 |
| 必需(七指数对照) | `index_daily` | 提供 `000001.SH`、`399006.SZ`、`000688.SH`、`000016.SH`、`000300.SH`、`000852.SH`、`932000.CSI` 的收盘序列。`index_daily` 官方明确不含申万行业,不能代替 `sw_daily`。 |
| 必需(完整成分解释层) | `daily`、`moneyflow_dc`、`moneyflow`、`stock_basic`、`suspend_d` | 复算 1/3/5/10/90 日行业成分股涨跌幅、主力净额和主买净额榜。仅研究 `D` 主序列时可以与主序列采集拆开。 |
| 推荐 | `index_basic`、`daily_basic`、`namechange` | 校验七指数代码、发布方和终止日期;提供流动性/规模质量特征和历史名称。 |
| 可选 | `adj_factor` | 用股票级复权价格复核多日个股收益;行业主序列优先用 `sw_daily`,页面公开的成分窗口已明确使用 `daily.pct_chg` 复合。 |
`index_member_all` 的输出含 `in_date`、`out_date` 和 `is_new`,适合保存成员区间;但官方页没有说明剔除日是否仍算有效,也没有证明返回历史覆盖能够重建 OneChartLab 的全部历史 universe。Macro 当前公开 universe 名称含 `current124`,现有页面报告也指出完整历史 point-in-time 成员未公开。因此历史计算不得把当前 124 行业及当前成员回填到过去。[Tushare 官方字段已确认;页面/manifest 已确认当前 universe;需实测边界与完整性]
## 接口字典:调用与落库
下表的“必要字段”是本任务最小字段集,不代表应丢弃供应商原始响应。生产中建议同时保存原始 JSON/Parquet、请求参数和响应 hash,以便接口字段调整或历史修订后重放。
Tushare 多个接口不会默认返回全部字段,尤其是 `daily_basic.limit_status`、`stock_basic.exchange/list_status/delist_date`、`index_classify.src` 和 `index_basic.exp_date`。生产调用必须显式传入本表列出的 `fields`,并把请求参数中的分类版本等维度一并落库;不能假设默认响应足以构造生命周期过滤或自然业务主键。[Tushare 官方文档已确认;复现建议]
| 接口 | 模块用途/级别 | 主要调用参数 | 必要输出字段 | 粒度与自然业务主键 |
| --- | --- | --- | --- | --- |
| [`dc_index`](https://tushare.pro/document/2?doc_id=362) | Radar 必需:每日东财概念、行业、地域板块身份与类型 | `trade_date`;回填用 `start_date/end_date`;`idx_type` 必填,也可按 `ts_code/name` | `ts_code,trade_date,name,idx_type,level,pct_change,leading_code` | 板块×交易日;`(trade_date,ts_code)` |
| [`dc_member`](https://tushare.pro/document/2?doc_id=363) | Radar 必需:每日板块成员 | 优先按 `trade_date`,必要时按 `ts_code` 分片;支持 `con_code,start_date,end_date` | `trade_date,ts_code,con_code,name` | 板块×股票×交易日;`(trade_date,ts_code,con_code)` |
| [`moneyflow_dc`](https://tushare.pro/document/2?doc_id=349) | 两模块必需:个股东财口径主力净额 | 日增量按 `trade_date`;回填按股票或日期窗口 | `trade_date,ts_code,name,net_amount,net_amount_rate,pct_change,close`,建议保留四档净流入字段 | 股票×交易日;`(ts_code,trade_date)` |
| [`moneyflow`](https://tushare.pro/document/2?doc_id=170) | 完整成分解释层必需:L2 主买净额 | 日增量按 `trade_date`;回填按 `ts_code` 或日期窗口 | `ts_code,trade_date,net_mf_amount,net_mf_vol`,建议保留各档买卖金额/量 | 股票×交易日;`(ts_code,trade_date)` |
| [`moneyflow_ind_dc`](https://tushare.pro/document/2?doc_id=344) | Radar 推荐:官方东财板块资金流,供 raw/ratio 对账 | `trade_date` 或日期窗口;`content_type` 区分行业/概念/地域 | `trade_date,content_type,ts_code,name,net_amount,net_amount_rate,pct_change,rank` | 板块类型×板块×交易日;`(trade_date,content_type,ts_code)` |
| [`daily`](https://tushare.pro/document/2?doc_id=27) | 两模块必需:个股涨跌幅、成交额与行情可用性 | 日增量按 `trade_date`;官方建议按日期循环全市场,避免逐股回填 | `ts_code,trade_date,close,pre_close,pct_chg,vol,amount` | 股票×交易日;`(ts_code,trade_date)` |
| [`daily_basic`](https://tushare.pro/document/2?doc_id=32) | 推荐:换手率、市值、量比和低流动性质量规则 | 日增量按 `trade_date`;单股历史用 `ts_code,start_date,end_date` | `ts_code,trade_date,turnover_rate,turnover_rate_f,volume_ratio,total_mv,circ_mv,limit_status` | 股票×交易日;`(ts_code,trade_date)` |
| [`trade_cal`](https://tushare.pro/document/2?doc_id=26) | 两模块必需:交易日序列、窗口和前序日 | `exchange,start_date,end_date`;A 股至少保存 SSE,建议同时校验 SZSE | `exchange,cal_date,is_open,pretrade_date` | 交易所×日历日;`(exchange,cal_date)` |
| [`stock_basic`](https://tushare.pro/document/2?doc_id=25) | 两模块必需:上市/退市生命周期和市场过滤 | 必须分别拉 `list_status=L,D,P,G,UN`,不可只用默认 `L`;可按 `exchange/market` | `ts_code,symbol,name,market,exchange,list_status,list_date,delist_date` | 当前证券主记录;`ts_code`,另存带 `observed_at` 的快照版本 |
| [`namechange`](https://tushare.pro/document/2?doc_id=100) | 推荐:历史名称、展示标签和 ST 名称审计 | 增量按公告区间 `start_date/end_date`,也可按 `ts_code` 回填 | `ts_code,name,start_date,end_date,ann_date,change_reason` | 股票×名称有效期;建议 `(ts_code,start_date,name)`,将其余字段视为可修订 |
| [`suspend_d`](https://tushare.pro/document/2?doc_id=214) | 两模块必需:区分停牌、复牌与数据缺失 | 日增量按 `trade_date`;回填用 `start_date/end_date`;可按 `suspend_type` | `ts_code,trade_date,suspend_timing,suspend_type` | 股票×停复牌事件日;建议 `(ts_code,trade_date,suspend_type,suspend_timing)` |
| [`adj_factor`](https://tushare.pro/document/2?doc_id=28) | 可选/推荐质检:复权和公司行动 | 日增量按 `trade_date`;回填可按 `ts_code` 全历史 | `ts_code,trade_date,adj_factor` | 股票×交易日;`(ts_code,trade_date)` |
| [`index_classify`](https://tushare.pro/document/2?doc_id=181) | Macro 必需:申万 2014/2021 分类与 L2 universe | `src=SW2021,level=L2`;可按 `index_code,parent_code` | `index_code,industry_name,parent_code,level,industry_code,is_pub,src` | 分类版本×行业;`(src,index_code)` |
| [`index_member_all`](https://tushare.pro/document/2?doc_id=335) | Macro 必需:申万 L1/L2/L3 成员区间 | 按 `l2_code` 分片;为历史回填分别核对 `is_new=Y/N`;也可按 `ts_code` | `l1_code,l1_name,l2_code,l2_name,l3_code,l3_name,ts_code,name,in_date,out_date,is_new` | 行业层级路径×股票×纳入区间;建议 `(l1_code,l2_code,l3_code,ts_code,in_date)` |
| [`sw_daily`](https://tushare.pro/document/2?doc_id=327) | Macro 必需:申万行业日线 | 日增量按 `trade_date`;回填按 `ts_code,start_date,end_date` | `ts_code,trade_date,name,close,pct_change,vol,amount`,需要时保留 OHLC/估值/市值 | 申万指数×交易日;`(ts_code,trade_date)` |
| [`index_basic`](https://tushare.pro/document/2?doc_id=94) | Macro 推荐:七指数代码和生命周期校验 | 按 `ts_code/symbol`,或分市场 `SSE/SZSE/CSI` 拉取 | `ts_code,name,market,publisher,category,list_date,exp_date` | 指数;`ts_code`,另存观察快照 |
| [`index_daily`](https://tushare.pro/document/2?doc_id=95) | Macro 必需:七个市场对照指数;不含申万行业 | 必须给 `ts_code`,按 `start_date/end_date` 回填或按日增量 | `ts_code,trade_date,close,pre_close,pct_chg`,建议保留 OHLC/量额 | 指数×交易日;`(ts_code,trade_date)` |
## 接口字典:拉取策略、时效与权限
| 接口 | 建议拉取方式、更新时点与历史深度 | 官方限量/权限页面快照 | 证据状态与待测项 |
| --- | --- | --- | --- |
| `dc_index` | 每个开市日按 `idx_type` 分三类拉取并保存完整日快照;回填按日期循环。官方称支持每日板块数据,未给稳定入库时点。 | 单次 5,000 行;页面写 6,000 积分 | 字段/限量/积分为 Tushare 官方确认;实际到达时点和账号频次需实测 |
| `dc_member` | 每个开市日按日期拉完整成员;超过上限时再按板块代码分片。历史成员按日期回填,绝不拿最新成员覆盖历史。 | 单次 5,000 行;页面写 6,000 积分 | 官方确认可按板块代码和日期取历史;最早历史、当日到达时点、频次需实测 |
| `moneyflow_dc` | 每日盘后按 `trade_date` 拉全市场;回填先按日期,失败再按股票分段。官方明确历史始于 2023-09-11。 | 单次 6,000 行;页面写至少 5,000 积分 | Tushare 官方确认;盘后具体完成时刻和账号频次需实测。2023-09-11 以前不能由本接口补齐 |
| `moneyflow` | 每日按 `trade_date` 拉全市场;长历史按日期或股票分片。官方写数据始于 2010 年。 | 单次 6,000 行、总量不限;页面写至少 2,000 积分且基础积分有流控 | Tushare 官方确认;每日到达时点和账号实际流控需实测 |
| `moneyflow_ind_dc` | 每日盘后按日期和 `content_type` 拉取,用于与逐股聚合和公开 Radar raw 做三方对账。 | 单次 5,000 行;页面写 6,000 积分 | Tushare 官方确认接口,但 OneChartLab manifest 未声明其为主雷达构建输入;实际等价性需实测 |
| `daily` | 交易日按日期全市场增量;官方建议按日期循环。15:00—16:00 入库,停牌期间不返回。 | 单次 6,000 行;官方页写基础积分每分钟 500 次 | Tushare 官方确认;目标账号仍需做限流和完整性实测 |
| `daily_basic` | 交易日按日期拉全市场;15:00—17:00 入库。历史可按日循环。 | 单次 6,000 行;至少 2,000 积分,官方称 5,000 积分无总量限制 | Tushare 官方确认;实际频次需账号实测 |
| `trade_cal` | 初始化拉完整日历,之后按年滚动刷新;不应每天全量重拉。历史起点和远期日历发布范围以响应为准。 | 页面写 2,000 积分;未给稳定行数/频次 | 字段/积分为官方确认;覆盖范围与账号频次需实测 |
| `stock_basic` | 初始化分别拉所有 `list_status`,每日或每周做小成本快照对比;事件发生后更新生命周期。 | 单次 6,000 行;2,000 积分起、每分钟 50 次 | Tushare 官方确认;接口默认仅 `L`,必须显式补 D/P/G/UN |
| `namechange` | 初始化按股票或公告日期回填,之后按最近公告窗口重叠拉取,允许旧记录修订。 | 官方页未给稳定积分、限量或频次 | 字段为 Tushare 官方确认;权限、频次、最早历史与日期边界需实测 |
| `suspend_d` | 每个开市日按日期拉,连续重叠回拉最近若干日以接纳“不定期”修订;历史按日期窗口。 | 官方页未给稳定积分、限量或频次 | Tushare 官方确认“不定期更新”;权限、频次、覆盖和同日多事件需实测 |
| `adj_factor` | 盘前 09:15—09:20 后按日期增量;回填按股票或日期。 | 2,000 积分起;5,000 以上可高频,未给精确频次 | Tushare 官方确认;实际频次需实测 |
| `index_classify` | 初始化按 `SW2014/SW2021 × L1/L2/L3` 保存版本快照;低频刷新并对比代码、名称、父级和 `is_pub`。 | 页面写 2,000 积分,未给稳定行数/频次 | Tushare 官方确认;版本修订时点和账号频次需实测 |
| `index_member_all` | 按 L2 行业分片回填 `is_new=Y/N`,原样保存 `in_date/out_date`;每日或每周检查新增/剔除,不覆盖区间历史。 | 单次 2,000 行、总量不限;页面写 2,000 积分 | Tushare 官方确认字段;`is_new=N` 的完整语义、剔除日边界、历史完整性和频次需实测 |
| `sw_daily` | 每日按日期拉全部发布行业,回填按行业代码分段;默认是申万 2021 版。 | 单次 4,000 行;页面写 5,000 积分 | Tushare 官方确认;最早历史、到达时点和实际频次需实测 |
| `index_basic` | 初始化按市场拉取,低频刷新;官方页写每天 18:00 更新、数据起始 1990-01-01。 | 单次 8,000 行;2,000 积分起 | Tushare 官方确认;账号频次需实测 |
| `index_daily` | 七个代码逐个按时间段回填,每日收盘后增量;指数自身上市前为空。 | 页面写 2,000 积分,5,000 以上频次相对较高,未给精确频次 | Tushare 官方确认;到达时点和实际频次需实测 |
### 接口名与口径更正
1. `moneyflow_dc` 是“个股资金流向(DC)”,其 `net_amount` 单位为万元;东财概念/行业板块的直接资金流接口是 `moneyflow_ind_dc`,其 `net_amount` 单位为元。两者不可因名称相近混用。[Tushare 官方文档已确认]
2. `moneyflow.net_mf_amount` 是基于 L2 主动买卖单统计的净流入,官方明确说不能简单由大小单总和相减;这与 OneChartLab manifest 的“主买净额”定义一致。[页面/manifest 与 Tushare 官方文档均确认]
3. `daily` 是未复权行情,`adj_factor` 只是复权因子;若需要 qfq 价格,应使用官方 [`pro_bar`](https://tushare.pro/document/2?doc_id=146) 的 `adj="qfq"` 或基于版本化规则计算,不能把 `adj_factor` 当价格。[Tushare 官方文档已确认]
4. `index_daily` 官方明确“不包含申万行业指数行情数据”;申万行业必须用 `sw_daily`。`index_classify(src="SW2021", level="L2")` 提供分类,`index_member_all` 提供带纳入/剔除日期的成员关系。[Tushare 官方文档已确认]
5. 当前官方页面确认的东财板块目录接口是 `dc_index`,成员接口是 `dc_member`。不存在用一个静态 `dc_member` 当前表安全回填全部历史的做法;必须按交易日保存成员快照。[Tushare 官方文档已确认;复现建议]
6. OneChartLab manifest 出现 `tushare.suspend`,但当前 Tushare 官方接口目录只找到 `suspend_d` 的公开页。实施清单中只把 `suspend_d` 视为已确认接口;`suspend` 作为历史/兼容调用名单独实测,未成功前不得建立生产依赖。[页面/manifest 已确认字符串;需实测]
## 最小采集集与增强采集集
### 目标 A:页面消费层最小集
无需 Tushare token,按发布物原样保存:Radar 的 `radar_manifest.json`、30 日日期分片、`rank_history`、成分排行 manifest 和日期文件;Macro 的 `state.json`、`current/last_good` 指向的 `series.json`、dispersion manifest、`latest180` 与所需年度块。保存 URL、HTTP 观测时间、内容 hash、schema 和所有批次身份。这个集合能复现页面已经公开的结果和消费公式,但不能生成缺失日期或重建私有指标。[页面/manifest 已确认]
### 目标 B:独立上游最小集
Radar 主榜最小集为 `trade_cal + dc_index + dc_member + stock_basic + suspend_d + daily + moneyflow_dc`;若要完整复现成分解释,再加入 `moneyflow`。Macro 主序列实验最小集为 `trade_cal + index_classify + index_member_all + sw_daily + index_daily`;若要完整成分解释,再复用 `stock_basic + suspend_d + daily + moneyflow_dc + moneyflow`。该集合只保证输入事实充分,不保证 `Ratio/Swing/D/C/O` 与原站一致。[复现建议;缺公式]
### 增强采集集
加入 `daily_basic` 做换手率、量比、市值和低流动性质量控制;加入 `moneyflow_ind_dc` 对账官方板块级净额/净占比;加入 `namechange` 保留历史名称区间;加入 `adj_factor` 做复权和公司行动质检;加入 `index_basic` 校验七指数代码、发布方和终止日期。增强字段参与计算时必须写入参数版本,不能静默改变历史结果。[复现建议]
## 历史回填、每日增量与幂等
### 历史回填顺序
1. 先回填 `trade_cal`、`stock_basic` 全状态、`namechange`、`index_basic`、`index_classify`,建立代码、生命周期、版本和交易日维表。
2. 再回填 `dc_member` 每日快照和 `index_member_all` 成员区间;先完成成员事实,避免用当前成员污染历史。
3. 回填 `daily`、`daily_basic`、`sw_daily`、`index_daily` 和 `adj_factor`,先形成价格与成交事实。
4. 最后回填 `moneyflow_dc`、`moneyflow` 和 `moneyflow_ind_dc`。`moneyflow_dc` 只能从 2023-09-11 开始,早期缺口必须显式标记,不能用 0 填充。
5. 在原始层通过覆盖率、重复键、单位和生命周期检查后,再生成成员有效日、窗口收益、资金聚合、替代指标和公开页面对账结果。
### 每日增量 DAG
```mermaid
flowchart TD
A[trade_cal 确认开市日 T] --> B[盘前 stock_basic / adj_factor / 分类变更]
B --> C[盘后轮询 daily / daily_basic / index_daily / sw_daily]
C --> D[拉 moneyflow_dc / moneyflow / moneyflow_ind_dc]
C --> E[拉 dc_index / dc_member / suspend_d]
D --> F[完整性与单位校验屏障]
E --> F
F --> G[生成 T 日 point-in-time 有效成员]
G --> H[逐股事实与板块/行业窗口聚合]
H --> I[公开发布物对账与版本化指标计算]
I --> J[原子发布 manifest + hash + last_good]
```
不能只按固定钟点启动一次。`daily`、`daily_basic`、资金流和成员数据的到达时间不同,应分别轮询到“日期正确、行数/覆盖率达标、关键字段非空”,再通过校验屏障;超时则保留上一个 `last_good` 并标记 stale/partial,不发布假完整结果。OneChartLab 页脚的约 17:00—17:30/视频简介的约 17:10—17:40 只应作为观察窗口,不是 Tushare SLA。[页面与作者自述;复现建议]
### 幂等与修订
所有事实表按上文自然业务主键 upsert。先对同一请求参数和规范化响应计算 hash:相同则记 `unchanged`,不同则在原始版本表新增 `observed_at/source_revision`,再更新当前投影。成员、名称和生命周期表不得做破坏性覆盖;派生结果主键至少包含 `(metric_version, universe_version, trade_date, entity_code)`。公开 OneChartLab 发布物还应把 `batch_id/bundle_id/revision_watermark/content_sha256` 放入唯一约束或发布身份,允许同一 `as_of_date` 多修订共存。[复现建议]
## Point-in-time 与证券状态处理
`dc_member` 已直接提供交易日粒度,某日成员只来自该日快照。缺少某日快照时应标记 `membership_unknown`,不能向前或向后填充,除非经过单独验证并记录填充策略版本。[Tushare 官方字段已确认;复现建议]
`index_member_all` 应原样保存 Y/N 两类响应和 `in_date/out_date`。候选有效规则是 `in_date <= T < out_date`,空 `out_date` 表示仍有效;但剔除日是否排除、`is_new=N` 是否返回全部历史、同股多次纳入如何表达,都需账号样本验证。验证前不要把这个候选规则写成供应商保证。[复现建议;需实测]
`stock_basic` 默认只返回上市状态 `L`,历史回填必须显式拉取 `D/P/G/UN`。证券只有在 `list_date <= T` 且 `delist_date` 为空或 `T <= delist_date` 时才进入生命周期候选;再按 OneChartLab manifest 排除 B 股和北交所。停牌日 `daily` 不返回数据,必须联合 `suspend_d` 判断:无行情既可能是停牌,也可能是未上市、已退市、成员无效或供应商延迟,不能把缺行自动变成收益 0。[Tushare 官方与页面/manifest 已确认;复现建议]
`namechange` 用代码和有效日期恢复历史展示名,不作为稳定身份。ST/风险警示状态若会影响自建 universe,应单独定义并验证数据源;OneChartLab 当前公开 ranking universe 契约没有声明排除 ST,不应根据本项目其他模块的“非 ST”规则擅自改变复现口径。[页面/manifest 已确认;复现建议]
## 单位、空值与质量规则
必须在原始层保留供应商单位,在规范层显式换算:
| 来源字段 | 原始单位 | 规范换算/注意事项 |
| --- | --- | --- |
| `moneyflow_dc.net_amount`、`moneyflow.net_mf_amount` | 万元 | 亿元值为 `/ 10_000`;元值为 `× 10_000` |
| `moneyflow_ind_dc.net_amount` | 元 | 亿元值为 `/ 100_000_000`;不能沿用个股资金流换算 |
| `daily.amount`、`index_daily.amount` | 千元 | 亿元值为 `/ 100_000` |
| `sw_daily.amount` | 万元 | 亿元值为 `/ 10_000` |
| `daily.vol`、`index_daily.vol` | 手 | 与 `sw_daily.vol` 的万股单位分开保存 |
| `sw_daily.vol` | 万股 | 不得与“手”直接相加或比较 |
| `daily.pct_chg`、`index_daily.pct_chg`、`sw_daily.pct_change` | 百分数 | `1.5` 表示 1.5%;做复合收益时先除以 100 |
| OneChart `Ratio_Raw_Pct/Swing_Ratio_Val` | 公开 payload 的小数比例 | 既有前端展示时乘 100;不要与 Tushare 的百分数字段直接混算 |
建议执行以下质量门槛,但阈值必须由数据分布和公开样本校准后版本化:
- 主键重复、代码格式错误、非有限数、日期不在交易日历、生命周期外观测属于硬错误;空字符串、`None`、`NaN` 统一规范为 NULL,不能转成 0。[复现建议]
- 对每个交易日分别计算 `daily`、`moneyflow_dc`、`moneyflow`、成员和指数覆盖率;缺失资金流不代表净流入为 0。只有在生命周期有效、非停牌且源接口应有数据时才进入缺失率分母。[复现建议]
- 板块有效样本数、总成交额、换手率和成员覆盖率必须随派生结果输出。小样本、低成交额、低换手率或分母接近 0 时标记 `available_limited_sample/low_liquidity`,禁止仅因比例高就视为强信号。具体阈值公开资料未披露,需校准。[作者自述与公开页面质量语义;需实测]
- 计算资金占比候选时,先把 `moneyflow_dc.net_amount` 和 `daily.amount` 统一到元,再做 `sum(net_amount)/sum(amount)`;分母为 0 或缺失则返回 NULL。该公式只是对账候选,不是已确认的 `Ratio_Raw_Pct`。[复现建议;需实测]
- 同日 `daily.pct_chg` 与 `moneyflow_dc.pct_change`、板块逐股聚合与 `moneyflow_ind_dc` 只用于差异告警,不应无版本地互相覆盖。不同数据源可能有到达时间、复权和成分口径差异。[Tushare 官方字段已确认;复现建议]
- `index_classify` 的 `is_pub=0` 行业和成分少于 5 的指数可能不发布行情;构造 Macro universe 时应记录剔除原因,不能用 0 收益补齐。[Tushare 官方文档已确认]
## 仍无法由 Tushare 直接提供或确认的内容
Tushare 数据补齐后,下列内容仍是明确缺口:
- `Amount_Score` 和 `Ratio_Score` 的标准化、截尾、规模/流动性调整及并列排序稳定键;
- `Ratio_Raw_Pct` 的精确分子、分母、先聚合后求比或先求比后聚合、异常值处理;
- 3—10 个交易日的具体权重、`Swing_Ratio_Val`、`Swing_Amount_Val` 和 `Swing_Score` 的组合公式;
- Macro 核心 `D` 使用的横截面统计量、相对收益基准、90 日自然日/交易日口径、截尾和历史校准;
- 行业贡献 `C` 的上游分解和 `O` 的定向符号规则。公开数据只能确认 `ΔD=ΣC`、`K=ΣO`、`abs(O)=abs(C)` 等下游恒等式;
- 原站如何生成 `accepted_input_hash`、`revision_watermark`、历史修订和 `last_good`,以及未公开的参数版本、输入快照和发布时间戳。
因此,独立实现必须给自建公式新的名称和 `metric_version`,并同时输出与 OneChartLab 公开序列的误差、覆盖率和样本状态。不能通过拟合一个倍率或选择最接近的 Tushare 字段来宣称“完全复刻”。[页面/公开数据复算已确认缺口;复现建议]
## Python 调用骨架
以下骨架只展示调用和幂等批次形状,token 从环境注入,不写入代码、日志或请求清单。Tushare 官方客户端的 [`DataApi.query`](https://github.com/waditu/tushare/blob/master/tushare/pro/client.py) 会把动态方法名映射为 `api_name`,因此 `pro.dc_member(...)` 与 `pro.query("dc_member", ...)` 属于同一调用模型。[Tushare 官方 GitHub 源码已确认]
```python
from __future__ import annotations
import os
from collections.abc import Callable
import pandas as pd
import tushare as ts
FIELDS = {
"daily": "ts_code,trade_date,close,pre_close,pct_chg,vol,amount",
"daily_basic": (
"ts_code,trade_date,turnover_rate,turnover_rate_f,volume_ratio,"
"total_mv,circ_mv,limit_status"
),
"moneyflow_dc": (
"trade_date,ts_code,name,net_amount,net_amount_rate,pct_change,close"
),
"moneyflow": "ts_code,trade_date,net_mf_amount,net_mf_vol",
"suspend_d": "ts_code,trade_date,suspend_timing,suspend_type",
"dc_index": "ts_code,trade_date,name,idx_type,level,pct_change,leading_code",
"dc_member": "trade_date,ts_code,con_code,name",
"sw_daily": "ts_code,trade_date,name,close,pct_change,vol,amount",
"stock_basic": (
"ts_code,symbol,name,market,exchange,list_status,list_date,delist_date"
),
"index_classify": (
"index_code,industry_name,parent_code,level,industry_code,is_pub,src"
),
"index_member_all": (
"l1_code,l1_name,l2_code,l2_name,l3_code,l3_name,ts_code,name,"
"in_date,out_date,is_new"
),
}
def build_client():
"""从运行环境创建 Tushare Pro 客户端,不泄露或持久化 token。"""
return ts.pro_api(os.environ["TUSHARE_TOKEN"])
def fetch_trade_date(pro, trade_date: str) -> dict[str, pd.DataFrame]:
"""拉取一个交易日的原始事实;重试、限流和落库由外层批次协调器负责。"""
return {
"daily": pro.daily(trade_date=trade_date, fields=FIELDS["daily"]),
"daily_basic": pro.daily_basic(
trade_date=trade_date, fields=FIELDS["daily_basic"]
),
"moneyflow_dc": pro.moneyflow_dc(
trade_date=trade_date, fields=FIELDS["moneyflow_dc"]
),
"moneyflow": pro.moneyflow(
trade_date=trade_date, fields=FIELDS["moneyflow"]
),
"suspend_d": pro.suspend_d(
trade_date=trade_date, fields=FIELDS["suspend_d"]
),
"dc_index_concept": pro.dc_index(
trade_date=trade_date,
idx_type="概念板块",
fields=FIELDS["dc_index"],
),
"dc_index_industry": pro.dc_index(
trade_date=trade_date,
idx_type="行业板块",
fields=FIELDS["dc_index"],
),
"dc_member": pro.dc_member(
trade_date=trade_date, fields=FIELDS["dc_member"]
),
"sw_daily": pro.sw_daily(
trade_date=trade_date, fields=FIELDS["sw_daily"]
),
}
def fetch_security_dimensions(pro) -> pd.DataFrame:
"""显式拉取全部上市状态及非默认生命周期字段。"""
parts = [
pro.stock_basic(list_status=status, fields=FIELDS["stock_basic"])
for status in ("L", "D", "P", "G", "UN")
]
return pd.concat(parts, ignore_index=True)
def fetch_macro_dimensions(pro) -> dict[str, pd.DataFrame]:
"""拉取申万 2021 二级分类及成员响应;历史/当前响应必须分开保存。"""
classification_version = "SW2021"
industries = pro.index_classify(
src=classification_version,
level="L2",
fields=FIELDS["index_classify"],
)
industries["classification_version"] = classification_version
current_parts = []
historical_parts = []
for l2_code in industries["index_code"].dropna().unique():
current = pro.index_member_all(
l2_code=l2_code,
is_new="Y",
fields=FIELDS["index_member_all"],
)
historical = pro.index_member_all(
l2_code=l2_code,
is_new="N",
fields=FIELDS["index_member_all"],
)
current["classification_version"] = classification_version
historical["classification_version"] = classification_version
current_parts.append(current)
historical_parts.append(historical)
return {
"index_classify": industries,
"index_member_current": pd.concat(current_parts, ignore_index=True),
"index_member_history": pd.concat(historical_parts, ignore_index=True),
}
def ingest(
api_name: str,
params: dict[str, str],
fetch: Callable[[], pd.DataFrame],
) -> None:
"""保存请求身份、原始响应 hash,再按自然业务主键幂等 upsert。"""
frame = fetch()
normalized = normalize_nulls_units_and_dates(
api_name,
frame,
request_params=params,
)
assert_unique_business_keys(api_name, normalized)
raw_version = persist_raw_response(api_name, params, frame)
upsert_current_projection(api_name, normalized, source_version=raw_version)
```
`dc_member` 全市场单日若超过 5,000 行,必须按 `ts_code` 或其他可验证分片重拉;不能接受被截断的“成功”响应。所有接口同理以官方单次上限、返回行数和预期覆盖率判断是否需要分片。[Tushare 官方限量;复现建议]
## 实施前账号实测清单
1. 用目标账号逐一调用 17 个接口,记录成功/失败、错误码、单次行数、分钟级限流和字段集合;尤其验证官方页未写稳定权限的 `namechange`、`suspend_d`,以及 manifest 中未找到当前官方页的 `suspend`。
2. 在连续至少 5 个交易日记录每个接口首次完整到达时间、后续修订时间和行数,以确定每日 DAG 的轮询、超时与 `last_good` 策略。
3. 验证 `dc_member` 的最早历史和按日完整性;验证 `index_member_all(is_new=Y/N)` 是否覆盖全部历史、重复纳入、空 `out_date` 和剔除日边界。
4. 对同一交易日抽取至少一个概念板块、一个东财行业和一个申万二级行业,比较逐股 `sum(moneyflow_dc.net_amount)`、`moneyflow_ind_dc.net_amount` 与 OneChartLab 公开 raw/成分榜,记录成员覆盖和单位差异。
5. 验证停牌日、上市首日、退市前后、更名日、B 股/北交所和当日无资金流的样本,确认“无行、NULL、0、无效成员”四种状态不会混淆。
## 主要第一方来源
- OneChartLab [`sector_rankings/manifest.json`](https://onechartlab.com/sector_rankings/manifest.json):成员、资金流、涨跌幅、停牌、生命周期、单位、聚合与股票池契约。
- OneChartLab [`radar_manifest.json`](https://onechartlab.com/radar_manifest.json):Radar 日期分片和排名历史入口。
- OneChartLab [`macro/state.json`](https://onechartlab.com/macro/state.json):Macro 当前/最近有效批次、hash 与修订水位。
- 作者视频[《我把自己的交易系统,做成了4个网页工具》](https://www.bilibili.com/video/BV16Hur6rEq2/):单日流入率的成交额分母语义、3—10 日窗口和 Macro 近 90 日申万二级行业输入语义;未披露精确公式。
- Tushare 官方接口页:[`dc_index`](https://tushare.pro/document/2?doc_id=362)、[`dc_member`](https://tushare.pro/document/2?doc_id=363)、[`moneyflow_dc`](https://tushare.pro/document/2?doc_id=349)、[`moneyflow`](https://tushare.pro/document/2?doc_id=170)、[`moneyflow_ind_dc`](https://tushare.pro/document/2?doc_id=344)、[`daily`](https://tushare.pro/document/2?doc_id=27)、[`daily_basic`](https://tushare.pro/document/2?doc_id=32)。
- Tushare 官方接口页:[`trade_cal`](https://tushare.pro/document/2?doc_id=26)、[`stock_basic`](https://tushare.pro/document/2?doc_id=25)、[`namechange`](https://tushare.pro/document/2?doc_id=100)、[`suspend_d`](https://tushare.pro/document/2?doc_id=214)、[`adj_factor`](https://tushare.pro/document/2?doc_id=28)。
- Tushare 官方接口页:[`index_classify`](https://tushare.pro/document/2?doc_id=181)、[`index_member_all`](https://tushare.pro/document/2?doc_id=335)、[`sw_daily`](https://tushare.pro/document/2?doc_id=327)、[`index_basic`](https://tushare.pro/document/2?doc_id=94)、[`index_daily`](https://tushare.pro/document/2?doc_id=95)。
- Tushare 官方 GitHub [`tushare/pro/client.py`](https://github.com/waditu/tushare/blob/master/tushare/pro/client.py):Pro 请求结构和动态接口方法实现。
本文结论用于数据工程与公开机制复现,不评价指标的投资有效性,也不代表能够复制原作者未公开的专有算法。