diff --git a/Framework/YLErp.Core/Interest/AccrualTrace.cs b/Framework/YLErp.Core/Interest/AccrualTrace.cs index 25775291..108b2692 100644 --- a/Framework/YLErp.Core/Interest/AccrualTrace.cs +++ b/Framework/YLErp.Core/Interest/AccrualTrace.cs @@ -41,10 +41,10 @@ public sealed class AccrualTrace => Add(AccrualTraceEvent.DayAccrual, segStart, $" [seg{idx}] [{segStart:yyyy-MM-dd},{segEnd:yyyy-MM-dd}) days={days} rate={rate:P6} basis={basis:F4} segInterest={segInterest:F6} acc={accumulated:F6}"); - /// EOD 上下文:计息日、重置日标志、平仓比例、差分基数公式各分量。供日终单日计息定位"重置日分支选对了吗"。 - public void EodContext(DateTime eodDate, bool isResetDay, decimal unwindFraction, decimal priorAccrued, decimal priorNotional, decimal notional, decimal baseNotional, decimal remainingFraction) + /// EOD 上下文:计息日、重置日标志、平仓比例、基数各分量。供日终单日计息定位"重置日分支选对了吗"。 + public void EodContext(DateTime eodDate, bool isResetDay, decimal unwindFraction, decimal priorAccrued, decimal priorNotional, decimal notional, decimal remainingFraction) => Add(AccrualTraceEvent.Start, eodDate, - $" CTX {eodDate:yyyy-MM-dd} reset={isResetDay} unwind={unwindFraction:P2} priorAccrued={priorAccrued:F4} priorNotional={priorNotional:F4} notional={notional:F4} baseNotional={baseNotional:F4} remainingFrac={remainingFraction:P4}"); + $" CTX {eodDate:yyyy-MM-dd} reset={isResetDay} unwind={unwindFraction:P2} priorAccrued={priorAccrued:F4} priorNotional={priorNotional:F4} notional={notional:F4} remainingFrac={remainingFraction:P4}"); /// 重置日:生效利率(旧)与计息本金(滚动前)。利率/本金切换的"因"。 public void ResetBefore(DateTime resetDate, decimal rateOld, decimal principalBefore) diff --git a/UnitTestProject/Modules/SwapModule/Accrual/CompoundEodShadowTest.cs b/UnitTestProject/Modules/SwapModule/Accrual/CompoundEodShadowTest.cs index ccb578be..4c2af32c 100644 --- a/UnitTestProject/Modules/SwapModule/Accrual/CompoundEodShadowTest.cs +++ b/UnitTestProject/Modules/SwapModule/Accrual/CompoundEodShadowTest.cs @@ -100,7 +100,7 @@ namespace UnitTestProject.Modules.SwapModule.Accrual var policy = new AccrualPolicy(AccrualBoundary.Both, true, 7, AnnualDays, true); var remainingPercent = Math.Max(0m, Math.Min(1m, Notional / Notional)); var result = FundingLegAccrual.AccrueCompoundEod( - 50_000m, Notional, Notional, 1m, Notional, rate, policy, + 50_000m, Notional, Notional, 1m, rate, policy, isResetDay: true, remainingPercent, EodDate); Console.WriteLine($"重置日: 旧 InterestAmount={oldInterest} Td={oldTd}"); @@ -132,7 +132,7 @@ namespace UnitTestProject.Modules.SwapModule.Accrual var rate = FundingLegRate.Fixed(FixedRate); var policy = new AccrualPolicy(AccrualBoundary.Both, true, 7, AnnualDays, true); var result = FundingLegAccrual.AccrueCompoundEod( - 30_000m, Notional, Notional, 1m, Notional, rate, policy, + 30_000m, Notional, Notional, 1m, rate, policy, isResetDay: false, 0m, nonResetDate); Console.WriteLine($"非重置日: 旧 InterestAmount={oldInterest} Td={oldTd}"); diff --git a/YLErpDAL/Modules/SwapModule/Accrual/FundingLegAccrual.cs b/YLErpDAL/Modules/SwapModule/Accrual/FundingLegAccrual.cs index 9a75aca4..4e3cd113 100644 --- a/YLErpDAL/Modules/SwapModule/Accrual/FundingLegAccrual.cs +++ b/YLErpDAL/Modules/SwapModule/Accrual/FundingLegAccrual.cs @@ -11,51 +11,35 @@ namespace YLErp.Modules.SwapModule.Accrual; /// - accrued → 累计应计利息 /// - unwindFraction → 平仓比例(0~1) /// - realizedInterest → 历史已结利息(legacy: consumedInterest) -/// - baseNotional → 差分公式基数(legacy: orginPv/originalPv) -/// - accrualBasis → 逐段滚动的计息基数(legacy: dynomicPrincipal=dynamicPrincipal typo) +/// - priorNotional → 昨日终滚动计息基数(legacy: TdInterestPrincipal / dynomicPrincipal) /// public static class FundingLegAccrual { private const int Precision = SwapInterest.FundingLegPrecision; - // ─────────────────────────────────────────────────────────────────── - // 计息基数(basis)——单一真相源,纯函数与调用方共用,消除公式重复。 - // - // 差分语义:basis = priorNotional + (notional − baseNotional) - // • priorNotional = 昨日终滚动计息基数(legacy: TdInterestPrincipal / dynomicPrincipal) - // • notional − baseNotional = 名义本金绝对增量(加法式 carry,非比例缩放) - // • EOD 路径:baseNotional == notional → 增量 0,basis 退化 = priorNotional - // • unwind 路径:baseNotional = 上一日终浮动端名义本金(orginPv) ≠ notional → 增量生效 - // ─────────────────────────────────────────────────────────────────── - - /// 单利日终计息基数(差分公式)。调用方与 AccrueSimpleEod 共用。 - public static decimal SimpleEodBasis(decimal priorNotional, decimal notional, decimal baseNotional) - => priorNotional + (notional - baseNotional); - - /// 复利日终计息基数:重置日 = notional + 累计利息×剩余比例(并本金); - /// 非重置日 = 差分公式。remainingFraction 对齐 legacy 钳制到 [0,1]。调用方与 AccrueCompoundEod 共用。 + /// 复利日终计息基数(单一真相源,纯函数与调用方共用): + /// 重置日 = notional + 累计利息×剩余比例(利息并入本金);非重置日 = priorNotional(昨日滚动基数)。 + /// remainingFraction 对齐 legacy 钳制到 [0,1]。 public static decimal CompoundEodBasis( - bool isResetDay, decimal notional, decimal priorAccrued, decimal remainingFraction, - decimal priorNotional, decimal baseNotional) + bool isResetDay, decimal notional, decimal priorAccrued, decimal remainingFraction, decimal priorNotional) => isResetDay ? notional + priorAccrued * Math.Max(0m, Math.Min(1m, remainingFraction)) - : priorNotional + (notional - baseNotional); + : priorNotional; /// /// 单利日终计息(替换 CalcDailySimpleInterestByEod 的纯数学部分)。 + /// EOD 无差分:basis = priorNotional(昨日终滚动计息基数)。 /// public static InterestResult AccrueSimpleEod( decimal priorAccrued, decimal priorNotional, - decimal notional, decimal unwindFraction, - decimal baseNotional, FundingLegRate rate, AccrualPolicy policy, DateTime eodDate, AccrualTrace? trace = null) { - var basis = SimpleEodBasis(priorNotional, notional, baseNotional); + var basis = priorNotional; var displayBasis = basis * unwindFraction; var allInRate = rate.AllInRate; @@ -80,14 +64,13 @@ public static class FundingLegAccrual /// /// 复利日终计息(替换 CalcDailyCompoundInterestByEod 的纯数学部分)。 /// 重置日:basis = notional + priorAccrued × remainingFraction(利息并入本金)。 - /// 非重置日:basis = 差分公式(priorNotional + notional − baseNotional)。 + /// 非重置日:basis = priorNotional(昨日终滚动计息基数)。 /// public static InterestResult AccrueCompoundEod( decimal priorAccrued, decimal priorNotional, decimal notional, decimal unwindFraction, - decimal baseNotional, FundingLegRate rate, AccrualPolicy policy, bool isResetDay, @@ -95,11 +78,11 @@ public static class FundingLegAccrual DateTime eodDate, AccrualTrace? trace = null) { - var basis = CompoundEodBasis(isResetDay, notional, priorAccrued, remainingFraction, priorNotional, baseNotional); + var basis = CompoundEodBasis(isResetDay, notional, priorAccrued, remainingFraction, priorNotional); var displayBasis = basis * unwindFraction; var allInRate = rate.AllInRate; - trace?.EodContext(eodDate, isResetDay, unwindFraction, priorAccrued, priorNotional, notional, baseNotional, remainingFraction); + trace?.EodContext(eodDate, isResetDay, unwindFraction, priorAccrued, priorNotional, notional, remainingFraction); var dayInterest = displayBasis * allInRate; var tdInterest = basis * allInRate; if (policy.IsAnnualized) diff --git a/YLErpDAL/Modules/SwapModule/SwapDealService.cs b/YLErpDAL/Modules/SwapModule/SwapDealService.cs index 974c7007..39f38529 100644 --- a/YLErpDAL/Modules/SwapModule/SwapDealService.cs +++ b/YLErpDAL/Modules/SwapModule/SwapDealService.cs @@ -1541,7 +1541,6 @@ namespace YLErp.Modules.SwapModule priorNotional: preEodPosition.TdInterestPrincipal, notional: posiPrincipal, unwindFraction: closePercent, - baseNotional: orginPv, rate: legRate, policy: accrualPolicy, isResetDay: isResetDay, @@ -1551,10 +1550,10 @@ namespace YLErp.Modules.SwapModule SwapCalcTrace.Write(interestTrace); // flowEvent.InterestPrincipal:当日计息基数(已按平仓比例缩放)——下游 EOD 用它播种次日 TdInterestPrincipal。 - // 复用 CompoundEodBasis 单一真相源(与 AccrueCompoundEod 内部同一公式),不再重复三元表达式。 + // 复用 CompoundEodBasis 单一真相源(与 AccrueCompoundEod 内部同一公式)。 flowEvent.InterestPrincipal = FundingLegAccrual.CompoundEodBasis( isResetDay, posiPrincipal, preEodPosition.InterestProfitSum, remainingFraction, - preEodPosition.TdInterestPrincipal, orginPv) * closePercent; + preEodPosition.TdInterestPrincipal) * closePercent; InterestAmount = result.Accrued; TdInterestAmount = result.AccruedToday; @@ -1608,9 +1607,7 @@ namespace YLErp.Modules.SwapModule var result = FundingLegAccrual.AccrueSimpleEod( priorAccrued: preEodPosition.InterestProfitSum, priorNotional: preEodPosition.TdInterestPrincipal, - notional: posiPrincipal, unwindFraction: closePercent, - baseNotional: orginPv, rate: legRate, policy: accrualPolicy, eodDate: endDate,