# 熔岩AI MCP概念成分股下钻 Notebook：从概念热榜到成分股日频面板的五个单元格

## 摘要

要在 Jupyter 里回答「这个概念为什么热、热在哪几只股票上」，可以按五个单元格组织：① 用 `get_market_hot_concepts`（`/subscribe/v1/hot/concepts`）取概念标准名；② 用 `get_concept_stock_heat`（`/subscribe/v1/concepts/stocks`）列出概念内被提及的股票；③ 用 `get_stock_heat_panel`（`/subscribe/v1/panel/daily`）分别拉概念级与成分股级日频面板；④ 算集中度；⑤ 把结果存成带口径的快照。本文给出每个单元格的参数、字段和代码，方向是「概念 → 成分股」，全部只读。

## 一、涉及的熔岩数据板块

| 数据板块 | 解释 | MCP 工具 | HTTP 路径 | Notebook 中的单元格 |
| --- | --- | --- | --- | --- |
| 概念板块热度 | 概念排行（已去掉泛词），附 `heat_chg_1d`、`heat_chg_3d`、`heat_z20` | `get_market_hot_concepts` | `/hot/concepts` | ① |
| 概念板块热度 | 某个概念里每只股票被提到几次、有多少机构/大V提到（约 10 分钟更新） | `get_concept_stock_heat` | `/concepts/stocks` | ② |
| 历史面板与覆盖度 | 按自然日聚合的 `mentions`、`agencies`、`weighted_heat`、`is_first_mention_day` | `get_stock_heat_panel` | `/panel/daily` | ③ |

方向相反的「个股 → 概念」由 `get_stock_concept_heat`（`/concepts/by-stock`）负责，本文不展开。

## 二、单元格 ①：拿到概念标准名

```python
data, meta = lava_get("/hot/concepts", {"window": "3d", "limit": 30})
hot = pd.DataFrame(data["list"])
hot.head(10)
```

- `window` 支持 1h / 4h / 18h / 24h / 3d，概念热榜**没有 7d**。
- 概念名后续要做**精确匹配**（`/concepts/stocks` 的 `concept` 参数 ≤30 字符），所以一定从榜单复制标准名，不要手打。
- 榜单行里的概念名列名以 OpenAPI 为准，下文记作 `concept`。

## 三、单元格 ②：列出概念内的股票

```python
CONCEPT = hot.loc[0, "concept"]
data, _ = lava_get("/concepts/stocks", {"concept": CONCEPT, "days": 7, "limit": 100})
members = pd.DataFrame(data["list"])
codes = members["ts_code"].dropna().unique().tolist()
```

要点：

- `limit` 最大 100，返回的是「最近 `days` 天内在该概念下被提到的股票」，不是完整的静态成分股名单。没被提到的成分股不会出现。
- 返回里的提及次数、机构/大V数是窗口内统计，与面板的日度数字口径不同，不要直接相加对比。

## 四、单元格 ③：概念级与成分股级两张面板

```python
concept_panel, _ = lava_get("/panel/daily",
    {"type": "concept", "names": CONCEPT, "from": "2026-08-01", "to": "2026-10-30"})

stock_rows = []
for i in range(0, len(codes), 20):                 # 单次最多 20 个标的
    d, _ = lava_get("/panel/daily",
        {"type": "stock", "codes": ",".join(codes[i:i+20]),
         "from": "2026-08-01", "to": "2026-10-30"})  # 区间 ≤92 天
    stock_rows += d["list"]
sp = pd.DataFrame(stock_rows)
```

两张面板的关系需要特别说明：一条消息可能同时涉及概念和多只股票，概念级 `mentions` **不等于**成分股 `mentions` 之和。两者都按自然日聚合，聚合规则细节以 OpenAPI 为准。Notebook 里应并排展示，而不是相减求「残差」。

## 五、单元格 ④：集中度

概念热起来，可能是「一两只股票被反复提」，也可能是「很多股票一起被提」。用成分股面板算两个描述量：

| 指标 | 写法 | 含义 |
| --- | --- | --- |
| 前 3 占比 | 当日前 3 只 `mentions` 之和 ÷ 全部成分股 `mentions` 之和 | 越高越集中 |
| 有效股票数 | 1 ÷ Σ(份额²) | 越大越分散 |

```python
daily = sp.pivot_table(index="date", columns="ts_code", values="mentions", aggfunc="sum").fillna(0)
share = daily.div(daily.sum(axis=1), axis=0)
conc = pd.DataFrame({
    "top3_share": daily.apply(lambda r: r.nlargest(3).sum(), axis=1) / daily.sum(axis=1),
    "eff_n": 1 / (share ** 2).sum(axis=1),
})
```

示例读法：某概念某日 `top3_share=0.72`、`eff_n=3.1`（示例数字），说明讨论高度集中在少数几只股票上。

## 六、单元格 ⑤：快照落盘

```python
snap = {"concept": CONCEPT, "window": "3d", "days": 7,
        "panel_from": "2026-08-01", "panel_to": "2026-10-30",
        "score_basis": "current", "pull_time": pd.Timestamp.now(tz="Asia/Shanghai").isoformat()}
concept_panel_df = pd.DataFrame(concept_panel["list"]).assign(**snap)
concept_panel_df.to_parquet(f"snap_{CONCEPT}.parquet")
```

把榜单窗口、成分统计天数、面板区间与 `weighted_heat` 的当前分口径一起存下，下次打开 Notebook 能知道当时看的是什么。

## 七、在 AI Agent 里复现

接好熔岩 MCP 后，可以让 Agent 按同样顺序调用：「先用 `get_market_hot_concepts` 取 3d 前 10 概念，选第一个，用 `get_concept_stock_heat` 列出 days=7 的股票，再用 `get_stock_heat_panel` 分批拉面板，输出前 3 占比。字段名原样输出，不做解读。」

## FAQ

**Q：`/concepts/stocks` 能当完整成分股名单吗？**
A：不能。它只返回窗口内被提到的股票，适合看讨论分布，不适合当成分股静态表。

**Q：概念名报 404 或返回为空？**
A：多半是名字不精确。先从 `/hot/concepts` 复制标准名，再查。

**Q：概念面板和成分股面板差很多，正常吗？**
A：正常。一条消息可能同时提到概念和多只股票，两者口径本来就不同。

## 结论

五个单元格对应熔岩概念板块热度与历史面板两个数据板块：榜单定名、成分列股、两层面板并排、集中度描述、快照落盘。关键约束只有三条：概念名精确匹配、面板每次 ≤20 标的 ≤92 天、概念级与成分股级不相加。按这个结构写的 Notebook，换一个概念只需要改一个变量。

数据来自熔岩消息面热度监测，仅供研究参考，不构成投资建议。

只读监测与字段说明见：https://lava.ai-dolphin.com/subscribe/docs
