# 选股分析牛马工具

> A股选股分析牛马工具——T+1 隔日接力选股全流程 SOP（首次使用先做基础数据录入 → 全市场扫描 → 行业主线 → 连板基因分类 → 深检回测 → T0/T1分层 → 概率标注 → 一键生成卡片式 HTML 深度分析报告）。当用户要求尾盘选票、明日介入预测、强势池扫描、竞价校准、个股五段式分析（基本面/资金面/技术面）、生成个股分析报告时使用。内置数据源限流降级链、判读铁律与踩坑清单；资金规模与交易权限由 config.json 定义，不含任何预设个人参数。

- Skill: `y5h1ggs/skill` (Agent Skill, multi-file: 6 files)
- Install (CLI): `npx skillmds@latest add y5h1ggs/skill`
- Raw SKILL.md: https://api.skillmd.com/api/skills/y5h1ggs/skill/raw
- Safety review: pending
- Works with: Claude Code, Claude.ai, OpenAI Codex
- Category: Web & Frontend
- License: 仅供个人研究使用，不构成投资建议
- Author: Y5H1ggs (https://skillmd.com/u/y5h1ggs)
- Updated: 2026-09-22
- Page: https://skillmd.com/skills/y5h1ggs/skill

---


# 选股分析牛马工具 · A股 T+1 隔日接力选股工作流

> 一个自包含、零依赖的 A 股短线研究工作流：**扫描 → 主线 → 接力基因 → 回测 → 评级 → 卡片式 HTML 报告**。
> 打工人自己用的牛马工具，不养闲人（不装任何 pip 包）。

为隔日超短模式（当日买 / 次日卖）产出"明日介入候选"。核心理念：
**选股能力决定打平，alpha 全在风控与情绪周期判定。**

---

## 自升级与校准协议（元规则 · 优先级最高）

> 本工具是**会自己进化**的：每次实盘都变成校准样本，每次校准都可能改变参数或口径。
> 但"自升级"必须有裁判规则，否则会退化成"向最近几笔行情拟合"——那是亏损加速器，不是进化。
> **完整裁判规则见 `references/calibration.md`；版本沿革见 `CHANGELOG.md`。**

### 固定流程（每次用到就按这七步走）

| 步 | 动作 | 命令 |
|---|---|---|
| ① | 实盘发生 → **先记事实，别急着解释** | `python scripts/journal.py add --code 000533 --name X --price 11.14 --shares 500` |
| ② | 平仓 → 记录卖出价与**卖出原因** | `python scripts/journal.py close --id 20260922-000533 --price 11.50 --reason take_profit` |
| ③ | 给出概率预测 → **落台账**（不落台账永远无法校准） | `predict.py` 输出 → `journal.record_forecast(...)` |
| ④ | 每 5 个交易日 / 每周末 → 出校准报告 | `python scripts/calibrate.py all` |
| ⑤ | 读门槛 → 判断证据强度够不够改参数 | 见 `calibration.md` §二 |
| ⑥ | 够 → 改 `config.json` / `references/*` **并写 CHANGELOG**（改前→改后 + 依据）<br>不够 → **什么都不改**，同样在 CHANGELOG 记一条"样本不足，维持原参数" | — |
| ⑦ | 同步源目录 → `~/.workbuddy/skills/选股分析牛马工具/` | `md5sum` 双向校验 |

**卖出原因枚举**：`stop_loss`（触发止损）· `take_profit`（达成兑现）· `board_break`（脱离板块）·
`triple_no`（开盘三连否）· `time_exit`（T+1 到期了结）· `manual`（主观）。
**归因分析完全依赖这个字段**——不填，`calibrate.py` 的业绩归因表就是空的。

**第 ⑥ 步的两条分支同等重要。** 一份只记录"这期不改"的 CHANGELOG，比一份满是"优化"记录的更可信。

### 三条硬门槛（动手改参数前必须自问）

1. **样本 < 30 笔 → 不许调参。** 30 笔样本下真实胜率 50% 的 95% 置信区间约 **32%~68%**，
   所谓"胜率从 45% 提到 55%"完全在噪声内。此时唯一正确动作是**继续记录**。
2. **一次只改一个变量**，否则结果无法归因。
3. **改动必须留痕**（依据哪份报告 + 哪几笔交易 + 改前→改后）。**没有留痕视为未发生。**

### 每次会话开工前的 30 秒自检

```bash
python scripts/cfg.py             # ① 配置是否就绪
python scripts/health.py --quick  # ② 数据源可用性 → 决定今天结论的置信度
python scripts/risk.py status     # ③ 账户是否处于回撤熔断期
```

### 交付时必须如实说明的三件事

1. 本报告的**数据完整度**（哪一维取了基准分、是否触发"数据不足"闸门）；
2. 给出的概率是**基准分布**还是**已叠加板块/资金/情绪修正**（见铁律 12）；
3. **样本量与适用边界**（N<10 必标"不可外推"）。

---

## 0. 首次使用：基础数据录入（**必须先做，否则不予启动**）

本工具**不含任何预设的个人参数**——资金规模、交易权限、费用率、止损纪律全部由使用者自己填。
原因很简单：本金 5 千和 50 万，能买的票、能吃几个点的波动、费用占多少，完全不是一回事。

**录入方式**：把 `config.example.json` 复制为 `config.json` 并填写（`config.json` 已被 `.gitignore` 屏蔽，不会外泄）。

| 字段 | 必填 | 默认 | 说明 |
|---|---|---|---|
| `cash` | ✅ | — | **可用资金（元）**。用于算"可买股数 / 资金占用 / 单次费用" |
| `allowed_prefixes` | | `["60","000","001","002","003"]` | **你有交易权限的板块前缀**。无创业板/科创板权限就不要加 `300/301/688` |
| `max_price` | | `null` | 单票价格上限（元）。资金少建议设 10~15，否则买不起整手 |
| `commission_rate` | | `0.00025` | 佣金费率（万 2.5 写成 `0.00025`） |
| `commission_min` | | `5.0` | **每边最低佣金（元）** ★ 小资金必填 |
| `stamp_tax_rate` | | `0.0005` | 印花税（**仅卖出方**收取） |
| `transfer_fee_rate` | | `0.00001` | 过户费（双边收取） |
| `fee_rate_roundtrip` | | `0.0018` | 旧版粗略比例口径（上面四项填了以后本项自动失效，仅为向后兼容保留） |
| `stop_loss_pct` | | `-4.0` | 止损纪律（%）。超短模式的生死线，**执行规则见 §2.5.3** |
| `take_profit_pct` | | `2.0` | 兑现目标（%），**执行规则见 §2.5.4** |
| `exclude_st` | | `true` | 是否剔除 ST / 退市风险股 |
| `position_mode` | | `single` | `single` = 全仓单吊单只 |
| `min_avg_amount_yi` | | `0.5` | 最小日均成交额（亿），过滤流动性差的票 |

> **为什么费用要拆成四项（不能用单一比例费率）**
> 小资金最大的成本不是比例费率，而是**每边 5 元的固定最低佣金**——它与成交金额无关，资金越少杀伤越大。
> 实证：本金 **6,172.80 元**的一笔 T+1 交易，双边实际费用 **13.12 元（0.212%）**，
> 其中 **10 元是两条边的最低佣金，占全部费用的 76%**。
> 若按旧版单一比例 0.0018 估算，会**低估约 18%**；3,000 元本金时误差扩大到约 2 倍。
> 因此改为四项参数，并提供计算入口：
> `cfg.buy_cost(价, 股数)` · `cfg.sell_proceeds(价, 股数)` · `cfg.break_even(价, 股数)` · `cfg.stop_price(成本, 股数)`。

**Agent 执行约定**：
1. 开工前先跑 `python scripts/cfg.py` 检查配置；不存在则**停下并要求用户录入**，不要用默认值硬跑；
2. 用户说"帮我初始化 config"时，按上表逐项问一遍，写好 `config.json` 再继续；
3. 任何脚本入口（`report.py` / `scan_pool.py`）在缺少 `config.json` 时会打印录入指引并退出——这是**设计如此**，不要绕过。

> 运行任何脚本前先自检：`python scripts/cfg.py`（打印当前配置）→ 再跑业务脚本。

---

## 0.5 硬约束（由 `config.json` 定义，非固定）

| # | 约束 | 说明 |
|---|---|---|
| 1 | **只做你权限内的板块** | 由 `allowed_prefixes` 决定；默认沪深主板 `60/000/001/002/003` |
| 2 | **仓位模式** | `position_mode` 决定；默认全仓单吊，输出"可买股数 = floor(资金/(价×100))×100" |
| 3 | **3 板及以上默认不接力** | 连板高位票只观察不推荐（这一条是策略纪律，不随资金变化） |
| 4 | **板块内资金接力偏好必查** | 候选票必须有同板块当日横向对比（成分涨幅 + 主力净额排名 + 涨幅中位数），脱离板块者显式标高风险 |
| 5 | **禁止绝对化表述** | 所有情景、触发条件、决策树分支必须附概率（单值或区间）；≥50% 或排序首位标 🔴高概率。无概率标注的情景表视为不合格输出 |
| 6 | **不给买卖指令** | 只做客观推演与情景概率；末尾强制风险提示（仅供参考、不构成投资建议） |
| 7 | **卖出侧纪律同等强制** | 次日必须执行 §2 竞价校准 + §2.5 持仓处置；三连否成立时按处置窗口（09:31~10:00）执行，**不得以"再等等/等回本"延后** |
| 8 | **连亏熔断** | 单周连续止损 3 笔 → 强制空仓 1 个交易日；单月累计回撤 ≥10% → 暂停交易（§2.5.5） |

**输出格式固定五段式**：数据速览 → 多空研判（利好利空都要讲）→ 技术分析（关键位表）→ 策略参考（情景×概率×触发条件）→ 风险提示。
个股分析必须拆 **基本面 / 资金面 / 技术面** 三维度。表格化、分点，拒绝大段无分段长文。

---

## 1. 执行流程（尾盘选票）

| 步 | 动作 | 关键点 |
|---|---|---|
| 1 | **全市场扫描** | 东财 clist 涨幅降序抓前 800~1000 只（含 f62 主力净额），过滤：**config 的板块权限** + 非ST + 涨幅≥2% + （量比≥1.5 或 换手≥3%）+ 主力净流入 |
| 2 | **行业/概念主线** | 按主力净额聚合 TOP8，判定资金主线；**必须交叉核对"涨停家数 TOP 板块"是否与主线一致** |
| 3 | **连板基因分类** | 涨幅≥9.5% 判涨停；K 线 streak 计数区分 首板 / 2板 / 3板+；输出 pos60、量比、换手 |
| 4 | **深检与形态回测** | 候选逐只拉 K 线，算 MA 多头排列、BOLL/MACD/RSI/CCI/KDJ/WR/MOM/KST、涨停基因、量能结构；按形态组做 T+1 回测（见 `references/backtest.md`） |
| 5 | **T0/T1 分层输出** | T0 = 低位首板 + 主力巨量 + 板块主线共振；T1 = 主线但位置偏高/分歧大。每只注明：代码/名称/收盘/涨幅/主力净额/pos60/主线归属/**板块内排名与相对强度**/介入逻辑/可买股数/接力概率 |
| 6 | **明日事件预测** | 大盘情景、情绪周期（冰点/回暖/延续）、竞价接力成功率、兑现窗口（09:30~10:00）情景表——全部带概率 |

> ⚠️ **上面 6 步只是"买入侧"的半条腿。** 同一套流程的后半段是：
> 次日 09:26 **竞价校准**（§2）→ 09:31 **开盘三连否判定**（§2.5.1）→ 触发则进入 **09:31~10:00 处置窗口**（§2.5.2）。
> 只跑前 6 步、不做后半段，选得再准也会把利润还回去。**两段必须成套使用。**

**三种口径按需求切换，开工前先确认用哪个**（不得用单一口径替他过滤）：

- **强势口径（默认）**：涨停/准涨停 + 主力巨量 + 板块龙头 + 连板基因 → T+1 打板接力。
- **趋势口径**：**排除涨停**，找"走势稳定向上"。四条同时满足才合格：①完整多头排列 `MA5>MA10>MA20>MA60`；②现价**站上 MA5**；③近 3 日主力净额负值天数 ≤ 1；④近 10 日低点逐级抬高（回调不破前低）。
- **潜力口径**：未涨停（涨幅 < 9.5%）+ 量比 ≥ 1.5 + 主力净流入 + 板块主线共振 → 次日补涨候选。

---

### 1.5 下单前准入清单（★ 逐条打勾，缺一条即不介入）

> 存在的意义：硬约束散落在 config / §0.5 / §2.5 / 铁律各处，人肉记忆必然漏项。
> **凡给出"可以介入"的结论前，必须把下表逐条写进回答**（每条给出"是/否 + 依据数值"），
> 不允许只用一句"综合来看可以"带过。任一条为「否」→ 结论降为**不介入**。

| # | 检查项 | 判据（机械可判） | 依据来源 |
|---|---|---|---|
| 1 | **板块权限** | 代码前缀 ∈ `config.allowed_prefixes`（默认 60/000/001/002/003） | §0.5 |
| 2 | **价格上限** | 现价 ≤ `config.max_price`（未设则跳过，并声明"未设上限"） | §0.5 |
| 3 | **资金够买** | `cash ÷ (现价×100) ≥ 1`，且买入总支出（含费）≤ cash | cfg.buy_cost() |
| 4 | **能成交** | 非一字板；封单比（封单额 ÷ 成交额）**< 0.5** 才有排队希望，≥1.0 视为买不到 | 铁律 4 |
| 5 | **连板高度** | streak ≤ 2（3 板+ 默认不接力） | §0.5 |
| 6 | **情绪温度** | 非冰点：涨停家数 ≥ 10 或昨日涨停股今日红盘率 ≥ 45% | `mood.py` |
| 7 | **板块相对强度** | 行业/题材双基准取更不利者 **≥ 0pp**（跑输即不介入） | §4 铁律 1 |
| 8 | **主力资金** | 主力净额 > 0 **且** 净占比 ≥ 5%；若取数失败 → 该条记「不可用」并**不予评级** | 铁律 2 |
| 9 | **时点在窗口内** | 14:00~14:45（尾盘选票）或已通过 §2 竞价校准；**盘中临时起意一律不介入** | §2.5.6 |
| 10 | **止损位可算** | 能给出精确的 `cfg.stop_price()`，且该价位距现价 ≥ 1%（过近则止损等于白挂） | 铁律 5 |

> ⚠️ 第 4 条与第 9 条是**最常被跳过**的两条：前者决定"能不能买上"，后者决定"该不该现在买"。
> 买不到（封单比 ≥1.0）就不要退而求其次去买炸板票 —— 炸板票恰恰是质量最差的一档。

---

## 2. 竞价校准规则（次日 09:26 执行）

| 竞价情形 | 处理 |
|---|---|
| 高开 2~6% + 放量（竞价量 ≥ 昨日 8%） | 接力到位，可按计划介入 |
| 高开 >7% 或一字板 | 溢价被吃掉，放弃 |
| 平开 / 低开 | 接力失败，放弃，等尾盘重选 |
| 板块锚 | 同板块须有 1 只以上红盘领涨，否则降级 |

> ⚠️ **竞价中途数据不可外推**：09:15~09:20 可撤单段看到的委买净多 / 撮合价，在 09:20~09:25 不可撤单段常被大单反向摆布，开盘价可能完全反转。
> **必须等 09:25 后的最终成交价 + 竞价量 + 内外盘** 再判读。

> ⚠️ **新增判据 —— 竞价漂移（09:20 → 09:25 匹配价变化）**：这是"卖压主导"的直接度量，比最终开盘价本身更有信息量。
> 记录 09:20 与 09:25 两个时点的虚拟匹配价：
> - 漂移 **≥ +1pp**（越竞越高）= 买方在不可撤单段加码 → **接力成色加分**；
> - 漂移 **0 ~ −2pp** = 中性，按最终价判读；
> - 漂移 **≤ −2pp**（越竞越低）= 卖压在不可撤单段持续施压 → **即便最终落在 2~6% 区间，接力等级自动降一档**。
> 实证（2026-09-18 实测，同一批昨日强势票）：

| 标的 | 09:20 匹配价 | 09:25 定盘 | 漂移 | 结果 |
|---|---|---|---|---|
| 安凯客车 000868 | +9.61% | **+1.23%** | **−8.4pp** | 近一字被砸回平开 |
| 山子高科 000981 | +5.98% | **+2.99%** | **−3.0pp** | 降档 |
| 积成电子 002339 | +9.23% | +2.55% | −6.7pp | 降档 |
| 海马汽车 000572 | −0.69% | **−1.61%** | −0.9pp | 结论未变（本就出局） |

> **只看 09:20 截图 = 把"假高开"当接力到位**；只看 09:25 结果 = 丢掉"是被砸下来的"这一层信息。**两时点都要取，差值才是判据。**

> ⚠️ **「竞价量 ≥ 昨日 8%」是粗略阈值，须按个股成交量基数校准**：该比例对**高换手票过严**、对**低换手票过松**。
> 实证（2026-09-18）：山子高科昨日全天成交 **8,866 万手（约 26 亿元）**，8% = 约 710 万手 / 2.1 亿元竞价成交，
> 对一只 3 元票几乎不可能达成；而它 09:25 实际竞价 30.6 万手（占比 3.45%）已是全场候选中量能绝对值最大的一只。
> **执行口径**：比例判据（8%）与**绝对量能**（竞价额 ≥ 2,000 万元，或竞价额进入昨日成交额前 3% 分位）**二者满足其一**即可，
> 并在报告中同时给出比例与绝对额，由使用者判断，不要只印一个百分比。

> ⚠️ **上表只覆盖"还没买入"的处置。已持仓请直接看 §2.5。**
> 「放弃」= 放弃**本次介入**，不等于「已持仓也应继续拿着」——这是本工具旧版最严重的语义漏洞。

---

## 2.5 持仓处置与卖出 SOP（★ T+1 的另一半）

> 第 1 节教的是"怎么买"，本节教的是"怎么卖"。**只学前半段，等于把止损交给情绪。**

### 2.5.1 「开盘三连否」——机械判据，不靠盯盘感觉

次日 **09:31 后**（板块数据恢复即可判定，无需反复刷新）三项**同时成立**即触发：

| # | 判据 | 阈值 | 取数方式 |
|---|---|---|---|
| ① | 开盘涨幅 | **< +2%**（含平开 / 低开） | 快照 `open` ÷ 昨收 − 1 |
| ② | 竞价量比 | **< 8%**（竞价量 ÷ 昨日全天量） | 快照 `vol` ÷ 昨日成交量 |
| ③ | 相对强度 | **< 0**（个股涨幅 − 板块全量成分涨幅中位数） | `sector_compare.py` |

三项同时为真 = **开盘三连否** → **接力逻辑已被市场否定**。
（若 09:25 最终竞价已明确低开 + 缩量，可提前至 09:26 判定，不必等到 09:31。）

### 2.5.2 触发后动作（按身份分支 · 旧版最大的坑）

| 身份 | 动作 |
|---|---|
| **未持仓（候选票）** | **放弃介入**，等 14:00 后尾盘重新选票。**不得以"跌了更便宜"为由改成低吸** —— 三连否否定的是接力逻辑，不是价格。 |
| **已持仓** | **进入处置窗口 09:31~10:00，窗口内完成减/清，不因盘中反抽而延后、不因"等回本"而推迟。** |

**为什么必须卡死时间**：三连否 = 接力资金不会来，日内大概率缩量阴跌。实证（2026-09-17 国脉科技 002093，成本 7.716 / 800 股）：

| 处置时点 | 价格 | 实际亏损 |
|---|---|---|
| 09:30 开盘 | 7.69 | ≈ −20 元 |
| 09:55 | 7.54 | ≈ −140 元 |
| **实际执行 10:05** | **7.49** | **−193.92 元** |

**延迟 35 分钟的成本 = 174 元 ≈ 本金的 2.8%。** 卡死窗口的全部价值就在这里。

### 2.5.3 止损执行规则（对应 config 的 `stop_loss_pct`）

1. **触发价**由 `cfg.stop_price(成本)` 计算（含双边费用的真实亏损口径，见 §0 费用模型）；
2. **盘中触及即执行，不等收盘** —— 收盘价用于记录，不是执行条件；
3. **逻辑止损优先于价格止损**：三连否已成立时，即便尚未触及 `stop_loss_pct`，也按 §2.5.2 处置窗口执行；
4. 执行后**当日不再对同一标的做任何操作**（禁止日内反手 / 补仓摊薄）；
5. 执行记录必须落盘：成本 / 卖价 / 费用 / 盈亏 / **触发原因**（价格止损 or 逻辑止损），用于累计胜率与盈亏比。

### 2.5.4 兑现规则（对应 `take_profit_pct`）

- 次日 **09:30~10:00 为兑现窗口**；冲高不封板即减/清，**不把"没封板"理解成"还有机会"**；
- 触达 `take_profit_pct` 即可了结，**不追求卖在最高点**；
- 若 10:00 仍未达目标且三连否成立 → 按 §2.5.2 处置。

### 2.5.5 连亏熔断

| 触发条件 | 动作 |
|---|---|
| 单周连续止损 **3 笔** | 次日起**强制空仓 1 个交易日**，复盘后再开工 |
| 单月累计回撤 **≥ 10%** | **暂停交易**，只做研究，直到定位亏损共性 |
| 连续 **5 笔** 止损 | 回到 §0 复查参数：止损位是否过紧？选股口径是否已偏移？ |

> 依据：`-4%` 硬止损下连亏 5 笔 = 本金 −20%。资金曲线崩溃多发生在连亏段，不是单笔巨亏。

### 2.5.6 空仓也是决策

卖出后到下一个尾盘窗口（14:00）之间，**默认动作是"不做"**。
- 空仓期的唯一任务：跑情绪温度 + 板块主线扫描，为尾盘准备候选池；
- **禁止**因"钱闲着"而在盘中临时买入 —— 盘中买入的买点未经过封板质量与尾盘主力净额的检验，违反 T+1 工作流。

---

## 3. 数据源与限流降级链（速查）

详见 **`references/datasources.md`**（完整接口 URL 与字段索引）。核心降级链：

- **实时快照**：腾讯 `qt.gtimg.cn`（**gbk 解码**，按请求顺序对齐返回行，勿用返回内代码做 key）
- **全市场列表**：东财 `clist`（ut 见参考文件；失败/限流 → 换 `push2delay` 主机 → 仍失败用新浪 `Market_Center.getHQNodeData`）
- **K 线源（回测用）**：腾讯 `fqkline?qfq` → **实测会返回 HTTP 501** → 降级 `fqkline?hfq` → 降级 **`kline/kline?...,day`（不复权，501 期间仍可用）** → 兜底新浪 `getKLineData`（**末根是前一交易日，必须检查日期对齐**）
  ⚠️ 降级到不复权源时，区间内有分红送转会使涨停价判定产生误差 → 回测数值约 ±0.1pp 偏差，须在报告中注明
- **资金流**：东财 `fflow`（个股/板块）；`ulist.np` 实时口径交叉验证
- **板块成员**：`slist?spt=3&secid=X.XXXXXX` 反查真实 BK 代码（**禁止猜代码**）
- 接口 429/断连**不是** IP 被拉黑 → 先换 ut token / 备用主机，加 1~1.5s 间隔重试

现成脚本在 `scripts/`：

| 脚本 | 作用 |
|---|---|
| `cfg.py` | **个人参数配置**（资金/权限/费用率/止损），入口脚本的"录入闸门" |
| `ds.py` | 便携数据源库（纯 stdlib，零依赖）——新脚本一律 `from ds import *` |
| `scan_pool.py` | 三口径候选池扫描（`strong` / `trend` / `potential`） |
| `sector_compare.py` | 板块横向对比（铁律 1 相对强度，**全量成分**中位数） |
| `mood.py` | 情绪温度 + 真实连板梯队 streak 扫描 |
| `backtest.py` | T+1 形态回测（含"开盘卖胜率"与基准对照） |
| `predict.py` | **次日涨跌概率预测**（形态邻域回测，开/收/盘中三口径分离，见铁律 12） |
| `report.py` | **一键生成 HTML 深度分析报告**（同款版式，见下节），`--pdf` 可导出 PDF |
| `journal.py` | **交易 / 预测台账**（append-only）—— 校准的数据底座，「记事实」而非「记解释」 |
| `calibrate.py` | **校准报告**：业绩归因 / 止损执行率与漏单 / 预测校准与 Brier / 参数建议 |
| `risk.py` | **账户级风控闸门**：回撤熔断 · 行业敞口 · 市值档 · 风险预算 → 是否允许开仓 |
| `fees.py` | 费用敏感度：最小可做资金线 · 成本矩阵 · 临界涨幅 |
| `pit.py` | **PIT 时点回测**：用公告日闸门消除财报前视偏差，并量化偏差幅度 |
| `verify.py` | 多接口交叉校验：快照（腾讯/东财）· K线（三源）· 板块资金（两个口径） |
| `health.py` | 数据源健康自检 + **当日结论置信度**评估 |
| `patterns.py` | 形态库扩展（22 条）+ 自定义形态 DSL |
| `factors.py` | **量化因子挖掘**：33 个因子的横截面 IC / ICIR / t值 / 胜率 / 分组单调性检验（**财务因子强制走 PIT**） |
| `selfcheck.py` | **自证校验**：不依赖第二数据源的**内部恒等关系**断言（17 项；已**常驻**接入 `report.py`） |
| `smoketest.py` | **随机票全链路冒烟 + 已知错误模式扫描**（改完代码就跑一遍） |
| `charts.py` | **纯 SVG 图表**（零依赖/无 JS/可导出 PDF）：K线+均线+成交量 · 资金流柱状 · 板块相对强度对比 · **回测逐笔高低范围** · 收益分布直方。报告已接入 5 张图 |

> ⚠️ **`ds.kline()` 返回二元组** `(K线数组, 源名)`，**不是数组**（P55）。
> 画图/算指标请用 `ds.kline_list(code, n)`；需要源名才用 `k, src = ds.kline_src(code, n)`。
> 直接把 `ds.kline()` 的返回值喂给画图函数**不会报错**，只会渲染出**空白图**。

### 次日概率预测（`predict.py`）

```bash
python predict.py 000533                 # 次日开/收/盘中三口径概率分布
python predict.py 000533 --cost 11.14    # 附带"次日盘中触及保本价"的概率
```

方法：算当日形态指标（RSI14 / BOLL %B / CCI20 / 均线结构 / 位置）→ 在 800 根历史 K 线里找
**参数邻域**（固定带宽逐级放宽 A→D，样本 ≥15 即停）→ 统计这些信号日**次日**分布。

**口径纪律（铁律 12）**：`P(开盘涨)` / `P(收盘涨)` / `P(盘中曾涨≥X%)` / `P(盘中曾跌≤Y%)`
**必须四个分开报**；把三者混成一个"上涨概率"视为不合格输出。
且输出仅为**该股自身形态的基准分布**，不含板块/资金/情绪——必须叠加三层修正后再下结论。

---

## 3.5 一键生成 HTML 报告（`report.py`）

```bash
python scripts/cfg.py                                      # ① 先自检配置（缺则按指引录入）
python report.py 002093                                    # 最简（资金取自 config.json）
python report.py 002093 --title 午盘深度分析 --sector-top 4
python report.py 002093 --notes notes.json                 # 注入人工研判段落
python report.py 002093 --no-bt --no-ladder                # 跳过回测/连板扫描（快）
python report.py 002093 --cash 100000                      # 临时覆盖 config 的资金
python report.py 002093 --anon                             # 脱敏版：报告给别人看时用（见下）
```

> ⚠️ **报告要发给别人 → 必须加 `--anon`（脱敏模式）**。默认输出会在三处暴露账户口径：
> ① 数据速览的「资金占用」行（本金 + 可买股数）；② 风险提示的成本条（按本金算的费用金额）；
> ③ 风险提示的纪律条（"你的止损纪律为 −4.0%、兑现目标 2.0%"）。
> `--anon` 的做法是 **`gather()` 内把 `cash` 归零**，下游派生值自动退化为中性，
> 而不是"传个假数"——假数会在报告里留下一个不存在的事实。
> **交付前必须扫描 HTML 复核**：断言不含 `config.json` / `allowed_prefixes` / `你的` / 本金数值。

产出 `名称+代码_标题_YYYYMMDD.html`，**全篇卡片式（每个小板块独立成卡）**，九节 + 顶部：
**抬头卡**（左＝标的 + 数据时点；右＝大字价格 + **2×2 关键指标格**：模型评级 / 当日主力 / 相对强度（双基准更不利者）/ 250 日位置）→
**评级 hero 卡**（深色大字评级 + 100 分制分数条 + 四维得分条 + **本期点评** + 数据完整度警示）→
**评级理由卡**（四个维度各一张子卡，逐条 ± 加减分）· 数据速览（KPI 卡 + 明细卡）·
分时形态 · 板块横向对比（**按板块主力净额自动挑主线板块**，全量成分中位数）· 多空研判 ·
技术分析（趋势与通道 / 动量与超买超卖 / 量能与位置，三张子卡）· 历史回测 + 最接近案例 ·
情景概率（由回测统计自动推导）· 情绪温度 + 连板梯队 · 风险提示。

**评级口径（100 分制 → 券商投资评级五档）**

> ⚙️ **校准原则（v2，重要）**：**各维度基准分 = 该维度满分的 50%**，
> 即"无任何特征的普通票 = 50 分"，正好落在「中性」档中心；
> 每维**加分项合计恰好等于基准分**，因此理论极值 = 100，**不靠上限截断凑分**。
> 早期版本基准分偏高（技术 15/30 + 大额加分）导致人人 29/30、区分度塌陷，v2 已修正。

| 维度 | 满分 | 基准分 | 加/减分项 |
|---|---|---|---|
| 技术面 | 30 | 15 | 多头排列 ±4 / 站上MA5 ±2~3 / MACD ±2 / BOLL ±1~5 / RSI14 ±2~5 / CCI20 ±1~4 / 量能 ±1~2 |
| 资金面 | 30 | 15 | 主力净额 ±4~5 / 净占比 ±1~3 / 近12日符号连续性 ±1~3 / 超大单 ±3 / 内外盘 ±2 |
| 板块面 | 25 | 12 | 相对强度（行业/题材双基准取更不利者）中位数 +6/+4/−3/−6/**−10**（严重跑输） / 板块主力 ±2~4 † / 红盘率 ±1~3 † / 是否在涨停名单 +1（† 板块级项，受相对强度闸门约束） |
| 情绪面 | 15 | 8 | 昨日涨停系"价强钱撤"±4 / 连板梯队高度 ±1~3 |

映射：`≥80 买入` / `65~79 增持` / `45~64 中性` / `30~44 减持` / `<30 卖出`（中性票恰为 50 分）。

> ⚠️ **取数失败绝不计负分**：任一维度数据取不到时，该维度按**基准（中性）分**计入并在评级卡上
> 标注「数据完整度」警示（否则会静默给出与事实相反的极低评级）。评级为模型机械输出，
> 非券商研报、不构成买卖指令。
>
> ⚠️ **但"资金面 + 板块面双缺失"时直接不予评级（P30）**：这两个维度合计 55 分，是判断
> "今日能不能做"的主维度。若两者都取数失败，用基准分凑出的总分会让**任何票都落在 50 分附近**
> —— 数字看似结论、实则零信息量，比明说"不予评级"更容易被误读成"这票可以看看"。
> 此时评级卡显示「数据不足 · 不予评级」，**不输出分数**，只提供技术面与情景分析。
>
> ⚠️ **单日相对强度是板块面的重扣项**：`个股涨幅 − 板块中位数` 落在 −2pp 以下即 **−10 分**
> （满分 25，基准 12）。板块走强而个股大幅落后 = 资金在同方向里选了别的票，
> 这是 T+1 模式最该躲开的形态，均线再漂亮也不救。
>
> ⚠️ **板块级加分闸门（P33）**：「板块主力净额」与「板块红盘率」是**板块级**指标，与该股无关。
> **个股相对强度为负（跑输板块中位数）时，这两项的正分一律归零**（负分照扣）。
> 否则板块一强，连被主线落下的票也白拿 3~7 分 —— 与铁律 1/3 直接矛盾：资金没流到它身上时，
> 板块走强对它反而是负面信号（说明钱在同方向里选了别的票）。
> 实证：600360 华微电子 09-18 半导体板指 +2.46%、176/179 红盘，它排 **128/179**（相对强度 −0.39pp），
> 旧口径白送 7 分 → 板块面 20/25 → 总评被抬到「中性 57」。
>
> ⚠️ **相对强度的基准必须"不可挑选"——行业基准 / 题材基准双轨，取更不利者（P35，2026-09-21 修订）**
>
> 旧口径（P34）用"成分数最少的板块"当同业，仍是**一个"挑"的动作** ——
> 而任何挑法都能挑出对自己最有利的解释。实证：002617 露笑科技同一时点，
> 挑中"光伏发电（13 只）"得 **+0.53pp、排 3/13**（像强势股），
> 挑中"碳化硅（47 只）"得 **−1.18pp、排 35/47**（是被落下的那只）—— 结论完全相反。
>
> **新口径（两个都由规则固定，不由分析者挑）**：
> ① **行业基准** = 东财 `stock/get` 的 `f127` 所属行业（交易所口径，每票恰一个）；
> ② **题材基准** = 该股 `f129` 概念列表中，**今日板块主力净额最大**者（规则固定为"取最大"）。
> **两个基准都印进报告；主判据取其中相对强度更低（更不利）的一个**；
> 二者方向相反时，必须打「**基准分歧**」标注并写明"按更不利者计分"。
> 报告表格里两行分别标「行业基准」「题材基准」。
> 实证：露笑科技 09-21 行业基准（电力 111 只）+0.14pp vs 题材基准（5G概念 304 只）−0.78pp
> → 按 −0.78pp 计 −3 分，板块面 **22/25 → 9/25**。
>
> ⚠️ **板块对比前必须先看主营构成（G9）**：`f127` 行业与 `f129` 概念都只是**分类标签**，
> 不等于它靠什么赚钱。报告已固定输出「主营构成（按产品）」卡片，并在
> **第 1 大主营占比 ≥50% 且与两个基准名称不相干**时自动打警示。
> 实证：002617 露笑科技主营 **漆包线 64.87%**、碳化硅连前列都没进 —— 涨的是题材标签；
> 002093 国脉科技主营 **教育 81.21%**，此前却拿"通信技术"做板块对比，结论被带偏。
>
> ⚠️ **情绪面是"市场级"指标**：同一天对所有个股几乎同分，只承担环境闸门作用（价强钱撤 →
> 全场接力难度上升）。看到情绪面普遍偏低不必惊讶，那是市场的问题不是这只票的问题。

**自动化程度**：数值、相对强度、超买超卖判定、概率表、评级全部自动算；
`--notes notes.json` 可覆盖/补充定性叙述（`rating_note` / `verdict` / `bull` / `bear` /
`scenarios` / `watch` / `oneline`，见 `examples/notes_sample.json`）。**不传 notes 也能出完整报告。**


---

## 4. 判读铁律（务必先读 `references/playbook.md`）

1. **板块内资金接力偏好**：拉同板块成分当日涨幅 + 主力净额排名，判定资金在接力"连板基因（2板+）"还是"低位首板"。目标票若不在资金接力的类型里 → 大概率被抛弃。
2. **相对强度**：必须算 `个股涨幅 vs 板块内涨幅中位数`。板块极强但个股跑输中位数 = 资金在这个方向里选了别的票（铁律 1 的量化形式）。
3. **脱离板块警示**：板块回暖它走弱 → 显式标高危，不得只用"个股形态"解释。
4. **情绪温度三指标**：昨日涨停/首板/连板的「板指涨跌 + 主力净额」；连板梯队高度；主线与涨停分布是否错位。
   - **板指涨但主力净流出 = 兑现型上涨** → 次日接力胜率塌方。
5. **托单消耗识别**：价格横盘 + 主力资金累计**加速流出** + 内盘 >> 外盘 = **托单被动消耗，不是支撑**。此时持股应减/清，不应因"价格没跌"而安心。
6. **行业字段不能靠 `clist` 的 `f100`**（实测时常整批缺失/返回 `-`）。**必须用 `stock/get` 的 `f127`（行业）+ `f129`（概念列表）+ `f128`（地域）**（`ds.stock_profile()` 已封装）。
   ⚠️ 但 `f127` 只是**分类标签**，不等于主业 —— 板块对比前必须核对**主营构成**（`ds.main_business()`，G9）。
7. **任何"取一个基准做对比"的地方，都要问一句：这个基准是不是被排序规则挑出来的"最有利解释"？**
   挑最有利的基准 = 用算法的方法论给自己找台阶（P34→P35 的教训）。
   解法只有一条：**基准由规则固定、不参与挑选**；若有多个合法基准，**取更不利者**并同时印出。

---

## 5. 关键坑点（勿重蹈 · 详见 `references/pitfalls.md`）

- **东财分钟级资金流 f52 是"当日累计值"，禁止累加**（累加会得出超过成交额的荒谬值）。取时点值或末条即可。
- **腾讯 `fqkline?qfq` 会返回 HTTP 501**（实测 2026-09-16）。**必须准备不复权降级路径**：`fqkline?hfq` → `kline/kline?...,day` → 新浪。原源上反复重试无用。
- **新浪 K 线 volume 单位是"股"，腾讯是"手"**——跨源混用会让"量比/几倍均量"差 100 倍，须 /100 归一。
- **板块内相对强度必须用「全量成分」中位数**：只取涨幅前 100 名算中位数 ≈ 全市场 90 分位，会把中游票误判为弱势票（曾据此错判"跑输 1.87pp"，实际是"领先 0.63pp"）。
- **挑"该股的主线板块"要用板块主力净额排序**，不能用 `slist` 的原始返回顺序（原始顺序第一个常是"学历教育 2 只"这类噪音板块）；成分 < 5 只的板块不参与相对强度。
- **RSI 公式** `100*ag/(ag+al)`，勿写补数。250 日位置用当日 live 价拼入序列。
- **新浪兜底 K 线末根日期必须与交易日对齐**，错位即整体错一天（新浪返回的是 JS 对象字面量，键无引号，需先正则补引号再 json.loads）。
- **主力净占比 = 主力净额 / 当日成交额**：<10% 属"温和"，不构成"大资金强力介入"；30%+ 才算强势介入。
- **回测/统计脚本**：亏损截断用 `max(r, -0.04)`；均值只对对应子集求，勿混全样本。
- **腾讯快照解析**按请求顺序 zip 对齐，勿用返回内代码字段做 key 匹配。
- **东财 `clist` 分页在实时排序（`fid=f3/f62`）下页间会重叠**：同一只票可能同时出现在第 2、3 页（实测 20 只重复）。`ds.clist()` 已内置按代码去重；**自己写分页循环时也必须去重**，否则候选池虚增、"筛出 N 只"统计失真（G8）。
- **基准不可挑选**：任何"挑一个板块/窗口/样本做对比"的动作，都可能挑出对自己最有利的解释。基准要么由规则固定，要么多个基准取**更不利**者（P35 / 铁律 7）。
- **本地工具用法**：同一个文件**不要并行发起两次编辑** —— 后一次会静默覆盖前一次的写入，且两次都会报"成功"。改完必须 grep 回读确认落点（本次 G8 修复就这么丢过一次）。

---

## 6. 风险与合规

所有输出末尾强制附风险提示，注明：
- 盘中数据为瞬时快照，收盘前价格/资金/量比/均线全部会变，须以收盘复核；
- 回测样本量常偏小、区间含多段强势市况，**换环境后适用性下降**，统计结果不代表未来；
- 资金流按成交单大小推断，存在拆单干扰，单日噪音大；
- 满仓单票结构下板块转弱个股难独善其身；
- **仅供参考，不构成任何买卖指令，据此操作风险自担。**

