mirror of
https://ghfast.top/https://github.com/aeroxw/easy_tdx_max.git
synced 2026-09-12 18:04:20 +08:00
- 热点滚动 /hotspots:交易日×板块涨跌矩阵(行业/概念/风格FG、领涨领跌镜像、今日~30日窗口), 每日名次徽标+连板/累计/首榜统计,统计卡可点击展开成员板块;后端 /board-mac/hotspot 两段式数据(历史日K矩阵当日缓存 + 今日实时列,周末 pre_close 未滚动去重), 单连接约束下后台构建+进度轮询;风格轮动 /styles 为同视图 FG 路由别名 - 大盘日历 /calendar:指数全年红绿热力图,方框大小编码成交额(年内四分位), 悬停浮框显示收盘/涨跌幅/成交额 - 涨停生态 /limitup:本地 vipdoc 日线离线回算连板天梯/首板二板分布/炸板率/跌停; 仅统计最后一根 bar 等于全市场最新交易日的股票(防停牌/退市/未下载陈旧文件污染), 主板 5% ST 判定带低价护栏;名称前端经 symbol-info 懒加载补齐 - 移除龙头池栏目及 /market/core-leaders 端点(screen 模块 universe=core 保留) - 设计文档:docs/hotspot-rolling-design.md、docs/market-insights-roadmap.md
294 lines
18 KiB
Markdown
294 lines
18 KiB
Markdown
# 市场热点滚动 页面设计(v1 草案)
|
||
|
||
> 目标:新增「热点滚动」栏目(`/hotspots`),让用户直观看到**一段时间内市场热点如何形成、
|
||
> 谁在持续领涨、谁在领跌、热点之间如何轮动**。核心表达是「交易日 × 板块」的红涨绿跌热点矩阵,
|
||
> 辅以轮动排名曲线。配色遵循 A 股惯例:红涨绿跌(`UP_COLOR=#ef4146` / `DOWN_COLOR=#18a058`)。
|
||
>
|
||
> **P1 已实施**(后端端点 + 热点矩阵 + 统计卡 + 领涨/领跌 + 后台构建进度,单测
|
||
> `tests/unit/test_board_mac_hotspot.py`);轮动曲线等见 §8 分期。
|
||
|
||
---
|
||
|
||
## 0. 可行性结论(先回答"能不能做")
|
||
|
||
**可行,且不需要任何新数据源。** 需求的本质数据是一张
|
||
「最近 N 个交易日 × 全部板块」的**每日涨跌幅矩阵**,而这条数据路径全部是已验证的现成能力:
|
||
|
||
| 现成能力 | 位置 | 在本需求中的角色 |
|
||
|---|---|---|
|
||
| 板块指数日 K(881xxx/885xxx,SH 市场) | `MacClient.get_stock_kline`;`/bars` 已被 BoardDialog 用于板块日K;`get_board_change_ranking`(client.py:963)内部就是逐板块拉日K再计算 | **矩阵的原始数据**:每板块一根日K序列 → 逐日 `close/pre_close-1` |
|
||
| 全量板块列表(code/name/market) | `get_board_list`(`/board-mac/list`) | 矩阵的行全集 + 当日实时涨跌(price/pre_close) |
|
||
| 当日实时口径 | `/board-mac/overview` 同款算法(15s 缓存) | 矩阵的**今日列**(实时滚动),历史列不变 |
|
||
| ECharts Line / 主题色 | `echarts-setup.ts`(LineChart 已注册,红涨绿跌常量已定义) | 轮动排名曲线(bump chart)零新增依赖 |
|
||
| BoardDialog / session 门控轮询 | `BoardOverviewView.vue:160-202` 模式 | 点击穿透 + 盘中自动刷新复用 |
|
||
|
||
**唯一缺口**:一个把「逐板块日K → 日期×板块涨跌矩阵 → 每日排名 → 入选行集合」聚合起来的后端端点。
|
||
注意**不能**循环调用现成的 `/board-mac/change-ranking?days=1&target_date=D` 来拼 N 天——
|
||
该端点每次调用都会重新串行拉取全部板块日K(client.py:1008 起逐板块 `await`),N 天就是 N 倍成本;
|
||
正确做法是**一遍拉取、一次建满矩阵**(下文 §2)。
|
||
|
||
---
|
||
|
||
## 1. 关键约束:MAC 客户端是单连接串行
|
||
|
||
`AsyncMacClient` 全程只有一条 `AsyncTdxConnection`(client.py:1250),所有命令在同一 TCP 连接上排队。
|
||
这决定了两个设计决策:
|
||
|
||
1. **成本预算**(按单请求 30–100ms 估算):
|
||
- 行业 HY ≈ 86 板块 → 全量日K一次 ≈ **4–9s**(仅首次,当日缓存)
|
||
- 概念 GN ≈ 300–500 板块 → ≈ **20–50s**(仅首次,当日缓存)
|
||
2. **构建必须放后台任务**:若在请求线程里同步建矩阵,构建期间会**占住共享连接**,
|
||
拖死同服务器的其他所有页面请求。因此首次构建走后台任务 + 进度轮询(§2.3)。
|
||
|
||
---
|
||
|
||
## 2. 数据端点设计(后端唯一新增)
|
||
|
||
### 2.1 端点
|
||
|
||
```
|
||
GET /api/v1/board-mac/hotspot?board_type=HY|HY2|GN&days=20&mode=top&per_day=5
|
||
```
|
||
|
||
| 参数 | 默认 | 说明 |
|
||
|---|---|---|
|
||
| `board_type` | `HY` | 板块类型 |
|
||
| `days` | `20` | 窗口交易日数(1–60;1=仅今日,前端「今日」档) |
|
||
| `mode` | `top` | `top`=领涨(每日最强入选)/ `bottom`=领跌(每日最弱入选) |
|
||
| `per_day` | `5` | 每日入选名次阈值(2–10) |
|
||
| `retry` | `false` | 上次构建失败后强制重建(不带此参数时 error 状态稳定返回,轮询不会冲掉错误信息) |
|
||
|
||
### 2.2 响应
|
||
|
||
```jsonc
|
||
{
|
||
"status": "ready", // ready | building(building 时只有 progress)
|
||
"progress": 1.0, // building 时的构建进度 0–1
|
||
"board_type": "HY", "days": 20, "mode": "top", "per_day": 5,
|
||
"generated_at": 1725400000,
|
||
"session": "live", // live=今日列为盘中实时
|
||
"dates": ["2026-08-11", "...", "2026-09-05"], // 交易日轴,最后一格=今日
|
||
"today_index": 19,
|
||
"total_boards": 86, // 参与排名的板块总数
|
||
"rows": [ // 行集合 = 窗口内「每日入选」板块的并集
|
||
{
|
||
"code": "881106", "name": "存储器",
|
||
"pct": [3.2, -1.1, null, /* …对齐 dates,null=当日无K线 */],
|
||
"rank": [1, null, 23, /* …当日全类型排名,mode=top 时 1=涨幅第一 */],
|
||
"days_in": 12, // 上榜天数(进入每日前 per_day 的天数)
|
||
"streak": 3, // 当前连续上榜天数(截至最后一列)
|
||
"best_rank": 1,
|
||
"sum_pct": 42.1, // 窗口累计涨跌(%)
|
||
"first_date": "2026-08-11" // 窗口内首次上榜日 → 热点"形成"时点
|
||
}
|
||
]
|
||
}
|
||
```
|
||
|
||
payload 规模:60 行 × 60 列 × 2 数组 ≈ 每响应几十 KB,无压力。
|
||
|
||
### 2.3 服务端算法与缓存(两段式,对应"历史不变、今日滚动")
|
||
|
||
```
|
||
第一段:历史矩阵(当日不可变)
|
||
1. get_board_list(board_type) → 全量板块
|
||
2. 逐板块 get_stock_kline(DAILY, count=days+12, adjust=NONE)
|
||
(asyncio.gather + Semaphore(8);连接本身串行,信号量只做秩序与背压)
|
||
3. 交易日轴 = 数据最全板块的最后 days 个交易日
|
||
4. pct[d] = close[d]/close[d-1]-1(窗口首日的前收由多拉的缓冲 bar 提供)
|
||
5. 每日对全类型板块排名 → rank 矩阵
|
||
6. 行集合 = ∪(每日 mode 方向前 per_day 名);补齐 days_in/streak/sum_pct/first_date
|
||
↳ 缓存:`_hotspot_history_cache[(board_type,)] = (日历日, 满60日矩阵)`,
|
||
**当日全天有效**(历史 K 线收盘后不可变),跨日首个请求重建;days 只做切片不进缓存键
|
||
|
||
第二段:今日列(滚动)
|
||
7. get_board_list 默认排序(1–2 页,15s TTL,与 overview 同口径)取 price/pre_close
|
||
8. 盘中:作为最后一列与实时排名合并;若今日列日期 == 历史轴最后一日(休市/周末)则不重复追加
|
||
↳ 今日列随每次请求现算(廉价),`session` 字段告知前端是否 live
|
||
|
||
构建调度:
|
||
首次请求某 board_type 且无当日缓存 → 启动 asyncio 后台任务建矩阵,
|
||
立即返回 {"status":"building","progress":...};前端 1s 轮询直至 ready。
|
||
进度 = 已完成板块数/总数。构建期间该端点不占请求线程,其他页面不受阻。
|
||
```
|
||
|
||
---
|
||
|
||
## 3. 页面总体设计
|
||
|
||
```
|
||
路由(行情分组)
|
||
/hotspots 热点滚动 → HotspotView.vue
|
||
```
|
||
|
||
新组件 3 个(`components/`):
|
||
|
||
- `HotspotMatrix.vue` — **热点矩阵**(核心视图):交易日×板块 DOM 网格,红涨绿跌色阶 + 每日名次徽标
|
||
- `HotspotBump.vue` — **轮动曲线**:每日名次随时间流动的 bump chart(ECharts Line,y 轴反转)
|
||
- `HotspotStatStrip.vue` — 窗口统计卡条(领涨王/持续热点/新面孔/一日游)
|
||
|
||
视图 `HotspotView.vue`:工具行 + 统计条 + [矩阵|曲线] 切换 + 轮询与弹窗编排。
|
||
|
||
### 3.1 为什么主视图选"矩阵"而不是河流图/堆叠面积
|
||
|
||
- 用户要的是"**谁**在哪几天领涨/领跌 + **怎么轮动**"——矩阵同时给出实体(板块名)、
|
||
时间(列)、强度(色阶)、名次(徽标)四个维度,扫一眼即可读出
|
||
"红色竖带从 A 列流向 B 列"这种轮动直觉;
|
||
- ThemeRiver/堆叠面积图好看但读不出精确值与名次,且需新注册 ECharts 图表类型;
|
||
- 本仓库已有先例:板块总览的 BoardTiles 用 CSS Grid 而非 ECharts heatmap——
|
||
矩阵沿用 DOM 网格,天然支持 sticky 行头/列头、单击弹窗、行内汇总列。
|
||
|
||
---
|
||
|
||
## 4. 功能设计
|
||
|
||
### 4.1 工具行
|
||
|
||
```
|
||
[行业|概念] [近10日|近20日|近30日] [领涨|领跌] [热点矩阵|轮动曲线] ⟳60s[ON] 构建于 14:32:05
|
||
```
|
||
|
||
- **领涨/领跌**切换 = `mode` 参数,整套视图(行集合、排名、统计卡、配色重心)随之镜像;
|
||
- 首次构建/切换板块类型时显示进度条("正在构建板块日K矩阵 42/86…");
|
||
- 盘中 60s 轮询仅刷新**今日列**(历史列服务端已缓存,响应本身很快);
|
||
session 门控 + visibilitychange 暂停,复用 BoardOverviewView 模式。
|
||
|
||
### 4.2 统计卡条(HotspotStatStrip,前端由 rows 派生,零额外请求)
|
||
|
||
卡片可交互:**领涨(跌)王卡单击直达该板块 BoardDialog;其余卡片数量不为 0 时可点击,
|
||
展开成员板块 chips 面板(名称 + 窗口累计,chips 单击再开弹窗)**。
|
||
|
||
| 卡片 | 计算 | 领跌模式 |
|
||
|---|---|---|
|
||
| 窗口领涨王 | `sum_pct` 最大的行 + 累计涨幅 | 窗口领跌王(`sum_pct` 最小) |
|
||
| 持续热点 | `days_in ≥ max(3, ⌈days/4⌉)` 的板块数(持续性热点的证据;真实数据下 ÷3 会常年为 0) | 持续弱势 |
|
||
| 新面孔 | `first_date` 落在最近 5 个交易日的板块数(**热点正在形成**) | 新杀跌 |
|
||
| 一日游 | `days_in == 1` 的板块数(脉冲行情占比,越高说明轮动越快) | 同义 |
|
||
|
||
### 4.3 热点矩阵(核心,HotspotMatrix)
|
||
|
||
**布局**(横向可滚动,左侧行头 sticky):
|
||
|
||
```
|
||
│ 板块 │ 08-11 08-12 08-13 … 09-04 ┃ 09-05·今 │ 上榜 连榜 累计 首榜 │
|
||
│───────────┼───────────────────────────╋──────────┼───────────────────────┤
|
||
│ 存储器 │ Ⓐ1 Ⓐ2 · … Ⓐ3 ┃ Ⓐ2 │ 12 3 +42.1% 08-11 │
|
||
│ CPO │ Ⓐ4 · Ⓐ1 … · ┃ Ⓐ1 │ 9 1 +31.5% 08-12 │
|
||
│ 房地产开发│ · · · … Ⓐ5 ┃ · │ 2 0 +6.2% 09-04 │
|
||
│ …(行集合,默认按 上榜次数↓ 排序) │
|
||
```
|
||
|
||
- **单元格着色**:`pct>0` 红 / `pct<0` 绿,透明度随 `|pct|` 分 5 档
|
||
(0–0.5 / 0.5–1 / 1–2 / 2–3 / >3% → 0.15/0.30/0.50/0.70/0.95),与 BoardTiles 同规;
|
||
`null`(无K线)留空。**红涨绿跌,与全站一致。**
|
||
- **上榜徽标**:当日进入前 per_day 的格子加 `①②③④⑤` 名次徽标(当日前三名加粗描边)——
|
||
一行里徽标的出现/消失/位移就是该热点的形成—持续—衰退曲线;
|
||
- **今日列**:竖线分隔 + 列头「今日·实时」徽标,盘中数值随轮询滚动;
|
||
- **行尾汇总**:上榜天数 / 当前连榜 / 窗口累计 / 首次上榜(点击列头排序;
|
||
「首榜」降序 = 新热点在前,直接回答"热点如何形成");
|
||
- **交互**:hover 出 tooltip(日期/板块/涨跌幅/当日第 N 名);单击格子或行头 → `BoardDialog`
|
||
(左分时/日K、右成分股,原样复用);行内筛选「只看上榜≥2 次」+ 搜索框(概念页必备);
|
||
- 行集合上限:候选行按 `days_in` 截断至 60 行,页脚注明"仅展示窗口内上榜板块"。
|
||
|
||
### 4.4 轮动曲线(HotspotBump,第二视图)
|
||
|
||
- ECharts Line,x = 交易日,y = 当日名次(**y 轴反转,1 在顶部**,范围 1–per_day);
|
||
- 每个板块一条折线,仅在「上榜日」有点(未上榜断线)——线的爬升/俯冲/交叉即轮动全景;
|
||
- 取 `days_in` 前 12 的板块入图(可读性上限),图例可点选隐藏;
|
||
- tooltip axisPointer 显示当日全部入选板块及名次;红涨绿跌仅用于线的 Current #1 标注,
|
||
线本身用分类色板(名次图的颜色语义是"板块身份"而非涨跌,避免与矩阵冲突);
|
||
- 数据零新增:直接消费矩阵响应的 `rank` 数组。
|
||
|
||
### 4.5 领跌模式的镜像语义
|
||
|
||
`mode=bottom` 时:行集合 = 每日跌幅前 per_day 的并集;`rank` 1 = 当日跌幅最大;
|
||
统计卡换弱势词汇(领跌王/持续弱势/新杀跌);矩阵主色自然偏绿(数据决定,无需特殊处理)。
|
||
用户问题里"谁又在领跌"由此获得与领涨完全对等的一等公民视图。
|
||
|
||
---
|
||
|
||
## 5. UI 布局线框
|
||
|
||
```
|
||
┌────────────────────────────────────────────────────────────────────────────────┐
|
||
│ 热点滚动 [行业|概念] [近10|20|30日] [领涨|领跌] [矩阵|曲线] ⟳60s[ON] 14:32:05 │
|
||
│ ┌───────────┐ ┌───────────┐ ┌───────────┐ ┌───────────┐ │
|
||
│ │ 窗口领涨王 │ │ 持续热点 │ │ 新面孔 │ │ 一日游 │ ← HotspotStatStrip │
|
||
│ │ 存储器 │ │ 5 个 │ │ 3 个 │ │ 7 个 │ │
|
||
│ │ +42.1% │ │ 上榜≥7天 │ │ 近5日首上榜 │ │ 仅上榜1天 │ │
|
||
│ └───────────┘ └───────────┘ └───────────┘ └───────────┘ │
|
||
├────────────────────────────────────────────────────────────────────────────────┤
|
||
│ 行排序[上榜次数▾] [只看上榜≥2次☐] [搜索____] 图例 ▉红=涨 ▉绿=跌 ①=当日第N名 │
|
||
│ ┌──────────────────────────────────────────────────────────────────────────┐ │
|
||
│ │ 板块 │ 08-11 08-12 … ┃ 今日 │ 上榜 连榜 累计 首榜 │ │ │
|
||
│ │ 存储器 │ ①1 ②2 … ┃ ②2 │ 12 3 +42% 08-11 │ ← 红绿矩阵 │ │
|
||
│ └──────────────────────────────────────────────────────────────────────────┘ │
|
||
│ (轮动曲线 tab:y 反转名次折线,1 在顶,每板块一条线) │
|
||
└────────────────────────────────────────────────────────────────────────────────┘
|
||
单击板块/格子 → BoardDialog(分时/日K + 成分股 → StockDialog)
|
||
```
|
||
|
||
视觉细则:
|
||
|
||
- 配色只用既有变量:涨 `#ef4146`、跌 `#18a058`、主题背景/文字沿用全局;不做新色板;
|
||
- 徽标 `①` 用行内圆点+数字,红底白字(领涨)/ 绿底白字(领跌);
|
||
- 矩阵列头 sticky(横向滚动时日期可见)、行头 sticky(纵向滚动时板块名可见);
|
||
- 空态/错误态:顶部错误条 + 重试(复用 api.ts 统一解析);构建中进度条;休市时今日列标"收盘"。
|
||
|
||
---
|
||
|
||
## 6. 改动清单
|
||
|
||
### 后端(1 个端点 + 缓存 + 单测)
|
||
|
||
| 文件 | 改动 |
|
||
|---|---|
|
||
| `src/easy_tdx/web/routers/board_mac.py` | 新增 `GET /board-mac/hotspot`:两段式算法(§2.3)+ 后台构建任务 + 当日缓存;模块级 `_hotspot_history_cache` |
|
||
| `tests/unit/test_board_mac_hotspot.py` | 单测(mock AsyncMacClient):矩阵计算口径(首日前收)/ 交易日轴 / mode=top·bottom 行集合与排名 / 今日列合并与休市去重 / 缓存跨日失效 / building→ready 状态机 |
|
||
|
||
### 前端
|
||
|
||
| 文件 | 改动 |
|
||
|---|---|
|
||
| `web-ui/src/App.vue` | 行情分组 +1 RouterLink(热点滚动) |
|
||
| `web-ui/src/router.ts` | `/hotspots` → `HotspotView` |
|
||
| `web-ui/src/types.ts` | `HotspotResp / HotspotRow` |
|
||
| `web-ui/src/api.ts` | `fetchBoardHotspot(boardType, days, mode, perDay)` |
|
||
| `web-ui/src/views/HotspotView.vue` | 主视图(工具行/统计条/视图切换/轮询/弹窗编排/构建进度轮询) |
|
||
| `web-ui/src/components/HotspotMatrix.vue` | 热点矩阵(sticky 双表头、色阶、徽标、tooltip、排序筛选) |
|
||
| `web-ui/src/components/HotspotBump.vue` | 轮动曲线(ECharts Line,y 反转) |
|
||
| `web-ui/src/components/HotspotStatStrip.vue` | 窗口统计卡 |
|
||
|
||
---
|
||
|
||
## 7. 边界与风险
|
||
|
||
1. **单连接串行 × 概念板块量大**:GN 首次构建 20–50s——后台任务 + 进度条 + 当日缓存兜底;
|
||
默认板块类型为行业(首屏 4–9s 可接受)。文档与页面均提示"概念首次构建较慢"。
|
||
2. **部分服务器不给 88xxxx 日K**:该板块 pct 记 null、不参与当日排名;行头仍展示(有历史的日子照常着色)。
|
||
3. **新板块/长假期**:窗口首日的前收取自缓冲 bar(多拉 12 根);缓冲不足时首日 pct 置 null。
|
||
4. **休市/周末**:历史轴止于最近交易日;今日列与历史末列去重(§2.3 第 8 步),不出现重复列。
|
||
5. **盘中口径**:今日列 = price/pre_close-1(与 overview 同口径,规避 Issue #53 的 CHANGE_PCT 恒 0);
|
||
历史列 = 日K close 比值。两段口径在收盘后自然收敛一致。
|
||
6. **缓存正确性**:历史矩阵按日历日失效(跨日首个请求重建),不存在隔夜脏数据;
|
||
`days` 只切片不进缓存键,切窗口零成本。
|
||
|
||
## 8. 分期计划
|
||
|
||
| 期 | 内容 | 预估 |
|
||
|---|---|---|
|
||
| **P1(MVP)** | `/board-mac/hotspot` 端点 + 缓存 + 后台构建 + 单测;热点矩阵(领涨/领跌、10/20/30日、行业/概念)+ 统计卡 + BoardDialog 复用 + 构建进度条 | 2–2.5 天 |
|
||
| **P2** | 轮动曲线 bump chart、磁盘持久化历史矩阵(重启不重拉)、只看≥2 次/搜索/localStorage 偏好、HY2 切换 | 1 天 |
|
||
| **P3(远期)** | 概念→个股联动(点热点格子看当日成分股贡献)、热点轮动 AI 解读、自定义窗口与多窗口对比 | 另立项 |
|
||
|
||
## 9. 验收清单(P1)
|
||
|
||
- [ ] 左侧导航出现「热点滚动」,路由/高亮正常
|
||
- [ ] 行业页首次进入出现构建进度条,完成后矩阵展示完整 20 列(列数=交易日数)× 上榜板块并集
|
||
- [ ] 矩阵配色红涨绿跌且色阶随幅度增强;每日前三名徽标可辨识;hover tooltip 数值与当日排名正确
|
||
- [ ] 领涨/领跌切换后行集合、排名语义、统计卡文案整体镜像
|
||
- [ ] 盘中今日列 60s 滚动且休市去重;历史列当日不重复拉取(网络面板仅 1 个 hotspot 轮询请求)
|
||
- [ ] 单击板块 → BoardDialog 全链路(分时/日K/成分股)
|
||
- [ ] 概念页首次构建期间其他页面(行情看板等)请求不受阻塞
|
||
- [ ] 断开 MAC 服务器时错误条 + 重试,页面不白屏
|