diff --git a/YLErpDAL/Modules/SwapModule/ClosePercentMath.cs b/YLErpDAL/Modules/SwapModule/ClosePercentMath.cs new file mode 100644 index 00000000..9269a449 --- /dev/null +++ b/YLErpDAL/Modules/SwapModule/ClosePercentMath.cs @@ -0,0 +1,65 @@ +using YLErp.DBModels; + +namespace YLErp.Modules.SwapModule; + +/// +/// 平仓比例(ClosePercent) 数学——占期初(A) / 占剩余(B) 两种口径的转换。 +/// 从 SwapDealService 提取为共享模块,两个 service 均可引用。 +/// +public static class ClosePercentMath +{ + /// + /// 取上一日终的浮动端名义本金(orginPv 的来源)。 + /// 优先取浮动腿 PosiNotionalValue 之和,取不到用 eod_swap 多空绝对值之和,都没有用 currentNotional 兜底。 + /// + public static decimal ResolveUnwindPreviousNotional( + eod_swap lastEod, + IEnumerable lastEodPositions, + decimal currentNotional) + { + var floatingPositions = lastEodPositions?.Where(x => x.PosiDirection > 0).ToList(); + decimal previousNotional; + if (floatingPositions?.Count > 0) + { + previousNotional = floatingPositions.Sum(x => x.PosiNotionalValue); + } + else + { + previousNotional = lastEod == null + ? currentNotional + : Math.Abs(lastEod.NotionalValueLong) + Math.Abs(lastEod.NotionalValueShort); + } + + return previousNotional == 0m && currentNotional != 0m + ? currentNotional + : previousNotional; + } + + /// + /// A(占期初) → B(占剩余),用于把前端传入的占期初比例换算成后端计算用的占剩余比例。 + /// + public static decimal ToRemainingClosePercent(decimal originalClosePercent, decimal notionalValue, decimal posiNotionalValue) + { + if (posiNotionalValue <= 0) return originalClosePercent; + var remaining = originalClosePercent * notionalValue / posiNotionalValue; + return remaining > 1 ? 1 : remaining; + } + + /// + /// B(占剩余) → A(占期初),用于落库 / 事件列表展示还原。见 ToRemainingClosePercent。 + /// + public static decimal ToOriginalClosePercent(decimal remainingClosePercent, decimal notionalValue, decimal posiNotionalValue) + { + if (notionalValue <= 0) return remainingClosePercent; + return remainingClosePercent * posiNotionalValue / notionalValue; + } + + /// + /// 计算 InitUnwind 默认占期初(A)平仓比例 = PosiNotionalValue / NotionalValue。 + /// 未平仓时 =1(平100%);部分平仓后自动变为剩余比例。 + /// + public static decimal CalcDefaultInitClosePercent(decimal notionalValue, decimal posiNotionalValue) + { + return notionalValue > 0 ? posiNotionalValue / notionalValue : 1; + } +} diff --git a/YLErpDAL/Modules/SwapModule/SwapDealService.cs b/YLErpDAL/Modules/SwapModule/SwapDealService.cs index eab142cc..8a3f0796 100644 --- a/YLErpDAL/Modules/SwapModule/SwapDealService.cs +++ b/YLErpDAL/Modules/SwapModule/SwapDealService.cs @@ -625,24 +625,7 @@ namespace YLErp.Modules.SwapModule eod_swap lastEod, IEnumerable lastEodPositions, decimal currentNotional) - { - var floatingPositions = lastEodPositions?.Where(x => x.PosiDirection > 0).ToList(); - decimal previousNotional; - if (floatingPositions?.Count > 0) - { - previousNotional = floatingPositions.Sum(x => x.PosiNotionalValue); - } - else - { - previousNotional = lastEod == null - ? currentNotional - : Math.Abs(lastEod.NotionalValueLong) + Math.Abs(lastEod.NotionalValueShort); - } - - return previousNotional == 0m && currentNotional != 0m - ? currentNotional - : previousNotional; - } + => ClosePercentMath.ResolveUnwindPreviousNotional(lastEod, lastEodPositions, currentNotional); /// /// 获取利息腿"已通过历史互换结出的累计利息"(用于复利重算时扣除,类比分红的 CalcConsumedDividend)。 @@ -822,32 +805,19 @@ namespace YLErp.Modules.SwapModule /// 分母为 0(无持仓等异常场景)时原样返回,避免除零。 /// public static decimal ToRemainingClosePercent(decimal originalClosePercent, decimal notionalValue, decimal posiNotionalValue) - { - if (posiNotionalValue <= 0) return originalClosePercent; - var remaining = originalClosePercent * notionalValue / posiNotionalValue; - return remaining > 1 ? 1 : remaining; - } + => ClosePercentMath.ToRemainingClosePercent(originalClosePercent, notionalValue, posiNotionalValue); /// /// B(占剩余) → A(占期初),用于落库 / 事件列表展示还原。见 ToRemainingClosePercent。 /// public static decimal ToOriginalClosePercent(decimal remainingClosePercent, decimal notionalValue, decimal posiNotionalValue) - { - if (notionalValue <= 0) return remainingClosePercent; - return remainingClosePercent * posiNotionalValue / notionalValue; - } + => ClosePercentMath.ToOriginalClosePercent(remainingClosePercent, notionalValue, posiNotionalValue); /// - /// 计算 InitUnwind 默认占期初(A)平仓比例 = "平掉剩余全部持仓"对应的占期初比例。 - /// 即:ClosePercent(A) = PosiNotionalValue / NotionalValue。 - /// 未平仓时 PosiNotionalValue==NotionalValue → 1(平100%); - /// 部分平仓后自动变为剩余比例(如已平 30% 则默认 0.7)。 - /// 与互换/提前终止 InitIncome 保持一致。抽出为纯函数以支持无库单测。 + /// 计算 InitUnwind 默认占期初(A)平仓比例 = PosiNotionalValue / NotionalValue。 /// public static decimal CalcDefaultInitClosePercent(decimal notionalValue, decimal posiNotionalValue) - { - return notionalValue > 0 ? posiNotionalValue / notionalValue : 1; - } + => ClosePercentMath.CalcDefaultInitClosePercent(notionalValue, posiNotionalValue); /// /// 读取"上一收盘日"浮动腿的待实现分红(eod_swap_position.PosiDividendSum),