9.2 KiB
板块资金雷达技术设计
设计结论
首个 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 保持两个深模块:
BuildSectorRadar.execute(command) -> BuildSummary隐藏目标交易日解析、采集、校验、持久化、指标计算、排名和发布切换。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 对账差异只记录为研究数据,不自动覆盖本地结果。