refactor: 删除 CalcNotionalByMode, 保证金/融资腿计息基数内联到调用点

CalcNotionalByMode 已完成历史使命:
- 融资腿(1/2/9)迁入 FundingLegStrategyFactory
- 保证金(5/6)迁入 MarginModes.Contains 分支
- 死代码(3/4/7/8)已删

调用点(GetInterests:843)现在内联两条路径:
- 保证金: closePrincipal = Fix × closePercent (用 MarginModes.Contains 判断)
- 融资腿: FundingLegStrategyFactory.Get(mode).CalcNotional(...)

删除:
- CalcNotionalByMode 方法(含注释)
- 2个用反射调私有方法的诊断测试(验证的逻辑已被FundingLegStrategyTest覆盖)

验证: 编译0错误, 全量509测试7失败(基线一致)。
This commit is contained in:
hjhan
2026-08-11 13:13:32 +08:00
parent a610b962a5
commit 29df4c480d
3 changed files with 22 additions and 73 deletions
@@ -66,33 +66,5 @@ namespace YLErp.Modules.SwapModule
TradeDate = D0, StartDate = D0, ExerciseDate = D1, TradeStatus = "确认成交", ValidState = "Valid",
StockEqvNotional = (double)RealLong, Notional = (double)RealLong, trade_extend = extend };
}
/// <summary>用反射调用 private CalcNotionalByMode,直接证明各 mode 的 closePrincipal 是否依赖 posiLong/posiShort。</summary>
private (decimal close, decimal posi, decimal pct) CallCalcNotionalByMode(swap_position position, decimal closePct, decimal posiNotional, decimal posiLong, decimal posiShort)
{
var m = typeof(SwapDealService).GetMethod("CalcNotionalByMode", System.Reflection.BindingFlags.NonPublic | System.Reflection.BindingFlags.Instance);
return ((decimal, decimal, decimal))m.Invoke(_svc, new object[] { position, closePct, posiNotional, posiLong, posiShort });
}
[TestMethod]
public void _mode9_标的期初全价_closePrincipal_不依赖posiLong_而用posiNotional()
{
// mode 9 分支:closePrincipal = posiNotional * closePercent
var baseP = OrigBasePrice();
var (close, posi, _) = CallCalcNotionalByMode(baseP, ClosePct, RealLong * ClosePct, OrigLong, 0m);
Console.WriteLine($"[mode9] posiNotional={RealLong * ClosePct} posiLong(orig)={OrigLong} → closePrincipal={close}");
Assert.AreEqual(RealLong * ClosePct * ClosePct, close, "mode9 应 = posiNotional(=real剩余*closePct) * closePct,与 posiLong(orig 100M) 无关");
}
[TestMethod]
public void _mode5_预付金_closePrincipal_用自身Fix_不依赖posiLong()
{
// mode 5 分支:closePrincipal = position.InterestPrincipalFix * closePercent(用 Clone 后的 real Fix
var prepay = RealPrepay(); // Fix = RealFix(66,813.12)
var (close, posi, _) = CallCalcNotionalByMode(prepay, ClosePct, RealLong * ClosePct, OrigLong, 0m);
Console.WriteLine($"[mode5] Fix(cloned real)={RealFix} posiLong(orig)={OrigLong} → closePrincipal={close}");
Assert.AreEqual(RealFix * ClosePct, close, "mode5 应 = 实时腿剩余本金(real Fix) * closePct,与 posiLong(orig 100M) 无关");
Assert.AreNotEqual(OrigFix * ClosePct, close, "务必不是期初 99,000 * closePct(证明后端修复生效)");
}
}
}
+21 -44
View File
@@ -840,7 +840,26 @@ namespace YLErp.Modules.SwapModule
bool swap = InitInterestDate(unwindDate, preDealDate, td, tdClose, out DateTime startDate, out DateTime endDate);
// 计算名义本金
var (closePrincipal, posiPrincipal, newClosePercent) = CalcNotionalByMode(position, closePrecent, posiNotionalValue, posiLongNotionalValue, posiShortNotionalValue);
decimal closePrincipal;
decimal posiPrincipal;
decimal newClosePercent = closePrecent;
var mode = (InterestModeEnum)position.InterestMode;
if (MarginModes.Contains(position.InterestMode))
{
// 保证金腿: 计息基数 = InterestPrincipalFix(保证金余额)
closePrincipal = position.InterestPrincipalFix * closePrecent;
posiPrincipal = position.InterestPrincipalFix;
}
else
{
// 融资腿(1/2/9): 走策略工厂
var r = FundingLegStrategyFactory.Get(mode)
.CalcNotional(position.InterestPrincipalFix, posiNotionalValue, posiLongNotionalValue, posiShortNotionalValue, closePrecent);
closePrincipal = r.ClosePrincipal;
posiPrincipal = r.PosiPrincipal;
newClosePercent = r.ClosePercent;
}
if ((InterestModeEnum)position.InterestMode == InterestModeEnum.
|| (InterestModeEnum)position.InterestMode == InterestModeEnum.
&& posiNotionalValue == 0m)
@@ -904,51 +923,9 @@ namespace YLErp.Modules.SwapModule
return interests;
}
/// <summary>
/// 根据计息模式计算名义本金。
///
/// 现状(过渡期)
/// - 融资腿(mode 1/2/9)已委托 FundingLegStrategyFactory,走新策略体系。
/// - 保证金(mode 5/6)暂保留——待 Margin 独立计息入口建成后迁出。
/// - 多空存续(mode 7/8)界面已禁用,保留 case 仅为防御性兜底。
/// - 死代码(mode 3/4)走 default。
///
/// 待保证金(mode 5/6)迁入 Margin 上下文后,本方法可整体删除,
/// 调用点直接走 FundingLegStrategyFactory.Get(mode)。
/// </summary>
private (decimal close, decimal posi, decimal closePct) CalcNotionalByMode(swap_position position, decimal closePercent, decimal posiNotional, decimal posiLong, decimal posiShort)
{
var mode = (InterestModeEnum)position.InterestMode;
// 融资腿(1/2/9)走策略工厂
if (mode == InterestModeEnum.
|| mode == InterestModeEnum.
|| mode == InterestModeEnum.)
{
var r = FundingLegStrategyFactory.Get(mode)
.CalcNotional(position.InterestPrincipalFix, posiNotional, posiLong, posiShort, closePercent);
return (r.ClosePrincipal, r.PosiPrincipal, r.ClosePercent);
}
// 以下 mode 尚未迁入新架构,保留原逻辑
decimal closePrincipal = posiNotional;
decimal posiPrincipal = posiNotional;
decimal newClosePercent = closePercent;
switch (mode)
{
case InterestModeEnum.:
case InterestModeEnum.:
closePrincipal = position.InterestPrincipalFix * closePercent;
posiPrincipal = position.InterestPrincipalFix;
break;
}
return (closePrincipal, posiPrincipal, newClosePercent);
}
/// <summary>
/// 平仓比例口径转换(解决"显示占期初 / 计算占剩余"双语义问题)。
/// 前端与事件列表展示用"占期初(original)"语义(A);后端 CalcNotionalByMode / 费用递减 /
/// 前端与事件列表展示用"占期初(original)"语义(A);后端计息基数计算 / 费用递减 /
/// 全平判定均按"占剩余(remaining)"语义(B)消费。
/// A → BB = A × 期初名义本金(NotionalValue) / 剩余名义本金(PosiNotionalValue),并 cap 到 1。
/// B → A:A = B × 剩余名义本金 / 期初名义本金。
+1 -1
View File
@@ -5,7 +5,7 @@
"launchBrowser": true,
"launchUrl": "http://localhost:49462",
"environmentVariables": {
"ASPNETCORE_ENVIRONMENT": "dev"
"ASPNETCORE_ENVIRONMENT": "local"
},
"applicationUrl": "http://localhost:49462"
}