using BaseOUDAL; using ClosedXML.Report.Utils; using Confluent.Kafka; using Microsoft.Office.Interop.Excel; using MoreLinq; using NPOI.OpenXmlFormats.Spreadsheet; using System.Collections.Generic; using YLErp.BLL.Calculation; using YLErp.DBModels; using YLErp.DBModels.Enums; using YLErp.Model; using YLErp.Modules.AppModule; using YLErp.Modules.EodModule.QueryModule; using YLErp.QdpModule; using static YLErp.ConsGlobal; namespace YLErp.Modules.SwapModule { /// /// 互换流水开平仓事件服务 /// public class SwapFlowEventService : SwapTradeBaseService { public SwapFlowEventService(OptUserInfo optUser) : base(optUser) { } public SwapFlowEventService(YLBaseService baseService) : base(baseService) { } #region 可测试化接缝(Seams)——借鉴 refactor-swap-event-testable 分支,override 可在测试中替换 DB/外部调用,生产代码行为不变 // FindTrade 已上提到基类 SwapTradeBaseService(三子类实现一致,消除重复) protected virtual trade_extend FindTradeExtend(int swapTradeId) => DbContext.trade_extend.First(x => x.TradeId == swapTradeId); protected virtual List FindPositions(int swapTradeId) => DbContext.swap_position.Where(x => x.SwapTradeId == swapTradeId && !x.IsInitial && !x.Invalid).AsNoTracking().ToList(); protected virtual List FindAndInvalidateFutureEvents(int swapTradeId, DateTime tradeDate) { var olds = DbContext.swap_flow_event.Where(x => x.EventDate > tradeDate && x.SwapTradeId == swapTradeId && x.DataState > 0); olds.ForEach(x => x.DataState = (int)SwapFlowDateStateEnum.废弃); return olds.ToList(); } protected virtual underlying_manager GetUnderlying(string underlyingCode) => DataCacheProvider.GetUnderlyingDataSource().GetData(underlyingCode); private int GetStorageDeliveryPriceRound(string underlyingInstrumentType, string underlyingCode) { if (ConsGlobal.InstrumentType.IsBond(underlyingInstrumentType)) { return ConsGlobal.PriceRound; } if (string.IsNullOrEmpty(underlyingCode)) { return ConsGlobal.SwapDeliveryPriceRound; } return GetUnderlying(underlyingCode)?.IsBond() == true ? ConsGlobal.PriceRound : ConsGlobal.SwapDeliveryPriceRound; } protected virtual DateTime GetNextBusinessDay(DateTime date) => QdpCalendarHelper.GetNonHoliday(date); protected virtual long ResolvePositionId(swap_flow_merge merge, DateTime maturityDate, int direction, string tradeNumber) => GetMaxPositionId(merge, maturityDate, direction, tradeNumber); protected virtual void PersistEvents(List events) { foreach (var evt in events) { if (!string.IsNullOrEmpty(evt.UnderlyingCode)) { evt.TradingAmountAvg = Math.Round( evt.TradingAmountAvg, GetStorageDeliveryPriceRound(evt.UnderlyingInstrumentType, evt.UnderlyingCode), MidpointRounding.AwayFromZero); } DbContext.swap_flow_event.Add(evt); } DbContext.SaveChanges(); } protected virtual IDisposable BeginTransaction() => DbContext.Database.BeginTransaction(); protected virtual void CommitTransaction(IDisposable transaction) => (transaction as Microsoft.EntityFrameworkCore.Storage.IDbContextTransaction)?.Commit(); protected virtual void RollbackTransaction(IDisposable transaction) => (transaction as Microsoft.EntityFrameworkCore.Storage.IDbContextTransaction)?.Rollback(); /// MergePageEvent 业务逻辑执行前钩子(默认空,录制golden用)。在DB查询完成、合成逻辑执行前触发 protected virtual void OnBeforeMergePageEvent(int swapTradeId, DateTime tradeDate, trade trade, trade_extend tradeExtend, List merges, List positions) { } /// MergePageEvent 业务逻辑执行后钩子(默认空,录制golden用)。在PersistEvents之后触发,捕获输出事件 protected virtual void OnAfterMergePageEvent(int swapTradeId, DateTime tradeDate, List resultEvents) { } #endregion /// /// 互换流水开平仓事件 /// public void SwapFlowEvent(DateTime tradeDate) { new SysJobService(UserInfo).UpdateJob("互换流水合成持仓", 31, "互换流水开平仓事件进行中"); //同一交易代码、交易日期、标的、买卖方向 最多存在2条记录 var flowqueryGroup = DbContext.swap_flow_merge.Where(n => n.DataState == (int)SwapFlowDateStateEnum.等待完成 && n.OccurTime == tradeDate).AsEnumerable().GroupBy(g => g.SwapTradeId); foreach (var flowMergeGroupItem in flowqueryGroup) { MergePageEvent(flowMergeGroupItem.Key ?? 0, flowMergeGroupItem.ToList(), tradeDate); } new SysJobService(UserInfo).UpdateJob("互换流水合成持仓", 32, "互换流水开平仓事件完成"); } /// /// 互换流水开平仓事件 /// public List SwapFlowEvent(List mergeList, DateTime tradeDate) { List flowEvents = new List(); new SysJobService(UserInfo).UpdateJob("互换流水合成持仓", 31, "互换流水开平仓事件进行中"); //同一交易代码、交易日期、标的、买卖方向 最多存在2条记录 var flowqueryGroup = mergeList.Where(n => n.DataState == (int)SwapFlowDateStateEnum.等待完成).GroupBy(g => g.SwapTradeId); foreach (var flowMergeGroupItem in flowqueryGroup) { flowEvents.AddRange(MergePageEvent(flowMergeGroupItem.Key ?? 0, flowMergeGroupItem.ToList(), tradeDate, false)); } new SysJobService(UserInfo).UpdateJob("互换流水合成持仓", 32, "互换流水开平仓事件完成"); return flowEvents; } /// /// 分页处理互换流水开平仓事件,暂时只按加权平均处理 /// /// protected virtual List MergePageEvent(int swapTradeId, List flowMergeList, DateTime tradeDate, bool needTrans = true) { List flowEvents = new List(); //按照同一互换编码、标的、买卖方向排序,一条买,一条卖//会存在买卖不在同一页 var flowquery = flowMergeList.OrderBy(o => o.SwapTradeId).ThenBy(o => o.UnderlyingCode).ThenBy(o => o.BsType).ToList(); if (flowquery.Count == 0) { return flowEvents; } var trade = FindTrade(swapTradeId); var tradeExtend = FindTradeExtend(swapTradeId); var trans = needTrans ? BeginTransaction() : null; try { var eodPositions = FindPositions(swapTradeId);//上一日终持仓信息 FindAndInvalidateFutureEvents(swapTradeId, tradeDate);//废弃当前清算日期及之后的开平仓事件 OnBeforeMergePageEvent(swapTradeId, tradeDate, trade, tradeExtend, flowquery, eodPositions); var mergeUnderlyingGroup = flowquery.GroupBy(g => g.UnderlyingCode); int direction = tradeExtend.ExtendObj.Direction; foreach (var underlyingGroup in mergeUnderlyingGroup) { var mergeList = underlyingGroup.OrderByDescending(o => o.TradingQty).ToList();//先按数量最大的排序 var flowMerge = mergeList.First(); var underlying = GetUnderlying(flowMerge.UnderlyingCode); var matuirityDate = trade.ExerciseDate; var positionId = ResolvePositionId(flowMerge, matuirityDate.Value, direction, trade.TradeNumber); var payPosition = eodPositions.FirstOrDefault(x => x.PositionId == positionId);//浮动腿 日终持仓信息 bool hasPayPosition = payPosition != null;//是否存在日终持仓 if (mergeList.Count == 1)//只有一条流水 { if (!hasPayPosition)//无日终持仓 { var flowEvent = InitEvent((int)SwapFlowEventTypeEnum.开仓, flowMerge, direction, positionId, flowMerge.BsType, flowMerge.TradingQty, flowMerge.TradingAmount, flowMerge.TradingFee, 0, 0, flowMerge.TradingAmountAvg, flowMerge.TradingAmountFeeAvg, flowMerge.TradingAmountNetFeeAvg, matuirityDate, underlying.UnderlyingInstrumentType, tradeExtend.ExtendObj.SettlementRules); flowEvents.Add(flowEvent); } else { if (flowMerge.BsType == payPosition.PositionType)//同向开仓 { var qty = flowMerge.TradingQtyAbs; var amount = flowMerge.TradingAmountAbs; var flowEvent = InitEvent((int)SwapFlowEventTypeEnum.开仓, flowMerge, direction, positionId, flowMerge.BsType, qty, amount, flowMerge.TradingFeeAbs, 0, 0, flowMerge.TradingAmountAvg, flowMerge.TradingAmountFeeAvg, flowMerge.TradingAmountNetFeeAvg, matuirityDate, underlying.UnderlyingInstrumentType, tradeExtend.ExtendObj.SettlementRules); flowEvents.Add(flowEvent); } else//反向平仓 { var qty = flowMerge.TradingQtyAbs - payPosition.PosiQuantity;//平仓剩余数量 var bsType = qty > 0 ? flowMerge.BsType : payPosition.PositionType; var unwindQty = qty > 0 ? payPosition.PosiQuantity : flowMerge.TradingQtyAbs;//平仓数量 var fee = payPosition.PosiQuantity == 0 ? 0 : flowMerge.TradingFeeAbs * -1 + (unwindQty / payPosition.PosiQuantity) * payPosition.PosiTradingFee;//平仓费用=平仓流水的交易费用佣金+平仓数量/平仓前的数量*平仓对象的(交易佣金费用+后付费用) var amount = (flowMerge.TradingAmountAvgAbs - payPosition.PosiGrossPrice) * unwindQty * flowMerge.ContractSize;//平仓剩余金额=(平仓流水的成交均价-平仓对象的期初价格不含费)*平仓流水的成交数量*合约乘数 var flowEvent = InitEvent((int)SwapFlowEventTypeEnum.平仓, flowMerge, direction, positionId, payPosition.PositionType, unwindQty, flowMerge.TradingAmountAbs, flowMerge.TradingFeeAbs, amount, fee, flowMerge.TradingAmountAvg, flowMerge.TradingAmountFeeAvg, flowMerge.TradingAmountNetFeeAvg, matuirityDate, underlying.UnderlyingInstrumentType, tradeExtend.ExtendObj.SettlementRules); flowEvents.Add(flowEvent); if (qty > 0)//平仓有剩余,开仓 { qty = Math.Abs(qty); amount = flowMerge.TradingAmountAvgAbs * qty * flowMerge.ContractSize; //positionId = GetPositionId(flowMerge, matuirityDate.Value, direction, trade.TradeNumber); var flowEvent2 = InitEvent((int)SwapFlowEventTypeEnum.开仓, flowMerge, direction, positionId, bsType, qty, Math.Abs(amount), flowMerge.TradingFeeAbs, 0, 0, flowMerge.TradingAmountAvg, flowMerge.TradingAmountFeeAvg, flowMerge.TradingAmountNetFeeAvg, matuirityDate, underlying.UnderlyingInstrumentType, tradeExtend.ExtendObj.SettlementRules); flowEvents.Add(flowEvent2); } } } flowMerge.DataState = 100; } else { var flowMerge2 = mergeList.Last(); if (!hasPayPosition)//无日终持仓 { //先将数量最大的开仓 var flowEvent = InitEvent((int)SwapFlowEventTypeEnum.开仓, flowMerge, direction, positionId, flowMerge.BsType, flowMerge.TradingQtyAbs, flowMerge.TradingAmountAbs, flowMerge.TradingFeeAbs, 0, 0, flowMerge.TradingAmountAvg, flowMerge.TradingAmountFeeAvg, flowMerge.TradingAmountNetFeeAvg, matuirityDate, underlying.UnderlyingInstrumentType, tradeExtend.ExtendObj.SettlementRules); //再将数量小的那条平仓 var qty = flowMerge.TradingQtyAbs - flowMerge2.TradingQtyAbs;//平仓剩余数量 var bsType = qty > 0 ? flowMerge.BsType : flowMerge2.BsType; var unwindQty = qty > 0 ? flowMerge2.TradingQtyAbs : flowMerge.TradingQtyAbs;//平仓数量 var fee = flowMerge2.TradingFeeAbs * -1 + (unwindQty / flowMerge.TradingQtyAbs) * flowMerge.TradingFeeAbs * -1;//平仓费用=平仓流水的交易费用佣金+平仓数量/平仓前的数量*平仓对象的(交易佣金费用+后付费用) var amount = (flowMerge2.TradingAmountAvgAbs - flowMerge.TradingAmountAvgAbs) * unwindQty * flowMerge2.ContractSize;//平仓剩余金额=(平仓流水的成交均价-平仓对象的期初价格不含费)*平仓流水的成交数量*合约乘数 var flowEvent2 = InitEvent((int)SwapFlowEventTypeEnum.平仓, flowMerge2, direction, positionId, flowMerge.BsType, unwindQty, flowMerge2.TradingAmountAbs, flowMerge2.TradingFeeAbs, amount, fee, flowMerge2.TradingAmountAvg, flowMerge2.TradingAmountFeeAvg, flowMerge2.TradingAmountNetFeeAvg, matuirityDate, underlying.UnderlyingInstrumentType, tradeExtend.ExtendObj.SettlementRules); flowEvents.Add(flowEvent); flowEvents.Add(flowEvent2); } else//有日终持仓,先平仓方向相反的流水,再处理另一条流水 { flowMerge = mergeList.First(x => x.BsType != payPosition.PositionType);//反向流水 flowMerge2 = mergeList.Last(x => x.BsType == payPosition.PositionType);//同向流水 var qty = flowMerge.TradingQtyAbs - payPosition.PosiQuantity;//平仓剩余数量 var bsType = qty > 0 ? flowMerge.BsType : payPosition.PositionType; var unwindQty = qty > 0 ? payPosition.PosiQuantity : flowMerge.TradingQtyAbs;//平仓数量 var fee = payPosition.PosiQuantity == 0 ? 0 : flowMerge.TradingFeeAbs * -1 + (unwindQty / payPosition.PosiQuantity) * payPosition.PosiTradingFee;//平仓费用=平仓流水的交易费用佣金+平仓数量/平仓前的数量*平仓对象的(交易佣金费用+后付费用) var amount = (flowMerge.TradingAmountAvgAbs - payPosition.PosiGrossPrice) * unwindQty * flowMerge.ContractSize;//平仓剩余金额=(平仓流水的成交均价-平仓对象的期初价格不含费)*平仓流水的成交数量*合约乘数 var flowEvent = InitEvent((int)SwapFlowEventTypeEnum.平仓, flowMerge, direction, positionId, payPosition.PositionType, unwindQty, flowMerge.TradingAmountAbs, flowMerge.TradingFeeAbs, amount, fee, flowMerge.TradingAmountAvg, flowMerge.TradingAmountFeeAvg, flowMerge.TradingAmountNetFeeAvg, matuirityDate, underlying.UnderlyingInstrumentType, tradeExtend.ExtendObj.SettlementRules); flowEvents.Add(flowEvent); if (qty > 0)//反向流水平仓有剩余,开仓 { //positionId = GetPositionId(flowMerge, matuirityDate.Value, direction, trade.TradeNumber); amount = flowMerge.TradingAmountAvgAbs * qty * flowMerge.ContractSize; var flowEvent2 = InitEvent((int)SwapFlowEventTypeEnum.开仓, flowMerge, direction, positionId, bsType, qty, amount, Math.Abs(fee), 0, 0, flowMerge.TradingAmountAvg, flowMerge.TradingAmountFeeAvg, flowMerge.TradingAmountNetFeeAvg, matuirityDate, underlying.UnderlyingInstrumentType, tradeExtend.ExtendObj.SettlementRules); flowEvents.Add(flowEvent2); if (bsType == flowMerge2.BsType)//平仓剩余与第二条流水同向 { var flowEvent3 = InitEvent((int)SwapFlowEventTypeEnum.开仓, flowMerge2, direction, positionId, bsType, flowMerge2.TradingQtyAbs, flowMerge2.TradingAmountAbs, flowMerge2.TradingFeeAbs, 0, 0, flowMerge2.TradingAmountAvg, flowMerge2.TradingAmountFeeAvg, flowMerge2.TradingAmountNetFeeAvg, matuirityDate, underlying.UnderlyingInstrumentType, tradeExtend.ExtendObj.SettlementRules); flowEvents.Add(flowEvent3); } else //方向相反,先平仓 { var qty2 = qty - flowMerge2.TradingQtyAbs; unwindQty = qty2 > 0 ? flowMerge2.TradingQtyAbs : qty;//平仓数量 bsType = qty2 > 0 ? bsType : flowMerge2.BsType; var amount2 = (flowMerge2.TradingAmountAvgAbs - flowMerge.TradingAmountAvgAbs) * unwindQty * flowMerge2.ContractSize; var fee2 = flowMerge2.TradingFeeAbs * -1 + (unwindQty / qty) * fee; var flowEvent3 = InitEvent((int)SwapFlowEventTypeEnum.平仓, flowMerge2, direction, positionId, flowMerge2.BsType, unwindQty, flowMerge2.TradingAmountAbs, flowMerge2.TradingFeeAbs, amount2, fee2, flowMerge2.TradingAmountAvg, flowMerge2.TradingAmountFeeAvg, flowMerge2.TradingAmountNetFeeAvg, matuirityDate, underlying.UnderlyingInstrumentType, tradeExtend.ExtendObj.SettlementRules); flowEvents.Add(flowEvent3); if (qty2 != 0)//平仓有剩余,开仓 { qty2 = Math.Abs(qty2); //positionId = GetPositionId(flowMerge2, matuirityDate.Value, direction, trade.TradeNumber); amount2 = flowMerge2.TradingAmountAvgAbs * qty2 * flowMerge2.ContractSize; var flowEvent4 = InitEvent((int)SwapFlowEventTypeEnum.开仓, flowMerge2, direction, positionId, bsType, qty2, amount2, Math.Abs(fee2), 0, 0, flowMerge2.TradingAmountAvg, flowMerge2.TradingAmountFeeAvg, flowMerge2.TradingAmountNetFeeAvg, matuirityDate, underlying.UnderlyingInstrumentType, tradeExtend.ExtendObj.SettlementRules); flowEvents.Add(flowEvent4); } } } else //无平仓剩余,开仓 { var flowEvent2 = InitEvent((int)SwapFlowEventTypeEnum.开仓, flowMerge2, direction, positionId, flowMerge2.BsType, flowMerge2.TradingQtyAbs, flowMerge2.TradingAmountAbs, flowMerge2.TradingFeeAbs, 0, 0, flowMerge2.TradingAmountAvg, flowMerge2.TradingAmountFeeAvg, flowMerge2.TradingAmountNetFeeAvg, matuirityDate, underlying.UnderlyingInstrumentType, tradeExtend.ExtendObj.SettlementRules); flowEvents.Add(flowEvent2); } } flowMerge.DataState = (int)SwapFlowDateStateEnum.完成; flowMerge2.DataState = (int)SwapFlowDateStateEnum.完成; } } PersistEvents(flowEvents); OnAfterMergePageEvent(swapTradeId, tradeDate, flowEvents); if (trans != null) CommitTransaction(trans); } catch (Exception ex) { if (trans != null) RollbackTransaction(trans); throw new Exception(ex.Message, ex); } finally { trans?.Dispose(); } return flowEvents; } /// /// 保存流水事件 /// /// 标的信息 /// 事件类型 /// 汇总流水 /// 浮动端收支方向 /// 持仓编码 /// 多空方向 /// 开平仓数量 /// 开平仓金额 /// 开平仓费用 /// 平仓浮动盈亏 /// 平仓浮动费用 /// 是否完全平仓 /// 0 T+0 1 T+1 protected virtual swap_flow_event InitEvent( int eventType, swap_flow_merge flow_merge, int direction, long swapPositionId, int positionType, decimal TradingQty, decimal TradingAmount, decimal TradingFee, decimal PayMarkUnwindPnl, decimal PayFeeUnwindPnl, decimal TradingAmountAvg, decimal TradingAmountFeeAvg, decimal? TradingAmountNetFeeAvg, DateTime? matuirityDate, string underlyingInstrumentType, int settleRules) { swap_flow_event flow_Event = new swap_flow_event() { EventReason = "交易", EventType = eventType, SwapTradeId = flow_merge.SwapTradeId ?? 0, SwapTradeNo = flow_merge.SwapTradeNo, ContractSize = flow_merge.ContractSize, CountRatio = 1, UnderlyingCode = flow_merge.UnderlyingCode, UnderlyingInstrumentType = underlyingInstrumentType, DataState = 1, EventDate = flow_merge.OccurTime, UnwindDate = QdpCalendarHelper.GetNonHoliday(flow_merge.OccurTime.AddDays(1)), TradingAmountAvg = Math.Round(TradingAmountAvg, GetStorageDeliveryPriceRound(underlyingInstrumentType, flow_merge.UnderlyingCode), MidpointRounding.AwayFromZero), TradingAmountFeeAvg = TradingAmountFeeAvg, TradingAmountNetFeeAvg = TradingAmountNetFeeAvg, TradingAmountNetAvg = flow_merge.TradingAmountNetAvg, ClientId = flow_merge.ClientId, TradingFeePending = flow_merge.TradingFeePending, }; UpdateDbOption(flow_Event); flow_Event.MatuirityDate = matuirityDate; flow_Event.PayDirection = direction; flow_Event.PositionType = positionType; flow_Event.PositionId = swapPositionId; flow_Event.PayDate = flow_Event.UnwindDate.Value.AddDays(settleRules); //.todo 支付日期 flow_Event.Quantity = TradingQty; flow_Event.TradingAmount = TradingAmount; flow_Event.TradingFee = TradingFee;//费用先按负数处理 flow_Event.MarkClosePnl = PayMarkUnwindPnl; flow_Event.CloseFee = PayFeeUnwindPnl; flow_Event.DataState = (int)SwapFlowDateStateEnum.等待完成; return flow_Event; } /// /// 新增开平仓事件(实时持仓用) /// /// /// /// /// /// /// /// /// /// /// public swap_flow_event AddEvent(int eventType, swap_flow_merge flow_merge, int direction, int positionType, decimal TradingQty, decimal TradingAmount, decimal TradingFee, long positionId) { swap_flow_event flow_Event = new swap_flow_event() { EventReason = "交易", EventType = eventType, SwapTradeId = flow_merge.SwapTradeId ?? 0, SwapTradeNo = flow_merge.SwapTradeNo, ContractSize = flow_merge.ContractSize, CountRatio = 1, UnderlyingCode = flow_merge.UnderlyingCode, DataState = 1, EventDate = flow_merge.OccurTime, UnwindDate = QdpCalendarHelper.GetNonHoliday(flow_merge.OccurTime.AddDays(1)), TradingAmountAvg = Math.Round( flow_merge.TradingAmountAvg, GetStorageDeliveryPriceRound(null, flow_merge.UnderlyingCode), MidpointRounding.AwayFromZero), TradingAmountFeeAvg = flow_merge.TradingAmountFeeAvg, TradingFeePending = flow_merge.TradingFeePending, ClientId = flow_merge.ClientId }; flow_Event.PayDirection = direction; flow_Event.PositionType = positionType; flow_Event.Quantity = TradingQty; flow_Event.TradingAmount = TradingAmount; flow_Event.TradingFee = TradingFee;//费用先按负数处理 flow_Event.PositionId = positionId; return flow_Event; } /// /// 确认交易开仓事件 /// /// /// public void InitEvent(List positions, trade td, string optLog) { foreach (swap_position position in positions) { swap_flow_event flow_Event = new swap_flow_event() { EventReason = "交易", EventType = (int)SwapFlowEventTypeEnum.开仓, SwapTradeId = position.SwapTradeId, SwapTradeNo = td.TradeNumber, ContractSize = position.ContractSize, CountRatio = position.CountRatio, UnderlyingCode = position.UnderlyingCode, DataState = (int)SwapFlowDateStateEnum.完成, EventDate = td.TradeDate.Value, UnwindDate = td.StartDate.Value, TradingAmountAvg = Math.Round( position.PosiGrossPrice, GetStorageDeliveryPriceRound(position.UnderlyingInstrumentType, position.UnderlyingCode), MidpointRounding.AwayFromZero), TradingAmountFeeAvg = position.PosiNetPrice, TradingAmountNetFeeAvg = position.PosiNetFeePrice, TradingAmountNetAvg = position.PosiNetNoFeePrice, TradingFeePending = position.PosiTradingFeePending, OptLog = optLog }; UpdateDbOption(flow_Event); flow_Event.ClientId = td.ClientId; flow_Event.MatuirityDate = td.ExerciseDate; flow_Event.PayDirection = position.PosiDirection; flow_Event.PositionType = position.PositionType; flow_Event.PositionId = position.id; flow_Event.Quantity = position.PosiQuantity; flow_Event.PositionQty = flow_Event.Quantity; flow_Event.TradingAmount = position.PosiNotionalValue; flow_Event.TradingFee = position.PosiTradingFee; flow_Event.MarkClosePnl = 0; flow_Event.CloseFee = 0; flow_Event.InterestDirection = position.InterestDirection; flow_Event.InterestRate = position.InterestRateDefault; flow_Event.InterestPrincipal = Math.Round(position.InterestPrincipalFix, ConsGlobal.MoneyRound, MidpointRounding.AwayFromZero); flow_Event.InterestSwapInterval = position.InterestSwapInterval; flow_Event.InterestMode = position.InterestMode; flow_Event.UnderlyingInstrumentType = position.UnderlyingInstrumentType; flow_Event.PayDate = flow_Event.UnwindDate.Value; DbContext.swap_flow_event.Add(flow_Event); } } /// /// 确认交易开仓事件 /// /// /// /// /// /// public swap_flow_event InitEvent(swap_flow_merge flowMerge, trade td, swap_position realPosition, string underlyingInstrumentType) { swap_flow_event flow_Event = new swap_flow_event() { EventReason = "交易", EventType = (int)SwapFlowEventTypeEnum.开仓, SwapTradeId = td.id, SwapTradeNo = td.TradeNumber, ContractSize = flowMerge.ContractSize, CountRatio = 1, UnderlyingCode = flowMerge.UnderlyingCode, DataState = 100, EventDate = td.TradeDate.Value, UnwindDate = QdpCalendarHelper.GetNonHoliday(td.TradeDate.Value.AddDays(1)), TradingAmountAvg = Math.Round(flowMerge.TradingAmountAvg, GetStorageDeliveryPriceRound(underlyingInstrumentType, flowMerge.UnderlyingCode), MidpointRounding.AwayFromZero), TradingAmountFeeAvg = flowMerge.TradingAmountFeeAvg, TradingAmountNetFeeAvg = flowMerge.TradingAmountNetFeeAvg, TradingAmountNetAvg = flowMerge.TradingAmountNetAvg, TradingFeePending = flowMerge.TradingFeePending, }; UpdateDbOption(flow_Event); flow_Event.ClientId = td.ClientId; flow_Event.MatuirityDate = td.ExerciseDate; flow_Event.PayDirection = 2; flow_Event.PositionType = flowMerge.BsType; flow_Event.PositionId = realPosition.PositionId; flow_Event.Quantity = flowMerge.TradingQty; flow_Event.PositionQty = realPosition.PosiQuantity; flow_Event.TradingAmount = realPosition.PosiNotionalValue; flow_Event.MarkClosePnl = 0; flow_Event.CloseFee = 0; flow_Event.UnderlyingInstrumentType = underlyingInstrumentType; DbContext.swap_flow_event.Add(flow_Event); DbContext.SaveChanges(); return flow_Event; } /// /// 互换交易流水查询 /// /// /// public SearchListResult SearchPositionFlowEvent(ClientSwapPositionRequest req) { var retListResult = GetSearchPositionEventList(req); var clientDataSource = DataCacheProvider.GetClientDataSource(); foreach (var item in retListResult.rows) { var client = clientDataSource.GetData(item.ClientId); item.ClientNumber = client.Number; } return retListResult; } /// /// 互换交易流水查询 /// /// /// private SearchListResult GetSearchPositionEventList(ClientSwapPositionRequest req) { // List eventTyps = new List() { (int)SwapEventTypeEnum.平仓, (int)SwapEventTypeEnum.互换, (int)SwapEventTypeEnum.自动互换 }; var predicate = PredicateBuilder.Create(n => n.DataState == (int)SwapFlowDateStateEnum.完成 ); var tradePredicate = PredicateBuilder.Create(n => n.TradeType == "收益互换" && n.ValidState == ConsGlobal.Valid); if (req.ClientId > 0) { tradePredicate = tradePredicate.And(x => x.ClientId == req.ClientId); } if (req.ValueDateFrom != null) { predicate = predicate.And(x => x.EventDate >= req.ValueDateFrom); } if (req.ValueDate != null) { predicate = predicate.And(x => x.EventDate <= req.ValueDate); } DbContext.SetDebugLog(); var positionQuery = DbContext.swap_flow_event.Where(predicate); var tradeQuery = DbContext.trade.Where(tradePredicate); var query = from td in tradeQuery //join posi in DbContext.swap_position.Where(s=>!s.Invalid&&s.IsInitial) on td.id equals posi.SwapTradeId join flow in positionQuery on td.id equals flow.SwapTradeId join tr in DbContext.trade_contract_r.Where(x=>x.IsValid&&x.Type=="交易确认书") on td.id equals tr.TradeId into tradeContractGroup from tradeContract in tradeContractGroup.DefaultIfEmpty() select new ClientSwapPositionResponse { FlowEvent = flow, SwapTradeNo = td.TradeNumber, StructureType = td.StructureType, ClientName = td.ClientName, ClientId = td.ClientId, ContractCode = tradeContract.ContractCode, // FloatRateUnderlyingCode=posi.FloatRateUnderlyingCode, StartDate = td.StartDate.Value, InitYtm = td.InitYtm, }; if (string.IsNullOrEmpty(req.sidx)) { req.sidx = "FlowEvent.EventDate,SwapTradeNo"; req.sord = "asc"; } var retListResult = query.ToSearchList(req); var tds= retListResult.rows.Select(x => x.FlowEvent.SwapTradeId).Distinct().ToList(); var posiList = DbContext.swap_position.Where(s => !s.Invalid && s.IsInitial && tds.Contains(s.SwapTradeId) && !string.IsNullOrEmpty(s.FloatRateUnderlyingCode)).ToList(); // 查询成交收益率 Dictionary ytmMap = DbContext.trade.AsNoTracking().Where(p => tds.Contains(p.id)).ToList().ToDictionary(t => t.id, t => t.InitYtm); foreach (var item in retListResult.rows) { item.FlowEvent.DividendPending = -item.FlowEvent.DividendPending; item.FlowEvent.MarkClosePnl = -item.FlowEvent.MarkClosePnl; item.FlowEvent.DividendIn = -item.FlowEvent.DividendIn; item.FlowEvent.CloseFee = -item.FlowEvent.CloseFee; item.FlowEvent.InterestFee = -item.FlowEvent.InterestFee; item.FlowEvent.TradingFee = -item.FlowEvent.TradingFee; item.FlowEvent.TradingFeePending = -item.FlowEvent.TradingFeePending; item.FlowEvent.InterestClosePnL = -item.FlowEvent.InterestClosePnL; item.FlowEvent.InitYtm = ytmMap.GetValueOrDefault(item.FlowEvent.SwapTradeId); var posi= posiList.FirstOrDefault(s => s.id == item.FlowEvent.PositionId); item.FloatRateUnderlyingCode= posi?.FloatRateUnderlyingCode; if (item.FlowEvent.EventType==(int)SwapFlowEventTypeEnum.开仓) { item.TradeFee = 0; item.TradingFee= item.FlowEvent.TradingFeePending; } else { item.TradeFee = item.FlowEvent.TradingFee; item.TradingFee = item.FlowEvent.TradingFeePending; item.PosiPnl= item.FlowEvent.MarkClosePnl;//MarkClosePnl 纯盯市不计算交易费用和分红 item.NetSettmentAmount = item.FlowEvent.FloatPnlSum + item.FlowEvent.InterestClosePnL; item.Days = (item.FlowEvent.UnwindDate - item.StartDate).Value.Days; } } return retListResult; } private void SetPosiPrice(swap_flow_event position) { var um = DataCacheProvider.GetUnderlyingDataSource().GetData(position.UnderlyingCode); if (um != null && um.IsBond()) { position.PosiNetPrice *= 100; position.TradingAmountAvg *= 100; position.TradingAmountFeeAvg *= 100; position.PosiGrossPrice *= 100; return; } } } }