using Newtonsoft.Json; using YLErp.DBModels; using YLErp.DBModels.Enums; namespace YLErp.Modules.SwapModule { /// /// 互换平仓全流程测试(SwapUnwind/ApproveSwapTrade/ApplySwapTrade/DealFloatPosition) /// ============================================================================ /// 借鉴 testable 分支 SwapUnwindScenarioTest,基于当前分支 TestableSwapDealService 共享 stub。 /// 命名规范说明(见《互换价格字段命名规范决策文档》): /// PosiGrossPrice 现状名,实为"期初全价不含费",规范名 EntryDirtyPrice /// TradingAmountAvg 现状名,实为"期末全价不含费",规范名 ExitDirtyPrice /// ============================================================================ [TestClass] public class SwapUnwindScenarioTest { // ================================================================ // 场景1:SwapUnwind 全平仓 —— 持仓归零、TradeStatus=已平仓 // ================================================================ [TestMethod] public void UW_001_SwapUnwind_全平仓_持仓归零且资金流水正确() { var td = SwapDealTestFactory.CreateTrade(); var service = new TestableSwapDealService(td); var unwindData = SwapDealTestFactory.CreateUnwindData( swapRealizedPnL: 5000m, swapMarginAmount: 0m, closeMethod: (int)CloseMethodEnum.全部平仓, closePercent: 1m, closeQty: 10000m, closeNotionalValue: 1000000m, positionQty: 10000m); service.SwapUnwind(unwindData); Assert.AreEqual(1, service.ClientCashCalls.Count, "全平无预付金时应1条资金流水"); Assert.AreEqual(-5000.0, service.ClientCashCalls[0].amount, 0.001, "资金流水=-SwapRealizedPnL"); Assert.AreEqual(ClientCashInCashOut.系统操作_平仓费, service.ClientCashCalls[0].action); Assert.AreEqual("已平仓", td.TradeStatus, "全平仓 TradeStatus=已平仓"); Assert.AreNotEqual(1, td.HasPartialUnWind, "全平仓不应设 HasPartialUnWind"); Assert.AreEqual(0.0, td.StockEqvNotional, 0.001, "StockEqvNotional 扣减后=0"); Assert.AreEqual(0.0, td.TradeAmount, 0.001, "TradeAmount 扣减后=0"); Assert.AreEqual((int)SwapEventTypeEnum.平仓, service.SaveSwapDealCalls[0].eventType, "事件类型=平仓(2)"); Console.WriteLine($"UW_001: TradeStatus={td.TradeStatus}, StockEqvNotional={td.StockEqvNotional} ✅"); } // ================================================================ // 场景2:SwapUnwind 部分平仓 —— HasPartialUnWind=1,TradeStatus 不变 // ================================================================ [TestMethod] public void UW_002_SwapUnwind_部分平仓_设HasPartialUnWind且TradeStatus不变() { var td = SwapDealTestFactory.CreateTrade(); var service = new TestableSwapDealService(td); var unwindData = SwapDealTestFactory.CreateUnwindData( swapRealizedPnL: 3000m, swapMarginAmount: 0m, closeMethod: (int)CloseMethodEnum.部分平仓, closePercent: 0.5m, closeQty: 5000m, closeNotionalValue: 500000m, positionQty: 10000m); service.SwapUnwind(unwindData); Assert.AreEqual(1, td.HasPartialUnWind, "部分平仓应设 HasPartialUnWind=1"); Assert.AreEqual("确认成交", td.TradeStatus, "部分平仓 TradeStatus 保持不变"); Assert.AreEqual(500000.0, td.StockEqvNotional, 0.001, "StockEqvNotional 扣减后=500000"); Assert.AreEqual(5000.0, td.TradeAmount, 0.001, "TradeAmount 扣减后=5000"); Assert.AreEqual(1, service.ClientCashCalls.Count, "部分平仓应1条资金流水"); Assert.AreEqual(-3000.0, service.ClientCashCalls[0].amount, 0.001, "资金流水=-SwapRealizedPnL"); Console.WriteLine($"UW_002: HasPartialUnWind={td.HasPartialUnWind}, TradeStatus={td.TradeStatus} ✅"); } // ================================================================ // 场景3:SwapUnwind 含预付金 —— 两条资金流水 // ================================================================ [TestMethod] public void UW_003_SwapUnwind_含预付金_两条资金流水() { var td = SwapDealTestFactory.CreateTrade(); var service = new TestableSwapDealService(td); var unwindData = SwapDealTestFactory.CreateUnwindData( swapRealizedPnL: 5000m, swapMarginAmount: 2000m, closeMethod: (int)CloseMethodEnum.全部平仓, closePercent: 1m, closeQty: 10000m, closeNotionalValue: 1000000m, positionQty: 10000m); service.SwapUnwind(unwindData); Assert.AreEqual(2, service.ClientCashCalls.Count, "含预付金时应2条资金流水"); Assert.AreEqual(-5000.0, service.ClientCashCalls[0].amount, 0.001, "第1条=平仓费"); Assert.AreEqual(ClientCashInCashOut.系统操作_平仓费, service.ClientCashCalls[0].action); Assert.AreEqual(2000.0, service.ClientCashCalls[1].amount, 0.001, "第2条=应付预付金"); Assert.AreEqual(ClientCashInCashOut.系统操作_应付预付金, service.ClientCashCalls[1].action); Console.WriteLine($"UW_003: 平仓费={service.ClientCashCalls[0].amount}, 应付预付金={service.ClientCashCalls[1].amount} ✅"); } // ================================================================ // 场景4:DealFloatPosition 含费价重算(后端唯二真做计算的地方) // ================================================================ /// /// 平仓事件重算三字段(SwapDealService DealFloatPosition): /// TradingAmountFeeAvg(ExitDirtyFeePrice) = TradingAmountAvg(ExitDirtyPrice) + Fee/CloseQty × shortRatio /// TradingAmountNetFeeAvg(ExitCleanFeePrice) = TradingAmountNetAvg(ExitCleanPrice) + Fee/CloseQty × shortRatio /// TradingAmount = TradingAmountAvg × CloseQty /// 手算:ExitDirtyPrice=1.02, Fee=50, CloseQty=1000, Long(shortRatio=-1) /// ExitDirtyFeePrice = 1.02 + 50/1000×(-1) = 0.97 /// ExitCleanFeePrice = 1.00 + 50/1000×(-1) = 0.95 /// TradingAmount = 1.02 × 1000 = 1020 /// [TestMethod] public void UW_004_DealFloatPosition_含费价重算正确() { var td = SwapDealTestFactory.CreateTrade(); var service = new TestableSwapDealService(td); var closeEvent = new swap_flow_event { EventType = (int)SwapEventTypeEnum.平仓, PositionType = (int)PositionTypeFlag.Long, TradingAmountAvg = 1.02m, // ExitDirtyPrice TradingAmountNetAvg = 1.00m, // ExitCleanPrice TradingFeePending = 50m, }; var unwindData = SwapDealTestFactory.CreateUnwindData(swapRealizedPnL: 0m, closeQty: 1000m); unwindData.FlowEvents.Add(closeEvent); service.SwapUnwind(unwindData); Assert.AreEqual(0.97m, closeEvent.TradingAmountFeeAvg, 0.0001m, $"TradingAmountFeeAvg(ExitDirtyFeePrice)=ExitDirtyPrice+Fee/Qty×(-1)=0.97"); Assert.AreEqual(0.95m, closeEvent.TradingAmountNetFeeAvg ?? 0m, 0.0001m, $"TradingAmountNetFeeAvg(ExitCleanFeePrice)=ExitCleanPrice+Fee/Qty×(-1)=0.95"); Assert.AreEqual(1020m, closeEvent.TradingAmount, 0.0001m, $"TradingAmount=ExitDirtyPrice×CloseQty=1020"); Console.WriteLine($"UW_004: ExitDirtyFeePrice={closeEvent.TradingAmountFeeAvg}, TradingAmount={closeEvent.TradingAmount} ✅"); } // ================================================================ // 场景5:ApproveSwapTrade 审核通过全平仓 —— 反序列化事件并记账 // ================================================================ [TestMethod] public void UW_005_ApproveSwapTrade_全平仓审核_反序列化事件并记账() { var td = SwapDealTestFactory.CreateTrade(); td.ExerciseDate = new DateTime(2026, 12, 31); var unwindData = SwapDealTestFactory.CreateUnwindData(swapRealizedPnL: 8000m, closeMethod: (int)CloseMethodEnum.全部平仓, closePercent: 1m, closeQty: 10000m, closeNotionalValue: 1000000m); var swapEvent = new swap_event { id = 1, SwapTradeId = SwapDealTestFactory.SwapTradeId, EventType = (int)SwapEventTypeEnum.平仓, Invalid = false, EventData = JsonConvert.SerializeObject(unwindData) }; var flowEvents = new Dictionary> { [1] = new List { new swap_flow_event { id = 1, EventId = 1, PositionId = 1 } } }; var service = new TestableSwapDealService(td, swapEvents: new Dictionary { [(int)SwapEventTypeEnum.平仓] = swapEvent }, flowEventsByEventId: flowEvents); service.ApproveSwapTrade(td, (int)SwapEventTypeEnum.平仓); Assert.AreEqual(1, service.ClientCashCalls.Count, "全平仓无预付金时应1条资金流水"); Assert.AreEqual(-8000.0, service.ClientCashCalls[0].amount, 0.001, "资金流水=-反序列化的SwapRealizedPnL"); Assert.AreEqual("已平仓", td.TradeStatus, "审核全平仓 TradeStatus=已平仓"); Console.WriteLine($"UW_005: 反序列化SwapRealizedPnL=8000, 资金流水={service.ClientCashCalls[0].amount}, TradeStatus={td.TradeStatus} ✅"); } [TestMethod] public void UW_005A_ApproveSwapTrade_互换审核_不返还预付金本金() { var td = SwapDealTestFactory.CreateTrade(); td.ExerciseDate = new DateTime(2026, 12, 31); var unwindData = SwapDealTestFactory.CreateUnwindData( swapRealizedPnL: 100m, swapMarginAmount: -10000m); var swapEvent = new swap_event { id = 2, SwapTradeId = SwapDealTestFactory.SwapTradeId, EventType = (int)SwapEventTypeEnum.互换, Invalid = false, EventData = JsonConvert.SerializeObject(unwindData) }; var service = new TestableSwapDealService(td, swapEvents: new Dictionary { [(int)SwapEventTypeEnum.互换] = swapEvent }); service.ApproveSwapTrade(td, (int)SwapEventTypeEnum.互换); Assert.AreEqual(1, service.ClientCashCalls.Count, "互换审批只应生成互换结算流水"); Assert.AreEqual(ClientCashInCashOut.系统操作_互换, service.ClientCashCalls[0].action); Assert.IsFalse(service.ClientCashCalls.Any(x => x.action == ClientCashInCashOut.系统操作_应付预付金), "互换审批不应生成应付预付金流水"); } // ================================================================ // 场景6:ApplySwapTrade 提交审核 —— 前置校验与保存事件 // ================================================================ [TestMethod] public void UW_006_ApplySwapTrade_提交审核_前置校验与保存事件() { var td = SwapDealTestFactory.CreateTrade(); var service = new TestableSwapDealService(td); var unwindData = SwapDealTestFactory.CreateUnwindData(swapRealizedPnL: 0m); unwindData.SwapCloseAmount = 6000m; service.ApplySwapTrade(unwindData, (int)SwapEventTypeEnum.平仓); Assert.AreEqual(1, service.CloseReCheckCallCount, "应调用 CloseReCheckSetTrade 1次"); Assert.AreEqual(1, service.SaveSwapDealCalls.Count, "应调用 SaveSwapDeal 1次"); Assert.AreEqual((int)SwapEventTypeEnum.平仓, service.SaveSwapDealCalls[0].eventType, "事件类型=平仓"); Assert.AreEqual(6000m, service.SaveSwapDealCalls[0].data.SwapRealizedPnL, 0.001m, "SwapRealizedPnL 应=SwapCloseAmount(6000)"); Console.WriteLine($"UW_006: CloseReCheck={service.CloseReCheckCallCount}次, SwapRealizedPnL={service.SaveSwapDealCalls[0].data.SwapRealizedPnL} ✅"); } // ================================================================ // 场景7:前端传"占期初(A)"语义,后端入口转"占剩余(B)" —— 全平判定 // 原始名义本金 100M / 剩余 60M,前端传 A=0.6(平掉原始 60M = 剩余全部) // B = A × Notional/Posi = 0.6 × 100/60 = 1.0 → 触发全平 // ================================================================ [TestMethod] 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, closeQty: 600000m, closeNotionalValue: 600000m, positionQty: 600000m); unwindData.NotionalValue = 1000000m; // 期初名义本金 unwindData.PosiNotionalValue = 600000m; // 剩余名义本金 service.SwapUnwind(unwindData); // 桩 SaveSwapDeal 收集的是转换后的 B(落库 A 还原在生产 SaveSwapDealInternal 中,桩跳过) Assert.AreEqual(1.0m, service.SaveSwapDealCalls[0].data.ClosePercent, 0.0001m, "入口 A=0.6 应转为 B=1.0(占剩余全平)"); Assert.AreEqual("已平仓", td.TradeStatus, "B==1 触发全平 TradeStatus=已平仓"); Console.WriteLine($"UW_007: A=0.6→B={service.SaveSwapDealCalls[0].data.ClosePercent}, TradeStatus={td.TradeStatus} ✅"); } // ================================================================ // 场景8:占期初(A)转占剩余(B) —— 部分平仓 // 原始 100M / 剩余 60M,前端传 A=0.3(平掉原始 30M = 剩余的 50%) // B = A × Notional/Posi = 0.3 × 100/60 = 0.5 → 部分平仓 // ================================================================ [TestMethod] public void UW_008_SwapUnwind_占期初A转占剩余B_部分平仓正确() { var td = SwapDealTestFactory.CreateTrade(); var service = new TestableSwapDealService(td); var unwindData = SwapDealTestFactory.CreateUnwindData( swapRealizedPnL: 0m, closeMethod: (int)CloseMethodEnum.部分平仓, closePercent: 0.3m, closeQty: 300000m, closeNotionalValue: 300000m, positionQty: 600000m); unwindData.NotionalValue = 1000000m; // 期初名义本金 unwindData.PosiNotionalValue = 600000m; // 剩余名义本金 service.SwapUnwind(unwindData); Assert.AreEqual(0.5m, service.SaveSwapDealCalls[0].data.ClosePercent, 0.0001m, "入口 A=0.3 应转为 B=0.5(占剩余 50%)"); Assert.AreEqual(1, td.HasPartialUnWind, "B≠1 应为部分平仓,设 HasPartialUnWind=1"); Assert.AreEqual("确认成交", td.TradeStatus, "部分平仓 TradeStatus 保持不变"); Console.WriteLine($"UW_008: A=0.3→B={service.SaveSwapDealCalls[0].data.ClosePercent}, HasPartialUnWind={td.HasPartialUnWind} ✅"); } [TestMethod] public void UW_009_SwapUnwind_名义本金写入前舍入两位小数() { var td = SwapDealTestFactory.CreateTrade(); td.StockEqvNotional = 1000000.006; var service = new TestableSwapDealService(td); var unwindData = SwapDealTestFactory.CreateUnwindData( swapRealizedPnL: 0m, closeMethod: (int)CloseMethodEnum.部分平仓, closePercent: 0.5m, closeQty: 5000m, closeNotionalValue: 500000.004m, positionQty: 10000m); unwindData.NotionalValue = 1000000.006m; unwindData.PosiNotionalValue = 1000000.006m; service.SwapUnwind(unwindData); var savedData = service.SaveSwapDealCalls[0].data; Assert.AreEqual(1000000.01m, savedData.NotionalValue, "期初名义本金应按两位小数写入事件"); Assert.AreEqual(1000000.01m, savedData.PosiNotionalValue, "剩余名义本金应按两位小数写入事件"); Assert.AreEqual(500000.00m, savedData.CloseNotionalValue, "平仓名义本金应按两位小数写入事件"); 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_现金与两位利息事件保持一致() { var td = SwapDealTestFactory.CreateTrade(); var service = new TestableSwapDealService(td); var unwindData = SwapDealTestFactory.CreateUnwindData( swapRealizedPnL: 10.0049m, closeMethod: (int)CloseMethodEnum.全部平仓, closePercent: 1m, closeQty: 10000m, closeNotionalValue: 1000000m, positionQty: 10000m); var floatEvent = new swap_flow_event { UnderlyingCode = "UT-FLOAT", PositionType = (int)PositionTypeFlag.Long, EventType = (int)SwapEventTypeEnum.平仓, PayDirection = 1, MarkClosePnl = 10m }; var interestEvent = new swap_flow_event { PositionType = 0, InterestAmount = 0.0049m, TdInterestAmount = 0.0049m, InterestClosePnL = 0.0049m, InterestFee = 0.0049m }; unwindData.FlowEvents.Add(floatEvent); unwindData.FlowEvents.Add(interestEvent); service.SwapUnwind(unwindData); Assert.AreEqual(0m, interestEvent.InterestAmount); Assert.AreEqual(0m, interestEvent.TdInterestAmount); Assert.AreEqual(0m, interestEvent.InterestClosePnL); Assert.AreEqual(0m, interestEvent.InterestFee); 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); } } }