QDII 全球宽基限购监控
环境要求与跨机器安装
- 零第三方依赖:全部脚本只用 Python 标准库(os/sqlite3/re/json/urllib/html 等),无 pip install
- Python 版本:3.8+(f-string + SQLite ON CONFLICT,Python 3.8 内置 SQLite 3.31+ 满足)
- 跨平台:Windows / macOS / Linux 均可运行,路径全部基于
__file__相对定位,无硬编码绝对路径(已审计) - 数据自包含:全部数据在
scripts/data/下(SQLite 单文件 + 每日 HTML),拷走整个目录即完成迁移;单独拷qdii_quota.db即历史全量备份 - 安装方式:将整个
twu-qdii-quota-monitor/目录放入技能目录即可,对方只需 Python 3.8+,无需配置任何 token / 环境变量 - 运行前提:需能访问天天基金公开页面(fund.eastmoney.com),公司内网/海外网络可能被拦
- 唯一注意:完整抓取约 6-8 分钟且每日只跑一次(限流铁律),用定时任务或手动后台执行
能力边界
- 覆盖指数:纳斯达克100 / 标普500 / 富时100 / 日经225 / 德国DAX / 法国CAC40
- 基金类型:被动指数型 QDII 场外基金(联接/指数/LOF),含全部份额类别(A/C/D/E/I/基础/美元现汇/美元美钞)
- 数据口径:天天基金详情页「交易状态」区块(第三方代销口径,与基金公司直销可能有差异)
- 输出:单文件 HTML(无外部依赖,双击即开)+ SQLite 历史库
分层架构(松耦合约定)
scripts/
├── db.py # 数据访问层:SQLite 读写,全库唯一 SQL 出口(含复活清 terminated_date 修复)
├── pool_sync.py # 池同步层:拉全市场清单 -> 过滤 -> diff 增量更新(只写 funds 表)
├── fetch_status.py # 抓取层:只抓取+解析,不写库,返回快照列表供编排层入库
├── stats.py # 统计层:纯函数聚合(每日总量指标 + 多日序列),不写库不渲染
├── merge_compare.py # 变化检测层:纯计算,只读库,输出变化明细(恢复观测/收紧放松标注)
├── render_report.py # 渲染层:读库 + 调 stats/merge_compare,输出单文件 HTML
└── main.py # 编排入口:唯一负责"抓取→写库→渲染"衔接的模块
依赖方向单向:main → {pool_sync, fetch_status, db, render_report} → {db, stats, merge_compare}。各层只通过明确定义的数据结构交互,不互相依赖内部实现。
执行流程
# 完整流程(同步池 + 抓取 + 入库 + 出报告),约 6-8 分钟
python scripts/main.py
# 只出报告(不抓取,用库内最新数据)
python scripts/main.py --render-only
# 跳过基金池同步
python scripts/main.py --skip-sync
# 指定观测日重出报告
python scripts/main.py --render-only --date 2026-08-03
# 报告不带趋势图
python scripts/main.py --render-only --no-trend
被其他 skill 调用(摘要接口)
其他 skill 想快速拿到限购结论,不打开 HTML 不翻数据库,用摘要模式:
# 可读文本摘要(一句话结论 + 关键数字 + 结构洞察)
python scripts/main.py --summary
# JSON 结构化摘要(含 verdict 结论句、状态分布、累计额度、容量集中方向、环比)
python scripts/main.py --summary-json
# 指定日期
python scripts/main.py --summary --date 2026-08-03
JSON 返回关键字段:verdict(一句话结论)/ total / open / limited / suspended / cny_accum(累计可申购额度)/ top_capacity(容量集中方向)/ us_mainline(美股方向容量)/ accum_delta(环比,次日起有值)/ changes_count。无快照日期返回 {"error": ...}。调用方(如 twu-finance-chief 等上层 skill)直接消费即可,也可用 summary.brief() / summary.brief_text() 以代码方式调用。
注意:完整流程超过 2 分钟,在终端前台跑会被 bash 超时杀掉,务必后台执行(run_in_background)或用 --render-only。--summary 为纯本地计算,毫秒级返回。
关键参数(集中在各脚本顶部常量)
| 参数 | 位置 | 默认值 |
|---|---|---|
| 请求间隔 | fetch_status.py MIN_DELAY/MAX_DELAY | 2.0-5.0 秒 |
| 重试次数 | fetch_status.py RETRY_TIMES | 3 次,指数退避 |
| 熔断阈值 | fetch_status.py MAX_CONSEC_FAIL | 连续 3 只失败停止 |
| 指数规则 | pool_sync.py INDEX_RULES | 六大指数关键词 |
| 数据库路径 | db.py DB_PATH | scripts/data/qdii_quota.db |
注意事项(实战踩坑)
- 每日只跑一次完整抓取。连续两次全量抓取,第二次会被天天基金限流(实测第二次跑到 33 只中断)。限购是事件型数据,基金公司公告后持续,日频足够。
- 抓取与入库解耦:fetch_status 不写库,快照由 main.py 编排入库。熔断或批量失败时返回 incomplete 标志,报告顶部显示黄色警告横幅,避免不完整数据被误读。
- 份额分类规则:美元份额金额不进入人民币统计(stats.parse_cny_amount);限额"万元/千元"单位已支持换算;优先匹配"上限X元"精确模式,多限条目取第一个上限。
- 异常分支:基金终止解析为「基金终止」;封闭期解析为「封闭期」;无效代码解析为「未知」,报告会展示但不中断。
- 首次观测:首日报告无变化区(无历史对比),明日起出现整体趋势线、变化高亮和单基金迷你趋势。
- 恢复观测 vs 首次纳入:非首日但上一观测缺失快照(如上次熔断)的基金标记为「恢复观测」(紫色),不会误标为绿色「首次纳入」。
- 复活逻辑:基金从清单消失又出现时,terminated_date 一并清空,二次终止仍可正常标记(修复审计发现的 bug)。
- 新基金自动收录:每日同步拉全市场清单,新成立的跟踪指数基金自动进池,清盘/转型自动标记终止。
- 迷你趋势语义:绿色圆点=开放申购(无限额),红色叉=暂停申购(断开),折线=限大额金额。
- 基础份额处理:无 A/C 之分的单份额老 LOF(如国泰纳斯达克100指数 160213)在展示层并入 A 类列并标 ¹,数据层 share_class 保持原始(方案:用户选定并入 A 类)。
- 效率优化:快照入库用单事务批量 executemany(92 条从 92 次连接降到 1 次,实测约 48 倍提速);迷你趋势图用批量预取单次查询(约 28 倍提速)。完整流程约 6-8 分钟,瓶颈在网络请求与反爬限速,非本地计算。
输出解读(v5 透视表版)
- 决策总览(Hero):累计可申购额度大数字 + 三档可得性比例条(可自由/受限可买/完全关闭)+ 市场结论句
- 全池状态热力矩阵:指数 × 份额类别,格色=主状态、格内只数+累计额度、title 明细,92 只基金全景
- 全池明细 · 分层透视表(核心明细):第一层=指数分组头(含只数/累计/可买%),第二层=基金产品行(92 份额合并为 36 产品),列=份额类型,格=当日额度($=美元份额,¹=含基础份额并入 A 类),色=申购状态
- 投资逻辑速读:对比驱动洞察(集中度/主流冻结/货币通道/结构对称),推断性结论明确标注
- 变化情况:全池累计额度环比(▲放宽/▼收紧/=持平)+ 单只变化明细
- 整体趋势:基金数量三线 + 累计可申购额度走势(元/日)
- 明细表:每只基金状态色标 + 当日限额 + 30 天迷你趋势
历史数据
SQLite 单库两表:funds(基金静态信息)+ quota_history(每日快照,联合主键幂等,同日重跑覆盖)。历史无限期保留,数据量小(约 92 只基金,全年约 2.3 万行)。回溯任意基金限购变化轨迹用 python -c "import db; print(db.get_history('040046', 30))"(需在 scripts/ 目录下运行)。