文档性质:终局说明(思路 + 踩坑 + 反思 + 定稿架构),不是施工流水账。 版本:2026-08-08 · 仓库:jason9356/trader-data 数据根:D:\trader-store(密钥仅 secrets.env,永不进 git) 早期工程约定已发表:博客 · Tushare 本地全量入库(与仓库 blog_tushare_ingest_week_postmortem.md 同源) 策略调用面见 research_sdk.md;存储铁律见 storage_single_source.md


0. 一句话结论

这是一套 给策略调用的本地市场数据基础设施,不是看盘软件。

  • 下载:按表登记拉取形状(截面 / 报告期 / 按码),可续跑、可熔断。
  • 存储:业务事实只在 DuckDB;Parquet 只是备份。
  • 调用:横截面用 panel,单票研究用 dossier / barsResearchClient)。
  • 界面:看板与检修;不承担生产力级验数以外的职责。

截至定稿:约 88 张业务表、约 3.12 亿行,A 股日线主链水位约 20260807crypto.ohlcv 空表为待导入,不是损坏。


1. 从哪来:产品意图如何收敛

1.1 起初想要什么

独立模块「数据底座」,服务日后回测 / 策略 / 实盘:

  1. 多源:Tushare(高权限)、Freqtrade 加密、已购期货文件,并预留扩展口;
  2. 数据不再是黑箱——能检索、能抽查;
  3. 与兄弟项目解耦,契约稳定,可被 HTTP 或 Python 包消费。

第一期先搭了 Catalog + Parquet + sidecar + 验数 UI(Demo 闭环),再接入加密 / 期货,最后才是 Tushare 全量——这条生长顺序是对的:先有壳与契约,再灌数据

1.2 中途偏过的方向

阶段 做法 后来为什么改
Parquet 为主、DuckDB 加速扫湖 当时以为「湖 = 真相、库 = 缓存」 策略调用要稳定 SQL/契约;单文件库反而更适合业务;湖适合备份搬运
Hermes「核心只在湖、零重叠」 避免库湖双份 用户纠偏:业务只认库;重叠的备份可以接受;零重叠让读路径分裂
UI 做成工程师三栏控制台 / 研究首页 验数需要界面 再次纠偏:库的价值在调用,不在呈现;UI 降级为看板

1.3 定稿定位(必须记住)

策略 / 回测 / 研究脚本 → ResearchClient / 直接 SQL(只读)
运维 / 日更 / 迁库     → ingest + MarketDB upsert
人眼看板 / 检修         → Web UI + /db + /series(次要)

横纵两个调用维度(比 UI 重要):

维度 含义 入口
宇宙 × 交易日 × 字段面板 calendar / universe / panel / freshness
单标的全量切片 resolve / bars(adj) / dossier

2. 下载构建的思路(定稿)

2.1 总流程

账户探测 → 策略矩阵登记 → 注册表与批次对账
  → 冒烟(1~2 个真实截面)→ 全量/续跑(Guard + 心跳)
  → 校验布局与行数 →(历史)迁入 DuckDB / 双写 upsert
  → 日更只走水位(库内 max(date) 优先于状态 JSON)

细节级约定(模式定义、熔断开路/半开、湖分区布局、检查清单)以博客原文为准,不必在此重复抄写。真源代码:

主题 路径
策略矩阵 src/trader_data/ingest/pull_modes.py
A 股 loader src/trader_data/ingest/tushare_ashare.py
Pool / 批次 src/trader_data/ingest/tushare_pool.py
Guard src/trader_data/ingest/guard.py
DuckDB src/trader_data/db/market.py
研究调用 src/trader_data/research/

2.2 铁律(下载侧)

  1. 先形状,后速度——能 by_trade_date / by_period 绝不全市场 by_code
  2. 对本账户探测,不死抄文档示例(有无 *_vip、是否支持只传日期)。
  3. 未登记策略禁止开跑
  4. i/N ≠ 入库;看分区、行数、first non-empty
  5. 传输层可恢复(开路/半开),逻辑层硬停;勿把 ProxyError 三次失败当成永久终态。
  6. 改策略先 quarantine 旧分区并重置绑定水位
  7. 注册表 − 批次 − 排除 = ∅
  8. 排除默认不做:全市场 1 分钟(如 stk_mins);小时级限频接口独立慢批次。

2.3 写入定稿路径

  1. Asharewrite_partition MarketDB.upsert_dataframe写 parquet 备份。
  2. Pool:直接 upsert DuckDB(既有行为)。
  3. 历史一次迁入scripts/migrate_ashare_to_db.py 等(湖 → 库)。
  4. 水位by_trade_date 优先信库内 max(trade_date),状态 JSON 只是辅助;过期 JSON 会导致审计误报(用 refresh_sync_state_from_db.py 对齐)。

2.4 批次逻辑顺序(经验)

  1. Meta / 日线主链(daily、adj_factor、daily_basic、stk_limit、moneyflow…)
  2. 财务 by_period(VIP)
  3. 市场广度与微观截面
  4. 跨品种日频(指数、基金、期货日线…)
  5. Snapshot 维表
  6. 结构类(周月、持仓等)
  7. 强制 by_code 重表(如筹码)
  8. 小时级限频(最后)

3. 踩坑记录(比博客更「事后」的那一层)

下列条目来自全量建设 + 库湖纠偏 + 调用层落地;与博客 §5 互补,不替代。

3.1 形状与吞吐

表现 教训
财务按码循环 很忙、磁盘几乎不涨 默认 by_period + *_vip
指数/基金误用 by_code 墙钟爆炸 能按日键截面就截面
改布局后新旧混存 行数对不上、重复 quarantine;迁移只认一种布局

3.2 观测与运维

表现 教训
早于 history_start 空烧 i/N 走完 0 文件 / 误熔断 写准起点;看首次非空
代理闪断 假死、误判 Tushare 挂了 全量倾向直连;开路冷却而非三次永停
心跳/日志文件互锁 写入失败、进程秒退 每 job 独立心跳;Redirect 与业务日志分离
看门狗强杀 + 睡眠误报 「假死」其实是机器休眠 观察优先于 taskkill;电源策略要管
只杀 uv 启动器 PID 子进程残留 用 jobctl / 认清进程树
状态 JSON 过期 audit 一直报不一致 以库为准刷新状态;别吓自己

3.3 本地库与质量

表现 教训
未引用标识符 SQL 在 limit 等词处炸 schema/table/column 一律双引号
pandas 类型污染 string → DOUBLE 失败 object→VARCHAR;冲突则按样本重建
无主键叠行 行数虚高(cyq_chips、ths_member、利率等) 全业务表补 PK;fix_pool_pks 类脚本去重
财务 PK 过粗 湖库行数对不齐 纳入 update_flag / ann_date 等细主键后重迁
index_basic 被单市场覆盖 只剩数百条 多市场拉取(SSE/SZSE/CSI/SW/CNI/BSE/OTH)
宏观/股东「暴跌」 审计吓人 多为叠行修复或下载 bug 修正后的正确收敛,先对账再恐慌
复权 错价策略结果全废 原始价与 adj_factor 分离;qfq/hfq 在调用层算
stk_factor 被当成日线 语义混乱 日线主链永远是 ashare.daily
涨跌停标记 新股 up_limit=99999… 策略侧过滤;文档坑位保留
指数早期 NaN OHLC UI/API500(JSON 不接受 NaN) 读路径 isfinite 过滤;勿把序列化失败当库坏

3.4 架构决策坑

表现 教训
库湖双读 有时读湖有时读库,空结果静默回落 parquet DB 优先且空结果也是结果is not None
「零重叠」教条 核心表只在湖,业务路径拧巴 用户指令:库为 SSOT,湖备份允许重叠
把 UI 当产品核心 工程师控制台很好看,策略零收益 精力给 ResearchClient 与水位/PIT
并发多 writer DuckDB 文件锁 入库与迁库串行;研究侧规划只读副本

3.5 范围与预期管理

表现 教训
crypto.ohlcv 审计/UI 当故障 合法待导入;审计白名单
hk_daily 极慢/降级 主链「不齐」焦虑 独立慢批次;不阻塞主链合格判定
stk_managers 无历史深度 当成长面板用会错 快照语义写进坑表
trade_cal 含未来日 日历直接用会穿 研究层截断 <= todayis_open=1

4. 反思

4.1 做对了什么

  1. 独立仓库 + 稳定契约——数据模块可以单独生长,不被回测 UI 绑架。
  2. 形状门禁——没有策略矩阵时,高权限等于加速烧配额。
  3. 熔断有恢复语义——代理世界里「急停」和「熔断」不是一回事。
  4. 及时否定错误架构——零重叠、湖主库辅,在真实调用压力下站不住,纠偏成本可控。
  5. 质量脚本化——audit / report / refresh_sync / fix_pk,比「感觉行数对」可靠。

4.2 付过的学费

  1. 把呈现当目标会吞噬上下文:控制台、主题、图表,对策略调用杠杆接近零。
  2. 状态文件不是真相——JSON 水位过期比数据坏更常见。
  3. 「行数变少」常常是变好——去重、修下载 bug 后的收敛。
  4. 调用层要单独建设——库装满了不等于 JOIN adj_factor 自动正确;面板、复权、宇宙、日历必须产品化。
  5. 单文件 DuckDB 是双刃剑——简单、够快;并发与备份要提前设计,别等回测农场撑爆再想。

4.3 仍然诚实的缺口

  • 财务 / 披露的 point-in-time(按 ann_date 可得日)尚未一等公民化。
  • 指数成分等 历史宇宙 未做成稳定 API。
  • 研究与入库并发的 只读副本 / 快照 未落地。
  • 港股日线、加密全量仍属「计划内未完成 / 慢做」。
  • Git 远程是否推齐取决于网络环境;本地 bundle 是兜底,不是替代纪律。

5. 最终设计(2026-08-08 定稿)

5.1 分层总图

                  ┌─────────────────────────────┐
                  │ Strategy / Research / BT   │
                  │ ResearchClient · SQL 只读   │
                  └─────────────▲───────────────┘
                                │
┌──────────────┐     ┌────────────┴────────────┐     ┌────────────┐
│ Tushare Pro │────►│     Ingest Pipeline     │────►│ DuckDB   │
│ Freqtrade   │     │ pull_modes · guard · pool│     │ SSOT     │
│ Futures zip │     │ ashare write: DB→parquet │     │ ~88 tables │
└──────────────┘     └────────────┬────────────┘     └─────▲──────┘
                                │ 备份双写                 │
                                ▼                         │
                          parquet/ashare/*           检修 UI /series
                          (不参与业务读)           /db · 看板

5.2 存储

路径 角色
SSOT db/market.duckdb 全部业务事实;策略只读这里
备份湖 parquet/ashare/* 与写入同批;迁移/拷贝/灾难恢复
状态 state/* 心跳、暂停旗、同步 JSON(可刷新,非 SSOT)
密钥 secrets.env 仅本机

禁止:业务路径为了「方便」再读 parquet 当行情/财务事实。

5.3 库内逻辑分区(schema)

Schema 内容
ashare 日线主链、复权、财务、广度、因子、两融…
index_cn 指数日线/周月/权重/基础
fund / fut_ts / fut_file / opt / bond / macro / hk / us / crypto 多资产
meta 同步与入库元数据(一般不进策略面板)

日线主链:ashare.daily + ashare.adj_factor(分离存储)。 不要用 stk_factor* 替代 daily。

5.4 调用面

场景 用法
策略截面 ResearchClient.panel(["core"], start=..., end=...)
单票研究 ResearchClient.dossier("茅台") / bars(..., adj="qfq")
就绪检查 ResearchClient.freshness()
HTTP 薄封装 /research/*(默认给工具用;重活用 Python)
检修扫表 /db/tables · 专家模式 UI
看图 /series · 行情页

字段包:ohlcv / valuation / limits / core 等,见 research/fields.py

5.5 质量与运维工具

脚本 用途
scripts/audit_datalake.py 健康审计(crypto 空表合法)
scripts/report_tables.py 逐表行数
scripts/refresh_sync_state_from_db.py 状态 JSON ← 库
scripts/verify_research_sdk.py 研究调用面冒烟
scripts/verify_db_query_layer.py 表扫描 / series 冒烟
scripts/daily_watermark.py 水位日报

5.6 合格标准(数据底座「能开工」)

  • 核心日线族 + adj_factor + daily_basic + index_daily 水位对齐到最近交易日量级。
  • 业务表主键齐全;无大面积叠行。
  • 复权可复现(qfq/hfq 锚点明确)。
  • 审计 0 真实问题(允许:crypto 空、状态已刷新、已知慢表降级)。
  • 策略侧能用 ResearchClient 不经 UI 取到面板与单票包。

6. 文档地图(以后改哪里)

文档 读什么
本文 终局:思路、踩坑、反思、定稿架构
博客 / postmortem 拉取策略、熔断、检查清单(工程公约)
storage_single_source.md 库主湖备铁律
research_sdk.md 横纵调用 API
database_intro.md 表账与已知数据坑位
architecture.md 仓库模块与消费边界
tushare_ingest_lessons_and_update_rules.md 日更操作记忆

7. 给未来自己的备忘

  1. 新表:探测 → 登记 → 入批次 → 冒烟 → 再全量。
  2. 行数骤降:先查是否去重/修 bug,再查是否真丢数。
  3. audit 报警:先看状态 JSON 是否过期、空表是否在白名单。
  4. 策略要字段:加 FIELD_SPECS / pack,而不是让策略手写五表 JOIN。
  5. 下一步杠杆最高的三件事:PIT 财务成分宇宙历史只读库快照——不是再做一个更炫的前端。

本文综合:公开博客约定、仓库内复盘材料,以及 2026-07-30~08-08 建设过程中的架构纠偏与验收结论。