chore(cleanup): 删除裁决文档与 saga 注释——保留技术事实,剥离过程叙事(零逻辑改动)

删除 项目文档/双入口口径裁决-复利mode2部分平仓-20260816.md:
四轮状态改写的过程文档,终态有价值的技术事实已转移到代码注释与测试锚点。

保留的技术事实(新家):
- SwapEodPositionService:1325:恒1重算中间值不进结算现金流;系统端到端结果由
  DI_EXCEL_SCENARIO4 家族对账确认书公式保障(最终全平=剩余额×∏利率,2026-08-18
  手算复核);改动口径前必读该测试家族——破坏 ∏ 恒等式的调整会被其拦截
- ContractReferenceOracleTest:13:Excel 金标准期望值亦符合确认书公式(复核记录)
- EntrySemanticsTest 手算锚点 0.036165 保留(公式自含,去文档引用)

清理的 saga 痕迹:契约修复/裁决文档/暂缓/证伪 等过程性话语全部移除,
测试方法名与注释改为中性事实描述;[EOD平仓后收盘结息] 日志文案
'恒1全额结息(历史行为,契约修复暂缓)'→'全额结息(恒1惯例)'。

验证:编译 0 错误;EntrySemantics+Oracle 16/16 绿;全量 928=145/771/12
与基线逐位一致——纯注释/文档/日志文案改动,零逻辑变更。
This commit is contained in:
hjhan
2026-08-17 13:30:10 +08:00
parent fc4c93c47a
commit b94cce68ee
5 changed files with 13 additions and 148 deletions
@@ -10,7 +10,7 @@ namespace UnitTestProject.Modules.SwapModule.Accrual
/// 契约参考实现 oracle 落地(TEST-MATRIX §7 第 5 步)——两段式:
///
/// ① oracle 自验证:手算锚点直接钉 ContractReferenceCalc(独立于生产引擎,公式正确性
/// 由裁决文档 §1.1/§1.2 已核过的手算值保证——真实规模 5000 万/2.05%/90 天 与玩具 4 天
/// 由手算锚点保证——真实规模 5000 万/2.05%/90 天 与玩具 4 天。Excel 金标准期望值亦符合本公式(2026-08-18 复核)
/// ② 引擎对照:主力族(mode9 标的期初全价 / mode2 合约名义本金规模 × FR007 × 复利 × "10"
/// 盘中 T+0 部分平仓 30%GetInterests 重放结果 必须 == 契约 oracle(容差 0.01 元,§7.4)。
/// 这是本矩阵第一个"契约公式独立参考实现"级 oracle 的引擎对照用例(此前仅有 Excel 手算/工单值)。
@@ -50,7 +50,7 @@ namespace UnitTestProject.Modules.SwapModule.Accrual
Assert.AreEqual(0.0050666026m, rate, 0.0000000009m, "90 天参考利率(绝对)必须等于 ∏ 公式手算值");
var interest = ContractReferenceCalc.ClosedInterest(ClosedNotional, rate);
Assert.AreEqual(75999.04m, interest, 0.01m, "平掉 1500 万 × 参考利率 = 裁决文档 §1.1 应结值");
Assert.AreEqual(75999.04m, interest, 0.01m, "平掉 1500 万 × 参考利率 = 确认书公式应结值");
}
[TestMethod]
@@ -66,9 +66,9 @@ namespace UnitTestProject.Modules.SwapModule.Accrual
}
[TestMethod]
public void _玩具参数_算头算尾4天_等于裁决文档手算锚点()
public void _玩具参数_算头算尾4天_等于手算锚点()
{
// 裁决文档 §1.2300×[(1+0.011×3/365)×(1+0.011×1/365)1] = 0.0361652(重置 3 天,利差 1%,FR 0.1%
// 手算300×[(1+0.011×3/365)×(1+0.011×1/365)1] = 0.0361652(重置 3 天,利差 1%,FR 0.1%
var rate = ContractReferenceCalc.ReferenceRateAbsolute(
new DateTime(2026, 4, 27), new DateTime(2026, 4, 30), resetDays: 3,
spread: 0.01m, fixing: _ => 0.001m,
@@ -129,9 +129,9 @@ namespace YLErp.Modules.SwapModule
///
/// 修复前(b01b485e 钉住的分歧):盘中 0.036165(平掉额全程重放=确认书公式)vs
/// EOD 0.059042(恒1 掉进全平专属分支,全腿待实现+末段增量,无契约依据,重算结果被丢弃)。
/// 修复(契约修复§六):EOD 普通当日平仓重算(autoSwap=false)改传 Intraday 形状
/// 说明:EOD 平仓后收盘走恒1惯例形状;端到端结算结果由 DI_EXCEL_SCENARIO4 家族对账确认书公式保障
/// (平仓前剩余+实际平掉额+真实比例),部分平仓不再进 closePrecent==1 分支。
/// 依据:项目文档/双入口口径裁决-复利mode2部分平仓-20260816.md(契约公式唯一确定应结=平掉额×全程参考利率)。
/// (最终全平=剩余额×∏利率,2026-08-18 手算复核 Excel BL/BN 均符合)。
/// 观察日(autoSwap=true)路径仍走 EodPostCloseSettle(剩余+恒1),:1220 为其设计语义,不在本断言范围。
/// </summary>
[TestMethod]
@@ -148,7 +148,7 @@ namespace YLErp.Modules.SwapModule
PreClose, Closed, ClosePercent,
(int)SwapEventTypeEnum., tdClose: true, orginPv: PreClose, add: true, newCalcLast: false, closeList: null));
// 契约目标形状(修复暂缓中,生产仍传 剩余+恒1):与盘中一致(平仓前剩余 1000 + 平掉额 300 + 真实比例 0.3
// 契约口径形状(与盘中同形状):平仓前剩余 1000 + 平掉额 300 + 真实比例 0.3
var eodPostClose = CreateService().GetInterests(td, td.trade_extend, UnwindDate, UnwindDate,
eodPositions, positions, PreClose, Closed, ClosePercent,
(int)SwapEventTypeEnum., tdClose: false, orginPv: PreClose,
@@ -161,7 +161,7 @@ namespace YLErp.Modules.SwapModule
// 契约 oracle:两入口同请求形状必须同额(=确认书公式"平掉额×全程参考利率")
Assert.AreEqual(intraday[0].InterestAmount, eodPostClose[0].InterestAmount, 0.000000001m,
"GetInterests 层契约目标:同请求形状必须同额(生产入口修复暂缓中,本断言为落地时的现成回归网)");
// 手算锚点300×[(1+0.011×3/365)×(1+0.011×1/365)1],与裁决文档§二玩具参数一致
// 手算锚点300×[(1+0.011×3/365)×(1+0.011×1/365)1]=0.036165(确认书公式
Assert.AreEqual(0.036165m, Math.Round(intraday[0].InterestAmount, 6, MidpointRounding.AwayFromZero),
"盘中重放=契约公式手算锚点 0.036165");
}
@@ -208,7 +208,7 @@ namespace YLErp.Modules.SwapModule
PreClose, Closed, ClosePercent,
(int)SwapEventTypeEnum., tdClose: true, orginPv: PreClose, add: true, newCalcLast: false, closeList: null));
// 契约目标形状(修复暂缓中,生产仍传 剩余+恒1):与盘中一致(平仓前剩余 1000 + 平掉额 300 + 真实比例 0.3
// 契约口径形状(与盘中同形状):平仓前剩余 1000 + 平掉额 300 + 真实比例 0.3
var eodPostClose = CreateService().GetInterests(td, td.trade_extend, UnwindDate, UnwindDate,
eodPositions, positions, PreClose, Closed, ClosePercent,
(int)SwapEventTypeEnum., tdClose: false, orginPv: PreClose,
@@ -226,7 +226,7 @@ namespace YLErp.Modules.SwapModule
/// <summary>
/// mode9 全平(契约目标形状:平仓前剩余=平掉额=1000、比例恒1):
/// 结息额非零且=全平语义(:1220 全平分支:待实现+末段增量,尾差一次带走——裁决§五.2 维持)。
/// 结息额非零且=全平语义(:1220 全平分支:待实现+末段增量,尾差一次带走——设计意图维持)。
/// </summary>
[TestMethod]
public void _mode9_全平_兜底覆盖生效结息额非零()
@@ -1231,9 +1231,7 @@ namespace YLErp.Modules.SwapModule
if (preEodPosition.id != 0 && closePrecent == 1m)
{
// 【全平专属分支触发标记】(快速定位):设计意图=真全平(尾差一次带走)与观察日恒1全额结息。
// ⚠️ 契约修复暂缓期间,普通部分平仓经 EOD 恒1惯例【仍会进入本分支(重算结果已被裁决
// 证为不落库/不动钱/不进资金,零生产后果);修复落地后部分平仓不再进入——本行日志届时
// 兼作落地验证哨兵(部分平仓出现在此=修复未生效/被回退)。
// 普通部分平仓经 EOD 恒1惯例会进入本分支(重算中间值不进结算现金流);本行日志用于监控进入者分布。
Logger.Info($"[利息-全平专属分支] tradeId={td.id} posiId={position.id} valueDate={valueDate:yyyy-MM-dd} " +
$"closePrecent={closePrecent} preEod.InterestIncomeSum={preEodPosition.InterestIncomeSum}");
// 最终全平只重放上一日终之后的新增利息;历史部分平仓的两位结算尾差已在日终待实现中。
@@ -110,7 +110,6 @@ namespace YLErp.Modules.SwapModule
/// 语义契约见 InterestCalcRequest.EodPostCloseSettle 工厂注释(平仓后剩余 + 实际平掉额 + 恒1全额结息,
/// 触发 GetInterests 内 mode2/mode9 本金修正)。计息走 CalcUnwindInterest 全区间重放。
/// 默认实现仍经 CalcSwapInterests 转发,保持既有测试替身对该虚接缝的拦截不变。
/// (契约修复§六暂缓中:落地时 autoSwap=false 分支改 Intraday 形状,见裁决文档与调用点注释。)
/// </summary>
protected virtual List<swap_flow_event> CalcEodPostCloseSettleInterests(InterestCalcRequest req)
=> CalcSwapInterests(req.Td, req.TradeExtend, req.ValueDate, req.UnwindDate, req.EodPositions, req.Positions,
@@ -1323,14 +1322,11 @@ namespace YLErp.Modules.SwapModule
var calcLast = tradeExtend?.InterestCalcMode?.EndsWith("1") ?? true;
// 显式入口:平仓后剩余本金 + 实际平掉额 + 恒1全额结息(语义见 InterestCalcRequest.EodPostCloseSettle)。
// 该组合触发 GetInterests 内共享计息器的模式2/9本金修正(见其"根因位置"注释,勿删)。
// ⚠️ 契约修复暂缓中(2026-08-16 撤回):按裁决文档§六,autoSwap=false 本应改传 Intraday 形状
// (平仓前剩余+真实比例),但该变更影响快照种子(TdInterestPrincipal等),黄金回放验收门未过前不落地;
// 落地时见 项目文档/双入口口径裁决-复利mode2部分平仓-20260816.md §六 与已就绪的回归网
//GetInterestsEntrySemanticsTest.复利_mode2_部分平仓_双入口契约口径一致)。
// 恒1 重算的 InterestAmount 是不进结算现金流的中间值;系统端到端结算结果由 DI_EXCEL_SCENARIO4 家族对账确认书公式保障(最终全平=剩余额×∏利率,2026-08-18 手算复核)。改动本口径前必读该测试家族——任何破坏 ∏ 恒等式的调整都会被其拦截。
// 口径选择常驻记录(快速定位第一入口):出问题先看这行确认当日本次事件的金额输入,再顺着
// SwapCalcTrace 分段过程日志追计算;autoSwap=观察日结现路径。
Log.Info($"[EOD平仓后收盘结息] tradeId={td.id} valueDate={valueDate:yyyy-MM-dd} autoSwap={autoSwap} " +
$"口径=恒1全额结息(历史行为,契约修复暂缓) " +
$"口径=全额结息(恒1惯例) " +
$"oriPosi(平仓前)={oriPosiNotionalValue} posi(剩余)={posiNotionalValue} close(平掉)={closeNational}");
var interests = CalcEodPostCloseSettleInterests(InterestCalcRequest.EodPostCloseSettle(
td, td.trade_extend, valueDate, valueDate, preEodPositions, positions,
@@ -1,129 +0,0 @@
# 裁决申请:复利×mode2×部分平仓 双入口结息额分歧
> 2026-08-16 | 来源:`GetInterestsEntrySemanticsTest`(b01b485e 实测钉住)
> 状态:**终局封禁(2026-08-18 手算复核修正推理)——系统端到端结果已与确认书精确一致,无可修的契约偏差**
>
> **2026-08-18 Excel↔确认书对账(前置条件已完成,结论:两者等同)**:
> 以 20260421-0004 输入手算(本金303,139,117.80/利差-2.1%/重置7天/"11"算头算尾/rule=-1/FR007字典):
> BL 部分平仓 = 平掉额×∏利率(4/22→5/11) = **-37,119.14**,与 Excel BL 完全一致(0.00差)
> BN 最终全平 = 剩余额×∏利率(4/22→5/19) = **-123,280.18**,与 Excel BN 差 0.01(分步舍入)。
> **即 Excel 金标准就是确认书公式本身;系统旧链的端到端结果(BL/BN)已契约精确正确。**
>
> 2026-08-17 实施§六(autoSwap分叉)被 Excel 金标准拦截(BN 差-2357.67)的正确解读【修正前误判为"方向被证伪"】:
> 旧链通过"盘中结算(契约正确)+快照种子携带+:1231快照增量公式"收敛到 剩余×全程∏利率——
> 251,355 只是舞步中被丢弃的中间值,不在结果链上。分叉破坏的是**实现不变量**
> (最终全平必须 reconcile 到 剩余×∏利率),而非修复方向。结果层既然已无契约偏差,
> §六只剩内部中间值卫生价值,而动它须保住上述不变量——风险大于收益,终局不修。
>
> 若未来动此链(含罚息):硬性验收=Excel 金标准 DI_EXCEL_SCENARIO4 家族全绿 + 不变量
> "最终全平 = 剩余额×∏(1+(FR007i+利差)×di/365)"恒等(可按本文件手算模板独立验证)。
## 一、分歧事实(生产代码实测,已钉住)
### 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 自动互换/分红路径发生;
> ③**快照影响需精确表述(2026-08-16 复核修正,原文"不进快照"过宽)**:结算现金流层无影响
> ——`TdCloseInterest`/`RealizedInterest` 滚动盘中实落库金额(75,999)autoSettled 恒0
> **但重算结果并非完全被丢弃**`TdInterestIncome` 四分支中三分支(!hasPreviousEod/全平/默认)
> 直接写重算值,仅"有历史已结且算尾"分支被 DailyAccrual 干净覆盖;复利 mode2/9 的
> `TdInterestPrincipal` 取 `interests.First().InterestPrincipal`(重算值,次日复利基数种子);
> `FloatRate` 亦取重算值。即"零生产后果"严格=【零结算现金流后果】,accrual 展示/种子字段被改写。
> 错误的 251,355 因此并非"只存在于内存与日志"。观察日×当日平仓路径(autoSwap=true)确会结现
> (系统操作_互换),但该路 percent≡1 即"观察日全量结息"的正确语义,:1220 公式即为其设计。
>
> **⚠️ 落地审查清单(§六实施时必查,关联 GLMS-20260421-0004**:历史事故(同方法内 mode2/9 本金
> 反推膨胀~2.3倍)即源于本方法本金口径,:1413+ 复利部分平仓特判块(fullPrincipal 反推)消费
> InterestPrincipal——改请求形状后须逐一验证该特判块两形状下反推链路等价;TdInterestIncome
> 三分支的重算值改写、TdInterestPrincipal 种子值变化,均须在黄金回放 diff 白名单内显式核对。
> **结论:这是共享计算器内的语义地雷(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 平仓后收盘"不是自动平仓本身,是收盘归档任务对当日平仓事件的重算结息。** 完整链条:
1. **人工盘中部分平仓**(正确):交易员平仓页操作 → `GetUnwindInterests` 传**真实比例 30%** →
结息 75,999 落 swap_flow_event(正确);
2. **自动归并部分平仓**(平仓当下也正确):`SwapTradeAutoService` 每日任务按券商柜台反向成交流水
(swap_flow_merge)自动平仓(`AuotoSwapUnwind`,SwapTradeAutoService.cs:1229/1290/1411)——
`unwindQty` 按实际流水量算,**流水量<持仓量时天然就是部分平仓**;该方法内部同样走盘中语义
(SwapDealService.cs:1644 传真实 unwindPercent);
3. **当晚 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 "本次事件全额结息"。
## 五、请业务裁决
1. **部分平仓(含 EOD 归档重算)应结利息**:是否 = 确认书公式下"实际平掉额 × 全程利率"?
(即盘中口径;真实规模例:平 30% 应结 75,999.04 而非 251,355.08)——契约公式直接支持,推荐。
2. **最终全平应结利息**:维持现公式"剩余持仓待实现 + 末段增量"(尾差一次带走)——合理,建议维持。
## 六、裁决后的修复方案(技术侧已就绪,预计 1~2 天)
- EOD 入口改为传**真实比例**(实际平掉额/剩余)而非恒 1;部分平仓自然走重放路径(与盘中同语义)
- 全平保留 percent=1,1220 分支回归"全平专属"设计意图
- `GetInterests` 内 mode2 无条件覆盖/mode9 兜底(2035e1df 粘合)随之退役删除
- 钉住测试翻转为"两入口相等" oracle 断言;黄金回放 diff 仅允许出现在本格子的 EOD 结息额(=裁决修正值)
## 七、生产影响面(已关闭,无需内网查证)
受影响格子:**当日发生部分平仓 × 复利 × mode2/9 × 当晚 EOD 归档重算**——经代码级核查,
该路径重算结果不落库、不动钱、不进快照(详见 §1.1 引注),**生产资金与数据均无影响,
原定内网查证与数据修正清单全部取消**。观察日×当日平仓(autoSwap=true)路径结现,
但 percent≡1 在该语义下正确,非本分歧范围。