公司回购与大股东减仓监控(望京博格版)
这个 skill 做什么
把「公司自己买回来的钱」和「大股东卖出去的钱」放在同一根时间轴上,看谁在用力。
口径:全 A 上市公司当月实际执行的股份回购金额(不是预案、不是授权额度),对上重要股东通过二级市场实际减持的金额,逐月取最近五年(默认 60 个月),底下垫一条沪深300 月末收盘线。看的是错位——哪个月公司在拼命买、大股东在拼命卖,以及这种错位之后指数往哪走。
一句话理解这两个指标:回购是公司用真金白银表的态度,减仓是大股东用脚投的票。 一个进、一个出,差额就是产业资本对 A 股的净态度。
⚠️ 先决条件:MCP 能不能按月跨全市场聚合
这个 skill 自己不带数据。核心能力只有两条:
> 一次调用返回「某月全 A 回购金额合计」,一次调用返回「某月全 A 重要股东减持金额合计」。
实测可用路径:wind-analytics-data 的 get_financial_data,自然语言问句。沪深300 用 wind-index-data 的 get_index_kline(period=12 月 K)。具体问句、哪些路走不通、QPS、坑,全在 references/data-sourcing.md,第一次用必读。
走不通的表现:返回「没找到数据」,或者数值量级差 1 万倍(单位陷阱,见下)。遇到先解决 MCP,不要改成逐只个股加总(5000 只 × 60 个月,跑不完),更不要凭印象编数。
装完先自检:
python scripts/check_env.py
它查文件、库的连续性、量级、回归锚点、运行时;MCP 那条要人工确认——随手问一句「2026年10月A股全部上市公司股份回购实际金额合计,单位亿元」,回 221.95 亿(原始数值 22,194,878,014)才算通。
硬规则:先读这段再动手
- 两个指标必须分开问,不能合并。 合在一条问句里,第二个指标会退化成「持股市值变动」并返回 null。
- 不能批量取月度序列。 「按月列出 2022 年各月」这类问法一律「没找到数据」,只能一月一发。首次建库 60 个月 = 120 次调用——这笔只付一次,之后月更只补新月份,3 次调用。
- 一轮消息最多发 2 条,串行推进。 3 条并行必挂中间那条(实测 5 条并行挂 3 条)。限流约 2 QPS。
- 单位陷阱(最容易翻车)。 返回的
unit字段两个指标都写「万人民币元」,但实际量纲不同:
- 回购数值是元,÷1e8 得亿元(22,194,878,014 → 221.95 亿)
- 减持数值是万元,÷1e4 得亿元(1,362,377 → 136.24 亿)
用年度合计反推验证:2024 年回购约 1650 亿、减仓约 1570 亿,对上就对了。 - 减持口径用「金额」,不要用「净减持」。 「净减持」措辞不稳定:同一批数据里 2021-12 会返回 38.93 亿(真实量级 900+ 亿),序列直接断掉。全序列统一「二级市场减持金额」,净额在报告里另行说明。
- 别把回购当抄底信号。 实测回购额与沪深300 当月涨跌幅相关系数只有 −0.03,涨月 109 亿、跌月 110 亿。写结论时照数据说,别套「越跌越买」的旧叙事。
- 2021 年 11–12 月的减仓偏高是真实的(解禁高峰 + 当时规则宽松),不是脏数据。但窗口内 2021 只有两个月,年度表里必须标「不完整」。
- 采样月份记录实际取到的那个月,不是你打算取的。差一天不影响趋势,但库里的月份必须真。
博格的固定偏好(2026-11 定版,别自己改回去)
| 项目 | 定版 |
|---|---|
| 页头标题 | 公司回购与大股东减仓,副标「2021.11–2026.10 · 60 个月 · 月度实际发生额」 |
| 默认窗口 | 最近 60 个月(--months 可改) |
| 主视觉 | 三联垂直图:上=回购柱(红)、中=减仓柱(绿)、下=沪深300 折线,共享 X 轴 |
| 配色 | 回购 #d93a33 红、减仓 #17994f 绿、指数 #2b4b6f 靛蓝——中国习惯,别反过来 |
| 数字格式 | 亿元、千分位、保留一位小数;表格里涨用红、跌用绿 |
| 产物 | HTML 单文件 + 2360 宽高清长图(视口 1180、2x) |
| 必带区块 | 数据准确性检查(十项)+ 口径边界说明 |
工作流 · 历史只存一次,以后只补增量
底座在 data/buyback-reduction-history.json,60 个月已经取好。已经进库的月份永远不再问 MCP,每次更新只补「库里没有的那些月」。正常一个月更,成本是 3 次调用(回购 1 + 减仓 1 + 沪深300 1),不是 120 次。
Step 0 · 看状态
python scripts/update.py status
一行给全:库到哪个月、窗口多大、校验过不过、缺几个月、下一步敲什么。
Step 1 · 定缺口(不联网)
python scripts/update.py plan
它做四件事:算出窗口内缺的月份 → 写 data/pending.json 待补表 → 打印每月两条可直接发给 MCP 的问句 → 打印沪深300 的一次批量行情调用(缺口区间整体取,不逐月问)。
中间断月也能捡回来:库里若缺 2022-07,plan 会单独把它列出来。
Step 2 · 取数(唯一的联网环节)
按 plan 打印的问句串行取,一轮消息 2 条(回购 + 减仓各一条)。沪深300 用 batched kline:
get_index_kline(windcode="000300.SH", begin_date=<缺口首月1日>, end_date=<缺口末月末日>, period="12")
取 MATCH 列(月收盘),按月对齐。抽取区间由 plan 直接给出,照抄。
抽检锚点(2026-10):回购 221.95 亿、减仓 136.24 亿、沪深300 收 3,985.50。2024-02:回购 261.67 亿(五年峰值)、减仓 42.70 亿(五年谷底)。对不上就是口径或单位错了。
Step 3 · 入库(取一个月存一个月)
python scripts/update.py fill 2026-11 --buy 120.04 --red 300.22 --hs300 4021.70
一条命令完成:量级校验 → 冲突检查 → 原子写 → 自动备份 → 记审计 → 报剩余缺口。中断不丢,下次接着补。
批量也行:手改 data/pending.json 把三个值填上,然后 python scripts/update.py apply。
写库的护栏(都是自动的):
| 护栏 | 行为 |
|---|---|
| 缺字段 | 拒收,宁可缺月不写假 0 |
| 量级越界(回购 1~800、减仓 10~1500、指数 1500~8000) | 拒收,提示「多半是单位错了」 |
| 同月数值不同 | 默认拒写并打印 diff;确认是 Wind 修订才加 --force |
| 写入中断 | 临时文件 + 原子替换,库不会写坏 |
| 每次写入 | 自动备份到 data/backups/(留最近 20 份)+ 追加 data/fetch-log.jsonl |
Step 4 · 出报告(不联网)
python scripts/update.py build # HTML + 2360 宽长图,一步
python scripts/update.py build --no-png # 只要 HTML
等价于 build.py + export_long.js,但会先跑一遍校验:缺月、断月、锚点不符会先喊出来,再出图。换机器、离线重出图照样能跑。
不要重新设计版式。 三联图的刻度留白、峰值标签、X 轴标签密度这些坑都记在 references/rendering.md,重画会重踩一遍。
Step 5 · 交付
先把 HTML 和 PNG 放进 present_files,再说结论。话术见文末。
月更三步(标准动作)
python scripts/update.py status # 库到哪、缺几个月
python scripts/update.py plan # 拿问句(沪深300 一次、两个指标每月各一次)
# → 取数 → fill 串行,一轮 2 条
python scripts/update.py build # 出 HTML + 长图
全量重建(想换窗口、补老月份)也一样,--months N 改窗口,plan 会把缺的一次列全。
fetch_plan.py / history.py 仍然可用,前者是 update.py plan 的旧入口,后者是底层库操作(stat / verify / csv / del)。
数据长什么样(2026-11-05 实际跑出来的)
| 观察 | 数值 |
|---|---|
| 五年累计回购 | 6,533.8 亿(月均 108.9) |
| 五年累计减仓 | 21,504.0 亿(月均 358.4) |
| 回购覆盖减仓 | 30.4%,净流出 14,970 亿 |
| 回购峰值 | 2024-02,261.7 亿(微盘股踩踏) |
| 减仓峰值 | 2021-12,936.8 亿(解禁高峰) |
| 减仓谷底 | 2024-02,42.7 亿(新规后) |
| 唯一净买入年 | 2024:回购 1,654 亿 > 减仓 1,569 亿,指数 +22.4% |
| 相关系数 | 回购~指数涨跌 −0.03;减仓~指数涨跌 0.14 |
这组数字可以当回归测试:跑出来差太多,先怀疑取数口径,别怀疑代码。
文件索引
| 文件 | 什么时候读 |
|---|---|
references/data-sourcing.md |
取数前必读:有效问句、走不通的路、单位陷阱、QPS、清洗、校验锚点 |
references/rendering.md |
改页面时读:三联图结构、配色、字号基线、已踩的坑 |
data/buyback-reduction-history.json |
月度数据底座,资产不是缓存。删了要重取 120 次 |
data/pending.json |
待补表,plan 生成、fill/apply 消费;可手改 |
data/fetch-log.jsonl |
审计流水,一行一次变更(含覆盖前的旧值) |
data/backups/ |
每次写库前的自动快照,留最近 20 份 |
assets/page_template.html |
单文件报告模板(SVG 内联,注入即用) |
scripts/update.py |
总入口:status / plan / fill / apply / build / all |
scripts/config.py |
路径、量级区间、回归锚点、月份与问句工具,改口径只动这里 |
scripts/history.py |
底层库:stat / verify / add / del / print / csv |
scripts/build.py |
读库 → 计算 → 生成 SVG → 出 HTML |
scripts/export_long.js |
整页长图导出(自动探测 playwright + 浏览器) |
scripts/runtime.py |
运行时探测:node / playwright-core / Chromium |
scripts/check_env.py |
环境自检 + 回归锚点 + 待补表状态 |
scripts/fetch_plan.py |
兼容入口,转发到 update.py plan |
交付时说什么
- 先给结论:五年回购多少、减仓多少、覆盖几成、净流出多少。
- 讲错位:哪个月两边同时用力(2026-10 最典型:指数跌、回购创新高、减仓砍到五年第三低)。
- 讲反直觉的那条:回购跟指数涨跌基本无关(相关 −0.03),它是制度动作不是抄底动作;减仓才是涨出来的(涨月 420 亿 vs 跌月 287 亿)。
- 提一句口径:减仓是「毛减仓」,净减仓通常低 10%–20%,所以年度对账出现 +10% 左右偏差属正常。
- 不构成投资建议。
更新日志
- 1.1.0 / 2026-11-05:取数流程改为增量。
update.py一把手(status / plan / fill / apply / build),
缺口自动算、待补表落盘、每月一条命令入库;写库加原子替换 + 自动备份 + 审计流水 + 冲突拦截;
沪深300 改为缺口区间一次取完;config.py收敛路径/区间/锚点。一个月更从 120 次调用降到 3 次。 - 1.0.0 / 2026-11-05:定版。60 个月、三联图(回购/减仓/沪深300)、十项数据校验、2360 宽长图;库只补新点。