DealInterests hasClose 分支未接收 SaveAutoEodWithCloseInterestPosition 返回值, UpdateDbOption 仅打审计字段不落库,快照滚动盘中实结金额——错误的251,355 只存在于内存与日志。内网查证与数据修正清单取消,修复定性为重构卫生项。 Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
7.1 KiB
裁决申请:复利×mode2×部分平仓 双入口结息额分歧
2026-08-16 | 来源:
GetInterestsEntrySemanticsTest.复利_mode2_部分平仓_双入口口径钉住现状(b01b485e实测钉住) 状态:待业务裁决——同一经济事件,两个入口算出两种结息额,当前测试锁死两值防漂移。
一、分歧事实(生产代码实测,已钉住)
1.1 真实规模复算(确认书生产参数,裁决请以此为准)
参数全部取自确认书规定与生产实配(见 TEST-MATRIX §8/§8a): 名义本金 5000 万;FR007=1.8% + 点差 +0.25% = all-in 2.05%;重置频率 7 天; 计息基准 A/365;持有 90 天(12×7 天完整重置期 + 6 天末段);部分平仓 30%(平掉 1500 万,剩余 3500 万)。
| 口径 | 结息额 | 计算方式 |
|---|---|---|
| 契约公式(应结) | 75,999.04 元 | 平掉额 1500万 × 参考利率(90天)= ∏公式,手算见 §二 |
| 盘中平仓试算 | 75,999.04 元 | 与契约公式一致(引擎实测,容差内) |
| EOD 平仓后收盘(现结) | 251,355.08 元 | 平仓前全仓 5000万 截至上一日终待实现(250,508.61) + 平掉额末段增量(846.43) |
重算事件金额是应结的 3.3 倍(多出部分 ≈ 剩余 3500 万份额对应的待实现利息,在只平 30% 时被算进重算结果)。
影响面最终裁定(2026-08-16 代码级核查,三层全部关闭):资金没多结,数据没错,本分歧零生产后果。 ①不落库:
UpdateDbOption仅打审计字段不做 DbSet.Add,且DealInterests的 hasClose 分支 (SwapEodPositionService.cs:524)未接收返回值——重算结果被整个丢弃,库里不存在这条错误流水; ②不动钱:该路径(autoSwap=false)无 AddClientCash;客户资金只在盘中流程与 EOD 自动互换/分红路径发生; ③不进快照:TdCloseInterest/RealizedInterest滚动盘中实落库金额(75,999),部分平仓×复利×mode2/9 的TdInterestIncome被后续 DailyAccrual 干净重算覆盖,剩余待实现(≈177,300)正确。 错误的 251,355 只存在于内存计算与 Log.Info 日志行。观察日×当日平仓路径(autoSwap=true)确会结现 (系统操作_互换),但该路 percent≡1 即"观察日全量结息"的正确语义,:1220 公式即为其设计。 结论:这是共享计算器内的语义地雷(percent≡1 复用),非结算错误;修复性质=重构卫生项, 与普通重构同优先级;生产数据修正与内网查证全部取消。
1.2 可复现实测锚点(玩具参数,字符化钉子)
场景:合约名义本金(mode2)×复利×浮动 FR007×部分平仓 30% 参数:本金 1000,平掉 300,剩余 700;FR007=0.1%,利差=1.0%;[4/27 起息,4/30 平仓] 算头算尾 4 天;重置周期 3 天。
| 入口 | 结息额 | 计算方式 |
|---|---|---|
| 盘中平仓试算 | 0.036165 | 平掉额 300 从 4/27 到 4/30 全程复利重放 |
| EOD 平仓后收盘 | 0.059042 | 剩余持仓全部待实现利息(0.05) + 平掉额 4/30 末段增量(0.009042) |
EOD 比盘中多结 63%(同一分歧在玩具参数下的比例;比例随持有天数/平仓比例变化,真实规模见 §1.1)。
二、契约验算(确认书公式,模板:国联民生收益互换确认书-现券/ETF 四份一致)
参考利率(绝对) = ∏[i=1..k] ( 1 + (FR007i + 利差) × di / 365 ) − 1
结息额(平仓部分) = 实际平掉额 × 参考利率(绝对)
真实规模手算(利率恒定 2.05% 时 ∏ 可合并):
- 90 天 = 12×7 天 + 6 天末段 → 参考利率 = (1+0.0205×7/365)^12 × (1+0.0205×6/365) − 1 = 0.0050666026
- 应结 = 1500万 × 0.0050666026 = 75,999.04 元 = 盘中口径(引擎实测一致)
EOD 口径(全仓待实现 250,508.61 + 末段增量 846.43 = 251,355.08)无法由契约公式推出—— 契约里不存在"平掉 30% 却结走 100% 持仓利息"的条款。
玩具参数手算(300×[(1+0.011×3/365)×(1+0.011×1/365)−1])= 0.0361652,同样 = 盘中口径。
三、触发链:谁在 EOD"平仓"(命名澄清)
"EOD 平仓后收盘"不是自动平仓本身,是收盘归档任务对当日平仓事件的重算结息。 完整链条:
- 人工盘中部分平仓(正确):交易员平仓页操作 →
GetUnwindInterests传真实比例 30% → 结息 75,999 落 swap_flow_event(正确); - 自动归并部分平仓(平仓当下也正确):
SwapTradeAutoService每日任务按券商柜台反向成交流水 (swap_flow_merge)自动平仓(AuotoSwapUnwind,SwapTradeAutoService.cs:1229/1290/1411)——unwindQty按实际流水量算,流水量<持仓量时天然就是部分平仓;该方法内部同样走盘中语义 (SwapDealService.cs:1644 传真实 unwindPercent); - 当晚 EOD 重算(分歧所在):收盘归档
DealInterests(SwapEodPositionService.cs:511/:524) 发现当日有平仓事件 →EodPostCloseSettle惯例传 closePrecent 恒 1("结算本次事件覆盖的全部") → 掉进closePrecent == 1m全平专属分支(:1220)→ 251,355 覆盖/修正归档。
即:平仓动作本身(人工/自动)当下结息都是对的;错发生在当晚收盘归档重算这一步。
四、机制定位(为什么 EOD 会算成这样)
SwapDealService.cs:1220 的 closePrecent == 1m 分支,设计意图是最终全平专属
(注释原文:"最终全平只重放上一日终之后的新增利息……以上一日 InterestIncomeSum 为起点,保证之前攒下的尾差最后一次带走")——
全平时该公式正确。
但 EOD 平仓后收盘入口的传参惯例是 closePrecent 恒 1(见 InterestCalcRequest.EodPostCloseSettle 工厂,
"结算本次事件覆盖的全部")。于是 EOD 部分平仓 30% 也掉进全平专属分支,把剩余 100% 持仓的
待实现利息全部结走。数字 1 承载了两种语义:"平仓比例=100%" vs "本次事件全额结息"。
五、请业务裁决
- 部分平仓(含 EOD 归档重算)应结利息:是否 = 确认书公式下"实际平掉额 × 全程利率"? (即盘中口径;真实规模例:平 30% 应结 75,999.04 而非 251,355.08)——契约公式直接支持,推荐。
- 最终全平应结利息:维持现公式"剩余持仓待实现 + 末段增量"(尾差一次带走)——合理,建议维持。
六、裁决后的修复方案(技术侧已就绪,预计 1~2 天)
- EOD 入口改为传真实比例(实际平掉额/剩余)而非恒 1;部分平仓自然走重放路径(与盘中同语义)
- 全平保留 percent=1,1220 分支回归"全平专属"设计意图
GetInterests内 mode2 无条件覆盖/mode9 兜底(2035e1df粘合)随之退役删除- 钉住测试翻转为"两入口相等" oracle 断言;黄金回放 diff 仅允许出现在本格子的 EOD 结息额(=裁决修正值)
七、生产影响面(已关闭,无需内网查证)
受影响格子:当日发生部分平仓 × 复利 × mode2/9 × 当晚 EOD 归档重算——经代码级核查, 该路径重算结果不落库、不动钱、不进快照(详见 §1.1 引注),生产资金与数据均无影响, 原定内网查证与数据修正清单全部取消。观察日×当日平仓(autoSwap=true)路径结现, 但 percent≡1 在该语义下正确,非本分歧范围。