# Daily HS300 20:00 任务 insight 图替换方案（2026-08-17 定稿并全部实施）

**背景**：用户体检晚8点任务末尾4张 insight 图后决定：废弃 funding + heatmap_rs，新建两张替代，并完善保留的 cycle_phase + heat_toplist。最终每日7张（5-ave + 轮动算法 + KDE + leverage + industry_rank + cycle_phase + heat_toplist），已手动 `cronjob action=run` 端到端验证（job d9adf97eec12）。

## 1. ~~funding~~ → leverage_*.png 杠杆资金情绪图（plot_leverage_chart()）

- 上面板：融资余额曲线（存量杠杆水位，万亿，紫色 #8E24AA）
- 下面板：**融资买入额÷两市成交额**（增量强度，真正的杠杆情绪温度计）；灰色单日细线 + 红色20日均线（主信号，#D32F2F）
- 阈值线：10% 警戒区（历史过热标志，2015-06曾14%+）/ 6% 低迷区（情绪冰点）
- 数据：`pro.margin` 的 `rzmre`（融资买入额，元）+ `rzye`（融资余额），按 trade_date groupby 求和后**剔除 exchange_id<2 的日期**（见 SKILL.md pitfall 36）；两市成交额 = 000001.SH + 399106.SZ `index_daily` amount（千元，/1e5→亿）。占比 = rzmre亿/成交额亿×100。近2年窗口（730天）
- 替代已失效的北向部分（见 pitfall 38 + northbound-moneyflow-break.md）；融资买入占比不受披露规则变更影响
- 锚定读数（2026-08-17）：融资余额 2.65万亿，融资买入占比 9.37%（近2年38分位）

## 2. ~~heatmap_rs~~ → industry_rank_*.png 行业RS排行榜（plot_industry_rank_chart()）

- 60日相对沪深300超额收益水平条形图，**只画 Top10红 + Bottom10绿 共20个行业**——中间90个不画。第一版画全部110个行业（即使只给20个加标签）仍重叠，删掉中间区后 vision 验证全部清晰。字号12加粗，条形height=0.62
- RS计算：`ind_cum = ∏(1+avg_pct/100)`，`rs60 = (ind_cum-1)*100 - hs_cum`（hs_cum 为沪深300同期累计涨幅，作0基准线）
- 数据缓存复用 `insight/industry_daily_pct.csv`（原热力图的缓存，90天滚动清理，按 trade_date+industry 去重）；缺失日逐日 `pro.daily(trade_date=d)` 补拉（sleep 0.2 防限流）
- 行业筛选：数据覆盖率≥80%（len(g) >= len(plot_dates)*0.8），实测有效行业约110个
- 锚定读数（2026-08-17）：最强 电气设备+22.0%/通信设备+16.6%；最弱 保险-6.9%；沪深300同期-2.1%

## 3. cycle_phase — 已完善（4处，2026-08-17实施并vision验证）

- **乘势期悖论已解开（非bug）**：活化阈值不是0而是 M1-M2 历史中位数（2005+ 约 -4.3%），当前 -3.7% > -4.3% 判乘势期。原图上有中位线但 alpha=0.25/字号7 几乎不可见 → 改为坐标范围确定后绘制，橙色 #E67E22 lw=1.3 + "活化分界 -4.3%" 大字标注
- 毛线球消除：全历史淡灰小点（#BBBBBB s=10 alpha=0.35）+ **近24个月红色轨迹高亮**（#D32F2F lw=1.8）；年份标注只保留近3年+起点锚
- 指标卡加环比：M1 和 M1-M2 卡显示"环比±X.Xpp"（df_cn.iloc[-2] 对比），提示语改"高于分界=资金活化"（不再说">0为好"）
- 相位标签从固定左上角改为**跟随当前红星位置** annotate（避免象限误导）；删除无用颜色条（轨迹改灰点+红线后颜色条失去意义）

## 4. heat_toplist — 已完善（3处，2026-08-17实施并vision验证）

- 右图气泡标签只标金额 Top8（`np.argsort(-x2)[:8]`），其余无标签
- X轴压缩：`x_cap = x_sorted[2]*1.5 if x_sorted[0] > x_sorted[2]*1.8`（Top1远超第3名时触发），超cap的气泡截断到cap位置并标注"{name} {金额}亿"；实测半导体32亿 vs 第3名触发，标签显示"半导体 32亿"
- 只数=1的底部行标签移到气泡**下方**（xytext=(0,-18), va='top'），避免与上方气泡重叠
- 左图负值标签留边距：`ax1.set_xlim(min(nets)*1.35, max(nets)*1.15+0.5)`

## 5. ⚠️ 残留问题（手动推送验证时发现，未修）

cron 的**文字报告**模板里仍有"💰 资金面监测"段落，含"北向资金日净X亿流入"——该数字同样来自已失效的 moneyflow_hsgt diff（pitfall 38），图删了但文字模板没删，每天仍推送误导性数据。修复方向：cron prompt 中把资金面段落替换为基于两融（融资余额/融资买入占比）的解读。另：行业RS段落文字（"最强: 电气设备+22.0%"）数据正确，来自新图。

## 废弃操作记录

- `market_charts.py` `plot_all_charts()`：删除 funding/heatmap_rs 调用（留废弃注释写明原因与日期），新增 leverage + industry_rank 调用；新增 `_get_fp()` 辅助函数返回 FontProperties
- cron prompt（job d9adf97eec12）同步改：MEDIA 清单 + "Total: 7 images" 计数 + 生成说明 + "IGNORE funding/heatmap_rs" 显式指令。prompt 示例里的 20260630 日期是模板残留，只改文件名模式不必纠日期
- `plot_funding_chart()` / `plot_industry_heatmap()` 函数体仍留在代码中但无人调用

## 通用经验（matplotlib批量patch技巧）

- 用 Python 脚本做多处字符串替换时，**matplotlib代码里的空行常带尾随空格**（`'    \n'`），整段多行匹配会失败；拆成不含空行的小段分别替换，或先 `cat -A` 查实际字节
- 替换脚本先写 `/tmp/*.py` 再执行（heredoc 嵌套中文引号/括号容易 syntax error）；每处 `assert old in s` 防静默漏替；写完跑 `py_compile` 再跑生成函数，最后 vision_analyze 验证（用户要求先Demo后定稿）
- 评估"定时任务里的图"先枚举 cron output 实际 MEDIA 行（见 SKILL.md pitfall 39），再逐张 vision_analyze 找数据真实性 > 视觉问题两个层级
- 数据真实性问题（字段静默改义）优先于呈现问题修；呈现问题里"标签重叠"类先看能否直接砍掉低价值元素（中间灰色区）而不是调布局
- 废弃一张图时同步确认它的 cron MEDIA 引用 + cron prompt 文字模板引用，代码删了但 prompt 还引用会导致任务找不到文件/推送失效数据
