diff --git a/UnitTestProject/Modules/SwapModule/DealInterestsScenarioTest.cs b/UnitTestProject/Modules/SwapModule/DealInterestsScenarioTest.cs index 42a9efec..9724c4b5 100644 --- a/UnitTestProject/Modules/SwapModule/DealInterestsScenarioTest.cs +++ b/UnitTestProject/Modules/SwapModule/DealInterestsScenarioTest.cs @@ -1447,8 +1447,10 @@ namespace YLErp.Modules.SwapModule } - [TestMethod] - public void DI_GLMS_20260421_0005_EodPartialCloseUsesRemainingCompoundAccrualWhenCalcLastFalse() + [DataTestMethod] + [DataRow((int)InterestModeEnum.合约名义本金规模)] + [DataRow((int)InterestModeEnum.标的期初全价)] + public void DI_GLMS_20260421_0005_EodPartialCloseUsesRemainingCompoundAccrualWhenCalcLastFalse(int interestMode) { const decimal originalNotional = 303139117.8m; const decimal partialNotional = 90941735.34m; @@ -1487,7 +1489,7 @@ namespace YLErp.Modules.SwapModule SwapTradeId = td.id, PositionType = (int)PositionTypeFlag.Unknown, InterestDirection = (int)SwapDirectionEnum.收取, - InterestMode = (int)InterestModeEnum.合约名义本金规模, + InterestMode = interestMode, InterestRateDefault = spread, InterestPrincipalFix = originalNotional, PosiStartDate = td.StartDate.Value, @@ -1571,38 +1573,6 @@ namespace YLErp.Modules.SwapModule Assert.IsTrue(Math.Abs(259348.386714765m - intermediateInterest.InterestAmount) <= 0.01m, $"0005 5/18 复利应承接部分平仓后的累计利息 Expected approximately 259348.386714765, Actual: {intermediateInterest.InterestAmount}"); - var intermediateEod = partialEod.Clone(); - intermediateEod.id = 18283; - intermediateEod.ValueDate = intermediateDate; - intermediateEod.InterestIncomeSum = intermediateInterest.InterestAmount; - intermediateEod.InterestProfitSum = intermediateInterest.InterestAmount; - intermediateEod.TdInterestPrincipal = remainingNotional; - intermediateEod.PosiNotionalValue = remainingNotional; - - var expectedEndFlow = new swap_flow_event { InterestRate = spread }; - decimal expectedAmountAtEnd = 0m; - decimal expectedTdAmountAtEnd = 0m; - dealService.CalcDailyCompoundInterest( - finalCloseDate, position, remainingNotional, expectedEndFlow, AnnualDays, false, - intermediateEod.FloatRate, 1m, originalNotional, true, false, - ref expectedAmountAtEnd, ref expectedTdAmountAtEnd); - var expectedPreviousFlow = new swap_flow_event { InterestRate = spread }; - decimal expectedAmountAtPreviousEod = 0m; - decimal expectedTdAmountAtPreviousEod = 0m; - dealService.CalcDailyCompoundInterest( - intermediateDate, position, remainingNotional, expectedPreviousFlow, AnnualDays, false, - intermediateEod.FloatRate, 1m, originalNotional, true, true, - ref expectedAmountAtPreviousEod, ref expectedTdAmountAtPreviousEod); - var expectedFinalInterest = intermediateEod.InterestIncomeSum - + expectedAmountAtEnd - expectedAmountAtPreviousEod; - var finalInterest = dealService.GetInterests( - td, td.trade_extend, finalCloseDate, finalCloseDate, - new List { intermediateEod }, new List { position }, - remainingNotional, remainingNotional, 0m, remainingNotional, 1m, - (int)SwapEventTypeEnum.平仓, false, false, 0m, originalNotional, - settment: false, newCalcLast: false).Single(); - AssertDecimal(expectedFinalInterest, finalInterest.InterestAmount, - "0005 最终全平重放时,历史5/18终点必须包含当日利息后再做差额"); } [TestMethod] diff --git a/YLErpDAL/Modules/SwapModule/SwapDealService.cs b/YLErpDAL/Modules/SwapModule/SwapDealService.cs index b39fe7de..9575f2b0 100644 --- a/YLErpDAL/Modules/SwapModule/SwapDealService.cs +++ b/YLErpDAL/Modules/SwapModule/SwapDealService.cs @@ -1249,8 +1249,8 @@ namespace YLErp.Modules.SwapModule var interestAtPreviousEod = new swap_flow_event { InterestRate = rate }; decimal amountAtPreviousEod = 0m; decimal tdAmountAtPreviousEod = 0m; - // The previous EOD is a historical endpoint, not the contract tail. - // Include that day's accrual even when the final contract date omits its tail. + // 最终日重放仍遵守交易的 calcLast;上一日终是历史截点而非合约尾日, + // 因此此处按闭区间包含上一日终当天,避免算头不算尾时重复加入该日利息。 CalcDailyCompoundInterest(preEodPosition.ValueDate, position, closePosiNotionalValue, interestAtPreviousEod, annualDays, needPrice, floateRate, closePrecent, orginPv, calcFirst, true, ref amountAtPreviousEod, ref tdAmountAtPreviousEod, consumedInterest); diff --git a/YLErpDAL/Modules/SwapModule/SwapEodPositionService.cs b/YLErpDAL/Modules/SwapModule/SwapEodPositionService.cs index 4d9b1b48..e3122909 100644 --- a/YLErpDAL/Modules/SwapModule/SwapEodPositionService.cs +++ b/YLErpDAL/Modules/SwapModule/SwapEodPositionService.cs @@ -1418,11 +1418,12 @@ namespace YLErp.Modules.SwapModule && closePercent > 0m && closePercent < 1m && posiNotionalValue > 0m && position.InterestType == (int)InterestTypeEnum.复利 - && position.InterestMode == (int)InterestModeEnum.合约名义本金规模) + && (position.InterestMode == (int)InterestModeEnum.合约名义本金规模 + || position.InterestMode == (int)InterestModeEnum.标的期初全价)) { - // 部分平仓日终的当日新增复利要保留未平仓本金的贡献。 - // 算尾时用平仓前全额本金;不算尾时只保留剩余本金,避免把 - // 30% 已平仓部分的当日利息再次带入后续日终。 + // 模式2(合约名义本金规模)和模式9(标的期初全价)都以名义本金 + // 作为复利基数,适用同一部分平仓递推;算尾用平仓前全额当日利息 + // 再扣实际结算,不算尾只计剩余本金,避免已平部分利息进入后续复利。 var fullPrincipal = lastTdInterestPrincipal > 0m ? lastTdInterestPrincipal : oriPosiNotionalValue;