From af94003939b56fa14cf37c33713a8f0a89aeaaad Mon Sep 17 00:00:00 2001 From: hjhan Date: Wed, 12 Aug 2026 14:33:23 +0800 Subject: [PATCH] =?UTF-8?q?refactor(eod):=20DirectionRatio.ReceivePay/Inte?= =?UTF-8?q?restLegPnl=E6=9B=BF=E6=8D=A221=E5=A4=84=E5=86=85=E8=81=94?= =?UTF-8?q?=E4=B8=89=E5=85=83?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit SwapEodPositionService中散落的收取?1:-1三元表达式和ratio+margin flip块: - 9处InterestLegPnl块(3-5行→1行): 收取?1:-1 + if(MarginModes)ratio=-ratio - 12处ReceivePay三元: 收取?1:-1 → DirectionRatio.ReceivePay(dir) - 含2处反向(收取?-1:1 → -DirectionRatio.ReceivePay) 净减约30行, 消除+1/-1写反的风险 SwapModule零回归(7基线/510通过) --- .../SwapModule/ReturnLegs/DirectionRatio.cs | 9 +++ .../SwapModule/SwapEodPositionService.cs | 80 +++++-------------- 2 files changed, 31 insertions(+), 58 deletions(-) diff --git a/YLErpDAL/Modules/SwapModule/ReturnLegs/DirectionRatio.cs b/YLErpDAL/Modules/SwapModule/ReturnLegs/DirectionRatio.cs index 13808855..e349bfe9 100644 --- a/YLErpDAL/Modules/SwapModule/ReturnLegs/DirectionRatio.cs +++ b/YLErpDAL/Modules/SwapModule/ReturnLegs/DirectionRatio.cs @@ -1,5 +1,6 @@ using YLErp.DBModels; using YLErp.DBModels.Enums; +using YLErp.Modules.SwapModule.Margin; namespace YLErp.Modules.SwapModule.ReturnLegs; @@ -19,4 +20,12 @@ public static class DirectionRatio /// 收付方向因子。收取=+1, 支付=-1。 public static int ReceivePay(int direction) => direction == (int)SwapDirectionEnum.收取 ? 1 : -1; + + /// 利息腿 PnL 方向因子。收取=+1/支付=-1;保证金腿翻转(利息现金流与本金方向相反)。 + /// 原 7 处内联 `收取?1:-1; if(MarginModes) ratio=-ratio` 收口到此。 + public static int InterestLegPnl(int interestDirection, int interestMode) + { + var ratio = ReceivePay(interestDirection); + return MarginModes.Contains(interestMode) ? -ratio : ratio; + } } diff --git a/YLErpDAL/Modules/SwapModule/SwapEodPositionService.cs b/YLErpDAL/Modules/SwapModule/SwapEodPositionService.cs index c70476bb..252a4cb4 100644 --- a/YLErpDAL/Modules/SwapModule/SwapEodPositionService.cs +++ b/YLErpDAL/Modules/SwapModule/SwapEodPositionService.cs @@ -662,7 +662,7 @@ namespace YLErp.Modules.SwapModule decimal premiumTotal = 0; premiumInterests.ForEach(x => { - var ratio = x.InterestDirection == (int)SwapDirectionEnum.收取 ? -1 : 1; + var ratio = -DirectionRatio.ReceivePay(x.InterestDirection); premiumTotal += x.InterestClosePnL * ratio; }); unwindData.SwapMarginRebatePnl = premiumTotal; @@ -670,7 +670,7 @@ namespace YLErp.Modules.SwapModule decimal interestTotal = 0; interestLegs.ForEach(x => { - var ratio = x.InterestDirection == (int)SwapDirectionEnum.收取 ? 1 : -1; + var ratio = DirectionRatio.ReceivePay(x.InterestDirection); interestTotal += x.InterestClosePnL * ratio; }); unwindData.SwapCloseAmount = interestTotal ; @@ -1041,11 +1041,7 @@ namespace YLErp.Modules.SwapModule eodPayPosition.PosiMatuirityDate = td.ExerciseDate.Value; } var tradeExtend = td.trade_extend.ExtendObj; - decimal ratio = position.InterestDirection == (int)SwapDirectionEnum.收取 ? 1m : -1m;//收取为正,支付为负 - if (MarginModes.Contains(position.InterestMode)) - { - ratio = -ratio; - } + var ratio = DirectionRatio.InterestLegPnl(position.InterestDirection, position.InterestMode); if (newEodPayPosition == null) { newEodPayPosition = new eod_swap_position(); @@ -1176,11 +1172,7 @@ namespace YLErp.Modules.SwapModule var tradeExtend = td.trade_extend.ExtendObj; decimal posiNotionalValue = posiLongNotional + posiShortNational; decimal closePercent = 1; - decimal ratio = position.InterestDirection == (int)SwapDirectionEnum.收取 ? 1m : -1m;//收取为正,支付为负 - if (MarginModes.Contains(position.InterestMode)) - { - ratio = -ratio; - } + var ratio = DirectionRatio.InterestLegPnl(position.InterestDirection, position.InterestMode); if (eodPayPosition == null) { eodPayPosition = new eod_swap_position(); @@ -1296,11 +1288,7 @@ namespace YLErp.Modules.SwapModule decimal oriPosiNotionalValue = posiLongNotional + posiShortNational + closeNational; decimal posiNotionalValue = posiLongNotional + posiShortNational; // ratio 只负责把腿内原始金额转换为本方盈亏方向,不参与计息金额本身的计算。 - decimal ratio = position.InterestDirection == (int)SwapDirectionEnum.收取 ? 1m : -1m;//收取为正,支付为负 - if (MarginModes.Contains(position.InterestMode)) - { - ratio = -ratio; - } + var ratio = DirectionRatio.InterestLegPnl(position.InterestDirection, position.InterestMode); // 首次日终结算可能包含当日收盘,因此尚无先前的日终利息持仓。 // 部分平仓仍要续接上一日日终:CalcUnwindInterest 会将 InterestProfitSum // 加入本次待实现,已实现字段也必须按日累计,不能从新建的临时对象重新开始。 @@ -1374,7 +1362,7 @@ namespace YLErp.Modules.SwapModule var autoInterest = interests[0]; autoInterest.InterestAmount = autoSettledInterestAmount; autoInterest.InterestClosePnL = autoSettledInterestAmount - * (autoInterest.InterestDirection == (int)SwapDirectionEnum.收取 ? 1m : -1m); + * DirectionRatio.ReceivePay(autoInterest.InterestDirection); } newEodPayPosition.ValueDate = valueDate; newEodPayPosition.PositionId = position.id; @@ -1604,11 +1592,7 @@ namespace YLErp.Modules.SwapModule { closePercent = 1; } - decimal ratio = eodPayPosition.InterestDirection == (int)SwapDirectionEnum.收取 ? 1m : -1m;//收取为正,支付为负 - if (MarginModes.Contains(position.InterestMode)) - { - ratio = -ratio; - } + var ratio = DirectionRatio.InterestLegPnl(eodPayPosition.InterestDirection, position.InterestMode); List positions = new List { position @@ -1692,7 +1676,7 @@ namespace YLErp.Modules.SwapModule bool open) { payQty = Math.Abs(payQty); - int ratio = eventFlow.PayDirection == (int)SwapDirectionEnum.收取 ? 1 : -1;//收取为正,支付为负 + int ratio = DirectionRatio.ReceivePay(eventFlow.PayDirection);//收取为正,支付为负 int shortRatio = DirectionRatio.LongShort(newEodPayPosition.PositionType); newEodPayPosition.ValueDate = eventFlow.PayDate.Value; newEodPayPosition.PositionId = eventFlow.PositionId; @@ -1792,7 +1776,7 @@ namespace YLErp.Modules.SwapModule } var dealDate = curretEod.ValueDate; int shortRatio = DirectionRatio.LongShort(eod.PositionType); - int directionRatio = eod.PosiDirection == (int)SwapDirectionEnum.收取 ? 1 : -1; + int directionRatio = DirectionRatio.ReceivePay(eod.PosiDirection); curretEod.PosiStatus = curretEod.PosiQuantity == 0 ? 1 : 0; var price = GetSwapValuationPrice(eod.UnderlyingCode, dealDate, out decimal vobp); curretEod.dv01 = Dv01Helper.CalcDv01(eod.UnderlyingCode, curretEod.PosiQuantity, eod.PosiDirection, eod.PositionType, vobp); @@ -1884,7 +1868,7 @@ namespace YLErp.Modules.SwapModule } var dealDate = curretEod.ValueDate; int shortRatio = DirectionRatio.LongShort(eod.PositionType); - int directionRatio = eod.PosiDirection == (int)SwapDirectionEnum.收取 ? 1 : -1; + int directionRatio = DirectionRatio.ReceivePay(eod.PosiDirection); var price = GetSwapValuationPrice(eod.UnderlyingCode, dealDate, out decimal vobp); var todayConsumedDividend = CalcConsumedDividend(curretEod, unwindEvents); var originNotional = (decimal)td.OriginalStockEqvNotional / swapPosition.PosiNetPrice; @@ -1972,7 +1956,7 @@ namespace YLErp.Modules.SwapModule return; } int shortRatio = DirectionRatio.LongShort(eod.PositionType); - int directionRatio = eod.PosiDirection == (int)SwapDirectionEnum.收取 ? 1 : -1; + int directionRatio = DirectionRatio.ReceivePay(eod.PosiDirection); var unwindFlowEvents = unwindEvents.Where(x => x.EventType == (int)SwapFlowEventTypeEnum.平仓).ToList(); var openFlowEvents = unwindEvents.Where(x => x.EventType == (int)SwapFlowEventTypeEnum.开仓).ToList(); decimal unwindQty = unwindFlowEvents.Sum(s => s.Quantity); @@ -2053,7 +2037,7 @@ namespace YLErp.Modules.SwapModule curretEod.PositionId = position.id; curretEod.ClientId = td.ClientId; int shortRatio = DirectionRatio.LongShort(position.PositionType); - int directionRatio = position.PosiDirection == (int)SwapDirectionEnum.收取 ? 1 : -1; + int directionRatio = DirectionRatio.ReceivePay(position.PosiDirection); curretEod.PositionType = position.PositionType; var eod = new eod_swap_position() { @@ -2205,11 +2189,7 @@ namespace YLErp.Modules.SwapModule // 因此保证金腿需要额外反转符号,确保 InterestPnL 表示我方的合约利率端收益。 interestPositions.ForEach(x => { - decimal ratio = x.InterestDirection == (int)SwapDirectionEnum.收取 ? 1 : -1;//收取为正,支付为负 - if (MarginModes.Contains(x.InterestMode)) - { - ratio = -ratio; - } + var ratio = DirectionRatio.InterestLegPnl(x.InterestDirection, x.InterestMode); interestPnL += x.InterestProfitSum * ratio; }); eod_Swap.InterestPnL = interestPnL; @@ -2273,20 +2253,12 @@ namespace YLErp.Modules.SwapModule eod_Swap.dv01 = positions.Sum(s => s.dv01 ?? 0); interestPositions.ForEach(x => { - decimal ratio = x.InterestDirection == (int)SwapDirectionEnum.收取 ? 1 : -1;//收取为正,支付为负 - if (MarginModes.Contains(x.InterestMode)) - { - ratio = -ratio; - } + var ratio = DirectionRatio.InterestLegPnl(x.InterestDirection, x.InterestMode); eod_Swap.InterestPnL += x.InterestProfitSum * ratio; }); eodSwapPositions.ForEach(x => { - decimal ratio = x.InterestDirection == (int)SwapDirectionEnum.收取 ? 1 : -1;//收取为正,支付为负 - if (MarginModes.Contains(x.InterestMode)) - { - ratio = -ratio; - } + var ratio = DirectionRatio.InterestLegPnl(x.InterestDirection, x.InterestMode); eod_Swap.TdRealizedPnL += x.TdCloseMtmPnl + x.TdCloseDividend + x.TdCloseFee + x.TdCloseInterest * ratio + x.TdCloseInterestFee; }); eod_Swap.PostionValue = eodSwapPositions.Sum(s => s.SwapPositionValue); @@ -2312,11 +2284,7 @@ namespace YLErp.Modules.SwapModule /// public static decimal CalculateSwapRealizedPnl(eod_swap_position position) { - var interestRatio = position.InterestDirection == (int)SwapDirectionEnum.收取 ? 1m : -1m; - if (MarginModes.Contains(position.InterestMode)) - { - interestRatio = -interestRatio; - } + var interestRatio = DirectionRatio.InterestLegPnl(position.InterestDirection, position.InterestMode); return position.RealizedMtmPnL + position.RealizedDividend @@ -2336,15 +2304,11 @@ namespace YLErp.Modules.SwapModule { if (position.InterestDirection <= 0) return; - var interestRatio = position.InterestDirection == (int)SwapDirectionEnum.收取 ? 1m : -1m; - if (MarginModes.Contains(position.InterestMode)) - { - interestRatio = -interestRatio; - } - else if (position.InterestMode == (int)InterestModeEnum.标的期初全价) + if (position.InterestMode == (int)InterestModeEnum.标的期初全价) { return; } + var interestRatio = DirectionRatio.InterestLegPnl(position.InterestDirection, position.InterestMode); position.TdCloseInterest = Math.Abs(position.TdCloseInterest) * interestRatio; position.RealizedInterest = Math.Abs(position.RealizedInterest) * interestRatio; // 兼容修复前已落库的利息腿:当时只累计了明细字段,未同步写入 RealizedPnl。 @@ -2838,11 +2802,11 @@ namespace YLErp.Modules.SwapModule var floatRateInterest = eodInterests.Where(x => !string.IsNullOrEmpty(x.FloatRateUnderlyingCode)).FirstOrDefault(); item.position.FloatRateUnderlyingCode = floatRateInterest?.FloatRateUnderlyingCode; item.position.FloatRate = floatRateInterest?.FloatRate ?? 0; - item.OpenMarginAmount = initialMargins.Sum(s => s.InterestPrincipalFix * (s.InterestDirection == (int)SwapDirectionEnum.收取 ? 1 : -1)); + item.OpenMarginAmount = initialMargins.Sum(s => s.InterestPrincipalFix * DirectionRatio.ReceivePay(s.InterestDirection)); item.OpenMarginRate = CalculateWeightedMarginRate(tradeMargins); - item.AdditionalMarginAmount = additionalMargins.Sum(s => s.InterestPrincipalFix * (s.InterestDirection == (int)SwapDirectionEnum.收取 ? 1 : -1)); + item.AdditionalMarginAmount = additionalMargins.Sum(s => s.InterestPrincipalFix * DirectionRatio.ReceivePay(s.InterestDirection)); item.MarginInterestAmount = CalculateWeightedMarginInterest(eodMargins); - item.InterestAmount = eodInterests.Sum(s => s.InterestIncomeSum * (s.InterestDirection == (int)SwapDirectionEnum.收取 ? -1 : 1)); + item.InterestAmount = eodInterests.Sum(s => s.InterestIncomeSum * (-DirectionRatio.ReceivePay(s.InterestDirection))); item.InterestRate = eodInterests.Sum(s => s.InterestRateDefault); // 到期轧差才把期间付息/分红并入净额结算;派息日支付已在现金流层独立结算,不能重复计入估值。 var nettingDividend = (tradeExtend?.ExtendObj?.DividendPayDate ?? 1) == 0 ? pendingDividend : 0m; @@ -2885,7 +2849,7 @@ namespace YLErp.Modules.SwapModule public static decimal CalculateWeightedMarginInterest(IEnumerable margins) { return margins.Sum(x => - x.InterestIncomeSum * (x.InterestDirection == (int)SwapDirectionEnum.收取 ? 1 : -1)); + x.InterestIncomeSum * DirectionRatio.ReceivePay(x.InterestDirection)); } ///