diff --git a/UnitTestProject/Modules/SwapModule/SwapUnwindFloatingLegDiagnosticTdd.cs b/UnitTestProject/Modules/SwapModule/SwapUnwindFloatingLegDiagnosticTdd.cs index 9340fe93..84a7230d 100644 --- a/UnitTestProject/Modules/SwapModule/SwapUnwindFloatingLegDiagnosticTdd.cs +++ b/UnitTestProject/Modules/SwapModule/SwapUnwindFloatingLegDiagnosticTdd.cs @@ -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 }; } - - /// 用反射调用 private CalcNotionalByMode,直接证明各 mode 的 closePrincipal 是否依赖 posiLong/posiShort。 - 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(证明后端修复生效)"); - } } } diff --git a/YLErpDAL/Modules/SwapModule/SwapDealService.cs b/YLErpDAL/Modules/SwapModule/SwapDealService.cs index 6e4aa810..e95b395b 100644 --- a/YLErpDAL/Modules/SwapModule/SwapDealService.cs +++ b/YLErpDAL/Modules/SwapModule/SwapDealService.cs @@ -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; } - /// - /// 根据计息模式计算名义本金。 - /// - /// 现状(过渡期): - /// - 融资腿(mode 1/2/9)已委托 FundingLegStrategyFactory,走新策略体系。 - /// - 保证金(mode 5/6)暂保留——待 Margin 独立计息入口建成后迁出。 - /// - 多空存续(mode 7/8)界面已禁用,保留 case 仅为防御性兜底。 - /// - 死代码(mode 3/4)走 default。 - /// - /// 待保证金(mode 5/6)迁入 Margin 上下文后,本方法可整体删除, - /// 调用点直接走 FundingLegStrategyFactory.Get(mode)。 - /// - 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); - } - /// /// 平仓比例口径转换(解决"显示占期初 / 计算占剩余"双语义问题)。 - /// 前端与事件列表展示用"占期初(original)"语义(A);后端 CalcNotionalByMode / 费用递减 / + /// 前端与事件列表展示用"占期初(original)"语义(A);后端计息基数计算 / 费用递减 / /// 全平判定均按"占剩余(remaining)"语义(B)消费。 /// A → B:B = A × 期初名义本金(NotionalValue) / 剩余名义本金(PosiNotionalValue),并 cap 到 1。 /// B → A:A = B × 剩余名义本金 / 期初名义本金。 diff --git a/YLErpWeb/Properties/launchSettings.json b/YLErpWeb/Properties/launchSettings.json index be61e851..6b0464ba 100644 --- a/YLErpWeb/Properties/launchSettings.json +++ b/YLErpWeb/Properties/launchSettings.json @@ -5,7 +5,7 @@ "launchBrowser": true, "launchUrl": "http://localhost:49462", "environmentVariables": { - "ASPNETCORE_ENVIRONMENT": "dev" + "ASPNETCORE_ENVIRONMENT": "local" }, "applicationUrl": "http://localhost:49462" }