From 5a731c4442022da6f2d0b9aaac4bec599ec05175 Mon Sep 17 00:00:00 2001 From: hjhan Date: Fri, 26 Jun 2026 13:41:32 +0800 Subject: [PATCH] =?UTF-8?q?docs(swap):=20MarkClosePnl=E5=AD=97=E6=AE=B5?= =?UTF-8?q?=E8=AF=AD=E4=B9=89=E7=BB=9F=E4=B8=80=E5=86=B3=E7=AD=96=E6=96=87?= =?UTF-8?q?=E6=A1=A3?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit 修复ab70531f让MarkClosePnl不再含分红后,梳理出消费端3处PosiPnl公式 不一致(①不减分红/②③减分红,2024~2026分批引入),其中②③对新数据 会多减一份分红导致结算单/收益偏小。 给出3种改法权衡(修正公式/数据迁移/TotalClosePnl分离)+建议分阶段路径, 列出4个需团队决策的问题。含字段引用全景表便于实施定位。 --- 项目文档/MarkClosePnl字段语义统一决策文档.md | 159 +++++++++++++++++++ 1 file changed, 159 insertions(+) create mode 100644 项目文档/MarkClosePnl字段语义统一决策文档.md diff --git a/项目文档/MarkClosePnl字段语义统一决策文档.md b/项目文档/MarkClosePnl字段语义统一决策文档.md new file mode 100644 index 00000000..363eb618 --- /dev/null +++ b/项目文档/MarkClosePnl字段语义统一决策文档.md @@ -0,0 +1,159 @@ +# MarkClosePnl 字段语义统一:决策文档 + +> **状态**:待团队决策 | **日期**:2026-06-26 | **前置**:[互换分红损益字段语义与重复计算分析.md](./互换分红损益字段语义与重复计算分析.md)、修复提交 `ab70531f`(2026-06-26,MarkClosePnl 不再含分红) + +--- + +## 一、问题背景:一个字段,三种语义 + +`swap_flow_event.MarkClosePnl`(浮动端平仓盈亏·盯市)在历史上**同时承担了三个互斥的职责**,导致各处使用公式不一致。2026-06-26 的修复(`ab70531f`)让它"不再含分红",但**消费端的公式没有同步调整**,新数据下会产生新的偏差。 + +### MarkClosePnl 的三种历史语义 + +| 语义 | 含义 | 谁在用 | +|---|---|---| +| **A. 纯价差盯市** | `MarkClosePnl = (平仓价-成本价)×量` | 修复后(`ab70531f`)的目标语义 | +| **B. 价差+费+分红 总和** | 历史上前端 `calcFloatClosePnl` 把费和分红加进去 | 界面"浮动"列(用户误以为是总盈亏)| +| **C. 收益计算基数** | 各处用不同公式减/不减分红得到 PosiPnl | 结算单、收益列表 | + +**根因**:2024-05-09 从山证 v2.3.0 拷贝(`f9d8a256`)时,`MarkClosePnl` 入库的就是 B(总和),但后续不同开发者按各自理解写了 C 的多种公式,造成不一致。 + +--- + +## 二、各处使用现状与不一致清单(含引入时间) + +### 2.1 写入点(MarkClosePnl 怎么生成) + +| 位置 | 公式 | 引入 | 语义 | +|---|---|---|---| +| `SwapDealService.cs:1101` 自动平仓 | `(平仓价-成本价)×量` | `f9d8a256` 2024-05-09 | **修复后=纯价差** | +| `SwapDealService.cs:1103` 自动平仓(加费) | 上面 `+费×-1` | `f9d8a256` | 修复后=价差-费 | +| `SwapEodPositionService.cs:463` 自动互换分红 | `=0`(修复前=`PosiDividendSum`)| `ab70531f` 2026-06-26 | **修复后=0(不含分红)** | +| 前端 `incomeSwapTrade.js:133` 手动互换 | `calcFloatClosePnl` 去 `+DividendIn` | `ab70531f` | **修复后=纯价差** | +| 前端 `unwindSwapTrade.js:203` 手动平仓 | 同上 | `ab70531f` | **修复后=纯价差** | + +> **关键**:`ab70531f` 已把 4 个写入点统一为"纯价差/价差-费",不含分红。 + +### 2.2 消费点(PosiPnl / 收益公式)—— ⚠ 不一致集中区 + +| # | 位置 | 公式 | 减分红? | 引入 | 修复后影响 | +|---|---|---|---|---|---| +| ① | `SwapFlowEvent.cs:384` | `PosiPnl = MarkClosePnl - TradingFee` | **否** | `f9d8a256` 2024-05-09 | 新数据偏大(没减分红)| +| ② | `TradeSettleBillGenerator.cs:110` 结算单 | `PosiPnl = -(MarkClosePnl - tradingFee - DividendIn)` | **是** | `48cf0592` 2026-05-06 | **新数据多减一份分红** ⚠ | +| ③ | `SwapFlowEventService.cs:528` 收益列表 | `PosiPnl = MarkClosePnl - TradeFee - DividendIn - TradingFee` | **是** | `829334a4` 2025-05-30 | **新数据多减一份分红** ⚠ | + +**核心风险(②③)**:修复前 MarkClosePnl 含分红,减去 DividendIn 正好还原纯价差;修复后 MarkClosePnl **不含**分红了,再减 DividendIn 就**多减了一份**,结算单和收益列表金额偏小。 + +### 2.3 当"总和"用的地方 + +| 位置 | 公式 | 引入 | 含义 | +|---|---|---|---| +| `SwapEodPositionService.cs:681` | `amount = MarkClosePnl + CloseFee + DividendIn` | `f9d8a256` 2024-05-09 | 判是否产生资金记录(三者总和)| +| `SwapFlowEventService.cs:529` | `NetSettmentAmount = MarkClosePnl + InterestClosePnL` | `083848fb` 2025-05-28 | 净结算额 | +| `SwapEodPositionService.cs:1610` | `TdCloseMtmPnl = Σ MarkClosePnl` | `f9d8a256` 2024-05-09 | eod 盯市列累加 | + +### 2.4 界面展示(eventlist/SwapflowList/EodPositionRisks) + +"浮动端平仓盈亏"在界面**拆成 3 个独立列**,没有"总和"列: +- `MarkClosePnl` → 列名"浮动端平仓盈亏·**浮动**" +- `DividendIn` → 列名"浮动端平仓盈亏·**分红**" +- `CloseFee` → 列名"浮动端平仓盈亏·**费用**" + +> 用户历史上看到的"浮动列"= 总盈亏,是 buggy 的 MarkClosePnl 凑出来的;修复后该列=纯价差,会变小。**"三者总和"从未有独立列**。 + +--- + +## 三、历史数据问题 + +已入库的 `swap_flow_event.MarkClosePnl` 不可逆——**历史记录含分红,新记录不含**。这意味着任何消费点公式,对历史和新数据只能选一种正确: + +| 公式策略 | 历史数据(含分红) | 新数据(纯价差) | +|---|---|---| +| 不减 DividendIn(如 ①)| 偏大 | 偏大 | +| 减 DividendIn(如 ②③)| 正确(抵消)| **偏小(多减)** | + +**没有一种公式能同时让历史和新数据都对**,除非: +- (a) 数据迁移:刷新历史 MarkClosePnl 还原为纯价差,或 +- (b) 加版本标记:区分"修复前/后"的事件,分别用不同公式 + +--- + +## 四、三种改法方案(供团队选择) + +### 方案一:修正消费点公式(最小改动,推荐作为第一步) + +**做法**:把 MarkClosePnl 的语义**正式定为"纯价差盯市"**,修正 ②③ 去掉 `-DividendIn`: + +| 位置 | 改动 | +|---|---| +| ② `TradeSettleBillGenerator.cs:110` | `-(MarkClosePnl - tradingFee)`(去掉 -DividendIn)| +| ③ `SwapFlowEventService.cs:528` | `MarkClosePnl - TradeFee - TradingFee`(去掉 -DividendIn)| +| ① `SwapFlowEvent.cs:384` | 保持,或补 `-DividendIn` 与 ③ 统一(需业务确认收益口径)| + +- ✅ **优点**:改动小(3 行),新数据全部正确,语义立即清晰 +- ⚠ **缺点**:**历史数据仍不一致**(见第三节),需配合数据迁移或接受历史偏差 +- 🎯 **适合**:快速止血,让修复(ab70531f)真正生效,不让新数据出错 + +### 方案二:数据迁移 + 方案一(彻底统一) + +**做法**:在方案一基础上,对历史 `swap_flow_event` 执行数据迁移: + +```sql +-- 把历史 MarkClosePnl 还原为纯价差(减去当时混入的分红和费) +UPDATE swap_flow_event +SET MarkClosePnl = MarkClosePnl - DividendIn - CloseFee +WHERE EventType IN (2,3,4) AND DataState = 100 + AND EventDate < '2026-06-26'; -- 修复提交前的事件 +``` +(实际迁移需先 SELECT 验证范围、备份、分批执行) + +- ✅ **优点**:历史与新数据语义完全统一,所有公式一套即可 +- ⚠ **缺点**:改库有风险,需备份+回滚预案;eod 历史表(RealizedMtmPnL 等)是否也需重算需评估 +- 🎯 **适合**:团队接受改历史数据,追求长期干净 + +### 方案三:新增 TotalClosePnl 字段,职责分离(最长远正确) + +**做法**:让每个字段职责单一: +1. `MarkClosePnl` 永远=纯价差(修复已做到,语义固定) +2. 新增 `[NotMapped] decimal TotalClosePnl => MarkClosePnl + DividendIn + CloseFee`,专给"总和"场景(结算单、资金记录判断、界面总和列) +3. 界面如需"总盈亏"列,显式绑定 `TotalClosePnl`,而非靠 MarkClosePnl 隐式承担 + +- ✅ **优点**:根除"一字段三语义"债务,未来不会再出现"改一处引发另一处偏差" +- ⚠ **缺点**:改动面最大(涉及结算单/资金记录/界面多处),需排期 +- 🎯 **适合**:作为技术债治理的长期目标,可与方案一/二分阶段实施 + +--- + +## 五、建议路径 + +**推荐分阶段推进**,兼顾止血与长期: + +1. **立即(方案一)**:修正 ②③ 消费点公式,避免修复后新数据出错(结算单/收益金额偏小)。这是 `ab70531f` 修复的必要补全,不做的话修复只做了一半。 +2. **近期(方案二)**:评估数据迁移可行性,统一历史数据语义。 +3. **中期(方案三)**:引入 TotalClosePnl,彻底消除语义债务。 + +--- + +## 六、需团队决策的问题 + +1. **收益(PosiPnl)口径**:① 不减分红、②③ 减分红——哪个是业务正确的"收益"定义?(决定公式统一方向) +2. **历史数据**:是否接受数据迁移(方案二)?还是接受历史偏差、只保证新数据正确? +3. **界面"总盈亏"列**:是否需要显式新增一列展示 `MarkClosePnl+DividendIn+CloseFee` 总和?(决定是否走方案三) +4. **实施优先级**:方案一是否立即执行(它阻塞 ab70531f 修复的完整生效)? + +--- + +## 附录:字段引用全景(便于实施时定位) + +| 文件:行 | 用法 | 语义 | +|---|---|---| +| `SwapFlowEvent.cs:384` | `PosiPnl = MarkClosePnl - TradingFee` | 收益(不减分红) | +| `SwapFlowEvent.cs:211` | 字段定义 | 入库列 | +| `TradeSettleBillGenerator.cs:110,112,116` | 减 DividendIn / 取负 / 净结算 | 收益+结算 | +| `SwapFlowEventService.cs:287,378,429,509,528,529` | 赋值/取负/收益/净结算 | 多用途 | +| `SwapEodPositionService.cs:463,681,694,1610` | 赋0/总和/累加/eod盯市 | 写入+累加 | +| `SwapDealService.cs:101,275,1101,1103,1219,1350` | 赋值 | 写入 | +| `SwapEndConfirmService.cs:96` | `SwapCloseAmount = MarkClosePnl` | 展示 | +| `RealTimeClientBanlanceService.cs:1337,1343` | `+ MarkClosePnl` 累加 | 收益累加 | +| `SwapFlowService.cs:381` | 导出格式化 | 导出 | +| 前端 `eventlist.js:247`/`SwapflowList.js:556` | 界面"浮动"列 | 展示 |