diff --git a/YLErpDAL/Modules/SwapModule/SwapDealService.cs b/YLErpDAL/Modules/SwapModule/SwapDealService.cs
index 18ade2a8..a8ee764f 100644
--- a/YLErpDAL/Modules/SwapModule/SwapDealService.cs
+++ b/YLErpDAL/Modules/SwapModule/SwapDealService.cs
@@ -338,6 +338,27 @@ namespace YLErp.Modules.SwapModule
interests = GetInterests(td, tradeExtend, valueDate, unwindDate, lastEodPositions, positions, stockEqvNotional, posiLongNotionalValue, posiShortNotionalValue, posiNotionalValue, closePercent, eventType, tdClose, false, grossPrice ?? 0, orginPv, true, false,false, closeList);
return interests;
}
+
+ ///
+ /// 获取利息腿"已通过历史互换结出的累计利息"(用于复利重算时扣除,类比分红的 CalcConsumedDividend)。
+ /// 数据源为事件级 swap_flow_event.InterestAmount(互换/自动互换 完成态事件,互换当时即落库,不依赖日终归档)。
+ ///
+ /// 交易id
+ /// 利息腿id
+ /// 结算日(不含,仅汇总此日之前的历史已结利息;当日事件由 closeList 去重逻辑单独处理)
+ /// 历史已结利息累计金额(绝对值)
+ public decimal GetConsumedInterest(int tradeId, long positionId, DateTime beforeDate)
+ {
+ List swapEventTypes = new List() { (int)SwapEventTypeEnum.互换, (int)SwapEventTypeEnum.自动互换 };
+ var consumed = DbContext.swap_flow_event
+ .Where(x => x.SwapTradeId == tradeId && x.PositionId == positionId
+ && swapEventTypes.Contains(x.EventType)
+ && x.DataState == (int)SwapFlowDateStateEnum.完成
+ && x.EventDate < beforeDate)
+ .Sum(s => (decimal?)s.InterestAmount) ?? 0m;
+ return consumed;
+ }
+
///
/// 计算利息腿计息详细
///
@@ -412,7 +433,11 @@ namespace YLErp.Modules.SwapModule
else
{
// 盘中互换场景,使用 CalcUnwindInterest
- interests.Add(CalcUnwindInterest(td, valueDate, endDate, positionClone, rate, floatRate, posiPrincipal, closePrincipal, newClosePercent, annualDays, preEodPosition, eventType, add, swap, orginPv, calcFirst, calcLast||newCalcLast));
+ // 取历史已结利息(事件级,互换当时落库),供复利重算扣除(仅复利需要;单利基于日终快照自带状态)
+ var consumedInterest = position.InterestType == (int)InterestTypeEnum.复利
+ ? GetConsumedInterest(td.id, position.id, endDate)
+ : 0m;
+ interests.Add(CalcUnwindInterest(td, valueDate, endDate, positionClone, rate, floatRate, posiPrincipal, closePrincipal, newClosePercent, annualDays, preEodPosition, eventType, add, swap, orginPv, calcFirst, calcLast||newCalcLast, consumedInterest));
}
}
//当日有平仓或互换记录时,避免重复结算
@@ -592,7 +617,7 @@ namespace YLErp.Modules.SwapModule
///
/// 计算盘中利息(平仓/互换)
///
- private swap_flow_event CalcUnwindInterest(trade td, DateTime valueDate, DateTime endDate, swap_position position, decimal rate, decimal floatRate, decimal posiPrincipal, decimal closePrincipal, decimal closePercent, int annualDays, eod_swap_position preEod, int eventType, bool add, bool swap, decimal orginPv, bool calcFirst, bool calcLast)
+ private swap_flow_event CalcUnwindInterest(trade td, DateTime valueDate, DateTime endDate, swap_position position, decimal rate, decimal floatRate, decimal posiPrincipal, decimal closePrincipal, decimal closePercent, int annualDays, eod_swap_position preEod, int eventType, bool add, bool swap, decimal orginPv, bool calcFirst, bool calcLast, decimal consumedInterest = 0m)
{
if (preEod.id == 0)
{
@@ -606,7 +631,7 @@ namespace YLErp.Modules.SwapModule
}
}
- return InitSwapDealInterest(td, valueDate, endDate, rate, position, add, swap, posiPrincipal, closePrincipal, closePercent, annualDays, eventType, preEod, false, orginPv, calcFirst, calcLast);
+ return InitSwapDealInterest(td, valueDate, endDate, rate, position, add, swap, posiPrincipal, closePrincipal, closePercent, annualDays, eventType, preEod, false, orginPv, calcFirst, calcLast, consumedInterest);
}
///
/// 初始化利息腿信息
@@ -638,8 +663,9 @@ namespace YLErp.Modules.SwapModule
eod_swap_position preEodPosition,
bool needPrice,
decimal orginPv,
- bool calcFirst,
- bool calcLast
+ bool calcFirst,
+ bool calcLast,
+ decimal consumedInterest = 0m
)
{
decimal interestProfitSum = preEodPosition.InterestProfitSum;
@@ -674,7 +700,7 @@ namespace YLErp.Modules.SwapModule
var floateRate = preEodPosition.FloatRate;
if (position.InterestType == (int)InterestTypeEnum.复利)
{
- CalcDailyCompoundInterest( endDate, position, closePosiNotionalValue, interest, annualDays, needPrice, floateRate, closePrecent, orginPv, calcFirst, calcLast, ref InterestAmount, ref TdInterestAmount);
+ CalcDailyCompoundInterest( endDate, position, closePosiNotionalValue, interest, annualDays, needPrice, floateRate, closePrecent, orginPv, calcFirst, calcLast, ref InterestAmount, ref TdInterestAmount, consumedInterest);
}
else
{
@@ -702,7 +728,7 @@ namespace YLErp.Modules.SwapModule
/// 是否年化
/// 年化天数
///
- public void CalcDailyCompoundInterest( DateTime endDate, swap_position position, decimal principal, swap_flow_event flowEvent, int annualDays, bool needPrice, decimal floateRate, decimal closePercent, decimal orginPv, bool calcFirst, bool calcLast, ref decimal InterestAmount, ref decimal TdInterestAmount)
+ public void CalcDailyCompoundInterest( DateTime endDate, swap_position position, decimal principal, swap_flow_event flowEvent, int annualDays, bool needPrice, decimal floateRate, decimal closePercent, decimal orginPv, bool calcFirst, bool calcLast, ref decimal InterestAmount, ref decimal TdInterestAmount, decimal consumedInterest = 0m)
{
var startDate = position.PosiStartDate;
decimal interestProfitSum = 0;
@@ -761,6 +787,11 @@ namespace YLErp.Modules.SwapModule
tdinterest += tdinterest1;
}
}
+ // 复利从头重放得到的是"假设从未结出"的整段总利息,需扣除历史已通过互换结出的利息,
+ // 否则已结部分会重复计息(类比分红 PosiDividendSum = totalToDate − RealizedDividend)。
+ // consumedInterest 为绝对值口径(swap_flow_event.InterestAmount 之和),与 interest 口径一致。
+ interest -= consumedInterest;
+ tdinterest -= consumedInterest;
InterestAmount = Math.Round(interest, ConsGlobal.PriceRound, MidpointRounding.AwayFromZero);
TdInterestAmount = Math.Round(tdinterest, ConsGlobal.PriceRound, MidpointRounding.AwayFromZero);
}