refactor(swap): Phase3 提取ClosePercentMath共享模块

SwapDealService的4个public static ClosePercent方法body搬到ClosePercentMath.cs:
- ResolveUnwindPreviousNotional (上一日终浮动端名义本金)
- ToRemainingClosePercent (A→B 占期初→占剩余)
- ToOriginalClosePercent (B→A 逆转换)
- CalcDefaultInitClosePercent (默认占期初比例)

DealService保留4个一行转发壳, 外部调用(含6+测试文件)零改动
核实EodService内联closePercent公式与此不同, 非重复, 不替换
SwapModule零回归(7基线/510通过)
This commit is contained in:
hjhan
2026-08-12 17:31:39 +08:00
parent 69295f31ce
commit c412f918d9
2 changed files with 70 additions and 35 deletions
@@ -0,0 +1,65 @@
using YLErp.DBModels;
namespace YLErp.Modules.SwapModule;
/// <summary>
/// 平仓比例(ClosePercent) 数学——占期初(A) / 占剩余(B) 两种口径的转换。
/// 从 SwapDealService 提取为共享模块,两个 service 均可引用。
/// </summary>
public static class ClosePercentMath
{
/// <summary>
/// 取上一日终的浮动端名义本金(orginPv 的来源)。
/// 优先取浮动腿 PosiNotionalValue 之和,取不到用 eod_swap 多空绝对值之和,都没有用 currentNotional 兜底。
/// </summary>
public static decimal ResolveUnwindPreviousNotional(
eod_swap lastEod,
IEnumerable<eod_swap_position> 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;
}
/// <summary>
/// A(占期初) → B(占剩余),用于把前端传入的占期初比例换算成后端计算用的占剩余比例。
/// </summary>
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;
}
/// <summary>
/// B(占剩余) → A(占期初),用于落库 / 事件列表展示还原。见 ToRemainingClosePercent。
/// </summary>
public static decimal ToOriginalClosePercent(decimal remainingClosePercent, decimal notionalValue, decimal posiNotionalValue)
{
if (notionalValue <= 0) return remainingClosePercent;
return remainingClosePercent * posiNotionalValue / notionalValue;
}
/// <summary>
/// 计算 InitUnwind 默认占期初(A)平仓比例 = PosiNotionalValue / NotionalValue。
/// 未平仓时 =1(平100%);部分平仓后自动变为剩余比例。
/// </summary>
public static decimal CalcDefaultInitClosePercent(decimal notionalValue, decimal posiNotionalValue)
{
return notionalValue > 0 ? posiNotionalValue / notionalValue : 1;
}
}
+5 -35
View File
@@ -625,24 +625,7 @@ namespace YLErp.Modules.SwapModule
eod_swap lastEod,
IEnumerable<eod_swap_position> 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);
/// <summary>
/// 获取利息腿"已通过历史互换结出的累计利息"(用于复利重算时扣除,类比分红的 CalcConsumedDividend)。
@@ -822,32 +805,19 @@ namespace YLErp.Modules.SwapModule
/// 分母为 0(无持仓等异常场景)时原样返回,避免除零。
/// </summary>
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);
/// <summary>
/// B(占剩余) → A(占期初),用于落库 / 事件列表展示还原。见 ToRemainingClosePercent。
/// </summary>
public static decimal ToOriginalClosePercent(decimal remainingClosePercent, decimal notionalValue, decimal posiNotionalValue)
{
if (notionalValue <= 0) return remainingClosePercent;
return remainingClosePercent * posiNotionalValue / notionalValue;
}
=> ClosePercentMath.ToOriginalClosePercent(remainingClosePercent, notionalValue, posiNotionalValue);
/// <summary>
/// 计算 InitUnwind 默认占期初(A)平仓比例 = "平掉剩余全部持仓"对应的占期初比例
/// 即:ClosePercent(A) = PosiNotionalValue / NotionalValue。
/// 未平仓时 PosiNotionalValue==NotionalValue → 1(平100%);
/// 部分平仓后自动变为剩余比例(如已平 30% 则默认 0.7)。
/// 与互换/提前终止 InitIncome 保持一致。抽出为纯函数以支持无库单测。
/// 计算 InitUnwind 默认占期初(A)平仓比例 = PosiNotionalValue / NotionalValue
/// </summary>
public static decimal CalcDefaultInitClosePercent(decimal notionalValue, decimal posiNotionalValue)
{
return notionalValue > 0 ? posiNotionalValue / notionalValue : 1;
}
=> ClosePercentMath.CalcDefaultInitClosePercent(notionalValue, posiNotionalValue);
/// <summary>
/// 读取"上一收盘日"浮动腿的待实现分红(eod_swap_position.PosiDividendSum),