test(swap): 补非预付金单利腿(标的期初全价)部分平仓回归,实证修复对所有单利腿通用

用户追问修复是否波及非预付金腿。CalcDailySimpleInterest 是所有单利腿
(InterestType=0)的盘中计息通用函数,预付金腿(mode5/6)只是使用者之一。
新增非预付金腿(标的期初全价=9,orginPv 走交易名义本金、不对齐 Fix)用例:
- 50%%→500,000、10%%→100,000、100%%→1,000,000(N=1,000,000)
- buggy 会给 N×closePercent^7,证明非预付金腿此前也中招、修复后统一线性缩放
- 100%% 用例锚定 closePercent=1 时修复前后恒等(零影响于日常计息/全平)
SwapUnwindPrepayPrincipalBugTdd 现 12 绿。
This commit is contained in:
hjhan
2026-07-14 16:45:27 +08:00
parent 83a55dab33
commit 9bf3c4b9c6
@@ -317,6 +317,98 @@ namespace YLErp.Modules.SwapModule
"100% 平仓: 应返还本金应=Fix=9,180,000closePercent=1 时指数 bug 不显现,须保持正确)");
}
// ---- 非预付金腿(标的期初全价=9)同样验证:证明修复对所有"单利盘中"腿通用且正确 ----
// CalcDailySimpleInterest 是所有单利腿(InterestType=0)的盘中计息通用函数,非预付金专用。
// 用户关切:修复会否波及非预付金腿?结论——
// · closePercent=1(日常计息/全平)时 1^N=1=1^1,修复前后逐位恒等,零影响;
// · closePercent<1(部分平仓)时,所有单利腿此前都被同一 bug 指数级缩小,修复后统一为
// 正确的线性缩放(平仓 X% => 本金×X),这是修正而非破坏。
// 本组用非预付金腿(标的期初全价=9,orginPv 不被对齐为 Fix、走交易名义本金)独立复现并锁定。
private const decimal NonPrepayNotional = 1_000_000m;
private swap_flow_event CalcUnwindMultiDayNonPrepay(decimal closePercent, decimal notional = NonPrepayNotional,
int restDays = 7, decimal rate = 0m)
{
var extend = new trade_extend
{
TradeId = 1,
ExtendJson = JsonConvert.SerializeObject(new TradeExtendJson
{
AnnualDays = AnnualDays,
InterestCalcMode = "10", // 算头不算尾(与生产一致)
SettlementRules = 0
})
};
var td = new trade
{
id = 1, TradeNumber = "UT-NONPREPAY-EXP", ClientId = 999998,
TradeType = "收益互换", TradeDate = ProdPosiStart, StartDate = ProdPosiStart,
ExerciseDate = ProdUnwindDate.AddYears(1), TradeStatus = "确认成交", ValidState = "Valid",
StockEqvNotional = (double)notional, Notional = (double)notional,
trade_extend = extend
};
var position = new swap_position
{
id = 2002, SwapTradeId = 1, PositionType = (int)PositionTypeFlag.Unknown,
InterestDirection = (int)SwapDirectionEnum.,
InterestMode = (int)InterestModeEnum., // 非预付金腿(=9)orginPv 不会被对齐为 Fix
InterestRateDefault = rate, InterestPrincipalFix = 0m,
PosiStartDate = ProdPosiStart, PosiMatuirityDate = ProdUnwindDate.AddYears(1),
IsInitial = true, Invalid = false, InterestType = (int)InterestTypeEnum.,
IsAnnualized = true, interest_rest_days = restDays,
interest_rule = 0, FloatRateUnderlyingCode = null,
InterestSwapInterval = "[]"
};
var eod = new List<eod_swap_position>
{
new eod_swap_position
{
id = 8, SwapTradeId = 1, PositionId = 2002,
ValueDate = ProdEodValueDate,
TdInterestPrincipal = notional, // 计息基数=名义本金 → dynomicPrincipal = eodTd + notional - orginPv = notional
PosiNotionalValue = notional,
InterestProfitSum = 0m, FloatRate = 0m
}
};
// orginPv 传 notional:非预付金腿不走 877-881 的 Fix 对齐,dynomicPrincipal = notional + notional - notional = notional
var interests = _svc.GetInterests(td, td.trade_extend, ProdUnwindDate, ProdUnwindDate,
eod, new List<swap_position> { position },
notional, notional, notional, notional * closePercent, closePercent,
(int)SwapEventTypeEnum.,
false, false, 0, notional, false, settment: false, newCalcLast: false, closeList: null);
Assert.AreEqual(1, interests.Count, "非预付金腿应生成 1 条 flow_event");
return interests[0];
}
[TestMethod]
public void _部分平仓50_盘中重置周期7天_应线性缩放不受指数bug影响()
{
var fe = CalcUnwindMultiDayNonPrepay(0.5m);
Console.WriteLine($"[TDD][非预付金50%] 实测 InterestPrincipal={fe.InterestPrincipal} (buggy=7,812.5, 期望=500,000)");
// 正确:N×0.5=500,000。buggyN×0.5^7=7,812.5(同一指数 bug,证明非预付金腿此前也中招)。
Assert.AreEqual(500_000m, fe.InterestPrincipal,
"非预付金腿(标的期初全价) 50% 平仓应=名义本金×0.5=500,000,不应被 closePercent^7 缩小");
}
[TestMethod]
public void _部分平仓10_盘中重置周期7天_应线性缩放不受指数bug影响()
{
var fe = CalcUnwindMultiDayNonPrepay(0.1m);
Console.WriteLine($"[TDD][非预付金10%] 实测 InterestPrincipal={fe.InterestPrincipal} (buggy=0.1, 期望=100,000)");
Assert.AreEqual(100_000m, fe.InterestPrincipal,
"非预付金腿(标的期初全价) 10% 平仓应=名义本金×0.1=100,000,不应被 closePercent^7 缩小");
}
[TestMethod]
public void _全平_修复前后恒等_零影响()
{
// closePercent=1 时 1^N=1=1^1:这是"修复不波及非平仓/全平计息"的数学不变量证明。
var fe = CalcUnwindMultiDayNonPrepay(1m);
Console.WriteLine($"[TDD][非预付金100%] 实测 InterestPrincipal={fe.InterestPrincipal} (期望=1,000,000)");
Assert.AreEqual(NonPrepayNotional, fe.InterestPrincipal,
"非预付金腿 全平应=名义本金(closePercent=1 时修复前后恒等,日常计息/全平零影响)");
}
[TestMethod]
public void _计息基数也被根因修复_利息基于保证金本金()
{