docs: 每日热点核心股/题材历史记录 + 活跃核心股滚动表格设计

- 每日采集核心股前100(按涨幅)+所属题材,题材涨幅前10,存历史
- asyncio 后台定时采集,幂等去重
- 新页面:股票×最近10个A股交易日涨幅矩阵,超10日未出现踢出

Co-Authored-By: Claude <noreply@anthropic.com>
This commit is contained in:
Sakurasan
2026-08-10 19:20:11 +08:00
co-authored by Claude
parent 84a01fb7b1
commit 75c7ff3670
@@ -0,0 +1,156 @@
# 每日热点核心股 / 题材历史记录 + 活跃核心股滚动表格
日期:2026-08-10
## 1. 目标
为数据分析和发掘积累历史数据,并提供活跃核心股的滚动展示:
1. 每个交易日收盘后,采集**核心股前 100**(当日全部股票按涨幅降序取前 100)及其**所属题材**,保存历史。
2. 每个交易日收盘后,采集**题材(概念)板块涨幅前 10**,保存历史。
3. 新展示页:**活跃核心股 × 最近 10 个 A 股交易日**涨幅矩阵表格;新核心股加入,**超过 10 个 A 股交易日未出现则踢出**。
## 2. 已确认的决策
| 决策点 | 选择 |
|---|---|
| 核心股口径 | 当日全部股票按涨幅(f3)降序取前 100(不限覆盖题材数) |
| 板块口径 | 题材/概念板块,按涨幅(bf3)降序取前 10 |
| 采集触发 | 方案 A:asyncio 后台任务,每日收盘后自动采集 |
| 表格布局 | 股票 × 最近 10 个 A 股交易日列矩阵 |
| 10 日窗口 | 10 个 A 股交易日(跳过节假/周末) |
| 所属题材完整度 | 折中:以热点穿透采样题材为主,东财压力允许时尽力补全 |
## 3. 数据模型(新增 3 张表)
```sql
CREATE TABLE daily_core_stocks (
id INTEGER PRIMARY KEY AUTOINCREMENT,
trade_date TEXT NOT NULL, -- 交易日 YYYY-MM-DD
stock_code TEXT NOT NULL, -- 股票代码
stock_name TEXT NOT NULL, -- 股票名称
f3 REAL, -- 当日涨幅%
cover_count INTEGER, -- 覆盖题材数(采样)
rank INTEGER, -- 当日涨幅排名 1-100
created_at TEXT DEFAULT (datetime('now','localtime')),
UNIQUE(trade_date, stock_code)
);
CREATE TABLE daily_core_stock_themes (
id INTEGER PRIMARY KEY AUTOINCREMENT,
trade_date TEXT NOT NULL,
stock_code TEXT NOT NULL,
theme_code TEXT NOT NULL,
theme_name TEXT NOT NULL,
created_at TEXT DEFAULT (datetime('now','localtime')),
UNIQUE(trade_date, stock_code, theme_code)
);
CREATE TABLE daily_top_themes (
id INTEGER PRIMARY KEY AUTOINCREMENT,
trade_date TEXT NOT NULL,
theme_code TEXT NOT NULL,
theme_name TEXT NOT NULL,
bf3 REAL, -- 题材涨幅%
hot_rank INTEGER, -- 热度排名
rank INTEGER, -- 当日板块涨幅排名 1-10
created_at TEXT DEFAULT (datetime('now','localtime')),
UNIQUE(trade_date, theme_code)
);
```
## 4. 采集服务 `backend/services/daily_collector.py`
### 4.1 触发机制(方案 A
- `lifespan` 启动时拉起一个 asyncio 后台任务协程。
- 协程循环(如每 5 分钟检查一次):
- 是否**交易日**(周一至周五,非节假日)。
- 是否**收盘后**(北京时间 > 15:00)。
- 当日数据是否**已采集**(按 `trade_date` 查库,幂等去重)。
- 条件满足 → 触发采集。
### 4.2 采集逻辑
1. **核心股前 100**:调用 `fetch_theme_list(1, False)``fetch_theme_graph` 得到当日全部股票,按 `f3` 降序取前 100。若不足 100 只,存实际数量。
2. **所属题材**:以热点穿透采样的 `themeCodes` 为主存入 `daily_core_stock_themes`
3. **题材前 10**:从 `fetch_theme_list(1, False)``bf3` 降序前 10,连同 `themeName`/`hotRank` 存入 `daily_top_themes`
4. 同一交易日重复触发不重复写入(UNIQUE 去重 + 检查)。
5. 采集失败(东财 403/网络)→ 跳过当日,下轮重试;记录日志。
### 4.3 补全(折中方案)
- 东财压力允许时,对核心股调用个股题材接口尽力补全。
- 优先保证核心股前 100 与题材前 10 的完整性,补全为附加增强,失败不影响主流程。
## 5. 新接口(`backend/routes/`
| 接口 | 说明 |
|---|---|
| `GET /api/core-stocks/active` | 活跃核心股 + 最近 10 日涨幅矩阵 |
| `GET /api/core-stocks/history?date=` | 指定交易日的核心股(含所属题材) |
| `GET /api/themes/history?date=` | 指定交易日的题材前 10 |
### active 接口返回结构
```json
{
"dates": ["2026-08-03", "...", "2026-08-10"],
"stocks": [
{
"stockCode": "601606",
"stockName": "长城军工",
"coverCount": 9,
"lastAppear": "2026-08-10",
"daysSinceLastAppear": 0,
"appearCount": 5,
"dailyGains": { "2026-08-03": 10.0, "2026-08-10": 10.0 }
}
]
}
```
- `dates`:最近 10 个 A 股交易日(升序,最右为最新)。
- `stocks`10 日窗口内出现过的活跃核心股。
- `dailyGains`:日期 → 当日涨幅;未上榜日无该键。
## 6. 新展示页(前端 `/core-stocks`
### 6.1 页面结构
- 顶栏:返回、标题「核心股追踪」、刷新按钮(与 `/hot-map` 一致风格)。
- 表格:**股票 × 最近 10 个 A 股交易日**列矩阵。
- 行:活跃核心股(10 个 A 股交易日内出现过),按 `appearCount` 降序、`lastAppear` 降序排列。
- 列:最近 10 个 A 股交易日,最右为最新。
- 单元格:当日涨幅(红涨绿跌,A 股惯例);未上榜留空(`·`)。
- 每只股票显示累计出现次数、最近上榜日期、所属题材数。
### 6.2 数据获取
- 前端 `useQuery``GET /api/core-stocks/active`
- `staleTime` 与题材页一致(30s 或 60s),盘中可手动刷新。
### 6.3 路由
- 新建 `src/routes/core-stocks.tsx`,路由 `/core-stocks`
- 从题材页 `/themes` 和热点穿透页 `/hot-map` 顶部加入口链接。
## 7. 错误处理与边界
- **东财 403/采集失败**:跳过当日采集,下轮重试;不影响已存历史。
- **当日重复采集**:UNIQUE 约束 + 入库前检查,幂等。
- **核心股不足 100**:存实际数量,不补齐。
- **无历史数据**:部署后开始累积;active 接口在无数据时返回空 `stocks` 与空 `dates`
- **交易日历**:以自然周一到周五判定交易日(不处理法定节假日调休的深度历法),与现有 `_is_trading_time` 一致。
## 8. 测试
- 采集服务:幂等(重复触发不重复写)、去重、失败重试逻辑。
- active 接口:窗口计算、踢出规则(>10 个交易日未出现不返回)、涨幅矩阵正确性。
- 前端页面:空态、有数据态、10 日窗口滚动。
## 9. 范围外(YAGNI
- 不做法定的深度交易日历(节假日调休)。
- 不做个股涨幅的增量更新(历史数据一次性采集,之后不补更)。
- 不做板块成分股的每日存储。