Files

85 lines
5.5 KiB
Markdown
Raw Permalink Normal View History

2026-08-11 13:32:21 +08:00
# 优化市场数据同步并增加完整校验入口
## 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
- 不新增分钟级或实时行情。
- 不改变选股公式、覆盖率阈值业务含义或六年保留边界。
- 不自动安排周期性完整检查。
- 不把日常同步改造成按交易日/复权因子增量拉取。
- 不在完整检查中自动修复、重试或重新下载异常对象。
- 不在本任务中引入与市场数据无关的通用任务平台。