diff --git a/UnitTestProject/Modules/SwapModule/ConsumedInterestScenarioTest.cs b/UnitTestProject/Modules/SwapModule/ConsumedInterestScenarioTest.cs index 0d603c62..ca3ea968 100644 --- a/UnitTestProject/Modules/SwapModule/ConsumedInterestScenarioTest.cs +++ b/UnitTestProject/Modules/SwapModule/ConsumedInterestScenarioTest.cs @@ -431,5 +431,47 @@ namespace YLErp.Modules.SwapModule AssertDecimal(expectedDailyInterest, result.InterestPrincipal * (result.InterestRate + result.FloatRate.Value) / AnnualDays); } + + [TestMethod] + public void CI_009_NonResetUnwindWithCalcLastFalseUsesPreviousEodPendingInterest() + { + const decimal pendingInterest = 10019.043756537721m; + const decimal remainingPrincipal = 30041492.070122881942m; + var startDate = new DateTime(2026, 7, 28); + var unwindDate = new DateTime(2026, 8, 7); + var td = CreateTrade(); + td.StartDate = startDate; + td.TradeDate = startDate; + var position = new swap_position + { + id = 1001, SwapTradeId = td.id, + InterestDirection = (int)SwapDirectionEnum.收取, + InterestMode = (int)InterestModeEnum.标的期初全价, + InterestType = (int)InterestTypeEnum.复利, + InterestRateDefault = 0.001234m, + PosiStartDate = startDate, PosiMatuirityDate = ExerciseDate, + IsAnnualized = true, interest_rest_days = 7, + FloatRateUnderlyingCode = "FR007" + }; + var preEod = new eod_swap_position + { + id = 1, PositionId = position.id, ValueDate = unwindDate.AddDays(-1), + TdInterestPrincipal = remainingPrincipal, + InterestIncomeSum = pendingInterest, + InterestProfitSum = pendingInterest, + FloatRate = 0.0213m + }; + var service = new StubSwapDealService( + new OptUserInfo(0, nameof(ConsumedInterestScenarioTest), OptUserFrom.UnitTest), 0.0213, 0m); + + var result = service.GetInterests(td, td.trade_extend, unwindDate, unwindDate, + new List { preEod }, new List { position }, + remainingPrincipal, remainingPrincipal, 0m, remainingPrincipal, 1m, + (int)SwapEventTypeEnum.平仓, false, false, 0m, remainingPrincipal, + add: false, settment: false, newCalcLast: false).Single(); + + AssertDecimal(pendingInterest, result.InterestAmount, + "calcLast=false must not accrue unwind-date interest after the previous EOD"); + } } } diff --git a/YLErpDAL/Modules/SwapModule/SwapDealService.cs b/YLErpDAL/Modules/SwapModule/SwapDealService.cs index 42704fed..b69b225b 100644 --- a/YLErpDAL/Modules/SwapModule/SwapDealService.cs +++ b/YLErpDAL/Modules/SwapModule/SwapDealService.cs @@ -1205,6 +1205,21 @@ namespace YLErp.Modules.SwapModule var floateRate = preEodPosition.FloatRate; if (position.InterestType == (int)InterestTypeEnum.复利) { + var daysFromStart = (endDate - position.PosiStartDate).Days; + var daysFromPreEod = preEodPosition.id != 0 + ? (endDate - preEodPosition.ValueDate).Days + : 0; + if (!calcLast && daysFromPreEod == 1 && daysFromStart % (position.interest_rest_days ?? 1) != 0) + { + interest.InterestPrincipal = preEodPosition.TdInterestPrincipal * closePrecent; + interest.FloatRate = preEodPosition.FloatRate; + InterestAmount = preEodPosition.InterestIncomeSum * closePrecent; + TdInterestAmount = preEodPosition.InterestIncomeSum; + interest.InterestAmount = Math.Round(InterestAmount, InterestCalculationPrecision, MidpointRounding.AwayFromZero); + interest.TdInterestAmount = Math.Round(TdInterestAmount, InterestCalculationPrecision, MidpointRounding.AwayFromZero); + interest.InterestClosePnL = interest.InterestAmount * interestRatio; + return interest; + } var remainingPercent = preEodPosition.TdInterestPrincipal > 0m ? closePosiNotionalValue / preEodPosition.TdInterestPrincipal : 1m;