Files
zhixing-system/.trellis/tasks/archive/2026-08/08-11-market-data-sync-performance-audit/prd.md
T
2026-08-11 13:32:21 +08:00

5.5 KiB

优化市场数据同步并增加完整校验入口

Goal

把当前接近四小时的市场数据同步缩短到可接受范围,同时保留六年 qfq 历史修订检查、单股票失败隔离、PostgreSQL/CSV 发布一致性和选股覆盖率契约。 日常同步由外部调度器执行;耗时较长的完整检查改为用户在 Web 页面中主动触发并查看进度与结果。

Background

  • 2026-08-10 生产批次处理 5002 只股票,总耗时 13339.3 秒;行情阶段后半段稳定在 2.525–2.820 秒/只,属于逐股固定成本。
  • 当前实现串行调用 5002 次 pro_bar(..., adj="qfq")。本机 Tushare 1.4.29 源码表明每次 qfq pro_bar 至少调用一次 daily 和一次 adj_factor。
  • Settings.market_data_max_workers 当前默认 4,但未被同步用例或 CLI 使用。
  • 旧项目 ../zgnb-project 使用 ThreadPoolExecutor(max_workers=8) 逐股拉取六年 pro_bar;识别频控错误后按 60/120/180 秒退避,普通错误按 5/10/15 秒退避。 它是八路逐股并发,不是按交易日一次请求全市场。
  • 当前 PostgreSQL 适配器在单股 upsert、单股审计记录和覆盖率存在性检查中频繁创建 新连接;bar 阶段完成后仍耗时约 110.2 秒才完成批次。
  • 当前 Web 导航已有未启用的“同步任务”入口,后端已有 selection 的 202 Accepted + BackgroundTasks + polling 长任务模式,但生产服务重启会中断进程内任务。

Requirements

R1. 八路受控并发

  • 需要同步单股票行情时默认使用 8 个 worker,并允许通过 ZHIXING_MARKET_DATA_MAX_WORKERS 配置。
  • worker 必须保留单股票事务、临时 CSV、数据库提交后原子发布和单股票失败隔离语义。
  • Tushare 频控由所有 worker 协同处理,不能让一个 worker 进入长退避时其他 worker 继续无界冲击接口;重试次数、退避和最终失败必须可观测且不泄露凭据。
  • 同一环境仍只允许一个市场数据同步或完整检查批次持有 advisory lock。

R2. 日常同步与完整检查分离

  • 外部调度器继续触发日常同步,日常同步不依赖浏览器或 FastAPI 生命周期定时器。
  • 日常同步继续按旧项目的数据口径,对全部目标股票逐股获取六年 qfq 完整快照;性能优化来自 8 路受控并发,而不是改成按目标交易日增量拉取。
  • 本任务不引入 adj_factor 持久化或按复权因子变化选择性重建历史的增量方案。
  • PostgreSQL/CSV 完整性检查不由 cron 自动触发,只允许用户从 HTTP/Web 主动发起;该检查 不调用 Tushare,也不重新下载行情。
  • 完整检查需要持久化批次、目标/已检查/问题计数和安全错误,并提供轮询查询契约。
  • 用户可在“同步任务”页面触发完整检查;运行中禁止重复触发,并持续展示进度和最终结果。
  • 完整检查只报告问题:除检查批次和问题明细外,不修改股票主数据、行情、估值或 CSV;发现的 异常由后续日常同步修复。

R3. PostgreSQL 批量化

  • 同一个批次复用有限数量的数据库连接,避免每只股票为 upsert、审计记录和覆盖率检查 分别建立新连接。
  • 同步 item 审计记录支持批量写入,同时保留失败股票的可定位记录。
  • 覆盖率通过集合 SQL 一次计算,不逐股票执行 has_bar / has_daily_basic。
  • 并发写入不得破坏 (ts_code, trade_date) 幂等约束或批次汇总计数。

R4. 兼容性

  • 保留现有 CLI 成功/部分成功/失败退出码、覆盖率阈值和失败重试语义。
  • 保留六年滚动窗口、qfq 唯一口径、PostgreSQL 事实源和 CSV 恢复快照边界。
  • 日常同步结果仍能作为选股批次的数据来源,完整检查不得让正在使用的最近成功批次 短暂变成不可用。

Acceptance Criteria

  • 默认并发数为 8,配置覆盖有效;并发测试能证明最多只有配置数量的单股任务同时执行。
  • 模拟 Tushare 频控时,所有 worker 服从共享冷却窗口,随后成功恢复或在重试预算耗尽后记录失败。
  • 单股票失败不发布其临时 CSV,不回滚其他成功股票,重复执行保持幂等。
  • 日常同步与手动完整检查的职责符合最终确认的数据口径。
  • POST 完整检查返回可轮询的批次标识;重复触发返回冲突;查询端点展示进度、状态和错误。
  • “同步任务”页面可触发完整检查并展示无记录、运行、通过、发现问题、执行失败状态。
  • 完整检查不调用 Tushare,不修改市场事实表或正式 CSV;有问题时只持久化安全报告。
  • 覆盖率计算不再产生逐股票查询;审计记录使用批量写入;连接创建数量不随股票数线性增长。
  • 5002 只股票的无网络性能测试/受控基准锁定并发调度和数据库调用数量;生产目标为正常频控条件下 完整下载约 30 分钟,结果需通过部署后日志验证,不能用本地 mock 代替生产结论。
  • 后端 Ruff、Pyright、pytest,前端格式、lint、typecheck、Vitest 和构建全部通过。

Out of Scope

  • 不新增分钟级或实时行情。
  • 不改变选股公式、覆盖率阈值业务含义或六年保留边界。
  • 不自动安排周期性完整检查。
  • 不把日常同步改造成按交易日/复权因子增量拉取。
  • 不在完整检查中自动修复、重试或重新下载异常对象。
  • 不在本任务中引入与市场数据无关的通用任务平台。