Files
zhixing-system/.trellis/tasks/archive/2026-08/08-28-sector-capital-radar/design.md
T
2026-08-29 20:37:06 +08:00

9.2 KiB
Raw Blame History

板块资金雷达技术设计

设计结论

首个 MVP 建设 Tushare 独立生产链,生产运行时不读取 OneChartLab。后端新增 sector_radar bounded context,通过一次性 CLI 采集七类最小事实、保存 point-in-time 输入、计算三个带版本的知行独立指标、生成横截面排名并发布;FastAPI 只读取已发布结果。前端新增 /sector-radar feature,提供日期、板块类型、指标、强弱榜、排名变化、搜索和分页。

首个安全里程碑不实现成分股详情、历史轨迹图、导出、moneyflow 主买净额、daily_basic 流动性增强或 moneyflow_ind_dc 对账。架构为这些能力保留数据版本与指标策略 seam,但不创建空实现。

Module 与 seam

sector_radar bounded context

外部 interface 保持两个深模块:

  1. BuildSectorRadar.execute(command) -> BuildSummary 隐藏目标交易日解析、采集、校验、持久化、指标计算、排名和发布切换。
  2. ReadSectorRadar.list_dates() 与 ReadSectorRadar.query(query) -> RankingPage 隐藏 last-good 选择、筛选、排序和分页。

内部保留三个真实 seam:

  • SectorRadarSource:Tushare 生产 adapter 与测试 fake adapter;负责 trade_cal、dc_index、dc_member、stock_basic、suspend_d、daily、moneyflow_dc。
  • SectorRadarRepository:PostgreSQL 生产 adapter 与 application 测试 fake adapter;负责输入版本、发布批次和读取投影。
  • MetricStrategy:金额、单日资金率和波段资金率是三个实际可替换 adapter;每个结果必须带 metric_version、显示标签、排序值、单位和质量状态。

Tushare 请求协调能力已有第二个真实消费者后,将 RequestCoordinator 从 market_data infrastructure 提升为无业务所有权的小型 shared 基础能力;原市场同步与雷达 adapter 同时复用,且保留现有行为测试。

领域模型

  • SectorType:concept 或 industry;地域板块不进入 MVP。
  • SectorMembershipSnapshot:trade_date + sector_code + stock_code,只表示该交易日的成员事实。
  • StockDailyFact:股票在交易日的生命周期、停牌、成交额和主力净额状态;缺行、NULL、0 和无效成员不同义。
  • MetricObservation:策略版本生成的值、单位、有效样本数、成员覆盖率和质量状态。
  • RadarPublication:针对一个目标交易日的不可变构建版本,状态为 running|success|partial|failed,包含输入 hash、universe 版本、指标版本集合、覆盖率、开始/完成时间和安全错误摘要。
  • RadarRanking:属于某个 publication 和排名池的指标值、1 基排名、排名百分位及 1—5 日变化。
  • LastGoodPublication:不使用可被失败构建覆盖的可变字段;读取时选择最近一个 status=success 的 publication。失败与 partial 批次仍保留审计。

数据采集与质量

每日 Job 的目标交易日由 trade_cal 确认。目标日采集 dc_index 的概念与行业、dc_member、全部上市状态的 stock_basic、suspend_d、daily 和 moneyflow_dc。首次初始化或补算按交易日顺序执行,至少准备 10 个交易日才能产生完整波段指标,至少保留 30 个已发布交易日供排名变化和后续轨迹使用。

原始响应按 api_name + normalized_params + observed_at 保存 JSONB、行数和 SHA-256;规范表保存 point-in-time 成员与股票事实。响应接近官方单次上限或覆盖率不足时必须分片重拉,不能接受可能截断的成功响应。

有效股票候选满足目标日生命周期并属于沪深 A 股,排除北交所与 B 股;不沿用选股模块的 ST 排除规则。停牌且无 daily 的股票标记为 suspended,不作为应有行情缺失;应有 daily 或 moneyflow_dc 却缺失的股票进入覆盖率缺口,不能补 0。

发布成功门使用可配置的全局事实覆盖率,默认沿用项目的 0.99。成员快照必须完整;任一必需接口硬失败、重复业务键、日期错误、非有限数、单位校验失败或覆盖率低于门槛,当前 publication 为 partial 或 failed,不得成为 last-good。每个板块仍输出有效样本数、成员覆盖率和质量状态;少于 5 个有效成员标记 available_limited_sample。

独立指标策略

对板块 s、交易日 t,使用当日 point-in-time 有效成员:

Net(s,t)      = sum(moneyflow_dc.net_amount) × 10_000 元
Turnover(s,t) = sum(daily.amount) × 1_000 元
  • zhixing_amount_net_bn_v1 = Net(s,t) / 100_000_000,排序值为亿元净额。
  • zhixing_ratio_turnover_v1 = Net(s,t) / Turnover(s,t);分母为 0 或输入不完整时为 NULL。
  • 对每个 w ∈ [3,10],WindowRatio_w = sum(Net(s,d)) / sum(Turnover(s,d)),其中每个 d 使用自己的成员快照;zhixing_swing_equal_3_10_v1 是八个完整 WindowRatio_w 的算术平均。任一窗口不完整时为 NULL。

这些名称和页面标签都明确写“知行独立实现”。接口不暴露原站的 Ratio_Score 或 Swing_Score 字段名,而统一返回 metric_value、metric_version、unit 和 implementation_kind=independent。

排名契约

概念与行业分别成池。NULL 指标不进入排名,但作为 unavailable 记录保留质量信息。非 NULL 值按 metric_value DESC, sector_code ASC 排序,后者是知行独立稳定键,不宣称原站并列规则。

RankPct = 100 * (N - RankPos + 1) / N
Top      = RankPct >= 90
Bottom   = RankPct <= 10
RankChg  = PastRank - CurrentRank

排名变化按过去第 1—5 个已发布交易日计算;过去或当前排名缺失时返回 NULL,而不是伪造 0。前端明确显示暂无可比历史。

持久化

新增一条 Alembic 迁移,至少包含:

  • sector_radar_source_snapshot:接口、参数、目标日、原始 JSONB、行数、hash、观测时间;
  • sector_radar_membership:来源快照、交易日、板块类型、板块代码、股票代码及展示名;
  • sector_radar_stock_fact:来源快照集合、交易日、股票代码、生命周期/停牌状态、成交额、主力净额和数据状态;
  • sector_radar_publication:构建身份、状态、版本、覆盖率、input hash、时间和错误摘要;
  • sector_radar_ranking:publication、板块身份、指标版本、数值、单位、质量、排名百分位和历史变化。

业务唯一键必须包含交易日和来源/发布版本,允许同一交易日修订共存。数值使用有限 NUMERIC/Decimal;批量写入沿用 staging + COPY + 幂等 upsert。原始 token、完整请求头和未经净化的异常不得持久化。

CLI 与发布流程

新增 sector-radar-build 一次性 CLI:

  • 默认构建最近一个已收盘交易日;
  • --trade-date YYYY-MM-DD 构建单日;
  • --start-date/--end-date 按交易日顺序初始化或回填;
  • --retry-publication-id 只重试失败来源分片。

Job 使用独立 advisory lock。顺序为准备 running publication、采集并保存原始版本、规范化与质量屏障、计算指标与排名、事务写入、标记 success。任何阶段失败都保留原 publication 审计,读取端继续选择 last-good。FastAPI 不启动定时器;生产 Compose 增加 job service,实际定时继续由外部调度器负责。

HTTP 契约

挂载前缀 /api/v1/sector-radar:

  • GET /dates:返回可用成功日期、当前尝试状态、last-good 日期和发布时间;
  • GET /rankings:参数为 trade_date(缺省 last-good)、sector_type、view=amount|ratio|swing|rank_change、rank_change_metric、rank_change_days=1..5、side=top|bottom|all、search、page 和 page_size。

响应包含 publication 元数据、指标定义与独立实现声明、分页信息和排名行。没有成功发布时返回稳定的 no_data 成功响应;非法参数由 Pydantic/FastAPI 返回 422;数据库不可用映射 503。外部源异常只发生在 Job,不从读取端点实时透传。

前端

新增 features/sector-radar 垂直切片以及 /sector-radar 路由和导航入口。URL 保存日期、类型、视角、榜侧、搜索和分页;服务器数据只进入 React Query。页面使用现有 PageLayout、Card、Input、Select、Badge、Pagination 和语义 table,显式显示 loading、error、no-data、stale/partial/success。

首个 MVP 使用排名表和状态摘要,不引入图表库。金额显示亿元,比率显示百分比,指标旁始终显示策略版本或“知行独立实现”。

兼容、回滚与风险

  • 新表、新路由和新 feature 不改变现有 market-data/selection 契约;shared 请求协调器移动必须先保持现有测试通过。
  • 数据库迁移 downgrade 只删除新上下文表,不触碰现有市场数据。
  • 外部调度在 job 验证稳定前保持关闭;手工构建与读取验证通过后再启用。
  • 主要风险是目标账号实际权限/限流、dc_member 分片完整性和数据到达时间。首次实现必须提供不泄密的 capability probe 与覆盖率报告;没有 live token 时以 fake/golden 完成自动化验证,但不得声称生产采集通过。
  • OneChartLab 对账差异只记录为研究数据,不自动覆盖本地结果。