diff --git a/UnitTestProject/Modules/SwapModule/GLMS20260817Fr007UnwindMorningTest.cs b/UnitTestProject/Modules/SwapModule/GLMS20260817Fr007UnwindMorningTest.cs index b62a2fd3..a83a0c41 100644 --- a/UnitTestProject/Modules/SwapModule/GLMS20260817Fr007UnwindMorningTest.cs +++ b/UnitTestProject/Modules/SwapModule/GLMS20260817Fr007UnwindMorningTest.cs @@ -577,9 +577,9 @@ namespace YLErp.Modules.SwapModule StringAssert.Contains(o.Ex.Message, "FR007"); } - // ── 到期日当天全平:replayEndDate=endDate+1 补计分支(relaxedFixingFromDate 的存在理由)── + // ── 到期日当天全平:replayEndDate=endDate+1 补计分支(exclusionStart 的存在理由)── // 不算尾时 InitInterestDate 把 endDate 回拨一天;最终全平的历史差分重放需把窗口补回真实 - // 平仓/到期日(replayEndDate=endDate+1),但该边界日的定盘经 relaxedFixingFromDate 标记为 + // 平仓/到期日(replayEndDate=endDate+1),但该边界日的定盘经 exclusionStart 标记为 // "有价则取/缺价跳过"——否则到期日上午全平会被尾日价误拦(EQD-6968 在到期日的镜像场景)。 [TestMethod] diff --git a/YLErpDAL/Modules/SwapModule/SwapDealService.cs b/YLErpDAL/Modules/SwapModule/SwapDealService.cs index 62e90352..44714bd6 100644 --- a/YLErpDAL/Modules/SwapModule/SwapDealService.cs +++ b/YLErpDAL/Modules/SwapModule/SwapDealService.cs @@ -1294,7 +1294,7 @@ namespace YLErp.Modules.SwapModule // 计算截至本次平仓日的累计利息 amountAtEnd CalcDailyCompoundInterest(replayEndDate, position, closePosiNotionalValue, interestAtEnd, annualDays, floateRate, closePrecent, - calcFirst, calcLast, ref amountAtEnd, ref tdAmountAtEnd, consumedInterest, relaxedFixingFromDate: endDate); + calcFirst, calcLast, ref amountAtEnd, ref tdAmountAtEnd, consumedInterest, exclusionStart: endDate); var interestAtPreviousEod = new swap_flow_event { InterestRate = rate }; decimal amountAtPreviousEod = 0m; decimal tdAmountAtPreviousEod = 0m; @@ -1356,11 +1356,13 @@ namespace YLErp.Modules.SwapModule /// /// 按重置周期切分利率段,每段记录 all-in 利率(spread+fixing)。返回 (分段列表, 末段浮动利率)。 /// fetchAfterDate: 仅该日期之后的重置日才取 FR007(单利传 ValueDate,复利传 null 全程取)。 + /// exclusionStart: 排除区间起点——该日期起(含)的重置日视为"排除日"(不算尾的不计息边界日), + /// 一概不取价;缺省=endDate。仅不算尾(calcLast=false)生效;算尾所有重置日照常强制取价。 /// private (List<(DateTime StartDate, decimal Rate)> Segments, decimal LastFloat) BuildSegmentRates( DateTime startDate, DateTime endDate, int interestPeriod, swap_position position, decimal spread, decimal initialFloat, - DateTime? fetchAfterDate, bool calcLast = true, DateTime? relaxedFixingFromDate = null) + DateTime? fetchAfterDate, bool calcLast = true, DateTime? exclusionStart = null) { var rates = new List<(DateTime, decimal)>(); var calcDays = (endDate - startDate).Days; @@ -1368,7 +1370,7 @@ namespace YLErp.Modules.SwapModule SwapCalcTrace.Critical( $"FIX Segments p{position.id} [{startDate:yyyy-MM-dd}→{endDate:yyyy-MM-dd}] period={interestPeriod} " + $"fetchAfter={(fetchAfterDate?.ToString("yyyy-MM-dd") ?? "全程")} calcLast={calcLast} " + - $"排除起点={(relaxedFixingFromDate?.ToString("yyyy-MM-dd") ?? (calcLast ? "无" : endDate.ToString("yyyy-MM-dd")))} seed={initialFloat:P6} spread={spread:P6}"); + $"排除起点={(exclusionStart?.ToString("yyyy-MM-dd") ?? (calcLast ? "无" : endDate.ToString("yyyy-MM-dd")))} seed={initialFloat:P6} spread={spread:P6}"); for (int i = 0; i <= calcDays; i += interestPeriod) { var resetDate = startDate.AddDays(i); @@ -1377,7 +1379,7 @@ namespace YLErp.Modules.SwapModule // 排除日(EQD-6968 自洽化)一概不取价(有价也不取),currentFloat 保持末段已消费利率: // 事件/快照回写的浮动利率与金额同源、与平仓时刻无关。剩余持仓的新周期利率由 // SwapEodPositionService 的"重置日再定盘"显式获取,不靠排除日顺带。算尾照常强制取价。 - bool isExcludedEnd = !calcLast && resetDate >= (relaxedFixingFromDate ?? endDate); + bool isExcludedEnd = !calcLast && resetDate >= (exclusionStart ?? endDate); if (needFetch && !isExcludedEnd) { currentFloat = ResolveFloatRate(position, resetDate, currentFloat); @@ -1405,17 +1407,17 @@ namespace YLErp.Modules.SwapModule /// public void CalcDailyCompoundInterest(DateTime endDate, swap_position position, decimal principal, swap_flow_event flowEvent, int annualDays, decimal floateRate, decimal closePercent, bool calcFirst, bool calcLast, - ref decimal InterestAmount, ref decimal TdInterestAmount, decimal consumedInterest = 0m, decimal resetCarryInterest = 0m, DateTime? relaxedFixingFromDate = null) + ref decimal InterestAmount, ref decimal TdInterestAmount, decimal consumedInterest = 0m, decimal resetCarryInterest = 0m, DateTime? exclusionStart = null) { var startDate = position.PosiStartDate; int interestPeriod = position.interest_rest_days ?? 1; // 分段取率:复利全程重放,每个重置日(含 startDate)取 FR007(fetchAfterDate=null)。 // calcLast 透传,与下方 AccruePeriod 的边界同源,避免两处漂移; - // relaxedFixingFromDate 仅重放补计不算尾漏计利息时非 null(=真实平仓日),锚定尾日跳过取价起点。 + // exclusionStart 仅重放补计不算尾漏计利息时非 null(=真实平仓日),锚定尾日跳过取价起点。 var (segmentRates, currentFloat) = BuildSegmentRates( startDate, endDate, interestPeriod, position, flowEvent.InterestRate, floateRate, - fetchAfterDate: null, calcLast: calcLast, relaxedFixingFromDate: relaxedFixingFromDate); + fetchAfterDate: null, calcLast: calcLast, exclusionStart: exclusionStart); // 纯函数复利计息:分段重置日并本金 + resetCarryInterest + 扣 consumedInterest var interestTrace = new AccrualTrace(); @@ -1445,7 +1447,7 @@ namespace YLErp.Modules.SwapModule /// /// 计算单利 盘中(按重置天数分段,每段使用对应浮动利率) /// - public void CalcDailySimpleInterest(eod_swap_position preEodPosition, DateTime endDate, swap_position position, decimal posiPrincipal, swap_flow_event flowEvent, int annualDays, decimal floateRate, decimal closePercent, decimal orginPv, bool calcFirst, bool calcLast, ref decimal InterestAmount, ref decimal TdInterestAmount, decimal consumedInterest = 0m, DateTime? relaxedFixingFromDate = null) + public void CalcDailySimpleInterest(eod_swap_position preEodPosition, DateTime endDate, swap_position position, decimal posiPrincipal, swap_flow_event flowEvent, int annualDays, decimal floateRate, decimal closePercent, decimal orginPv, bool calcFirst, bool calcLast, ref decimal InterestAmount, ref decimal TdInterestAmount, decimal consumedInterest = 0m, DateTime? exclusionStart = null) { var startDate = position.PosiStartDate; int interestPeriod = position.interest_rest_days ?? 1; @@ -1458,7 +1460,7 @@ namespace YLErp.Modules.SwapModule // calcLast 透传,与下方 AccruePeriod 的边界同源,避免两处漂移。 var (segmentRates, currentFloat) = BuildSegmentRates( startDate, endDate, interestPeriod, position, flowEvent.InterestRate, floateRate, - fetchAfterDate: preEodPosition.ValueDate, calcLast: calcLast, relaxedFixingFromDate: relaxedFixingFromDate); + fetchAfterDate: preEodPosition.ValueDate, calcLast: calcLast, exclusionStart: exclusionStart); // 纯函数计息:Accrued=缩放累计(InterestAmount),AccruedToday=未缩放累计(TdInterestAmount) var interestTrace = new AccrualTrace();