ContractReferenceCalc 独立实现 §8a 确认书公式(∏/重置期切分/末段收口/ 利率确定日=重置日上一营业日),禁止引用生产计息类防同源;4 例手算锚点 自验证(真实规模 5000万/2.05%/90天=75999.04 与玩具锚点)。 引擎对照首批 3 例全绿:mode9/mode2 × 复利 × "10" × T+0 部分平仓30%, 盘中重放与契约 oracle 容差 0.01 元内逐分吻合——盘中口径=确认书公式 自此有机器强制,后续补格直接复用 oracle 供给(TEST-MATRIX §7.5 已登记)。 Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
144 lines
11 KiB
Markdown
144 lines
11 KiB
Markdown
# 融资腿计息测试矩阵
|
||
|
||
> 配套 [ARCHITECTURE.md](ARCHITECTURE.md)。目的:把"覆盖"从用例计数变成格子坐标运算——
|
||
> 每个用例/fix 显式登记命中坐标,空洞一眼可见。2026-08 建立,依据近 6 周 fix 热力图回溯登记。
|
||
|
||
## 0. 范围声明
|
||
|
||
- 本矩阵只覆盖**融资腿 FundingLeg(mode 1 固定值 / 2 合约名义本金规模 / 9 标的期初全价)**。
|
||
- **mode 5/6(保证金/预付金)不属于本矩阵**(历史遗留:被错误建模为计息腿,概念上与融资腿无关,
|
||
见 `Margin/MarginModes.cs` 注释)。保证金有独立的余额模型与专属黄金回放(96 库 60 条,0 差异)作为护栏。
|
||
禁止向本矩阵添加 5/6 格子。
|
||
- 主力生产组合(确认书规定)**必须全格覆盖**,见 §1。
|
||
|
||
## 1. 主力族(第一优先级,必须全盖)
|
||
|
||
```
|
||
InterestMode = 9 标的期初全价 × FR007 浮动(±点差) × InterestType = 复利 × InterestCalcMode = "10"(算头不算尾)
|
||
```
|
||
|
||
代码锚点:`SwapDealService.GetInterests`(calcFirst=true / calcLast=false,SwapDealService.cs:646)。
|
||
近 6 周 ≥9 个 fix 落在本族内——生产用得最多 = 人工测试打得最狠,fix 清单就是炸点热力图。
|
||
|
||
## 2. 维度定义
|
||
|
||
| 维度 | 取值 | 代码/数据锚点 |
|
||
|---|---|---|
|
||
| A 生命周期终点 | 持有至到期结算 / 盘中全平 / 盘中部分后持有 / 部分N次后全平 / EOD自动平仓(部分·全) / 互换(续作) | `SwapEventTypeEnum`;到期:`SwapEodPositionService` 到期结算路径 |
|
||
| B 重置几何 | 第1重置期内平仓 / 跨≥1完整重置期 / 第3重置期内 / **平仓日=重置日** / 重置日±1天 / **末段非整周期**(di<7) | 重置频率=7天(已定格,§8);契约重置期定义见 §8a |
|
||
| C 比例与次数 | 单次部分(30%) / 同日两次 / 跨重置期多次 / 全平(剩余=0) | `closePrecent`;双语义转换 `ClosePercentMath` |
|
||
| D 交收 | T+0 / T+1 | `valueDate` vs `unwindDate` |
|
||
| E FR007 形态 | 每重置日有价 / 加点(+0.25%) / 减点(-2.10%) / **取价日=重置日上一营业日**(契约规定) / 缺价分支 | `TryGetFloatRate` / `ResolveFloatRate`;66a97e03 对应此维 |
|
||
| F 入口 | 见 §3 | |
|
||
| G 断言投影 | ①最终利息金额 ②`TdInterestPrincipal` 逐日携带链 ③`InterestIncomeSum`+flow_event 全字段 ④方向/符号(报表口径) | 每格必须断言全部 4 个投影 |
|
||
|
||
## 3. 入口枚举(F 维)
|
||
|
||
| 入口 | 代码路径 |
|
||
|---|---|
|
||
| 盘中平仓/互换结息试算 | `SwapDealService.GetInterestsForUnwind`(SwapDealService.cs:617,settment:false → `CalcUnwindInterest`) |
|
||
| EOD 正常收盘 | `GetInterests(settment:true)` → `CalcEodInterest` |
|
||
| EOD 平仓后收盘 | `SwapEodPositionService.SaveAutoEodWithCloseInterestPosition`(:1246)→ `CalcSwapInterests`(:1579) |
|
||
| EOD 自动互换 | `CalcSwapInterests`(:1161,EventType=自动互换) |
|
||
|
||
已知风险:`GetInterests` 参数语义随入口漂移(EOD 平仓后收盘传"剩余本金+percent=1",
|
||
盘中传"平仓前本金+实际比例"),`GetInterestsEntrySemanticsTest` 曾实测双入口复利口径分歧(b01b485e)。
|
||
|
||
## 4. fix 热力图(本族,近 6 周)
|
||
|
||
| fix | 落点 | 格子坐标 | 自带测试 |
|
||
|---|---|---|---|
|
||
| 66a97e03 重置日=平仓日 calcLast 不跳过 FR007 取价 | SwapDealService:1249/1295 | B=重置日=平仓日 × E=取价边界 | GLMS20260805FR007UnderlyingIdDiagnoseTest(581行) |
|
||
| 48e84479 重置日部分平仓本金 | SwapEodPositionService:1422 | B=重置日=平仓日 × C=部分 | SwapCloseConversationCasesRegressionTest(358行) |
|
||
| d3afa6d2 T+1 部分平仓复利本金(算头不算尾快速路径) | SwapDealService:1243 | D=T+1 × C=部分 × A=部分后持有 | DealInterestsScenarioTest +36行 |
|
||
| aa5a5ed8 算头不算尾期初复利部分平仓 | SwapEodPositionService:1418-1573 | **本族正中心** | DealInterestsScenarioTest |
|
||
| a0be0eb0 复利平仓已结利息扣除 | SwapDealService:1294 | 已结利息差分(CalcDailyCompoundInterest 回放) | ConsumedInterestScenarioTest |
|
||
| 5539bd9c 复利部分平仓后 EOD 本金 | SwapEodPositionService:782/1381 | G=携带链投影 | DealInterestsScenarioTest +34行 |
|
||
| feffc196 Bug A/B/C 浮动部分/全平尾差 | SwapDealService:1214/1292 | C=部分/全平 × E=浮动 | **SwapInterestScenario3And4FloatingTest(24用例,Excel oracle)** |
|
||
| 2035e1df EOD 平仓后收盘结息本金语义 | SwapDealService:1293 / EodService:860,1360 | **F=EOD平仓后收盘 × C** | **无测试** |
|
||
| b01b485e 双入口口径分歧(实测发现) | — | F=入口 × 全族 | GetInterestsEntrySemanticsTest(字符化,非 oracle) |
|
||
|
||
**规律:fix 全部落在 `CalcUnwindInterest`(SwapDealService 1240-1300)和
|
||
`SaveAutoEodWithCloseInterestPosition` 族(SwapEodPositionService 1380-1580)两个带。**
|
||
|
||
## 5. 现有用例登记
|
||
|
||
| 测试文件 | 覆盖格子 | oracle 类型 |
|
||
|---|---|---|
|
||
| SwapInterestScenario3And4FloatingTest(24) | 本族 A=全平/部分30%→全平 × B=第3重置期内 × D=T+0/T+1 × E=加减点 × F=EOD平仓后收盘 × G=仅金额投影 | Excel 手算(业务源) |
|
||
| SwapInterestScenario1And2Test(32) | A=收盘平仓 × B=第1重置期内 × E=固定/浮动 | Excel 手算 |
|
||
| DealInterestsScenarioTest(24 方法,工单逐个追加) | 部分平仓×复利族各点,含"10"×3 行 | 工单期望值 |
|
||
| ConsumedInterestScenarioTest | 已结利息差分族 | 工单期望值 |
|
||
| SwapUnwindSameDayDoublePartialTest | C=同日两次 | **字符化(非独立 oracle)** |
|
||
| GetInterestsEntrySemanticsTest | F=双入口一致性 | **字符化** |
|
||
| **ContractReferenceOracleTest(Accrual/,7)** | mode9/mode2 × 复利 × "10" × T+0 × 部分30% × B=跨12整期+末段(90/89天) × E=恒定利率(取价日免疫) | **契约公式参考实现(§7.4 第一级)**——引擎盘中重放已逐分对齐 oracle |
|
||
| GetInterestsUnitTest_T0/T1(89) | mode 1 固定值 T+0/T+1 族(非本族) | 单点断言 |
|
||
| GLMS20260805FR007UnderlyingIdDiagnoseTest | B=重置日=平仓日 × E | 诊断+断言 |
|
||
|
||
## 6. 空洞清单(热力图 ∩ 未覆盖,按优先级)
|
||
|
||
1. **F=EOD平仓后收盘 × C=部分平仓 × 本族** —— 2035e1df 无测试落地即合入,该入口×比例格子全裸。
|
||
2. **G=携带链投影(全族)** —— 现有断言几乎全是最终金额;`TdInterestPrincipal` 逐日携带链无一处断言
|
||
(7528670e 在单利上炸过同款,复利同投影裸奔)。
|
||
3. **B=重置日±1天 / 跨重置期多次部分平仓** —— 热力图边缘未扫。
|
||
4. **A=到期结算 × 本族** —— db46e48e 修过到期结算(28 断言),但非本族参数。
|
||
5. **A=互换(续作) × 本族** —— 7411b9d2/421662a0 炸过续作初始化,本族续作无 oracle。
|
||
6. **C=同日两次** —— 只有字符化测试,无独立 oracle(字符化=锁定现状,不证正确)。
|
||
7. **E=缺价/取价日边界** —— 66a97e03 只修了取价跳过,缺价分支行为未钉。
|
||
|
||
## 7. 补盖执行顺序
|
||
|
||
1. 先铺**守恒不变量**(免 oracle,全格便宜):部分平仓后"期初=平掉+剩余"逐日守恒;全平后持仓=0;
|
||
复利重置日动态本金=前段本金+利息;多次平仓 closePercent 连乘=累计比例。
|
||
2. 空洞 1/2 优先:按 §2-G 四投影补 EOD平仓后收盘 × 部分 用例,oracle 用确认书公式 Excel 模板。
|
||
3. 空洞 6 补独立 oracle(确认书公式),替换字符化地位(保留字符化作回归钉)。
|
||
4. 每格期望值来源分级(已升级,见 §8a):**契约公式独立参考实现** > 生产已对账数字 > 业务签认 Excel > 新旧影子对比;**禁止当前代码输出充当 oracle**。
|
||
5. 契约参考实现(§8a 公式)**已落地**(`UnitTestProject/Modules/SwapModule/Accrual/ContractReferenceCalc.cs`,
|
||
独立于生产引擎,禁止引用计息类防同源),引擎对照首批 3 例全绿(mode9/mode2 × "10" × 部分30%,
|
||
`ContractReferenceOracleTest`)。后续补格直接复用:期望值 = `ClosedInterest(平掉额, ReferenceRateAbsolute(...))`。
|
||
待办:变利率引擎侧对照(取价日 E 维)、确认书生成器参数同源断言(`swap_position`)。
|
||
|
||
## 8. 生产参数(已确认,2026-08)
|
||
|
||
- **重置频率 = 7 天**(确认书:"重置频率每【周】";完整重置期 di=7 天)
|
||
- **年化基数 = 365**(确认书:"计息基准 A/365",固定利率公式同除 365)
|
||
- 生产只有这一种组合,无 360/其他重置频率。现有测试参数 `ResetPeriod=7 / AnnualDays=365` **即为生产主力参数,格子按此定格**。
|
||
|
||
## 8a. 契约 oracle(确认书公式原文)
|
||
|
||
模板:`Plugins/YLErp.Plugins.GuoLian/App_Docs/contract_template/*.docx`(看多/看空 × 现券/债券ETF 共 4 份,计息条款一致);
|
||
变量替换:`Plugins/YLErp.Plugins.GuoLian/DocumentGenerator/TradeConfirmationGenerator.cs`
|
||
(`重置频率=interest_rest_days天`、`利差=InterestRateDefault×10000bp`,与计息引擎同源 `swap_position` 字段)。
|
||
|
||
**浮动利率复利公式(本族契约正文)**:
|
||
|
||
```
|
||
参考利率(绝对) = ∏[i=1..k] ( 1 + (FR007i + 利差) × di / 365 ) − 1
|
||
```
|
||
|
||
- k = 计息期包含的重置期个数;di = 第 i 个基准利率适用的日历日数,**完整重置期 di=7,末段不足 7 按实际日历日**(测试必须盖非整周期:如持有 17 天 = 2×7+3)
|
||
- **利率确定日 = 每个重置期首日(重置日)的上一个营业日**,取该日 FR007;营业日准则=上一营业日(→ E 维度取值锚点,与 66a97e03 修复直接对应)
|
||
- FR007 取中国货币网每日公布值
|
||
|
||
**计息期定义(= 算头不算尾的契约原文)**:自起始日(含)至到期日(不含)的自然日天数。
|
||
⚠ 债券ETF 模板变体:计息期自**期初观察日**(含)至**期末观察日**(不含)——观察日→代码日期字段的映射需单独核实,是一个潜在口径分叉点。
|
||
|
||
**重置期定义**:每个重置期自上一重置日(含)至下一重置日(不含);首个重置期始于计息期首日;最后一个重置期的最后一日为计息期最后一日(末段收口)。重置日从计息期首日按重置频率依次推算。
|
||
|
||
**固定利率公式**:参考利率(绝对) = 固定利率 × 计息期 / 365。
|
||
**期初预付金利息**:支付日(含)至到期日(**含**)×利率/计息基准——注意预付金契约上是"含尾"的,与利率腿"不含尾"相反。
|
||
|
||
**oracle 使用方式(升级 §7)**:最强形式是**按契约公式写独立参考实现**(约 20 行:重置日推算 + 分段取价 + ∏ 公式 + 末段收口),作为测试 oracle 与生产引擎对照,容差 0.01。它比逐格 Excel 手算更便宜且零同源风险;Excel 模板退化为抽样校验参考实现本身。
|
||
|
||
## 9. 合入规则(硬约束)
|
||
|
||
1. 计息类 fix:**先失败测试,后修代码**;测试须登记本矩阵坐标。
|
||
2. 修一格必须**扫同矩阵行兄弟格子**(同 fix 家族的邻格)。
|
||
3. 任何触碰 `GetInterests`/`CalcUnwindInterest`/`SaveAutoEodWithCloseInterestPosition` 的 PR:
|
||
跑 `DealInterestsGoldenReplayTest` 全量 + 保证金黄金回放(防共享管线殃及)。
|
||
4. 登记 fix 时发现同格已有用例而 bug 仍发生 → 先修断言投影,再修代码。
|
||
5. **oracle 用例与裁决材料一律取 §8 生产参数**(7 天重置 / A365 / 真实点差 ±0.25%·−2.10% /
|
||
千万级名义本金,如 5000 万)。玩具参数(千元级/重置 3 天/点差 1%)仅限字符化钉子测试——
|
||
其用途是锁行为防漂移,不承担"证明数字正确"职责;用玩具数字做裁决依据会掩盖金额量级
|
||
(0.03 vs 0.06 看着"不大",同参数放大到生产即 7.6 万 vs 25 万/笔)。
|