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