feat(market): 存下行情源给的涨跌幅,产品列表与排行页不再显示"暂无"

## 问题

产品列表与排行页的「最近涨跌」全是"暂无"。原因是它只能由**两个交易日的收盘价**
现算,而 `fin_market_price` 每只产品每天只有一行 —— 库里头一天只有一天数据时
根本算不出来。

## 但行情源本来就给了这个数

腾讯行情接口的第 32 位就是**当日涨跌幅**(适配器此前只解析了开高低收量额,没取它)。
所以不必等第二个交易日去算,把源给的数存下来即可。

## 改动

- **迁移** `20260913_market_price_change_pct`:给 `fin_market_price` **新增一个可空列**
  `change_pct DECIMAL(10,4)`。只加列、不改任何已有字段(AGENTS.md 规则 2 允许),
  可空且不回填,既有读写方全部不受影响。
- **适配器**:`_parse_tencent` 增解析位置 4(昨收)与位置 32(涨跌幅)。
- **同步服务**:写入 `change_pct`;降级路径(净值兜底)没有这个数就写 **NULL**。
- **接口**:`_resolve_change_pct` **优先用存下来的值**;迁移前的历史行没有该列的值,
  才回退到"今日收盘 vs 昨收"现算。仍为 `null` 时前端显示"暂无" ——
  **不得当成 0**(`formatPercent(null)` 会渲染成 `+0.00%`,那等于说"今天平盘")。

## 契约测试同步

两处契约断言因结构演进需要跟进 —— 它们的作用正是拦住这类变更:

- `test_fund_readonly_contract.py`:`fin_market_price` 列数 **13 → 14**
- `test_advisor_migration_contract.py`:迁移 head 更新为新版本
  (核心断言仍是"链收敛到唯一 head",此处只是钉住末端)

验证:实测 20 只产品**全部有真实涨跌幅**(如 515450 −0.43%、159511 +1.23%);
unit+contract **1397 passed**;integration **110 passed**;ruff 通过;
mypy 251 文件 0 错;`audit_schema` 与 `migration_state_check` 均通过;e2e 冒烟 40/40。
This commit is contained in:
2026-09-14 00:30:06 +08:00
parent de55c5c60c
commit 9a5259d787
9 changed files with 79 additions and 11 deletions
+4 -1
View File
@@ -89,9 +89,12 @@ python -m app.worker
> (如把海富通的 `511360` 标成"南方短融ETF"),净值也是编的。该文件已删除。
>
> **两条不能想当然的口径**(改前端前先读):
> 1. `change_pct` **可能是 `null`**(行情只同步过一个交易日时算不出涨跌)。
> 1. `change_pct` **可能是 `null`**(该行由净值降级路径写入、源没给涨跌幅,或行情未同步)。
> 页面必须显示"暂无"—— `formatPercent` 收到 `null` 会渲染成 `+0.00%`,
> 那等于告诉客户"今天平盘"。
> **2026-09-13 起已正常有值**:行情源(腾讯)直接返回涨跌幅,由
> `20260913_market_price_change_pct` 迁移新增的 `fin_market_price.change_pct` 存下,
> 同步脚本写入;接口优先用它,历史行才回退到"今日收盘 vs 昨收"现算。
> 2. **历史净值走势图**:`fin_nav_history` 由 `tools/sync_nav_history.py` 同步
> (东财净值接口,近 120 个交易日,实测 20 只 / 2513 行)。
> **表为空时不画曲线**、显式显示"尚未接入",绝不回退到编造点位 ——
+4 -2
View File
@@ -86,8 +86,10 @@ python tools/e2e_smoke_test.py --read-only
> 行情由 `tools/sync_market_prices.py` 从行情源同步,页面底部有来源声明。
>
> ⚠️ 两个**可能被问到、但其实不是故障**的地方:
> - 产品列表的「最近涨跌」列当前显示 **"暂无"** —— 当日涨跌要**两个交易日**的收盘价才算得出,
> 而行情库目前只有一个交易日。跑过第二次行情同步后自然会显示。我们**不编这个数**。
> - 「最近涨跌」列取的是**行情源直接给出的当日涨跌幅**(腾讯返回,存在
> `fin_market_price.change_pct`),已接入。若某只显示"暂无",说明它走的是净值降级
> 路径(源没给涨跌幅),或行情没同步过 —— 跑一次 `sync_market_prices.py` 即可。
> 我们**不编**这个数:缺数据就显示"暂无",而不是 0(0 会被读成"今天平盘")。
> - 产品详情的**净值走势图现在是真实数据**:`fin_nav_history` 由
> `tools/sync_nav_history.py`(演示数据第 5 步)从东财净值接口同步,近 **120 个交易日**。
> 若图上显示"尚未接入",说明第 5 步没跑 —— 补跑即可,不用重启服务。