国家队买卖ETF监控(望京博格版)
这个 skill 做什么
回答三个问题,一句话一个:
- 今天上午国家队动手了吗 —— 看一档 7 只 ETF 的成交额有没有炸到常态 2 倍。
- 这段时间买了多少、什么节奏 —— 逐日归因出净买入,按沪深300 波段切开。
- 哪些 ETF 算国家队标的 —— 名单是固定的,但能不能用成交额反推是数据说了算。
核心逻辑:国家队买 ETF 走一级市场申购,一笔几十到上百亿,必然先把二级成交额顶上去。
所以「成交额 → 净申购额」这条弹性关系,就是反推国家队动向的地基。
数据怎么累积(这是这个 skill 最重要的设计)
data/daily.json 是一个按日期字典存的历史库,不是一张写死的表:
{"benchmark": {"close": {"2026-11-05": 4113.41}},
"etfs": {"510300.SH": {"turnover": {"2026-11-05": 7523081499},
"flow": {"2026-11-05": 5.3844}}}}
- 已经内置 2026-10-08 ~ 2026-11-05 共 21 个交易日 × 12 只的实测数据,首次使用不需要抓历史。
- 以后每天只补新增的那几天,
apply会按键合并(同一天重复写等于覆盖,幂等)。 - 每次写入前自动备份到
data/backup/(留最近 30 份),并往data/audit.log追加一行流水。 - 模型每次从库里重算,窗口默认最近 21 个交易日,换窗口/加天数/加标的都只改数据,不改脚本。
标准流程(五步)
# 1. 看库覆盖到哪一天、缺什么
python scripts/update.py status
# 2. 生成「要抓什么」的清单(JSON,含 MCP 工具名与参数)
python scripts/update.py plan
# 输出 write 到 out/plan.json,calls 里每一条都对应一次 MCP 调用
# 3. 按清单用 Wind MCP 抓数,拼成 inbox.json(格式见下)
# 然后入库(会自动做单位自检、跳过问题数据、打警告)
python scripts/update.py apply inbox.json
# 4. 重算 + 渲染 + 出长图
python scripts/update.py all
# 盘中(可选):把上午收盘的半日成交额入库,看板自动切到实时判定
python scripts/update.py apply-intraday intraday.json
产物在 out/:result.json(模型全量结果)、国家队买卖ETF监控.html、国家队买卖ETF看板_长图.png(2160 宽)。
第 3 步:inbox.json 长什么样
按 out/plan.json 的 calls 逐条抓,然后拼成一个文件:
{
"source": "wind-mcp",
"benchmark": {"000300.SH": {"2026-11-06": 4060.11}},
"etfs": {
"510300.SH": {"turnover": {"2026-11-06": 4210000000},
"flow": {"2026-11-06": 12.5}}
}
}
三条硬规则:
| 字段 | 单位 | 来源 |
|---|---|---|
turnover |
元 | 场内基金日 K 的 amount |
flow |
亿元 | ETF 日频「基金资金净流入额」 |
benchmark.close |
点位 | 沪深300 日 K 的 close |
apply 会做单位自检:若 |flow| × 1e8 超过当日成交额的 3 倍,判定单位不对,跳过该条并打警告——
这一步挡掉过真实踩过的坑(有的基金返回万元、有的返回百万元)。看到警告不要忽略,回去确认 Wind 返回的口径。
盘中快照 intraday.json:
{"date": "2026-11-06", "time": "12:32", "ratio": 0.55,
"hs": 4052.48, "hs_chg": -1.48,
"half": {"510300.SH": 30.84, "510310.SH": 7.75, "...": 0}}
half 是上午收盘的半日成交额(亿元);ratio 是上午占全天的经验比例(缺省 0.55,用来折全天)。
模型:为什么分一档二档
12 只标的按季报口径固定(沪深300 四只 / 上证50 / 中证500 两只 / 中证1000 两只 / 科创50 / 创业板 / A500),
但能不能用成交额反推要看数据:
- 对每只做「成交额 → 净申购额」的线性回归,得到弹性 b 和拟合度 R²
- b ≥ 0.8 且 R² ≥ 0.7 → 一档:成交额基本就是申购的镜像,盘中放量能直接读成国家队动手
- 其余 → 二档:成交额里混了大量二级换手,反推会严重失真,只能等收盘用份额变动实算
一档组合再回归一次,得到监控用的地基方程:
净申购(亿) = a + b × 当日一档成交额(亿)
国家队净买入(亿) = 净申购 × w w = 0.35 ~ 0.75,随放量程度递增
w 是假设不是实测(净申购里国家队占几成没有公开数据),所以看板上给的是区间不是点值。
分档会随数据自动重算——某只 ETF 流动性结构变了,档位会自己迁移,不要写死。
细节见 references/methodology.md。
已知的坑(都踩过了,别再踩)
get_fund_performance多代码会截断:一次传超过 5 只只返回前几只。必须单只查。- 返回里带 .SH / .OF 两份:联接基金那份要过滤掉,只取场内代码。
- 当日盘中净流入为 null:所以盘中只能用成交额判定,
flow必须等收盘。 - 单位不统一:有的代码返回万元、有的百万元。不要相信字段名,用「净申购不该超过当日成交额」这条自检兜底。
get_fund_price_indicators的「基金规模」与「流通份额 × 净值」口径差 0.5–0.6 倍:用后者(universe.json里的shares × price)更自洽。- QPS 约 2 次/秒:12 只 × 2 个接口 = 24 次调用,串行发、别并发轰炸。
换标的 / 换窗口
- 加标的:改
data/universe.json增加一条(code / name / short / index / group / shares / price),
再在data/daily.json的etfs下补它的历史,然后python scripts/update.py all。分档会自动判定。 - 换窗口:
python scripts/update.py all --window 30(默认 21,0 = 用库里全部)。 - 改波段切分灵敏度:
--seg 3表示切出的波段最少 3 天(默认 3)。
输出长什么样
单页 HTML,六节,顺序是「先今天、再这一个月、最后交代方法」:
- 实时监控 —— 左:状态卡(未入场绿 / 已入场红)+ 推算 + 判定线;右:最近 10 个交易日动态
- 买卖节奏 —— 每日净买入柱 + 沪深300 线、累计曲线、波段表、每日明细
- 标的池 —— 弹性横向条形图(红=一档、金=二档)、分组名单
- 分标的明细 —— 规模/成交额/净申购/转化率/b/R²/归因 九列
- 定量关系 —— 公式块 + 成交额散点与回归线
- 可靠性 —— 自己拆自己的台:w 是假设、二档更不可靠、样本短、极端行情失效
正文里的「哪一天买了多少」「相关性多少」全部由程序从数据生成,不写死——
换窗口、加天数后文案不会和数字打架。
免责声明固定附在文末,不要删。
排版规范(改样式时对表,别越改越小)
v1.0.1 起正文整体上调一档,容器同步放宽,改 CSS 时按这张表:
| 元素 | 选择器 | 字号 |
|---|---|---|
| 一级标题 | h1 |
32px |
| 板块标题 | h2 |
21px |
| 正文段落 | .p |
18px / 行高 2.0 |
| 提示块 | .note |
16.5px |
| 图注 | .cap |
14px |
| 表格正文 / 标的名 | table / .nm |
15.5 / 16px |
| 右表「近十个交易日」 | .mini |
14.5px |
| 状态卡大字 | .live .left .v |
44px |
| 公式块 | .formula |
15.5px |
| 内容容器 | .wrap |
max-width 1180px |
窄屏两档:≤820px 正文 17px、表格 13.5px;≤520px 正文 16.5px、表格 13px。
放大字号必须同步放宽 .wrap,否则表格「净买入」列会被压扁。