diff --git a/UnitTestProject/Modules/SwapModule/InitUnwindTradingFeeTest.cs b/UnitTestProject/Modules/SwapModule/InitUnwindTradingFeeTest.cs index 8ebfb199..34000da7 100644 --- a/UnitTestProject/Modules/SwapModule/InitUnwindTradingFeeTest.cs +++ b/UnitTestProject/Modules/SwapModule/InitUnwindTradingFeeTest.cs @@ -1,4 +1,3 @@ -using System.Reflection; using YLErp.DBModels; namespace YLErp.Modules.SwapModule @@ -7,26 +6,10 @@ namespace YLErp.Modules.SwapModule public class InitUnwindTradingFeeTest { private static decimal InvokeCalcInitTradingFee(swap_position position, UnwindData unwindData) - { - var method = typeof(SwapDealService).GetMethod( - "CalcInitTradingFee", - BindingFlags.NonPublic | BindingFlags.Static); - - Assert.IsNotNull(method, "未找到 CalcInitTradingFee 私有静态方法"); - - return (decimal)method.Invoke(null, new object[] { position, unwindData }); - } + => TradingFeeCalc.CalcInitTradingFee(position, unwindData); private static decimal InvokeCalcInitTradingFeePending(swap_position oriPosition, swap_position position, UnwindData unwindData) - { - var method = typeof(SwapDealService).GetMethod( - "CalcInitTradingFeePending", - BindingFlags.NonPublic | BindingFlags.Static); - - Assert.IsNotNull(method, "CalcInitTradingFeePending was not found"); - - return (decimal)method.Invoke(null, new object[] { oriPosition, position, unwindData }); - } + => TradingFeeCalc.CalcInitTradingFeePending(oriPosition, position, unwindData); [TestMethod] public void 百分比模式_按平仓名义本金计算并四舍五入到两位() diff --git a/YLErpDAL/Modules/SwapModule/SwapDealService.cs b/YLErpDAL/Modules/SwapModule/SwapDealService.cs index 8ae2c8d3..7364c691 100644 --- a/YLErpDAL/Modules/SwapModule/SwapDealService.cs +++ b/YLErpDAL/Modules/SwapModule/SwapDealService.cs @@ -46,106 +46,17 @@ namespace YLErp.Modules.SwapModule /// 原 private 改 protected virtual,使测试 stub 可整体 override,规避内部 new SwapEventService 连库。 protected virtual long SaveSwapDeal(UnwindData unwindData, int eventType, int clientCashId, string eventResason = "", bool approve = false) { - NormalizeNotionalValues(unwindData); + UnwindNormalizer.NormalizeNotionalValues(unwindData); return SaveSwapDealInternal(unwindData, eventType, clientCashId, eventResason, approve); } - private static void NormalizeNotionalValues(UnwindData unwindData) - { - unwindData.NotionalValue = Math.Round(unwindData.NotionalValue, ConsGlobal.MoneyRound, MidpointRounding.AwayFromZero); - unwindData.PosiNotionalValue = Math.Round(unwindData.PosiNotionalValue, ConsGlobal.MoneyRound, MidpointRounding.AwayFromZero); - 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 = MarginModes.Contains(leg.InterestMode) - ? 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) 日终快照;精度常量统一引用 SwapInterest.FundingLegPrecision,消除重复定义。 private const int InterestCalculationPrecision = SwapInterest.FundingLegPrecision; - /// - /// 手工平仓、手工互换及收益结算的利息事件按金额两位落库。 - /// 自动平仓保留原有计算与落库口径,不适用本阶段的手工结算规则。 - /// - private static bool NormalizeSettledInterestAmounts(IEnumerable flowEvents, int eventType, string eventReason) - { - if ((eventType != (int)SwapEventTypeEnum.平仓 && eventType != (int)SwapEventTypeEnum.互换) - || eventReason == "系统操作_自动平仓") - { - return false; - } - - foreach (var flowEvent in flowEvents.Where(x => string.IsNullOrEmpty(x.UnderlyingCode))) - { - // 只处理利息腿;浮动腿损益在日终快照入口统一按两位落库。 - flowEvent.InterestPrincipal = Math.Round(flowEvent.InterestPrincipal, ConsGlobal.MoneyRound, MidpointRounding.AwayFromZero); - flowEvent.InterestAmount = Math.Round(flowEvent.InterestAmount, ConsGlobal.MoneyRound, MidpointRounding.AwayFromZero); - flowEvent.TdInterestAmount = Math.Round(flowEvent.TdInterestAmount, ConsGlobal.MoneyRound, MidpointRounding.AwayFromZero); - flowEvent.InterestClosePnL = Math.Round(flowEvent.InterestClosePnL, ConsGlobal.MoneyRound, MidpointRounding.AwayFromZero); - flowEvent.InterestFee = Math.Round(flowEvent.InterestFee, ConsGlobal.MoneyRound, MidpointRounding.AwayFromZero); - } - return true; - } - // 客户现金在 SaveSwapDeal 之前创建,手工结算必须先收敛流水并重算汇总金额。 private void NormalizeManualSettlementAmounts(UnwindData unwindData, int eventType, string eventReason) { - if (!NormalizeSettledInterestAmounts(unwindData.FlowEvents, eventType, eventReason)) + if (!UnwindNormalizer.NormalizeSettledInterestAmounts(unwindData.FlowEvents, eventType, eventReason)) { return; } @@ -372,7 +283,7 @@ namespace YLErp.Modules.SwapModule floatEvent.UnderlyingInstrumentType = position.UnderlyingInstrumentType; floatEvent.CloseFee = 0; floatEvent.BeforeCloseFee = oriPosition.PosiTradingFeePending; - floatEvent.TradingFee = CalcInitTradingFee(oriPosition, unwindData); + floatEvent.TradingFee = TradingFeeCalc.CalcInitTradingFee(oriPosition, unwindData); floatEvent.PosiTradingFeeUnit = oriPosition?.PosiTradingFeeUnit ?? 0; floatEvent.PosiFeeType = oriPosition?.PosiFeeType ?? 0; floatEvent.MarkClosePnl = 0; @@ -387,7 +298,7 @@ namespace YLErp.Modules.SwapModule floatEvent.ContractSize = position.ContractSize; floatEvent.TradingAmount = floatEvent.Quantity * floatEvent.ContractSize; var ratio = -DirectionRatio.ReceivePay(position.PosiDirection); - floatEvent.TradingFeePending = CalcInitTradingFeePending(oriPosition, position, unwindData); + floatEvent.TradingFeePending = TradingFeeCalc.CalcInitTradingFeePending(oriPosition, position, unwindData); floatEvent.DataState = (int)SwapFlowDateStateEnum.完成; floatEvent.InterestMode = position.InterestMode; floatEvent.ClientId = td.ClientId; @@ -397,37 +308,6 @@ namespace YLErp.Modules.SwapModule } return unwindData; } - private static decimal CalcInitTradingFee(swap_position oriPosition, UnwindData unwindData) - { - if (oriPosition == null || unwindData == null) - { - return 0; - } - - if (oriPosition.PosiFeeType == 1) - { - return Math.Round(oriPosition.PosiTradingFeeUnit * unwindData.CloseQty, ConsGlobal.MoneyRound, MidpointRounding.AwayFromZero); - } - - return Math.Round(oriPosition.PosiTradingFeeUnit / 100m * unwindData.CloseNotionalValue, ConsGlobal.MoneyRound, MidpointRounding.AwayFromZero); - } - - private static decimal CalcInitTradingFeePending(swap_position oriPosition, swap_position position, UnwindData unwindData) - { - if (oriPosition == null || unwindData == null || oriPosition.PosiTradingFeeUnit == 0) - { - return position?.PosiTradingFeePending ?? 0; - } - - var closeBase = oriPosition.PosiFeeType == 1 ? unwindData.CloseQty : unwindData.CloseNotionalValue; - var originalBase = oriPosition.PosiFeeType == 1 ? unwindData.NotionalQty : unwindData.NotionalValue; - if (originalBase <= 0) - { - return position?.PosiTradingFeePending ?? 0; - } - - return Math.Round(oriPosition.PosiTradingFeePending * closeBase / originalBase, ConsGlobal.MoneyRound, MidpointRounding.AwayFromZero); - } /// /// 校验上日是否收盘 /// @@ -1573,16 +1453,16 @@ namespace YLErp.Modules.SwapModule { throw new ServiceException("未找到交易信息"); } - NormalizeEventUnwindDate(unwindData); - NormalizeNotionalValues(unwindData); + UnwindNormalizer.NormalizeEventUnwindDate(unwindData); + UnwindNormalizer.NormalizeNotionalValues(unwindData); NormalizeManualSettlementAmounts(unwindData, (int)SwapEventTypeEnum.平仓, "系统操作_平仓"); //CheckLastEod(unwindData.ValueDate, td.StartDate.Value, unwindData.SwapTradeId); //去掉平仓收盘限制 // 前端按"占期初(original)"语义传 ClosePercent(A);后端全链路按"占剩余(remaining)"语义(B)消费。 // 入口统一转换为 B,落库展示用的 A 由 SaveSwapDealInternal 还原。 unwindData.ClosePercent = ToRemainingClosePercent(unwindData.ClosePercent, unwindData.NotionalValue, unwindData.PosiNotionalValue); - if (NormalizeFullCloseRequest(unwindData)) + if (UnwindNormalizer.NormalizeFullCloseRequest(unwindData)) { - RecalculateNormalizedUnwindAmounts(unwindData); + UnwindNormalizer.RecalculateNormalizedUnwindAmounts(unwindData); } ValidateFrontendPnL(unwindData, isIncome: false); // 只读校验告警,不阻断交易 bool cofirm = false; @@ -1595,7 +1475,7 @@ namespace YLErp.Modules.SwapModule var eventId = SaveSwapDeal(unwindData, (int)SwapEventTypeEnum.平仓, clientCashId, "系统操作_平仓"); 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); + var isFullClose = UnwindNormalizer.IsFullCloseAfterDeduction(unwindData, remainingStockEqvNotional, remainingTradeAmount); if (isFullClose) { td.TradeStatus = "已平仓"; @@ -1968,7 +1848,7 @@ namespace YLErp.Modules.SwapModule { throw new ServiceException("未找到交易信息"); } - NormalizeEventUnwindDate(unwindData); + UnwindNormalizer.NormalizeEventUnwindDate(unwindData); unwindData.SwapRealizedPnL = unwindData.SwapCloseAmount; NormalizeManualSettlementAmounts(unwindData, (int)SwapEventTypeEnum.平仓, "系统操作_平仓"); var trans = DbContext.Database.BeginTransaction(); @@ -2006,7 +1886,7 @@ namespace YLErp.Modules.SwapModule { throw new ServiceException("未找到交易信息"); } - NormalizeEventUnwindDate(unwindData); + UnwindNormalizer.NormalizeEventUnwindDate(unwindData); unwindData.SwapRealizedPnL = unwindData.SwapCloseAmount; NormalizeManualSettlementAmounts(unwindData, (int)SwapEventTypeEnum.互换, "系统操作_互换"); var trans = DbContext.Database.BeginTransaction(); @@ -2046,7 +1926,7 @@ namespace YLErp.Modules.SwapModule { throw new ServiceException("未找到交易信息"); } - NormalizeEventUnwindDate(unwindData); + UnwindNormalizer.NormalizeEventUnwindDate(unwindData); ValidateIncomeValueDate(unwindData, td); NormalizeManualSettlementAmounts(unwindData, (int)SwapEventTypeEnum.互换, "系统操作_互换"); //CheckLastEod(unwindData.ValueDate, td.StartDate.Value, unwindData.SwapTradeId); //去掉平仓收盘限制 @@ -2084,8 +1964,8 @@ namespace YLErp.Modules.SwapModule throw new Exception("该笔交易状态为平仓待复核,未找到相关记录,请检查该笔交易是否有效"); } swapEvent.unwindData = JsonConvert.DeserializeObject(swapEvent.EventData); - NormalizeEventUnwindDate(swapEvent.unwindData); - NormalizeNotionalValues(swapEvent.unwindData); + UnwindNormalizer.NormalizeEventUnwindDate(swapEvent.unwindData); + UnwindNormalizer.NormalizeNotionalValues(swapEvent.unwindData); // Stored events keep display ratio A; approval calculations consume remaining ratio B. swapEvent.unwindData.ClosePercent = ToRemainingClosePercent( swapEvent.unwindData.ClosePercent, @@ -2100,9 +1980,9 @@ namespace YLErp.Modules.SwapModule swapEvent.unwindData.FlowEvents = flowList; if (eventType == (int)SwapEventTypeEnum.平仓) { - if (NormalizeFullCloseRequest(swapEvent.unwindData)) + if (UnwindNormalizer.NormalizeFullCloseRequest(swapEvent.unwindData)) { - RecalculateNormalizedUnwindAmounts(swapEvent.unwindData); + UnwindNormalizer.RecalculateNormalizedUnwindAmounts(swapEvent.unwindData); } } if (eventType == (int)SwapEventTypeEnum.互换) @@ -2131,7 +2011,7 @@ namespace YLErp.Modules.SwapModule { 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); + var isFullClose = UnwindNormalizer.IsFullCloseAfterDeduction(swapEvent.unwindData, remainingStockEqvNotional, remainingTradeAmount); if (isFullClose) { td.TradeStatus = "已平仓"; @@ -2176,7 +2056,7 @@ namespace YLErp.Modules.SwapModule { throw new ServiceException("未找到交易信息"); } - NormalizeEventUnwindDate(unwindData); + UnwindNormalizer.NormalizeEventUnwindDate(unwindData); if (eventType == (int)SwapEventTypeEnum.互换) { ValidateIncomeValueDate(unwindData, td); @@ -2190,9 +2070,9 @@ namespace YLErp.Modules.SwapModule unwindData.ClosePercent = ToRemainingClosePercent(unwindData.ClosePercent, unwindData.NotionalValue, unwindData.PosiNotionalValue); if (eventType == (int)SwapEventTypeEnum.平仓) { - if (NormalizeFullCloseRequest(unwindData)) + if (UnwindNormalizer.NormalizeFullCloseRequest(unwindData)) { - RecalculateNormalizedUnwindAmounts(unwindData); + UnwindNormalizer.RecalculateNormalizedUnwindAmounts(unwindData); } } string action = eventType == (int)SwapEventTypeEnum.互换 ? ClientCashInCashOut.系统操作_互换 : ClientCashInCashOut.系统操作_平仓费; @@ -2225,11 +2105,6 @@ namespace YLErp.Modules.SwapModule } } - private static void NormalizeEventUnwindDate(UnwindData unwindData) - { - unwindData.UnwindDate = unwindData.ValueDate; - } - /// /// 保存平仓/互换事件 /// @@ -2243,7 +2118,7 @@ namespace YLErp.Modules.SwapModule throw new ServiceException("未找到交易信息"); } var flowList = new List(unwindData.FlowEvents); - NormalizeSettledInterestAmounts(flowList, eventType, eventResason); + UnwindNormalizer.NormalizeSettledInterestAmounts(flowList, eventType, eventResason); unwindData.FlowEvents.Clear(); // 落库展示用"占期初(original)"语义(A);计算链(费用递减/全平判定)用"占剩余(remaining)"语义(B)。 // 序列化前把 ClosePercent 还原为 A,序列化后立即还原回 B 供后续使用。 diff --git a/YLErpDAL/Modules/SwapModule/TradingFeeCalc.cs b/YLErpDAL/Modules/SwapModule/TradingFeeCalc.cs new file mode 100644 index 00000000..3c9cea1c --- /dev/null +++ b/YLErpDAL/Modules/SwapModule/TradingFeeCalc.cs @@ -0,0 +1,43 @@ +using YLErp.DBModels; +using YLErp.DBModels.Consts; + +namespace YLErp.Modules.SwapModule; + +/// +/// 平仓手续费计算——纯 static,无 this 依赖。 +/// 从 SwapDealService 提取,零行为变更。 +/// +public static class TradingFeeCalc +{ + public static decimal CalcInitTradingFee(swap_position oriPosition, UnwindData unwindData) + { + if (oriPosition == null || unwindData == null) + { + return 0; + } + + if (oriPosition.PosiFeeType == 1) + { + return Math.Round(oriPosition.PosiTradingFeeUnit * unwindData.CloseQty, ConsGlobal.MoneyRound, MidpointRounding.AwayFromZero); + } + + return Math.Round(oriPosition.PosiTradingFeeUnit / 100m * unwindData.CloseNotionalValue, ConsGlobal.MoneyRound, MidpointRounding.AwayFromZero); + } + + public static decimal CalcInitTradingFeePending(swap_position oriPosition, swap_position position, UnwindData unwindData) + { + if (oriPosition == null || unwindData == null || oriPosition.PosiTradingFeeUnit == 0) + { + return position?.PosiTradingFeePending ?? 0; + } + + var closeBase = oriPosition.PosiFeeType == 1 ? unwindData.CloseQty : unwindData.CloseNotionalValue; + var originalBase = oriPosition.PosiFeeType == 1 ? unwindData.NotionalQty : unwindData.NotionalValue; + if (originalBase <= 0) + { + return position?.PosiTradingFeePending ?? 0; + } + + return Math.Round(oriPosition.PosiTradingFeePending * closeBase / originalBase, ConsGlobal.MoneyRound, MidpointRounding.AwayFromZero); + } +} diff --git a/YLErpDAL/Modules/SwapModule/UnwindNormalizer.cs b/YLErpDAL/Modules/SwapModule/UnwindNormalizer.cs new file mode 100644 index 00000000..63db255c --- /dev/null +++ b/YLErpDAL/Modules/SwapModule/UnwindNormalizer.cs @@ -0,0 +1,104 @@ +using YLErp.Helpers; +using YLErp.Modules.SwapModule.Margin; + +namespace YLErp.Modules.SwapModule; + +/// +/// 平仓数据(UnwindData)规范化——纯 static,无 this 依赖。 +/// 从 SwapDealService 提取,零行为变更。 +/// +internal static class UnwindNormalizer +{ + internal static void NormalizeNotionalValues(UnwindData unwindData) + { + unwindData.NotionalValue = Math.Round(unwindData.NotionalValue, ConsGlobal.MoneyRound, MidpointRounding.AwayFromZero); + unwindData.PosiNotionalValue = Math.Round(unwindData.PosiNotionalValue, ConsGlobal.MoneyRound, MidpointRounding.AwayFromZero); + unwindData.CloseNotionalValue = Math.Round(unwindData.CloseNotionalValue, ConsGlobal.MoneyRound, MidpointRounding.AwayFromZero); + } + + internal 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; + } + + internal 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 = MarginModes.Contains(leg.InterestMode) + ? 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; + } + + internal static bool IsFullCloseAfterDeduction(UnwindData unwindData, double remainingNotional, double remainingQuantity) + { + return unwindData.ClosePercent == 1 || (remainingNotional == 0 && remainingQuantity == 0); + } + + /// + /// 手工平仓、手工互换及收益结算的利息事件按金额两位落库。 + /// 自动平仓保留原有计算与落库口径,不适用本阶段的手工结算规则。 + /// + internal static bool NormalizeSettledInterestAmounts(IEnumerable flowEvents, int eventType, string eventReason) + { + if ((eventType != (int)SwapEventTypeEnum.平仓 && eventType != (int)SwapEventTypeEnum.互换) + || eventReason == "系统操作_自动平仓") + { + return false; + } + + foreach (var flowEvent in flowEvents.Where(x => string.IsNullOrEmpty(x.UnderlyingCode))) + { + flowEvent.InterestPrincipal = Math.Round(flowEvent.InterestPrincipal, ConsGlobal.MoneyRound, MidpointRounding.AwayFromZero); + flowEvent.InterestAmount = Math.Round(flowEvent.InterestAmount, ConsGlobal.MoneyRound, MidpointRounding.AwayFromZero); + flowEvent.TdInterestAmount = Math.Round(flowEvent.TdInterestAmount, ConsGlobal.MoneyRound, MidpointRounding.AwayFromZero); + flowEvent.InterestClosePnL = Math.Round(flowEvent.InterestClosePnL, ConsGlobal.MoneyRound, MidpointRounding.AwayFromZero); + flowEvent.InterestFee = Math.Round(flowEvent.InterestFee, ConsGlobal.MoneyRound, MidpointRounding.AwayFromZero); + } + return true; + } + + internal static void NormalizeEventUnwindDate(UnwindData unwindData) + { + unwindData.UnwindDate = unwindData.ValueDate; + } +}