diff --git a/UnitTestProject/Modules/SwapModule/GLMS20260817Fr007UnwindMorningTest.cs b/UnitTestProject/Modules/SwapModule/GLMS20260817Fr007UnwindMorningTest.cs index cd559fdb..ead98451 100644 --- a/UnitTestProject/Modules/SwapModule/GLMS20260817Fr007UnwindMorningTest.cs +++ b/UnitTestProject/Modules/SwapModule/GLMS20260817Fr007UnwindMorningTest.cs @@ -91,6 +91,7 @@ namespace YLErp.Modules.SwapModule { public swap_flow_event Fe; public Exception Ex; + public StubSwapDealService Svc; public bool Threw => Ex != null; } @@ -156,14 +157,14 @@ namespace YLErp.Modules.SwapModule }; } - private static eod_swap_position BuildPreEod(DateTime valueDate) + private static eod_swap_position BuildPreEod(DateTime valueDate, decimal floatRate = 0.01425m) { return new eod_swap_position { id = 5001, PositionId = 1001, ValueDate = valueDate, - FloatRate = 0.01425m, + FloatRate = floatRate, InterestProfitSum = -100000m, TdInterestPrincipal = Notional, InterestIncomeSum = -150000m @@ -211,7 +212,7 @@ namespace YLErp.Modules.SwapModule (int)SwapEventTypeEnum.平仓, false, Notional, false, settment: settment, newCalcLast: newCalcLast, closeList: null); Assert.AreEqual(1, interests.Count, "应返回恰好 1 条利息事件"); - return new Outcome { Fe = interests[0] }; + return new Outcome { Fe = interests[0], Svc = svc }; } catch (Exception ex) { @@ -473,6 +474,40 @@ namespace YLErp.Modules.SwapModule "事件利率必须与平仓时刻(尾日价发布前后)无关"); } + // ── 快照利率携带契约(carry-forward):带 preEod 时不重复取 ≤ValueDate 的重置日 ── + // fetchAfterDate=preEod.ValueDate + seed=preEod.FloatRate 是设计分工:≤上一日终的重置日 + // 沿用快照携带的"截至 ValueDate 生效利率"(真实 EOD 快照由当日重置日再定盘写入), + // >上一日终的重新取价。与无日终场景的根本区别:种子有正确来源,不需要强制重取首重置日。 + + [TestMethod] + public void CarryForward_Simple_NoHead_PreEodAtStartCarriesFirstPeriodRate() + { + // preEod.ValueDate=开始日(7/6),FloatRate=7/6定盘0.0142(模拟开始日EOD快照的真实语义) + var o = Run(InterestTypeEnum.单利, includeCloseDate: true, calcMode: "00", + closeDate: new DateTime(2026, 7, 10), preEod: BuildPreEod(StartDate, 0.0142m)); + Assert.IsFalse(o.Threw, "不应抛:" + o.Ex?.Message); + Assert.AreEqual(0, o.Svc.PricedDates.Count, + "≤上一日终(7/6)的重置日不重复取价——首段利率由快照携带(carry-forward 契约)"); + Assert.AreEqual(0.0142m, o.Fe.FloatRate, + "首段(也是末段)利率=快照携带的 7/6 定盘"); + // 不算头不算尾:计息日 [7/7,7/10) 共 3 天 @ (spread+0.0142);单利重放含上日待实现(-100000) + Assert.AreEqual(-100000m + Notional * (Spread + 0.0142m) * 3m / AnnualDays, o.Fe.InterestAmount, 0.0000001m, + "金额=上日待实现+3天×(spread+快照利率),首段未误用种子外的任何值"); + } + + [TestMethod] + public void Eod_NoHead_StartDay_SnapshotRateCarriesFirstFixing() + { + // 链条起点钉死:开始日当天 EOD("00",窗口为空 interestStart=7/7>interestEnd=7/6)—— + // 窗口为空时"不算尾不取价"分支不命中,走正常取价,快照 FloatRate=开始日定盘。 + // 次日重放才能以 fetchAfter=开始日 + 携带利率=开始日定盘 正确续算(见上一用例)。 + var o = Run(InterestTypeEnum.单利, includeCloseDate: true, calcMode: "00", + closeDate: StartDate, settment: true); + AssertNoThrow(o, "开始日EOD(00) 不应抛"); + Assert.AreEqual(0.0142m, o.Fe.FloatRate, + "开始日EOD快照利率=当日(首重置日)定盘——窗口为空不触发不取价分支"); + } + // ── EOD 收盘归档路径(settment=true,此前全套件仅覆盖盘中 settment:false)── // EOD 不取尾日价的依赖链:InitInterestDate 到期日回拨(endDate=D-1) + CalcEodInterest 的 // calcToday=false(valueDate==到期日且不算尾) 整体跳过 ByEod 重算——ByEod 的取价