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

377 lines
39 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.
# 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 请求结构和动态接口方法实现。
本文结论用于数据工程与公开机制复现,不评价指标的投资有效性,也不代表能够复制原作者未公开的专有算法。