test(swap): 补 e2fb456b InitUnwind 默认 ClosePercent 单元测试

抽 CalcDefaultInitClosePercent 静态纯函数(PosiNotionalValue/NotionalValue), InitUnwind L270 改为调用此函数, 新增 6 个单元测试覆盖: 未平仓/已平30%/除零保护/GLMS-20260701-0013 真实快照/与 ToRemainingClosePercent 联动自洽不变式/全平完边界场景. 填补 e2fb456b 提交的测试覆盖缺口.
This commit is contained in:
hjhan
2026-07-17 08:23:50 +08:00
parent 059867424d
commit 43679393bb
2 changed files with 116 additions and 3 deletions
@@ -0,0 +1,103 @@
namespace YLErp.Modules.SwapModule
{
/// <summary>
/// InitUnwind 默认 ClosePercent 计算的回归测试。
/// ---------------------------------------------------------------
/// 守卫提交 e2fb456b "fix 平仓(InitUnwind L267)硬编码 1 而不是剩余平仓比例"。
///
/// 旧 bugInitUnwind 默认把 ClosePercent 硬编码为 1(按"占剩余 100%"),
/// 但前端约定 ClosePercent 是"占期初(original)"口径(A)1 表示平掉原始本金的 100%。
/// 多次部分平仓后剩余本金 < 期初本金,此时默认 1 在前端语义上意味着"还要平掉原始全部",
/// 与"平掉剩余全部"意图不符,且会触发后端 ToRemainingClosePercent 转换后 >1 被 cap 到 1
/// 表面看无差异但语义混乱,且若前端 / 事件展示直接用此值会出错。
///
/// 修复:ClosePercent = PosiNotionalValue / NotionalValue(占期初口径的"平剩余全部")。
/// 抽出为纯函数 CalcDefaultInitClosePercent 以支持无库单测。
/// </summary>
[TestClass]
public class InitUnwindDefaultClosePercentTest
{
// ================================================================
// 场景1:未平仓 PosiNotionalValue == NotionalValue → ClosePercent = 1
// ================================================================
[TestMethod]
public void _剩余等于期初_默认ClosePercent为1()
{
var result = SwapDealService.CalcDefaultInitClosePercent(
notionalValue: 1_000_000m, posiNotionalValue: 1_000_000m);
Assert.AreEqual(1m, result, "未平仓:默认应平 100%(占期初)");
}
// ================================================================
// 场景2:已平 30%(剩 70%)→ ClosePercent = 0.7
// ================================================================
[TestMethod]
public void 30_70_ClosePercent为0_7()
{
var result = SwapDealService.CalcDefaultInitClosePercent(
notionalValue: 1_000_000m, posiNotionalValue: 700_000m);
Assert.AreEqual(0.7m, result, 0.0001m, "已平 30% 剩 70%:默认 ClosePercent=0.7(占期初)");
}
// ================================================================
// 场景3:除零保护 NotionalValue = 0 → 返回 1(容错)
// ================================================================
[TestMethod]
public void _返回1_容错不除零()
{
var result = SwapDealService.CalcDefaultInitClosePercent(
notionalValue: 0m, posiNotionalValue: 100_000m);
Assert.AreEqual(1m, result, "期初本金为 0 时容错返回 1,不应抛除零异常");
}
// ================================================================
// 场景4GLMS-20260701-0013 真实快照(已平 2 次)
// 期初 NotionalValue = 980,000 / 剩余 PosiNotionalValue = 686,000.07
// 期望 ClosePercent ≈ 0.7686000.07/980000
// ================================================================
[TestMethod]
public void GLMS20260701_0013_已平两次_默认ClosePercent约为0_7()
{
var result = SwapDealService.CalcDefaultInitClosePercent(
notionalValue: 980_000m, posiNotionalValue: 686_000.07m);
// 686000.07 / 980000 = 0.700000071...
Assert.AreEqual(0.7m, result, 0.0001m,
"GLMS-20260701-0013 已平两次:默认 ClosePercent 应≈0.7(占期初),旧 bug 会硬编码 1");
}
// ================================================================
// 场景5:与 ToRemainingClosePercent 联动验证
// 前端拿 InitUnwind 返回的 A(占期初) 默认值,经 ToRemainingClosePercent 转 B(占剩余)
// 应恰好 = 1.0(因为"平剩余全部"在占剩余语义下就是 100%)
// ================================================================
[TestMethod]
public void A经ToRemainingClosePercent转B应为1_平剩余全部()
{
const decimal notionalValue = 1_000_000m;
const decimal posiNotionalValue = 600_000m; // 已平 40%,剩 60%
var defaultA = SwapDealService.CalcDefaultInitClosePercent(notionalValue, posiNotionalValue);
var convertedB = SwapDealService.ToRemainingClosePercent(defaultA, notionalValue, posiNotionalValue);
Assert.AreEqual(0.6m, defaultA, 0.0001m, "占期初默认 A=0.6");
Assert.AreEqual(1.0m, convertedB, 0.0001m,
"A=0.6 经 ToRemainingClosePercent 转换 → B=1.0(占剩余 100% = 平剩余全部),此为占期初/占剩余双语义自洽的关键不变式");
}
// ================================================================
// 场景6:全平完(PosiNotionalValue=0)→ ClosePercent=0(边界,实际不会进 InitUnwind)
// ================================================================
[TestMethod]
public void _ClosePercent为零()
{
var result = SwapDealService.CalcDefaultInitClosePercent(
notionalValue: 1_000_000m, posiNotionalValue: 0m);
Assert.AreEqual(0m, result, "剩余本金为 0 时 ClosePercent=0(边界场景,实际全部平完不会再进 InitUnwind");
}
}
}