# xiaobai-datahub 独立行情数据中枢(HEL-382 / P0)。与 `xiaobai-review` 同仓库、不同容器、不共享数据库文件。 本阶段不部署现网;只提供可本地运行、可自测的底座和盘后正式数据链路。 ## 做什么 - SQLite WAL `datahub.db`,容器名 `xiaobai-datahub`,端口 `8766` - Tushare 盘后正式数据:交易日历、股票主档、daily、daily_basic、adj_factor、index_daily、moneyflow、stk_auction - 暂存 → 校验 → 整批原子发布 → 可回滚 - `/v1` 稳定接口(`X-Datahub-Token`) - `/admin/` 最小管理后台(总览 / 数据源 / 调度 / 发布 / 数据集 / 审计) - 东财/腾讯/同花顺/选股宝/AKShare/iFinD 适配器位已预留,本阶段不拉实时源 ## 单位口径(相对现站) 现站 `xiaobai-review` 按 Tushare 原始单位入库、展示时再换算。中枢在归一化层一次换算: | 字段 | Tushare / 现站 | 中枢 canonical | |---|---|---| | `daily.amount` / `index_daily.amount` | 千元 | 元(×1000) | | `daily.vol` / `index_daily.vol` | 手 | 股(×100) | | `moneyflow.*_amount` | 万元 | 元(×1e4) | | `daily_basic.total_mv` / `circ_mv` | 万元 | 元(×1e4) | | `stk_auction.amount` | 元 | 元 | 差异为口径升级,golden 测试按上表对照,不为 0 的字段都有说明。 ## 本地启动(不走 Docker) ```bash cd xiaobai-datahub python -m venv .venv && .venv/bin/pip install -r requirements.txt cp .env.example .env # 填入 DATAHUB_ENCRYPTION_KEY / DATAHUB_TOKEN / DATAHUB_ADMIN_PASSWORD / TUSHARE_TOKEN # 生成 Fernet 密钥: # python -c "from cryptography.fernet import Fernet; print(Fernet.generate_key().decode())" .venv/bin/python server.py --host 127.0.0.1 --port 8766 ``` - 管理后台:http://127.0.0.1:8766/admin/ - 存活检查:http://127.0.0.1:8766/livez (无需 token) - `/v1/*` 必须带请求头 `X-Datahub-Token` ## Docker(独立 compose,不改现网 review 服务) ```bash cd xiaobai-datahub cp .env.example .env # 填密钥 mkdir -p data docker compose build docker compose up -d ``` 仓库根目录另有 `compose.datahub.yaml`,供总工以后与现有 `compose.yaml` 叠加部署,本卡不执行现网 `up`。 ## 自测 ```bash cd xiaobai-datahub python -m unittest discover -s tests -v ``` 不调用真实 Tushare;用内存/临时库和假适配器。 ## 历史回补 交易日历默认从 `20160101` 拉到今天后 30 天;盘前 `precheck` 与手动回补都走同一 UPSERT,可重复执行。 网站实际使用的指数(上证、深成、创业板、沪深300)按交易日增量发布,默认覆盖 260 个交易日(大于现有 90 天窗口,并覆盖智能选股基准回看)。已发布日期默认跳过。 ```bash cd xiaobai-datahub python -m datahub history-backfill # 可选:--calendar-start 20160101 --index-days 260 --force ``` 管理后台也可手动跑 `history_backfill` 任务,或 `POST /admin/api/backfill` 且 `dataset=history`、确认词 `history:full`。 区间接口在 `meta.coverage` / `meta.incomplete` 标明覆盖是否完整;网站只读接入把不完整区间视为不可用并回旧链路。个股日 K 的 90 天区间查询依赖已核实,本阶段不回补全市场历史。 ## 估值字段级质量门 `hub-quality.config.json` 的 `field_gates` 按数据集配置关键字段:非空率下限(支持按字段覆盖,如 `dv_ttm` 合法高空值)、非有限值比例上限、以及相对上一已发布批次的非空率塌陷保护。字段大面积为空的批次会被拒绝发布、保留上一份正常正式数据,失败原因逐字段写入 `batches.error` / `quality_json`。被拒后数据集仍视为缺失,盘后自动重试(HEL-435 机制)会继续尝试直到成功或截止。配置对任意数据集生效,不写死单日或单字段。 ## 股票主档每日刷新与发布 交易日 20:00 与 23:10(`stocks_refresh_times` 可配)自动刷新股票主档并发布版本化快照(`eod_stocks` + `publications.dataset='stocks'`),覆盖当日新上市、证券简称变化和上市首日 N/C 前缀摘除;无变化则跳过,重复执行幂等。`/v1/stocks` 从最新已发布快照提供数据并带 `batch_id` / `published_at`;`/v1/datasets/status` 同步展示 stocks 状态。 ```bash cd xiaobai-datahub python -m datahub stocks-refresh # 手动触发;--force 无变化也重发 ``` ## 资金流历史回补 网站会沿真实调用链查最近若干交易日的 moneyflow(个股详情任意日期点查 + 智能选股最近 5 个交易日),默认回补最近 60 个交易日(`moneyflow_history_trading_days` 可配,已发布日期自动跳过)。点查未覆盖的历史日期返回 `DATASET_NOT_PUBLISHED` 并附 `available_from` / `available_to`(低于下界时 `reason=history_not_backfilled`),网站据此明确回退旧链路,不会静默拿到半截数据。 ```bash cd xiaobai-datahub python -m datahub moneyflow-backfill # --trading-days 60 --end-date --force 可选 ``` ## 盘后补跑与强制重发 ```bash cd xiaobai-datahub python -m datahub eod-refresh --trade-date 20260904 # 只补缺失数据集 python -m datahub eod-refresh --trade-date 20260904 --force --dataset valuation # 强制重取重发:仍走全部质量门,生成新批次,上一批次保留可回滚 ``` ## 备份 每日 00:40 任务把 `datahub.db` 备份到 `data/backups/`(保留 14 份)。也可手动: ```bash python -c "from pathlib import Path; from datahub.db import HubDB; HubDB(Path('data/datahub.db')).backup_to(Path('data/backups/manual.db'))" ``` ## 安全 - 密钥只以 `configured / 末4位 / 更新时间` 出现在后台,不进日志、不进 `/v1` - HTTP 解析失败只记录“请求不是合法 JSON”,不把请求正文、密码或 Token 写入容器日志 - 回滚、补数需重新输入密码 + 确认词 - 容器非 root(uid 10002)、read_only、cap_drop ALL