docs: 删除不适用的利息部分平仓尾差缺陷分析文档

该文档分析的几个 bug 结论不正确,已不再适用,移除。
This commit is contained in:
hjhan
2026-08-13 08:38:51 +08:00
parent 50bc6562da
commit beb13ff52f
@@ -1,103 +0,0 @@
# 缺陷分析:利息部分平仓尾差(业务场景3 / 业务场景4 浮动利率)
> 数据来源:`缺陷测试-利息20260807晚.xlsx`(独立手算 oracle,非代码 re-baseline
> 分析日期:2026-08-08 关联分支:`glms/feature/1.4.2`
## 1. 失败用例清单(当前代码仍不通过)
### 场景3:第3重置期内全平(平仓日 2026-05-11closePercent=1
| 变体 | 计息 | 算头算尾 | rule | oracle(全部平仓返还利息) | Excel结论 |
|---|---|---|---|---|---|
| row6 | 复利 | 算头算尾 | 当前营业日 | -124062.54 | **不通过** |
| row7 | 复利 | 算头算尾 | 当前营业日 | 280303.16 | **不通过** |
| row8 | 复利 | 算头不算尾 | 当前营业日 | -117918.47 | 通过 |
| row9 | 复利 | 算头不算尾 | 当前营业日 | 266674.35 | 通过 |
| row10 | 复利 | 算头算尾 | 前一营业日 | -123730.45 | **不通过** |
| row11 | 复利 | 算头算尾 | 前一营业日 | 279733.07 | **不通过** |
| row12/13 | 复利 | 算头不算尾 | 前一营业日 | -/+ | 通过 |
| row14~17 | 单利 | 任意 | 当前营业日 | -/+ | 通过 |
**规律:场景3 仅「算头算尾 + 复利」挂,不算尾/单利全过。**
### 场景4:部分平(05-11,30%)后再全平(05-19)
失败 8 个(row6/7/8/9/10/11/13/15),通过 4 个(row12/14/16/17,均为不算尾或单利)。
Excel 备注(row6)原文:
> 不通过,部分平仓时,利息端平仓金额没有跟随平仓比例变化。全部平仓时,居然没有考虑已经过大支付了利息。
场景4 row6 量化:oracle 最终全平 = -124093.74,系统 = -124122.96**系统多算 29.22**
部分用例偏差更大(row8 多算 3685.96row15 多算 3961.58)。说明部分平仓后再全平的尾差链路在复利下整体脆弱。
## 2. 根因(代码实证)
### Bug A:部分平仓利息端未随平仓比例缩放
`SwapDealService.cs` 盘中平仓(复利分支,`CalcDailyCompoundInterest``daysFromPreEod==1` 早退分支):
```
InterestAmount = preEodPosition.InterestIncomeSum * closePrecent; // 已按 closePrecent
TdInterestAmount = preEodPosition.InterestIncomeSum; // ← 未乘 closePrecent
```
`TdInterestAmount`(当日实现利息)未乘 `closePrecent`,导致部分平仓时利息端金额没跟随 30% 比例。
对应场景4 备注第一条「利息端平仓金额没有跟随平仓比例变化」。
### Bug B:全部平仓未扣减已部分平仓已付利息
`SwapEodPositionService.cs` EOD 平仓结算:
```
TdCloseInterest = flowEvents.Sum(x => x.InterestAmount);
isMaturityFinalSettlement = RoundMoney(incomeBefore) == RoundMoney(TdCloseInterest);
if (isMaturityFinalSettlement) InterestIncomeSum = 0; // 直接清零
else InterestIncomeSum = RoundEodInterest(incomeBefore - TdCloseInterest);
```
全部平仓时 `TdCloseInterest` 取的是「整段重算利息」(复利 `CalcDailyCompoundInterest` 末尾 `interest -= consumedInterest*closePercent` 的口径)
但**未先减去部分平仓那一步已经结算/支付的利息**,于是已付部分被重复计入,尾差偏差。
对应场景4 备注第二条「全部平仓时没考虑已大支付了利息」。
### Bug C(加剧项):近期"精度配置 + 尾差重写"纠缠
- `3670dde9`(07-30) / `01d7f0c5`(08-06) 重写了平仓利息/待实现尾差逻辑(`priorClosePositionIds` 排除已平头寸、`pendingInterestBeforeSettlement` 由预付金腿改为所有非 autoSwap)。
- 同期 `bff3e920`(07-29) `swappriceprecision.js``yield 6→4``price 11→9``a4906010` 净价/全价精度分开。
- 尾差 = 高精度应结 − 结算(2位)。精度配置改变 → 舍入残差落点变 → 与重写后的尾差逻辑在"部分平后再全平"长链路(场景4)上交互出错。固定利率 4-2 路径短未触发,浮动 4-2 路径长直接爆。
## 3. 为什么现有测试没护住好代码
1. **测试被 re-baseline 到代码**`01d7f0c5` 把期望常量从 `0.006383561644` 改成 `-0.010438356164`,拿新代码输出当期望值 → 测试只是复述代码行为。
2. **浮动 4-2 无自动化测试**`GetInterestsUnitTest_T1` 仅有 `FIX_*` 固定利率 4-2 用例;浮动 4-2 全靠人工 Excel。
3. **断言容差太松**:既有 `AssertInterestEqual``ConsGlobal.PriceRound-2` 容差(约 0.01),尾差差在 4~6 位小数全被放过。
4. **真 oracle 躺在 Excel 未自动化**:「善洁方法二」30%/70% 守恒检查是极佳 golden,但人肉比对,CI 不响。
## 4. 已修复(2026-08-08
### Fix ABug A — `TdInterestAmount` 未乘 `closePrecent`
`SwapDealService.cs` `CalcDailyCompoundInterest``daysFromPreEod==1` 早退分支:
```csharp
// 修复前(Bug A):
TdInterestAmount = preEodPosition.InterestIncomeSum;
// 修复后:
TdInterestAmount = preEodPosition.InterestIncomeSum * closePrecent;
```
`InterestAmount` 已按 `closePrecent` 缩放,`TdInterestAmount` 必须同步缩放,否则部分平仓时利息端金额未跟随平仓比例。
### Fix B:根因 — `resetCarryInterest` 使用 EOD 舍入快照导致精度偏差
`SwapDealService.cs` `CalcDailyCompoundInterest` 内重置日复利逻辑:
```csharp
// 修复前(569002e5 引入的 resetCarryInterest 机制):
var interestToReset = i == 0 || resetCarryInterest == 0m ? interest : resetCarryInterest;
// 修复后:始终使用循环内高精度累加的 interest
var interestToReset = interest;
```
`resetCarryInterest` 取自 EOD 快照的 `InterestIncomeSum`(2 位小数舍入值),在非重置日 EOD 场景下包含了多个周期利息,注入首重置日会导致:
1. 精度损失(舍入值 vs 循环高精度累加值)
2. 多周期利息错误注入(EOD 的 InterestIncomeSum 是整段累计,不是当前周期利息)
此修复与 `253a89b7``CalcDailyCompoundInterestByEod`EOD 路径)的修复逻辑一致。
### Fix C:测试 `posiLongNotional` 传参修正
`SwapInterestScenario3And4FloatingTest.cs``ExecuteClose` 调用的 `posiLongNotional` 应为**平仓后剩余**名义本金(非平仓前):
- 场景3全平:`posiLongNotional = 0`(全平后无剩余)
- 场景4部分平:`posiLongNotional = Notional - partialCloseNotional`70% 剩余)
- 场景4全平:`posiLongNotional = 0`(全平后无剩余)
这使得 `oriPosiNotionalValue = remaining_after + close = original``closePercent` 计算正确。
## 5. 验证手段
- **C# 测试**`UnitTestProject/Modules/SwapModule/SwapInterestScenario3And4FloatingTest.cs`
— 24 个 Excel oracle 用例,通过 EOD 结算路径(`SaveAutoEodWithCloseInterestPosition`)复现,
断言 `TdCloseInterest`(容差 0.01)。修复前 → RED(偏差 29~3961 元),修复后 → 预期 GREEN。
⚠️ 需在 Windows + VS 运行验证。
- **禁止 re-baseline**:今后任何 fix 改测试期望值常量,必须附注来源(本 Excel 手算 or 文档公式),否则评审红线。