fix(swap): 修复重置日期利息计算逻辑

- 修正了 calcLast=false 时重置日取价被跳过的问题
- 确保重置日之前的复利能够正确并入本金
- 调整了计息日判断逻辑的位置以保证本金重置正常执行
- 修复了重置日当天利息不应被计入的逻辑错误
This commit is contained in:
张名锐
2026-08-07 21:54:04 +08:00
parent 19d8b2a926
commit 569002e551
2 changed files with 83 additions and 7 deletions
@@ -369,6 +369,67 @@ namespace YLErp.Modules.SwapModule
Assert.IsTrue(Math.Abs((result.FloatRate ?? 0) - (decimal)rateNew) < 0.0001m,
$"平仓日=重置日时 FloatRate 应={rateNew}(取到新利率)。" +
$"实际={result.FloatRate},若={rateOld} 说明 calcLast=false 跳过了重置日取价(GLMS-JIATT-20260805 根因)");
var interestBeforeResetDate = Principal * (FixedRate + (decimal)rateOld) * 7m / AnnualDays;
AssertDecimal(Principal + interestBeforeResetDate, result.InterestPrincipal,
"calcLast=false 的重置日仍应将前 7 天复利并入本金");
AssertDecimal(interestBeforeResetDate, result.InterestAmount,
"calcLast=false 不应计入重置日当天利息");
}
[TestMethod]
public void CI_008_ResetDayPartialCloseCarriesRemainingInterestIntoPrincipal()
{
const decimal previousPrincipal = 50061728.39m;
const decimal remainingPrincipal = 30037037.04m;
const decimal previousInterest = 7425.050203320057m;
const decimal fixedRate = 0.001234m;
const double oldFloatRate = 0.0123;
const double newFloatRate = 0.0213;
var startDate = new DateTime(2026, 7, 28);
var resetDate = new DateTime(2026, 8, 4);
var service = new StubSwapDealService(
new OptUserInfo(0, nameof(ConsumedInterestScenarioTest), OptUserFrom.UnitTest),
d => d >= resetDate ? newFloatRate : oldFloatRate);
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 = fixedRate,
PosiStartDate = startDate, PosiMatuirityDate = ExerciseDate,
IsAnnualized = true, interest_rest_days = 7, interest_rule = 0,
FloatRateUnderlyingCode = "FR007",
InterestSwapInterval = JsonConvert.SerializeObject(new List<IntervalModel>
{
new IntervalModel { Date = ExerciseDate, Rate = fixedRate, Settlement = 0 }
})
};
var preEod = new eod_swap_position
{
id = 1, PositionId = position.id, ValueDate = resetDate.AddDays(-1),
TdInterestPrincipal = previousPrincipal,
InterestIncomeSum = previousInterest,
InterestProfitSum = previousInterest,
FloatRate = (decimal)oldFloatRate
};
var result = service.GetInterests(td, td.trade_extend, resetDate, resetDate,
new List<eod_swap_position> { preEod }, new List<swap_position> { position },
remainingPrincipal, remainingPrincipal, 0m, remainingPrincipal, 1m,
(int)SwapEventTypeEnum., true, false, 0m, remainingPrincipal,
add: false, settment: false, newCalcLast: false).Single();
var remainingInterest = previousInterest * remainingPrincipal / previousPrincipal;
var expectedPrincipal = remainingPrincipal + remainingInterest;
var expectedDailyInterest = expectedPrincipal * (fixedRate + (decimal)newFloatRate) / AnnualDays;
AssertDecimal(expectedPrincipal, result.InterestPrincipal);
AssertDecimal(expectedDailyInterest,
result.InterestPrincipal * (result.InterestRate + result.FloatRate.Value) / AnnualDays);
}
}
}