+```
+
+格式器定义(swapTradeEdit.js,仅配置开关):
+- `inputFormatMarginRateNoPercent`:`percent: true`(注意:名字里的 "NoPercent" 指"显示时不带%号",而非"不做换算",容易误解)
+- `inputFormatTradeSinglePrice`:`percent: false`
+
+**÷100 的真正实现**(`statics/bundles/bundle.js` 的 `FastVue.parseNumber`):
+```js
+FastVue.parseNumber = function (val, percent) {
+ // ...
+ let isPercent = val.endsWith('%') || percent == true; // format.percent=true 时
+ let number = new Number(val.replace(/%+$/, ''));
+ return isPercent ? number / 100 : number; // ← ÷100 在这里执行
+};
+```
+即:组件本身基于第三方库(打包进 bundle.js),但 `percent:true` 时 `number / 100` 的换算是项目自定义的 `FastVue.parseNumber` 实现的,不是第三方库原生行为。
+
+### 2. 展示(TradeView.cshtml)
+
+互换 Tab(浮动收益端)对期初/期末价的处理,**依赖 `UnderlyingInstrumentType` 判断是否债券**:
+
+```cshtml
+@{
+ // 关键: multiplier 由标的类型决定, 债券=100, 非债券=1
+ var multiplier = ConsGlobal.InstrumentType.IsBond(closeFloat.UnderlyingInstrumentType) ? 100 : 1;
+}
+@if (isBond) {
+ | @((closeFloat.PosiGrossPrice * multiplier).OtcFormat(OtcFormatFlag.umprice)) |
+ @((closeFloat.TradingAmountAvg * multiplier).OtcFormat(OtcFormatFlag.umprice)) |
+} else {
+ @(closeFloat.PosiGrossPrice.OtcFormat(OtcFormatFlag.umprice)) |
+ @(closeFloat.TradingAmountAvg.OtcFormat(OtcFormatFlag.umprice)) |
+}
+```
+
+> 🔑 **`UnderlyingInstrumentType` 是展示层判断的关键**。所有生成 `swap_flow_event` 的地方都必须赋值这个字段,否则 `IsBond` 判断失败,债券标的会走非债券分支(不 ×100),导致显示成相对价(1.00 而非 100.00)。
+
+### 3. 收益结算页(SwapIncome.cshtml + incomeSwapTrade.js)
+
+前端 `priceFormat()` 同样 `× multiplier`:
+
+```js
+priceFormat(price) {
+ price = price * this.multiplier; // 债券类 ×100
+ return otcformat.trading.umprice(price);
+}
+```
+
+---
+
+## 五、互换事件的生成路径与价格赋值
+
+互换 Tab 会按时间序列展示所有互换事件(EventType=互换/自动互换),手动与自动用**同一套渲染代码**,所以入库基准必须一致。
+
+### 手动互换(EventType=3)
+- 入口:`SwapDealService.SwapUnwind` / `InitIncome`
+- 价格赋值:`floatEvent.TradingAmountAvg = unwindPrice`(前端用户输入,已 ÷100)
+- **已赋值 `UnderlyingInstrumentType`**(来自 position)
+
+### 自动互换-分红型(EventType=4)⚠️ 重点
+- 入口:`SwapEodPositionService.DealDividends`(`PosiDividendSum != 0` 触发)
+- 价格赋值:`TradingAmountAvg = eodPosi.PosiNetPrice`(相对价,来自 eod 持仓)
+- **必须赋值 `UnderlyingInstrumentType` 和 `TradingAmountNetAvg`**(曾经遗漏导致 Bug)
+
+### 自动互换-利息端(EventType=4)
+- 入口:`DealAutoInterests` → `SaveAutoEodWithCloseInterestPosition`
+- `UnderlyingCode` 为空(利息腿),不出现在互换 Tab 浮动端
+
+---
+
+## 六、已修复的 Bug 与教训
+
+| Bug | 根因 | 修复提交 |
+|-----|------|---------|
+| 收益结算期末价缩放/显示错误 | `incomeSwapTrade.js` initDeal 合并冲突丢 ×multiplier | 已修 |
+| 收益结算期初净价显示 0 | `PosiNetNoFeePrice` 可空,为 0 时未回退 `PosiNetPrice` | 已修 |
+| 收益结算期末价不可编辑/无刷新图标 | `ab09cbc9` 误删输入框与方法 | 已修 |
+| **分红型自动互换显示未 ×100** | `DealDividends` 漏赋 `UnderlyingInstrumentType` | 已修(d9c5ce4c)|
+| **分红型自动互换期初净价显示 0** | `DealDividends` 漏赋 `TradingAmountNetAvg` | 已修(d9c5ce4c)|
+
+### 教训
+
+1. **生成 `swap_flow_event` 时必须赋全字段**,尤其是 `UnderlyingInstrumentType`——它是展示层判断债券/非债券(决定 ×100)的关键,遗漏会导致显示数量级错误。
+2. **手动互换与自动互换共用同一套展示代码**,入库基准(相对价)必须保持一致。
+3. **字段命名误导**:`TradingAmountNetAvg` 名字像"期末",但收益结算初始化时装的是期初净价,改代码时务必核对实际数据流向。
+4. **可空字段要兜底**:`PosiNetNoFeePrice`、`TradingAmountNetAvg` 可空,展示时为 0/空要有回退值。
+
+---
+
+## 七、历史脏数据说明
+
+排查过程中发现,测试环境里同一笔手动互换的 `swap_flow_event` 可能存在多条记录,`TradingAmountAvg` 取值不一致(有的相对价如 1.0011,有的被 ÷100 如 0.010011),成交金额相应相差 100 倍。这是早期代码版本更迭残留的脏数据。
+
+判断脏数据的依据:成交金额 `TradingAmount = TradingAmountAvg × Quantity × ContractSize`(后端 `SwapDealService:1103` 计算)。若同一交易的两条互换记录成交金额相差正好 100 倍,则其中一条的 `TradingAmountAvg` 被多 ÷100。
+
+这类历史数据问题用 SQL 单独修复,**不应在代码里迁就脏数据而保留错误的逻辑**。排查与修复 SQL 见文末附录。
+
+---
+
+## 附录 A:排查工具箱 SQL
+
+> 以下 SQL 是 2026-06 价格显示 Bug 排查过程中验证有效的查询,数据库为 MySQL,库名 `glms_yltrs_ylcms`(ylcms 连接)。后续遇到类似"显示数量级不对"问题可直接复用。
+
+### A.1 查互换交易期初持仓的 4 个价格字段(确认入库基准)
+
+```sql
+SELECT
+ t.TradeNumber AS trade_no,
+ t.StructureType AS 结构类型,
+ sp.UnderlyingCode AS 标的代码,
+ sp.PosiGrossPrice AS 期初全价,
+ sp.PosiNetPrice AS 期初净价,
+ sp.PosiNetNoFeePrice AS 期初净价不含费,
+ sp.PosiNetFeePrice AS 期初净价含费
+FROM trade t
+JOIN swap_position sp ON sp.SwapTradeId = t.id
+ AND sp.IsInitial = 1
+ AND sp.UnderlyingCode IS NOT NULL -- 过滤利息腿/预付金腿
+WHERE t.TradeType = '收益互换'
+ AND t.StructureType IN ('普通债券类收益互换', '普通收益互换')
+ AND t.ValidState = 'valid'
+ORDER BY t.id DESC
+LIMIT 20;
+```
+
+### A.2 查互换/自动互换事件入库值(区分手动/自动、用成交金额反推正确基准)
+
+⚠️ `swap_flow_event` 表的 `PosiGrossPrice`/`PosiNetPrice` 是 `[NotMapped]`,**数据库查不到这两列**;只能查真正入库的 `TradingAmountAvg`、`TradingAmountNetAvg`、`TradingAmount`。
+
+```sql
+SELECT
+ t.TradeNumber AS trade_no,
+ fe.id AS 事件ID,
+ fe.EventType AS 事件类型,
+ CASE fe.EventType WHEN 3 THEN '手动互换' WHEN 4 THEN '自动互换' END AS 类型,
+ fe.EventReason AS 事件原因,
+ fe.EventDate AS 业务日期,
+ fe.UnderlyingCode AS 标的,
+ fe.PayDirection AS 收支方向,
+ fe.PositionType AS 多空方向,
+ fe.UnderlyingInstrumentType AS 标的类型_判断债券用,
+ fe.TradingAmountAvg AS 期末全价_入库,
+ fe.TradingAmountNetAvg AS 期末净价_入库,
+ fe.TradingAmount AS 成交金额,
+ fe.DividendIn AS 分红金额
+FROM trade t
+JOIN swap_flow_event fe ON fe.SwapTradeId = t.id
+WHERE t.id IN (/*填trade id*/)
+ AND t.ValidState = 'valid'
+ AND fe.EventType IN (3, 4)
+ AND fe.UnderlyingCode IS NOT NULL -- 只要浮动端事件,排除利息腿
+ORDER BY t.id, fe.EventDate DESC, fe.id DESC
+LIMIT 30;
+```
+
+**判断要点**:
+- `UnderlyingInstrumentType` 为空 → 该事件的展示会走"非债券分支(不×100)",债券标的显示会错(这就是自动互换的 Bug 根因)。
+- 同一交易同一腿出现 `TradingAmountAvg` 相差 100 倍的两条记录 → 其中一条是被多 ÷100 的脏数据。
+
+### A.3 查分红型自动互换的数据源(eod_swap_position,确认 PosiDividendSum 触发条件)
+
+`PosiDividendSum != 0` 时触发 `DealDividends` 生成自动互换事件。注意测试数据可能为 0 查不到,需去掉该条件排查。
+
+```sql
+SELECT
+ esp.SwapTradeId AS trade_id,
+ esp.ValueDate AS 持仓日期,
+ esp.UnderlyingCode AS 标的代码,
+ esp.UnderlyingInstrumentType AS 标的类型,
+ esp.PosiDividendSum AS 浮动端未实现分红_触发条件,
+ esp.TdPosiDividend AS 当日浮动端分红,
+ esp.PosiNetPrice AS 持仓净价_自动互换TradingAmountAvg来源,
+ esp.PosiGrossPrice AS 持仓全价,
+ esp.PosiNetNoFeePrice AS 持仓净价不含费_期初净价来源
+FROM eod_swap_position esp
+WHERE esp.SwapTradeId IN (/*填trade id*/)
+ORDER BY esp.ValueDate DESC
+LIMIT 20;
+```
+
+### A.4 查中债估值数据源(确认录入时默认读取的价格)
+
+簿记交易时标的默认价格来自 `china_bond_valuation`(中债估值)表。⚠️ **该表的字段命名与业务常用名差异很大**,特别注意:债券代码列是 `bond_id`(不是 UnderlyingCode),价格列是 `dirty_price_close`(日终估值全价)、`net_price`(估价净值)、`yield`(收益率%)、`vobp`(基点价值)。
+
+```sql
+SELECT
+ cbv.bond_id AS 债券代码,
+ cbv.valuation_date AS 估值日期,
+ cbv.dirty_price_close AS 日终估值全价, -- 录入默认读取的"全价"来源
+ cbv.net_price AS 估价净值,
+ cbv.yield AS 估价收益率_pct,
+ cbv.vobp AS 估价基点价值, -- DV01 计算用此字段
+ cbv.acc_rint_close AS 日终应计利息,
+ cbv.credibility AS 可信度 -- GetBondPrice 按可信度降序取最新
+FROM china_bond_valuation cbv
+WHERE cbv.bond_id = '180205.IB'
+ORDER BY cbv.credibility DESC, cbv.valuation_date DESC
+LIMIT 10;
+```
+
+> 后端 `EodPriceQueryService.GetBondPrice` 取数逻辑:按 `bond_id` 匹配,`valuation_date <= 指定日`,按 `credibility` 降序、`valuation_date` 降序取第一条。返回的 `ClosePrice = dirty_price_close × bondPriceMultiple`(默认乘数通常为 1)。测试环境可能被维护成离谱值(如全价 1999)。
+
+### A.5 历史脏数据修复(手动互换被多 ÷100 的记录)
+
+⚠️ **执行前务必先用 A.2 核对,并备份**。以下仅作为模板,需根据实际范围调整 WHERE:
+
+```sql
+-- 备份
+CREATE TABLE swap_flow_event_bak_yyyymmdd AS
+SELECT * FROM swap_flow_event WHERE /*同下方WHERE条件*/;
+
+-- 修复: 被多÷100的TradingAmountAvg ×100还原, 成交金额同步×100
+UPDATE swap_flow_event fe
+JOIN trade t ON t.id = fe.SwapTradeId
+SET fe.TradingAmountAvg = fe.TradingAmountAvg * 100,
+ fe.TradingAmount = fe.TradingAmount * 100
+WHERE t.StructureType = '普通债券类收益互换'
+ AND fe.EventType = 3 -- 手动互换
+ AND fe.UnderlyingCode IS NOT NULL
+ AND fe.TradingAmountAvg > 0
+ AND fe.TradingAmountAvg < 0.5; -- 相对价合理范围判断(债券相对价通常0.5~2)
+```
+
+---
+
+## 附录 B:字段命名与数据流向速查
+
+容易混淆的字段对应关系(排查时务必核对实际数据流向,不要被字段名误导):
+
+| 界面列(表头) | 取值字段 | 字段所在表 | 实际数据来源 | 备注 |
+|---|---|---|---|---|
+| 期初标的交割全价% | `PosiGrossPrice` | swap_position(入库)/ swap_flow_event([NotMapped]运行时拷贝) | `swap_position.PosiGrossPrice` | 相对价,展示×100 |
+| 期初标的交割净价% | `TradingAmountNetAvg` | swap_flow_event(入库) | `swap_position.PosiNetNoFeePrice`(收益结算初始化时) | **字段名像期末,实装期初值**;可空需兜底 |
+| 期末标的交割全价% | `TradingAmountAvg` | swap_flow_event(入库) | 手动互换=用户输入;自动互换=`eod.PosiNetPrice` | 相对价,展示×100 |
+| 标的类型判断 | `UnderlyingInstrumentType` | swap_flow_event(入库) | 生成事件时赋值 | **展示层判断债券(×100)的关键,必须赋值** |
+| 成交金额 | `TradingAmount` | swap_flow_event(入库) | `TradingAmountAvg × Quantity × ContractSize` | 反推正确基准的依据 |
+
+**关键提醒**:`swap_flow_event.PosiGrossPrice` 和 `PosiNetPrice` 是 `[NotMapped]`,数据库**没有这两列**,查库会报 `Unknown column`。它们只在运行时从 `swap_position` 拷贝过来用于展示。