diff --git a/UnitTestProject/Modules/SwapModule/SwapUnwindScenarioTest.cs b/UnitTestProject/Modules/SwapModule/SwapUnwindScenarioTest.cs index ea9bf5c0..96b14d5f 100644 --- a/UnitTestProject/Modules/SwapModule/SwapUnwindScenarioTest.cs +++ b/UnitTestProject/Modules/SwapModule/SwapUnwindScenarioTest.cs @@ -197,6 +197,8 @@ namespace YLErp.Modules.SwapModule public void UW_007_SwapUnwind_占期初A转占剩余B_全平判定正确() { var td = SwapDealTestFactory.CreateTrade(); + td.StockEqvNotional = 600000; + td.TradeAmount = 600000; var service = new TestableSwapDealService(td); var unwindData = SwapDealTestFactory.CreateUnwindData( swapRealizedPnL: 0m, closeMethod: (int)CloseMethodEnum.全部平仓, closePercent: 0.6m, @@ -259,6 +261,33 @@ namespace YLErp.Modules.SwapModule Assert.AreEqual(500000.01, td.StockEqvNotional, 0.000001, "trade 剩余名义本金应在扣减后舍入两位小数"); } + [TestMethod] + public void UW_013_SwapUnwind_合法零点零一剩余不应判定全平() + { + var td = SwapDealTestFactory.CreateTrade(); + td.StockEqvNotional = 1000000.01; + td.TradeAmount = 10000.01; + var service = new TestableSwapDealService(td); + var unwindData = SwapDealTestFactory.CreateUnwindData( + swapRealizedPnL: 0m, + closeMethod: (int)CloseMethodEnum.部分平仓, + closePercent: 1000000m / 1000000.01m, + closeQty: 10000m, + closeNotionalValue: 1000000m, + positionQty: 10000.01m); + unwindData.NotionalValue = 1000000.01m; + unwindData.PosiNotionalValue = 1000000.01m; + + service.SwapUnwind(unwindData); + + Assert.AreEqual("确认成交", td.TradeStatus, + "剩余名义本金和数量均为0.01时仍应保持部分平仓状态"); + Assert.AreEqual(1, td.HasPartialUnWind, + "合法的0.01尾差不应被清零"); + Assert.AreEqual(0.01, td.StockEqvNotional, 0.000001); + Assert.AreEqual(0.01, td.TradeAmount, 0.000001); + } + [TestMethod] public void UW_010_SwapUnwind_现金与两位利息事件保持一致() { @@ -289,5 +318,183 @@ namespace YLErp.Modules.SwapModule Assert.AreEqual(10m, unwindData.SwapRealizedPnL); Assert.AreEqual(-10d, service.ClientCashCalls[0].amount, 0.001d); } + + [TestMethod] + public void UW_011_trade2308_full_close_uses_remaining_values_and_zeroes_tail() + { + var td = SwapDealTestFactory.CreateTrade(); + td.StockEqvNotional = 4906156.15; + td.TradeAmount = 5000000; + td.Notional = 5000000; + var service = new TestableSwapDealService(td); + var unwindData = SwapDealTestFactory.CreateUnwindData( + swapRealizedPnL: 0m, + closeMethod: (int)CloseMethodEnum.部分平仓, + closePercent: 0.5m, + closeQty: 5000000.01m, + closeNotionalValue: 4906156.15m, + positionQty: 5000000m); + unwindData.NotionalValue = 9812312.31m; + unwindData.PosiNotionalValue = 4906156.15m; + + service.SwapUnwind(unwindData); + + var saved = service.SaveSwapDealCalls[0].data; + Assert.AreEqual((int)CloseMethodEnum.部分平仓, saved.CloseMethod, + "CloseMethod 保留本次部分平仓意图,终态由扣减后的持仓事实决定"); + Assert.AreEqual(5000000m, saved.CloseQty); + Assert.AreEqual(4906156.15m, saved.CloseNotionalValue); + Assert.AreEqual(0d, td.StockEqvNotional, 0.000001); + Assert.AreEqual(0d, td.TradeAmount, 0.000001); + Assert.AreEqual(0d, td.Notional, 0.000001); + Assert.AreEqual("已平仓", td.TradeStatus); + } + + [TestMethod] + public void UW_014_full_close_quantity_normalization_recalculates_pnl_and_cash() + { + var td = SwapDealTestFactory.CreateTrade(); + td.StockEqvNotional = 4906156.15; + td.TradeAmount = 5000000; + td.Notional = 5000000; + var service = new TestableSwapDealService(td); + var unwindData = SwapDealTestFactory.CreateUnwindData( + swapRealizedPnL: 50000000.10m, + closeMethod: (int)CloseMethodEnum.部分平仓, + closePercent: 0.5m, + closeQty: 5000000.01m, + closeNotionalValue: 4906156.15m, + positionQty: 5000000m); + unwindData.NotionalValue = 9812312.31m; + unwindData.PosiNotionalValue = 4906156.15m; + unwindData.SwapCloseAmount = 50000000.10m; + var floatEvent = new swap_flow_event + { + UnderlyingCode = "UT-FLOAT", + PositionType = (int)PositionTypeFlag.Long, + EventType = (int)SwapEventTypeEnum.平仓, + PayDirection = 1, + PosiGrossPrice = 1m, + TradingAmountAvg = 11m, + MarkClosePnl = 50000000.10m + }; + unwindData.FlowEvents.Add(floatEvent); + + service.SwapUnwind(unwindData); + + Assert.AreEqual(5000000m, unwindData.CloseQty); + Assert.AreEqual(50000000m, floatEvent.MarkClosePnl); + Assert.AreEqual(50000000m, unwindData.SwapRealizedPnL); + Assert.AreEqual(50000000m, unwindData.SwapCloseAmount); + Assert.AreEqual(-50000000d, service.ClientCashCalls.Single().amount, 0.001d); + } + + [TestMethod] + public void UW_012_approve_restores_A_to_B_and_normalizes_flow_for_full_close() + { + var td = SwapDealTestFactory.CreateTrade(); + td.StockEqvNotional = 4906156.15; + td.TradeAmount = 5000000; + var unwindData = SwapDealTestFactory.CreateUnwindData( + swapRealizedPnL: 0m, + closeMethod: (int)CloseMethodEnum.部分平仓, + closePercent: 4906156.15m / 9812312.31m, + closeQty: 5000000.01m, + closeNotionalValue: 4906156.15m, + positionQty: 5000000m); + unwindData.NotionalValue = 9812312.31m; + unwindData.PosiNotionalValue = 4906156.15m; + var floatEvent = new swap_flow_event + { + EventId = 1, + UnderlyingCode = "261031.IB", + PositionType = (int)PositionTypeFlag.Long, + Quantity = 5000000.01m, + PositionQty = -0.01m + }; + var swapEvent = new swap_event + { + id = 1, + SwapTradeId = SwapDealTestFactory.SwapTradeId, + EventType = (int)SwapEventTypeEnum.平仓, + Invalid = false, + EventData = JsonConvert.SerializeObject(unwindData) + }; + var service = new TestableSwapDealService(td, + swapEvents: new Dictionary + { + [(int)SwapEventTypeEnum.平仓] = swapEvent + }, + flowEventsByEventId: new Dictionary> + { + [1] = new List { floatEvent } + }); + + service.ApproveSwapTrade(td, (int)SwapEventTypeEnum.平仓); + + Assert.AreEqual(1m, swapEvent.unwindData.ClosePercent); + Assert.AreEqual((int)CloseMethodEnum.部分平仓, swapEvent.unwindData.CloseMethod, + "审批不应把部分平仓事件改写为全平意图"); + Assert.AreEqual(5000000m, swapEvent.unwindData.CloseQty); + Assert.AreEqual(4906156.15m, swapEvent.unwindData.CloseNotionalValue); + Assert.AreEqual(5000000m, floatEvent.Quantity); + Assert.AreEqual(0m, floatEvent.PositionQty); + Assert.AreEqual("已平仓", td.TradeStatus); + Assert.AreEqual(0d, td.StockEqvNotional, 0.000001); + Assert.AreEqual(0d, td.TradeAmount, 0.000001); + } + + [TestMethod] + public void UW_015_approve_full_close_recalculates_normalized_pnl_before_cash() + { + var td = SwapDealTestFactory.CreateTrade(); + td.StockEqvNotional = 4906156.15; + td.TradeAmount = 5000000; + var unwindData = SwapDealTestFactory.CreateUnwindData( + swapRealizedPnL: 50000000.10m, + closeMethod: (int)CloseMethodEnum.部分平仓, + closePercent: 4906156.15m / 9812312.31m, + closeQty: 5000000.01m, + closeNotionalValue: 4906156.15m, + positionQty: 5000000m); + unwindData.NotionalValue = 9812312.31m; + unwindData.PosiNotionalValue = 4906156.15m; + unwindData.SwapCloseAmount = 50000000.10m; + var floatEvent = new swap_flow_event + { + EventId = 1, + UnderlyingCode = "261031.IB", + PositionType = (int)PositionTypeFlag.Long, + PayDirection = 1, + PosiGrossPrice = 1m, + TradingAmountAvg = 11m, + MarkClosePnl = 50000000.10m, + Quantity = 5000000.01m, + PositionQty = -0.01m + }; + var swapEvent = new swap_event + { + id = 1, + SwapTradeId = SwapDealTestFactory.SwapTradeId, + EventType = (int)SwapEventTypeEnum.平仓, + Invalid = false, + EventData = JsonConvert.SerializeObject(unwindData) + }; + var service = new TestableSwapDealService(td, + swapEvents: new Dictionary + { + [(int)SwapEventTypeEnum.平仓] = swapEvent + }, + flowEventsByEventId: new Dictionary> + { + [1] = new List { floatEvent } + }); + + service.ApproveSwapTrade(td, (int)SwapEventTypeEnum.平仓); + + Assert.AreEqual(5000000m, swapEvent.unwindData.CloseQty); + Assert.AreEqual(50000000m, swapEvent.unwindData.SwapRealizedPnL); + Assert.AreEqual(-50000000d, service.ClientCashCalls.Single().amount, 0.001d); + } } } diff --git a/YLErpDAL/Modules/SwapModule/SwapDealService.cs b/YLErpDAL/Modules/SwapModule/SwapDealService.cs index 4248ae87..ef329abb 100644 --- a/YLErpDAL/Modules/SwapModule/SwapDealService.cs +++ b/YLErpDAL/Modules/SwapModule/SwapDealService.cs @@ -47,6 +47,65 @@ namespace YLErp.Modules.SwapModule unwindData.CloseNotionalValue = Math.Round(unwindData.CloseNotionalValue, ConsGlobal.MoneyRound, MidpointRounding.AwayFromZero); } + private static bool NormalizeFullCloseRequest(UnwindData unwindData) + { + if (unwindData.CloseMethod != (int)CloseMethodEnum.全部平仓 + && unwindData.ClosePercent < 1 + && !(unwindData.PositionQty > 0 && unwindData.CloseQty >= unwindData.PositionQty) + && !(unwindData.PosiNotionalValue > 0 && unwindData.CloseNotionalValue >= unwindData.PosiNotionalValue)) + { + return false; + } + + var closeQty = unwindData.CloseQty; + var closeNotionalValue = unwindData.CloseNotionalValue; + unwindData.ClosePercent = 1; + if (unwindData.PositionQty > 0) unwindData.CloseQty = unwindData.PositionQty; + if (unwindData.PosiNotionalValue > 0) unwindData.CloseNotionalValue = unwindData.PosiNotionalValue; + return closeQty != unwindData.CloseQty || closeNotionalValue != unwindData.CloseNotionalValue; + } + + private static void RecalculateNormalizedUnwindAmounts(UnwindData unwindData) + { + var floatLeg = unwindData.FlowEvents.FirstOrDefault(x => !string.IsNullOrEmpty(x.UnderlyingCode)); + if (floatLeg == null || floatLeg.PosiGrossPrice == 0) return; + + var input = new UnwindInput + { + Multiplier = ConsGlobal.InstrumentType.IsBond(floatLeg.UnderlyingInstrumentType) ? 100 : 1, + PosiGrossPrice = floatLeg.PosiGrossPrice, + TradingAmountAvg = floatLeg.TradingAmountAvg, + CloseQty = unwindData.CloseQty, + PositionQty = unwindData.PositionQty, + ContractSize = floatLeg.ContractSize, + CloseNotionalValue = unwindData.CloseNotionalValue, + PayDirection = floatLeg.PayDirection, + PositionType = floatLeg.PositionType, + TradingFee = floatLeg.TradingFee.ToString(), + TradingFeePending = floatLeg.TradingFeePending.ToString(), + DividendIn = floatLeg.DividendIn.ToString() + }; + foreach (var leg in unwindData.FlowEvents.Where(x => string.IsNullOrEmpty(x.UnderlyingCode))) + { + var target = leg.InterestMode == (int)InterestModeEnum.初始预付金 + || leg.InterestMode == (int)InterestModeEnum.追加预付金 + ? input.MarginLegs + : input.InterestLegs; + target.Add(new LegInput { InterestClosePnL = leg.InterestClosePnL }); + } + + var result = FrontendCalcReference.CalcUnwind(input); + floatLeg.MarkClosePnl = result.MarkClosePnl; + unwindData.SwapCloseAmount = result.SwapCloseAmount; + unwindData.SwapRealizedPnL = result.SwapRealizedPnL; + unwindData.SwapMarginRebatePnl = result.SwapMarginRebatePnl; + } + + private static bool IsFullCloseAfterDeduction(UnwindData unwindData, double remainingNotional, double remainingQuantity) + { + return unwindData.ClosePercent == 1 || (remainingNotional == 0 && remainingQuantity == 0); + } + // 待实现利息会进入 decimal(30,12) 日终快照 private const int InterestCalculationPrecision = 12; @@ -1335,10 +1394,14 @@ namespace YLErp.Modules.SwapModule NormalizeNotionalValues(unwindData); NormalizeManualSettlementAmounts(unwindData, (int)SwapEventTypeEnum.平仓, "系统操作_平仓"); //CheckLastEod(unwindData.ValueDate, td.StartDate.Value, unwindData.SwapTradeId); //去掉平仓收盘限制 - ValidateFrontendPnL(unwindData, isIncome: false); // 只读校验告警,不阻断交易 // 前端按"占期初(original)"语义传 ClosePercent(A);后端全链路按"占剩余(remaining)"语义(B)消费。 // 入口统一转换为 B,落库展示用的 A 由 SaveSwapDealInternal 还原。 unwindData.ClosePercent = ToRemainingClosePercent(unwindData.ClosePercent, unwindData.NotionalValue, unwindData.PosiNotionalValue); + if (NormalizeFullCloseRequest(unwindData)) + { + RecalculateNormalizedUnwindAmounts(unwindData); + } + ValidateFrontendPnL(unwindData, isIncome: false); // 只读校验告警,不阻断交易 bool cofirm = false; ExecuteInTransaction(() => { @@ -1354,18 +1417,24 @@ namespace YLErp.Modules.SwapModule DealFloatPosition(unwindData); var flowList = new List(unwindData.FlowEvents); var eventId = SaveSwapDeal(unwindData, (int)SwapEventTypeEnum.平仓, clientCashId, "系统操作_平仓"); - if (unwindData.CloseMethod == (int)CloseMethodEnum.全部平仓 || unwindData.ClosePercent == 1) + var remainingStockEqvNotional = Math.Round(td.StockEqvNotional - Convert.ToDouble(unwindData.CloseNotionalValue), ConsGlobal.MoneyRound, MidpointRounding.AwayFromZero); + var remainingTradeAmount = td.TradeAmount - Convert.ToDouble(unwindData.CloseQty); + var isFullClose = IsFullCloseAfterDeduction(unwindData, remainingStockEqvNotional, remainingTradeAmount); + if (isFullClose) { td.TradeStatus = "已平仓"; + td.StockEqvNotional = 0; + td.TradeAmount = 0; CallSaveSwapTradeClientCash(td, unwindData.ValueDate); } else { td.HasPartialUnWind = 1; + td.StockEqvNotional = remainingStockEqvNotional; + td.TradeAmount = remainingTradeAmount; } + td.Notional = td.TradeAmount; td.UnWindDate = unwindData.UnwindDate; - td.StockEqvNotional = Math.Round(td.StockEqvNotional - Convert.ToDouble(unwindData.CloseNotionalValue), ConsGlobal.MoneyRound, MidpointRounding.AwayFromZero); - td.TradeAmount -= Convert.ToDouble(unwindData.CloseQty); SaveAllChanges(); cofirm = true; }); @@ -1860,12 +1929,36 @@ namespace YLErp.Modules.SwapModule throw new Exception("该笔交易状态为平仓待复核,未找到相关记录,请检查该笔交易是否有效"); } swapEvent.unwindData = JsonConvert.DeserializeObject(swapEvent.EventData); + NormalizeNotionalValues(swapEvent.unwindData); + // Stored events keep display ratio A; approval calculations consume remaining ratio B. + swapEvent.unwindData.ClosePercent = ToRemainingClosePercent( + swapEvent.unwindData.ClosePercent, + swapEvent.unwindData.NotionalValue, + swapEvent.unwindData.PosiNotionalValue); + var flowList = FindFlowEventsByEventId(swapEvent.id); + swapEvent.unwindData.FlowEvents = flowList; + if (eventType == (int)SwapEventTypeEnum.平仓) + { + if (NormalizeFullCloseRequest(swapEvent.unwindData)) + { + RecalculateNormalizedUnwindAmounts(swapEvent.unwindData); + } + } if (eventType == (int)SwapEventTypeEnum.互换) { NormalizeIncomeUnwindDate(swapEvent.unwindData); ValidateIncomeValueDate(swapEvent.unwindData, td); } - var flowList = FindFlowEventsByEventId(swapEvent.id); + if (eventType == (int)SwapEventTypeEnum.平仓) + { + foreach (var item in flowList.Where(x => x.PositionType > 0)) + { + item.Quantity = swapEvent.unwindData.CloseQty; + item.PositionQty = swapEvent.unwindData.ClosePercent == 1 + ? 0 + : swapEvent.unwindData.PositionQty - swapEvent.unwindData.CloseQty; + } + } string action = eventType == (int)SwapEventTypeEnum.互换 ? ClientCashInCashOut.系统操作_互换 : ClientCashInCashOut.系统操作_平仓费; int clientCashId = AddClientCash(td, Convert.ToDouble(-swapEvent.unwindData.SwapRealizedPnL), action, swapEvent.unwindData.ValueDate); if (swapEvent.unwindData.SwapMarginAmount != 0) @@ -1873,7 +1966,28 @@ namespace YLErp.Modules.SwapModule AddClientCash(td, Convert.ToDouble(swapEvent.unwindData.SwapMarginAmount), ClientCashInCashOut.系统操作_应付预付金, swapEvent.unwindData.ValueDate); } swapEvent.ClientCashId = clientCashId; - if (swapEvent.unwindData.CloseMethod == (int)CloseMethodEnum.全部平仓) + td.UnWindDate = swapEvent.unwindData.UnwindDate; + if (eventType != (int)SwapEventTypeEnum.互换) + { + var remainingStockEqvNotional = Math.Round(td.StockEqvNotional - Convert.ToDouble(swapEvent.unwindData.CloseNotionalValue), ConsGlobal.MoneyRound, MidpointRounding.AwayFromZero); + var remainingTradeAmount = td.TradeAmount - Convert.ToDouble(swapEvent.unwindData.CloseQty); + var isFullClose = IsFullCloseAfterDeduction(swapEvent.unwindData, remainingStockEqvNotional, remainingTradeAmount); + if (isFullClose) + { + td.TradeStatus = "已平仓"; + td.StockEqvNotional = 0; + td.TradeAmount = 0; + CallSaveSwapTradeClientCash(td, swapEvent.unwindData.ValueDate); + } + else + { + td.TradeStatus = ConsTrade.确认成交; + td.HasPartialUnWind = 1; + td.StockEqvNotional = remainingStockEqvNotional; + td.TradeAmount = remainingTradeAmount; + } + } + else if (swapEvent.unwindData.CloseMethod == (int)CloseMethodEnum.全部平仓) { td.TradeStatus = "已平仓"; CallSaveSwapTradeClientCash(td, swapEvent.unwindData.ValueDate); @@ -1883,12 +1997,6 @@ namespace YLErp.Modules.SwapModule td.TradeStatus = ConsTrade.确认成交; td.HasPartialUnWind = 1; } - td.UnWindDate = swapEvent.unwindData.UnwindDate; - if (eventType != (int)SwapEventTypeEnum.互换) - { - td.StockEqvNotional = Math.Round(td.StockEqvNotional - Convert.ToDouble(swapEvent.unwindData.CloseNotionalValue), ConsGlobal.MoneyRound, MidpointRounding.AwayFromZero); - td.TradeAmount -= Convert.ToDouble(swapEvent.unwindData.CloseQty); - } td.Notional = td.TradeAmount; UpdateInitalPosition(flowList, swapEvent.unwindData, eventType); @@ -1920,6 +2028,13 @@ namespace YLErp.Modules.SwapModule // 与 SwapUnwind(L1270) 保持一致——缺少此转换会导致 SaveSwapDealInternal 的 B→A 还原出错 // (例如第二次部分平仓 50%(A) → 错误还原为 0.325 而非 0.50)。 unwindData.ClosePercent = ToRemainingClosePercent(unwindData.ClosePercent, unwindData.NotionalValue, unwindData.PosiNotionalValue); + if (eventType == (int)SwapEventTypeEnum.平仓) + { + if (NormalizeFullCloseRequest(unwindData)) + { + RecalculateNormalizedUnwindAmounts(unwindData); + } + } string action = eventType == (int)SwapEventTypeEnum.互换 ? ClientCashInCashOut.系统操作_互换 : ClientCashInCashOut.系统操作_平仓费; ExecuteInTransaction(() => { @@ -2053,8 +2168,17 @@ namespace YLErp.Modules.SwapModule else { // 平仓时才扣减持仓 - position.PosiQuantity -= unwindData.CloseQty; - position.PosiNotionalValue = Math.Round(position.PosiNotionalValue - unwindData.CloseNotionalValue, ConsGlobal.MoneyRound, MidpointRounding.AwayFromZero); + var remainingPositionQty = position.PosiQuantity - unwindData.CloseQty; + var remainingPositionNotional = Math.Round( + position.PosiNotionalValue - unwindData.CloseNotionalValue, + ConsGlobal.MoneyRound, + MidpointRounding.AwayFromZero); + position.PosiQuantity = unwindData.ClosePercent == 1 + ? 0 + : remainingPositionQty; + position.PosiNotionalValue = unwindData.ClosePercent == 1 + ? 0 + : remainingPositionNotional; position.PosiTradingFee -= position.PosiTradingFee * unwindData.ClosePercent; position.PosiTradingFeePending -= position.PosiTradingFeePending * unwindData.ClosePercent; } @@ -2068,10 +2192,13 @@ namespace YLErp.Modules.SwapModule position.InterestFeePending += interest.InterestFee; if ((interest.InterestMode == (int)InterestModeEnum.追加预付金 || interest.InterestMode == (int)InterestModeEnum.初始预付金) && eventType == (int)SwapEventTypeEnum.平仓) { - position.InterestPrincipalFix = Math.Round( + var remainingInterestPrincipal = Math.Round( position.InterestPrincipalFix - interest.InterestPrincipal, ConsGlobal.MoneyRound, MidpointRounding.AwayFromZero); + position.InterestPrincipalFix = unwindData.ClosePercent == 1 + ? 0 + : remainingInterestPrincipal; } } } diff --git a/YLErpWeb/fe-tests/swapCalc.test.js b/YLErpWeb/fe-tests/swapCalc.test.js index 87c6e17d..3e32a3fa 100644 --- a/YLErpWeb/fe-tests/swapCalc.test.js +++ b/YLErpWeb/fe-tests/swapCalc.test.js @@ -182,6 +182,19 @@ describe('多次部分平仓:全部↔部分切换 CloseQty 不跳变(占期 expectClose(closeQty, 25000000, '应=25000000 不受 JS 浮点偏差影响'); expect(closeQty).not.toBe(24999999.999999996); }); + + test('trade2308: full close uses the remaining quantity after fixed6 percent formatting', () => { + const notionalValue = 9812312.31; + const posiNotionalValue = 4906156.15; + const oriClosePercent = posiNotionalValue / notionalValue; + const closePercent = 0.5; + const positionQty = 5000000; + + const closeQty = SwapCalc.calcCloseQtyByOriginalPercent( + closePercent, oriClosePercent, positionQty); + + expect(closeQty).toBe(positionQty); + }); }); describe('交叉校验:对齐 C# FrontendCalcCharacterizationTest 金标准', () => { diff --git a/YLErpWeb/wwwroot/Scripts/app/swaptrade/swapCalc.js b/YLErpWeb/wwwroot/Scripts/app/swaptrade/swapCalc.js index dfcea9a7..670a0748 100644 --- a/YLErpWeb/wwwroot/Scripts/app/swaptrade/swapCalc.js +++ b/YLErpWeb/wwwroot/Scripts/app/swaptrade/swapCalc.js @@ -94,6 +94,7 @@ function calcCloseQtyByOriginalPercent(closePercent, oriClosePercent, positionQty) { var ori = Number(oriClosePercent); if (ori === 0) return 0; + if (Number(closePercent) >= ori) return Number(positionQty); return roundHalfAwayFromZero(Number(positionQty) * (Number(closePercent) / ori), 2); }