refactor(eod): DirectionRatio.ReceivePay/InterestLegPnl替换21处内联三元

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通过)
This commit is contained in:
hjhan
2026-08-12 14:33:23 +08:00
parent bfa230381b
commit af94003939
2 changed files with 31 additions and 58 deletions
@@ -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
/// <summary>收付方向因子。收取=+1, 支付=-1。</summary>
public static int ReceivePay(int direction)
=> direction == (int)SwapDirectionEnum. ? 1 : -1;
/// <summary>利息腿 PnL 方向因子。收取=+1/支付=-1;保证金腿翻转(利息现金流与本金方向相反)。
/// 原 7 处内联 `收取?1:-1; if(MarginModes) ratio=-ratio` 收口到此。</summary>
public static int InterestLegPnl(int interestDirection, int interestMode)
{
var ratio = ReceivePay(interestDirection);
return MarginModes.Contains(interestMode) ? -ratio : ratio;
}
}
@@ -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<swap_position> positions = new List<swap_position>
{
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
/// </summary>
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<eod_swap_position> margins)
{
return margins.Sum(x =>
x.InterestIncomeSum * (x.InterestDirection == (int)SwapDirectionEnum. ? 1 : -1));
x.InterestIncomeSum * DirectionRatio.ReceivePay(x.InterestDirection));
}
/// <summary>