# 切片 04：市场天梯与板块轮动

> 基线：`b3555d2`（切片 03）
> 回档标签：`xiaobai-preservation-slice-04-20260731`
> 结论：源码、API、真实页面和浏览器回归通过；最终视觉仍等待全站人工验收

## 1. 原实现归位

本切片从原版副本机械移动板块轮动服务，没有从 `next/` 取用代码，也没有修改天梯、轮动的计算、
排序、展开、配色、页面结构或交互。

| 原位置 | 新的唯一实现位置 | 原位置兼容 |
|---|---|---|
| `app/backend/application.py` 的 2 个轮动方法 | `app/backend/features/rotation/service.py` | `DashboardService` 继承 `RotationServiceMixin` |
| Tushare Provider 的天梯与轮动构造函数 | 保持 `app/backend/data/providers/tushare_client.py` | 切片 02 已归位的公共数据实现 |

市场天梯没有独立后端 API 或第二套计算，直接展示 `/api/dashboard` 中原 Tushare 实现生成的
`ladders`；因此没有为目录形式建立空的天梯服务。

## 2. 等价证据

- `test_preservation_slice_ladder_rotation.py` 对 2 个轮动服务方法逐项执行无位置信息 AST 比较，
  全部与根目录原版 `server.py` 完全相同。
- `_build_ladders` 与 `_build_sector_rotation` 两个原数据构造函数的 AST 与根目录原版完全相同。
- 原版 `8784` 与迁移版 `8785` 在相同账号、日期和数据库副本上返回的天梯数据及 9 日轮动历史
  JSON 逐字段完全相同。
- 成分股接口在当前外部网络状态下两版均返回 HTTP 400、`bad_request` 和相同的
  `该板块成分股暂不可用：Tushare request failed:`，没有改变错误或增加静默降级。
- `config/api.config.json`、API 路径、鉴权、数据库 schema 和 `app/static/` 未修改。

## 3. 真实运行检查

- 市场天梯：8 个层级（含断层）、18 个首屏股票单元格、3 个结构分析模块正常；1920×1080 下
  页面宽度无溢出，首板展开入口保留。
- 板块轮动：9 个交易日、每日 Top 12 共 108 个板块单元格、由远到近/由近到远两个排序入口正常；
  1920×1080 下页面宽度无溢出并保持全页滚动。
- 日间模式页面控制台没有错误或警告。
- `app-light-ladder-1920x1080.png` SHA-256：
  `9e57d18d92e745fd92131f7bf08f21faaaa745476dd942cdaa2a703b9a7a303a`。
- `app-light-rotation-1920x1080.png` SHA-256：
  `03c092bb40bd0eb672136dff853abffc87e9790840107c2f22e6cf31d5f83c09`。

## 4. 自动验证

| 验证 | 结果 |
|---|---:|
| `python -m unittest discover -s tests -q` | 252 项通过 |
| `python -m unittest tests.test_preservation_slice_ladder_rotation -q` | 4 项通过 |
| 切片 02 至 04 与总览缓存专项集合 | 24 项通过 |
| `npx.cmd playwright test --reporter=dot` | 45 项通过 |
| `git diff --check` | 通过 |

## 5. 保留边界

- 成分股接口依赖的日行情与因子持久化方法仍由原 `ReviewDatabase` 提供，因其同时服务智能选股，
  待切片 06 随完整共享职责归位。
- 天梯和轮动前端资产保持原位，切片 10 再按页面职责归档；当前没有复制或改写。
- 没有删除待定代码、没有改动根目录正式数据库、没有切换 Docker/NAS。
