fix(swap): 修复复利计算和部分平仓处理中的多个问题(算头算尾合约复利平仓)

- 修复合约名义本金规模模式下部分平仓时名义本金计算错误
- 修复最终全平时复利利息计算中历史平仓尾差处理问题
- 修复复利重置日处理中利息重复注入历史本金的错误
- 修复部分平仓后日终待实现利息计算中本金比例应用问题
- 修复全平且实际金额覆盖应结利息后待实现利息清零逻辑
- 新增复合复利交换服务用于单元测试验证
- 添加多个测试用例覆盖复利计算边界场景
This commit is contained in:
张名锐
2026-08-08 15:51:29 +08:00
parent c379e31b4d
commit 6fdc7d80b2
3 changed files with 538 additions and 21 deletions
+34 -2
View File
@@ -830,6 +830,12 @@ namespace YLErp.Modules.SwapModule
// 计算名义本金
var (closePrincipal, posiPrincipal, newClosePercent) = CalcNotionalByMode(position, closePrecent, posiNotionalValue, posiLongNotionalValue, posiShortNotionalValue);
if ((InterestModeEnum)position.InterestMode == InterestModeEnum.
|| (InterestModeEnum)position.InterestMode == InterestModeEnum.
&& posiNotionalValue == 0m)
{
closePrincipal = closePosiNotionalValue;
}
if ((InterestModeEnum)position.InterestMode == InterestModeEnum. || (InterestModeEnum)position.InterestMode == InterestModeEnum.)
{
positionClone.InterestDirection = position.InterestDirection == (int)SwapDirectionEnum. ? (int)SwapDirectionEnum. : (int)SwapDirectionEnum.;
@@ -910,6 +916,9 @@ namespace YLErp.Modules.SwapModule
closePrincipal = posiShort * closePercent;
posiPrincipal = posiShort;
break;
case InterestModeEnum.:
closePrincipal = posiNotional * closePercent;
break;
case InterestModeEnum.:
closePrincipal = posiNotional * closePercent;
break;
@@ -1228,6 +1237,24 @@ namespace YLErp.Modules.SwapModule
CalcDailyCompoundInterest(endDate, position, closePosiNotionalValue, interest, annualDays, needPrice,
floateRate, closePrecent, orginPv, calcFirst, calcLast, ref InterestAmount, ref TdInterestAmount,
consumedInterest, resetCarryInterest);
if (preEodPosition.id != 0 && closePrecent == 1m)
{
// 最终全平只重放上一日终之后的新增利息;历史部分平仓的两位结算尾差已在日终待实现中。
var interestAtEnd = new swap_flow_event { InterestRate = rate };
decimal amountAtEnd = 0m;
decimal tdAmountAtEnd = 0m;
CalcDailyCompoundInterest(endDate, position, closePosiNotionalValue,
interestAtEnd, annualDays, needPrice, floateRate, closePrecent, orginPv,
calcFirst, calcLast, ref amountAtEnd, ref tdAmountAtEnd, consumedInterest);
var interestAtPreviousEod = new swap_flow_event { InterestRate = rate };
decimal amountAtPreviousEod = 0m;
decimal tdAmountAtPreviousEod = 0m;
CalcDailyCompoundInterest(preEodPosition.ValueDate, position, closePosiNotionalValue,
interestAtPreviousEod, annualDays, needPrice, floateRate, closePrecent, orginPv,
calcFirst, calcLast, ref amountAtPreviousEod, ref tdAmountAtPreviousEod, consumedInterest);
InterestAmount = preEodPosition.InterestIncomeSum + amountAtEnd - amountAtPreviousEod;
TdInterestAmount = preEodPosition.InterestIncomeSum + tdAmountAtEnd - tdAmountAtPreviousEod;
}
}
else
{
@@ -1291,8 +1318,13 @@ namespace YLErp.Modules.SwapModule
{
if (i % interestPeriod == 0)
{
// 复利时:利息并入本金(FR007 取价已提前到 calcFirst/calcLast 跳过之前完成)
var interestToReset = i == 0 || resetCarryInterest == 0m ? interest : resetCarryInterest;
// resetCarryInterest 是上一日终待实现按本次平仓比例分摊后的存量,
// 只能在 endDate 恰好是当前复利重置日时并入本金。历史重置点必须使用
// 重放到当时的 interest,否则会把上一日终存量反复注入历史本金,
// 例如 0007 的 5/11 部分平仓会由 84,090.95 被多算为 84,114.88。
var interestToReset = i > 0 && accrueDate == endDate && resetCarryInterest != 0m
? resetCarryInterest
: interest;
dynomicPrincipal = principal + interestToReset;
tdDynomicPrincipal = principal + interestToReset;
flowEvent.InterestPrincipal = tdDynomicPrincipal;