diff --git a/UnitTestProject/Modules/SwapModule/GLMS20260817Fr007UnwindMorningTest.cs b/UnitTestProject/Modules/SwapModule/GLMS20260817Fr007UnwindMorningTest.cs index ead98451..b62a2fd3 100644 --- a/UnitTestProject/Modules/SwapModule/GLMS20260817Fr007UnwindMorningTest.cs +++ b/UnitTestProject/Modules/SwapModule/GLMS20260817Fr007UnwindMorningTest.cs @@ -508,6 +508,41 @@ namespace YLErp.Modules.SwapModule "开始日EOD快照利率=当日(首重置日)定盘——窗口为空不触发不取价分支"); } + // ── 窗口判定语义(InitInterestDate 直测;死子句 td.StartDate>interestStart 删除后的边界钉死)── + + [TestMethod] + public void WindowSemantics_NoHeadStartDay_EmptyViaFirstClause() + { + var svc = new StubSwapDealService(MakeOptUser(), new HashSet()); + var td = BuildTrade(StartDate, "00"); // ExerciseDate=7/7 + bool empty = svc.InitInterestDate(StartDate, null, td, tdClose: false, + out var start, out var end); + Assert.IsTrue(empty, "不算头首日:interestStart=7/7 > interestEnd=7/6 → 窗口为空(第一子句兜住)"); + Assert.AreEqual(StartDate, end); + } + + [TestMethod] + public void WindowSemantics_SameDaySettle_EqualDates_NotEmpty() + { + var svc = new StubSwapDealService(MakeOptUser(), new HashSet()); + var td = BuildTrade(new DateTime(2026, 7, 10), "10"); // ExerciseDate=7/11 + bool empty = svc.InitInterestDate(new DateTime(2026, 7, 10), new DateTime(2026, 7, 10), td, tdClose: false, + out var start, out var end); + Assert.IsFalse(empty, "当日已结息(日期相等)窗口非空——利息归零由 GetInterests closeList 净额层处理,不在此判定"); + Assert.AreEqual(new DateTime(2026, 7, 10), start); + Assert.AreEqual(new DateTime(2026, 7, 10), end); + } + + [TestMethod] + public void WindowSemantics_SameDaySettle_OnMaturityRollback_Empty() + { + var svc = new StubSwapDealService(MakeOptUser(), new HashSet()); + var td = BuildTrade(CloseDate, "00", exerciseDate: CloseDate); // 到期日=7/20 且不算尾 + bool empty = svc.InitInterestDate(CloseDate, CloseDate, td, tdClose: false, + out var start, out var end); + Assert.IsTrue(empty, "当日已结息+到期日不算尾回拨:interestStart=7/20 > interestEnd=7/19 → 窗口为空"); + } + // ── EOD 收盘归档路径(settment=true,此前全套件仅覆盖盘中 settment:false)── // EOD 不取尾日价的依赖链:InitInterestDate 到期日回拨(endDate=D-1) + CalcEodInterest 的 // calcToday=false(valueDate==到期日且不算尾) 整体跳过 ByEod 重算——ByEod 的取价 diff --git a/YLErpDAL/Modules/SwapModule/SwapDealService.cs b/YLErpDAL/Modules/SwapModule/SwapDealService.cs index 41e08ecf..62e90352 100644 --- a/YLErpDAL/Modules/SwapModule/SwapDealService.cs +++ b/YLErpDAL/Modules/SwapModule/SwapDealService.cs @@ -655,7 +655,8 @@ namespace YLErp.Modules.SwapModule // 计算计息区间 int interestPeriod = position.interest_rest_days ?? 1; - // true=计息窗口为空(不计利息,利率与金额归零;典型场景=互换当日已结息,但判定只看日期窗口,与事件类型无关) + // true=计息窗口为空(不计利息,利率与金额归零;典型触发=不算头首日/不算尾到期日回拨翻转, + // 判定只看日期窗口与事件类型无关;当日已结息日期相等时窗口非空,归零由下方 closeList 净额层处理) bool interestWindowEmpty = InitInterestDate(unwindDate, preDealDate, td, tdClose, out DateTime startDate, out DateTime endDate); // 获取利率(保证金/融资腿共用:SwapIntervalList 取当日适用固定利率 + 精度收口) diff --git a/YLErpDAL/Modules/SwapModule/SwapTradeBaseService.cs b/YLErpDAL/Modules/SwapModule/SwapTradeBaseService.cs index 2229c443..8a3768fd 100644 --- a/YLErpDAL/Modules/SwapModule/SwapTradeBaseService.cs +++ b/YLErpDAL/Modules/SwapModule/SwapTradeBaseService.cs @@ -365,8 +365,11 @@ namespace YLErp.Modules.SwapModule /// 计息方式 /// 计息开始日期 /// 计息结束日期 - /// true=计息窗口为空(interestStart>interestEnd,本次不计利息,调用方将利率与金额归零; - /// 典型场景=互换当日已结息/不算头首日,但判定只看日期窗口,与事件类型无关);false=正常计息。 + /// true=计息窗口为空(interestStart>interestEnd,本次不计利息,调用方将利率与金额归零); + /// 典型触发=①不算头首日(valueDate==StartDate → StartDate+1>StartDate) ②不算尾到期日回拨后窗口翻转 + /// (interestEnd=到期日−1 < interestStart)。判定只看日期窗口,与事件类型无关。 + /// 注:当日已结息(preSettleDate==valueDate)日期相等时本函数返回 false——利息归零由 + /// GetInterests 的 closeList 净额层处理,不在本判定。 public bool InitInterestDate(DateTime valueDate, DateTime? preSettleDate, trade td, bool tdClose, out DateTime interestStart, out DateTime interestEnd) { interestStart = td.StartDate.Value; @@ -383,7 +386,9 @@ namespace YLErp.Modules.SwapModule { interestEnd = interestEnd.AddDays(-1); } - if (interestStart > interestEnd || td.StartDate > interestStart) + // 原第二 OR 子句 td.StartDate > interestStart 恒 false(interestStart 经上面调整恒 ≥ td.StartDate: + // =开始日 / 不算头+1天 / preSettleDate 且仅当 ≥interestStart 才覆盖),死代码已删(2026-08-19)。 + if (interestStart > interestEnd) { interestStart = interestEnd; return true;//不记利息