Files
zszq-trs/项目文档/MarkClosePnl字段语义统一决策文档.md
T
hjhan 5a731c4442 docs(swap): MarkClosePnl字段语义统一决策文档
修复ab70531f让MarkClosePnl不再含分红后,梳理出消费端3处PosiPnl公式
不一致(①不减分红/②③减分红,2024~2026分批引入),其中②③对新数据
会多减一份分红导致结算单/收益偏小。

给出3种改法权衡(修正公式/数据迁移/TotalClosePnl分离)+建议分阶段路径,
列出4个需团队决策的问题。含字段引用全景表便于实施定位。
2026-06-26 13:41:32 +08:00

8.9 KiB
Raw Blame History

MarkClosePnl 字段语义统一:决策文档

状态:待团队决策 | 日期2026-06-26 | 前置互换分红损益字段语义与重复计算分析.md、修复提交 ab70531f2026-06-26MarkClosePnl 不再含分红)


一、问题背景:一个字段,三种语义

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 执行数据迁移:

-- 把历史 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 界面"浮动"列 展示