using ClosedXML.Report.Utils; using Confluent.Kafka; using CsvHelper; using Dapper; using DocumentFormat.OpenXml.Drawing; using MoreLinq; using NPOI.SS.Formula.Functions; using Qdp.ComputeServiceV2.Data.CommonModels.TradeInfos; using Qdp.Pricing.Base.Enums; using Qdp.Pricing.Base.Implementations; using System.Linq; using System.Linq.Expressions; using System.Reflection; using YLErp.BLL; using YLErp.BLL.Calculation; using YLErp.BLL.Eod; using YLErp.DataBase; using YLErp.DBModels; using YLErp.DBModels.Enums; using YLErp.Enums; using YLErp.Helpers; using YLErp.Model; using YLErp.Model.Enum; using YLErp.Models; using YLErp.Modules.AppModule; using YLErp.Modules.EodModule.QueryModule; using YLErp.Modules.RiskModule; using YLErp.Modules.TradeMsgOutputModule; using YLErp.QdpModule; using static alglib; namespace YLErp.Modules.SwapModule { public class SwapTradeAutoService : YLBaseService { protected static IYcLogger Log = LogFactory.GetLogger(typeof(SwapTradeAutoService).FullName); private static string LongShortStructType = "定义文件型债券收益互换"; public SwapTradeAutoService(OptUserInfo optUser) : base(optUser) { } /// /// 从db获取流水自动簿记 /// public void GenerateSwapTradeFromDb(DateTime valueDate, bool reset) { using (var bondDb = new BondOmsDBContext()) { var bondDbConn = bondDb.Database.GetDbConnection(); var clientDealSql = GetClientDealSql(valueDate); var currentDeals = bondDbConn.Query(clientDealSql); var swapFlows = DbContext.swap_flow.Where(x => x.OccurTime >= valueDate && x.OccurTime < valueDate.AddDays(1)).ToList(); var underlyingCodes = DbContext.underlying_manager.Where(x => x.LaunchState == "1"); foreach (var item in currentDeals) { var swapFlow = swapFlows.FirstOrDefault(x => x.trs_deal_id == item.id); if (swapFlow != null && !reset) { continue; } else if (swapFlow == null) { swapFlow = new swap_flow(); } var underlying = underlyingCodes.FirstOrDefault(x => x.UnderlyingCode == item.security_id); swapFlow.ContractSize = underlying == null ? 1 : Convert.ToDecimal(underlying.ContractSize); swapFlow.OccurTime = valueDate; swapFlow.trs_deal_id = item.id; swapFlow.BsType = item.side + 1; swapFlow.UnderlyingCode = item.security_id; swapFlow.TradingQty = (item.last_shares ?? 0) * 10000; swapFlow.TradingFee = (item.commission ?? 0); if (swapFlow.id == 0 || reset) { swapFlow.SwapTradeId = null; swapFlow.SwapTradeNo = null; swapFlow.DataState = (int)SwapFlowDateStateEnum.等待完成; } swapFlow.TradingAmountAvg = (item.deal_full_price ?? 0) * 0.01m; swapFlow.TradingAmountFeeAvg = (item.deal_full_price_include_fee ?? 0) * 0.01m; swapFlow.TradingAmount = swapFlow.TradingQty * swapFlow.ContractSize; swapFlow.ClientId = Convert.ToInt32(item.client_id ?? 0); swapFlow.ytm = (item.ytm ?? 0) * 0.01m; swapFlow.TradingAmountNet = (item.deal_price ?? 0) * 0.01m; swapFlow.TradingAmountNetFee = (item.deal_price_include_fee ?? 0) * 0.01m; swapFlow.ClientName = item.client_name; swapFlow.SetOpt(UserInfo); swapFlow.OptTime = item.create_time.HasValue ? item.create_time.Value : DateTime.Now; swapFlow.UnderlyingName = item.symbol; swapFlow.DealType = item.deal_type; swapFlow.SettleDate = valuedateBLL.GetNonHoliday(valueDate.AddDays(1)).Date; if (swapFlow.id == 0) { DbContext.swap_flow.Add(swapFlow); } } if (reset) { foreach (var flow in swapFlows) { flow.DataState = (int)SwapFlowDateStateEnum.等待完成; } } DbContext.SaveChanges(); Task.Run(() => { RealtimePnlCalc.RealtimeSwapPosition(new OptUserInfo(0, "互换实时持仓服务", OptUserFrom.Service)); }); } } private string GetClientDealSql(DateTime valueDate) { var tomorrowDate = valueDate.AddDays(1); Type type = typeof(ClientDeal); PropertyInfo[] properties = type.GetProperties(); string[] fieldNames = properties.Where(s => s.CustomAttributes.Count() == 0).Select(f => "cd." + f.Name).ToArray(); var selectStr = string.Join(",", fieldNames); string sql = $"select cd.id,{selectStr},co.settl_type from client_deal cd inner join client_order co on co.id=cd.client_order_id where cd.create_time<'{tomorrowDate.ToString("yyyy-MM-dd")}' and cd.create_time>='{valueDate.ToString("yyyy-MM-dd")}'"; return sql; } public void ResetTradeByDate(DateTime valueDate, int? clientId, string underlyingCode, Action? action, List tradeIds) { var swapEvents = DbContext.swap_event.Where(x => x.EventReason.Contains("自动") && x.ValueDate == valueDate); //bool resetSingle = false; if (tradeIds == null || tradeIds.Count() == 0) { tradeIds = swapEvents.Select(s => s.SwapTradeId).ToList(); } //else //{ // resetSingle = true; //} var swaptrades = DbContext.trade.Where(x => tradeIds.Contains(x.id) && x.ValidState != ConsGlobal.InValid).ToList(); var swapflowMerges = DbContext.swap_flow_merge.Where(x => x.OccurTime == valueDate); var swapflowDeals = DbContext.swap_flow_deal.Where(x => x.OccurDate == valueDate); var swapFlows = DbContext.swap_flow.Where(x => x.OccurTime == valueDate); if (clientId.HasValue) { swapFlows = swapFlows.Where(x => x.ClientId == clientId); swapflowDeals = swapflowDeals.Where(x => x.ClientId == clientId); swapflowMerges = swapflowMerges.Where(x => x.ClientId == clientId); } if (!string.IsNullOrEmpty(underlyingCode)) { swapFlows = swapFlows.Where(x => x.UnderlyingCode == underlyingCode); swapflowMerges = swapflowMerges.Where(x => x.UnderlyingCode == underlyingCode); swapflowDeals = swapflowDeals.Where(x => x.UnderlyingCode == underlyingCode); } var trsDealIds = swapFlows.Where(x => x.trs_deal_id > 0).Select(s => s.trs_deal_id ?? 0).ToList(); var swaptradesCount = swaptrades.Count(); decimal backProcessedCount = 0; if (swaptradesCount == 0) { backProcessedCount = 100; } foreach (var td in swaptrades) { var isAutoOpenTrade = td.IsAutoGenerate == true; new SwapTradeService(UserInfo).TradeBack(td.id, valueDate, isAutoOpenTrade); backProcessedCount++; var processedPercent = backProcessedCount * 100 / swaptradesCount; action?.Invoke(processedPercent); } swapFlows.ForEach(x => { x.DataState = (int)SwapFlowDateStateEnum.等待完成; }); new TradeRiskCheckLogService(UserInfo).DeleteLogs(trsDealIds); DbContext.swap_flow_merge.RemoveRange(swapflowMerges); DbContext.swap_flow_deal.RemoveRange(swapflowDeals); DbContext.SaveChanges(); } /// /// 校验是否能重置流水 /// /// /// /// /// public bool CheckFlowAfter(int? clientId, string underlyingCode, DateTime valueDate) { Expression> expression =x=> x.OccurTime > valueDate && x.DataState == (int)SwapFlowDateStateEnum.完成; if (clientId.HasValue) { expression = expression.And(x=>x.ClientId==clientId); } if (!string.IsNullOrEmpty(underlyingCode)) { expression = expression.And(x => x.UnderlyingCode == underlyingCode); } return DbContext.swap_flow.Any(expression); } /// /// 获取需要重置的交易 /// /// /// /// /// public List GetNeedResetTradeIds(int? clientId, string underlyingCode, DateTime valueDate) { var swapEvents = DbContext.swap_event.Where(x => x.EventReason.Contains("自动") && x.ValueDate == valueDate); var tradeIds = swapEvents.Select(s => s.SwapTradeId).ToList(); Expression> expression = x => tradeIds.Contains(x.id) && x.ValidState != ConsGlobal.InValid; if (clientId.HasValue) { expression = expression.And(x => x.ClientId == clientId); } if (!string.IsNullOrEmpty(underlyingCode)) { expression = expression.And(x => x.UnderlyingCode == underlyingCode); } var resetTradeIds = DbContext.trade.Where(expression).Select(s=>s.id).ToList(); return resetTradeIds; } /// /// 校验交易是否收盘 /// /// /// /// public void CheckTradEods(DateTime valueDate, List mergeList) { var preSettleDate = QdpCalendarHelper.GetNonHolidayDefore(valueDate.AddDays(-1)); new BaseTradeAfterEodOutputService().CheckEodStatus(preSettleDate);//上日收盘校验 var clientIds = mergeList.Select(s => s.ClientId).Distinct().ToList(); var swaptrades = DbContext.trade.Where(t => t.TradeType == "收益互换" && t.TradeDate <= valueDate && t.ValidState != ConsGlobal.InValid && clientIds.Contains(t.ClientId) && !ConsTrade.TradeCompleteStatus.Contains(t.TradeStatus)).ToList(); var tradeIds = swaptrades.Select(s => s.id).ToList(); var swapEodTrades = new SwapEodPositionService(UserInfo).GetEodSwaps(tradeIds, preSettleDate); foreach (var swap in swaptrades) { var eodTrade = swapEodTrades.FirstOrDefault(x => x.SwapTradeId == swap.id); if (eodTrade == null && swap.StartDate.Value < valueDate) { throw new ServiceException($"交易{swap.TradeNumber}在{preSettleDate:yyyy-MM-dd}日未收盘"); } } } /// /// 检查当日是否流水合成簿记 /// /// /// public void CheckBookByDate(DateTime valueDate) { if (DbContext.swap_flow.Any(x => x.OccurTime == valueDate && x.DataState == (int)SwapFlowDateStateEnum.完成)) { throw new ServiceException($"{valueDate:yyyy-MM-dd}日已经处理过流水合成簿记,请重置再重新合成簿记!"); } } /// /// 修改风控日志记录 /// /// /// public void UpdateRiskCheckLog(List swapFlows, List mergeList) { var riskCheckLogService = new TradeRiskCheckLogService(UserInfo); foreach (var item in mergeList) { var flows = swapFlows.Where(x => x.ClientId == item.ClientId && x.UnderlyingCode == item.UnderlyingCode && x.BsType == item.BsType && x.trs_deal_id > 0); var trsDealIds = flows.Select(s => s.trs_deal_id ?? 0).ToList(); riskCheckLogService.UpdateLogTradeNumber(trsDealIds, item.SwapTradeNo); } } public List GetFlows(DateTime valueDate) { var swapFlows = DbContext.swap_flow.Where(x => x.OccurTime == valueDate && x.DataState == (int)SwapFlowDateStateEnum.等待完成 && x.ClientId > 0).ToList(); return swapFlows; } public List GetFlows(List ids) { var swapFlows = DbContext.swap_flow.Where(x => ids.Contains(x.id) && x.DataState == (int)SwapFlowDateStateEnum.等待完成 && x.ClientId > 0).ToList(); return swapFlows; } /// /// 簿记前自动校验 /// /// public void BookingValidate(List mergeList) { foreach (var merge in mergeList) { var client = DataCacheProvider.GetClientDataSource().GetData(merge.ClientId ?? 0); if (client == null) { throw new ServiceException($"找不到id为{merge.ClientId}的客户信息"); } if (!client.DerivativesInvestmentVarieties.Contains((int)DerivativesInvestmentVarietiesEnum.场外互换 + "")) { throw new ServiceException($"客户:{client.Name}未设置交易种类“场外互换”,无法生成互换交易!"); } merge.SwapTradeType = client.SwapTradeType ?? 0; var etradeRule = new EtradingRuleService(UserInfo).GetEtradingRuleAccont(client.BoundSide, client.Number); if (etradeRule == null || string.IsNullOrEmpty(etradeRule.AssetAccount_0)) { throw new ServiceException($"{client.Name}未设置TRS对客簿记账户"); } string clearingAgency = etradeRule.ClearingAgency_0; var asset = DataCacheProvider.GetAssetUnitDataSource().AsQueryable(x => x.Name == etradeRule.AssetAccount_0).FirstOrDefault();//取对客簿记账户 if (asset == null) { throw new ServiceException($"找不到名为{etradeRule.AssetAccount_0}的簿记账户信息"); } if (asset.TraderIdsInt.Count == 0) { throw new ServiceException($"{etradeRule.AssetAccount_0}的簿记账户未设置交易员"); } var underlying = DbContext.underlying_manager.FirstOrDefault(x => x.UnderlyingCode == merge.UnderlyingCode); if (underlying == null) { throw new ServiceException($"找不到标的代码为{merge.UnderlyingCode}的标的信息"); } } } /// /// 汇总流水 /// /// public List SummaryFlow(List swapFlows, bool save = true) { var swapFlowGroup = swapFlows.GroupBy(g => new { g.ClientId, g.OccurTime, g.UnderlyingCode, g.BsType }).ToList(); List list = new List(); foreach (var gourpItem in swapFlowGroup) { var flowList = gourpItem.OrderBy(O => O.OptTime).ToList(); var swapflow = flowList.First(); swap_flow_merge swap_flow_summary = new swap_flow_merge() { OccurTime = swapflow.OccurTime.Value, FundAccount = swapflow.FundAccount, SwapTradeId = swapflow.SwapTradeId, SwapTradeNo = swapflow.SwapTradeNo, UnderlyingCode = swapflow.UnderlyingCode, BsType = swapflow.BsType, TradingQty = gourpItem.Sum(s => s.TradingQty), TradingFeePending = gourpItem.Sum(s => s.TradingFee), DataState = (int)SwapFlowDateStateEnum.等待完成, ContractSize = swapflow.ContractSize, ClientId = swapflow.ClientId, }; int tradeSide = swap_flow_summary.BsType == (int)EnumDirection.Long ? 1 : -1; swap_flow_summary.FirstFlowTime = swapflow.OptTime; swap_flow_summary.SettleDate = gourpItem.Max(s => s.SettleDate); swap_flow_summary.TradingAmount = swap_flow_summary.TradingQty * swap_flow_summary.ContractSize; swap_flow_summary.TradingAmountAvg = swap_flow_summary.TradingQty == 0 ? 0 : gourpItem.Sum(s => s.TradingAmountAvg * s.TradingQty) / swap_flow_summary.TradingQty; swap_flow_summary.TradingAmountFeeAvg = swap_flow_summary.TradingQty == 0 ? swap_flow_summary.TradingAmountAvg : swap_flow_summary.TradingAmountAvg + swap_flow_summary.TradingFeePending * tradeSide / swap_flow_summary.TradingQty; swap_flow_summary.TradingAmountNetAvg = swap_flow_summary.TradingQty == 0 ? 0 : gourpItem.Sum(s => s.TradingAmountNet * s.TradingQty) / swap_flow_summary.TradingQty; swap_flow_summary.TradingAmountNetFeeAvg = swap_flow_summary.TradingQty == 0 ? swap_flow_summary.TradingAmountNetAvg : swap_flow_summary.TradingAmountNetAvg + swap_flow_summary.TradingFeePending * tradeSide / swap_flow_summary.TradingQty; swap_flow_summary.SetOpt(UserInfo); if (save) { DbContext.swap_flow_merge.Add(swap_flow_summary); } list.Add(swap_flow_summary); } return list; } /// /// 汇总流水 /// /// public List SummaryFlow(List swapFlows) { var swapFlowGroup = swapFlows.GroupBy(g => new { g.ClientId, g.OccurDate, g.UnderlyingCode, g.BsType }).ToList(); List list = new List(); foreach (var gourpItem in swapFlowGroup) { var flowList = gourpItem.OrderBy(O => O.OptTime).ToList(); var swapflow = flowList.First(); swap_flow_merge swap_flow_summary = new swap_flow_merge() { OccurTime = swapflow.OccurDate, SwapTradeId = swapflow.SwapTradeId, SwapTradeNo = swapflow.SwapTradeNo, UnderlyingCode = swapflow.UnderlyingCode, BsType = swapflow.BsType, TradingQty = gourpItem.Sum(s => s.TradingQty), TradingFeePending = gourpItem.Sum(s => s.TradingFee), DataState = (int)SwapFlowDateStateEnum.等待完成, ContractSize = swapflow.ContractSize, ClientId = swapflow.ClientId, }; int tradeSide = swap_flow_summary.BsType == (int)EnumDirection.Long ? 1 : -1; swap_flow_summary.FirstFlowTime = swapflow.OptTime; swap_flow_summary.SettleDate = gourpItem.Max(s => s.SettleDate); swap_flow_summary.TradingAmount = swap_flow_summary.TradingQty * swap_flow_summary.ContractSize; swap_flow_summary.TradingAmountAvg = swap_flow_summary.TradingQty == 0 ? 0 : gourpItem.Sum(s => s.FullPrice * s.TradingQty) / swap_flow_summary.TradingQty; swap_flow_summary.TradingAmountFeeAvg = swap_flow_summary.TradingQty == 0 ? swap_flow_summary.TradingAmountAvg : swap_flow_summary.TradingAmountAvg + swap_flow_summary.TradingFeePending * tradeSide / swap_flow_summary.TradingQty; swap_flow_summary.TradingAmountNetAvg = swap_flow_summary.TradingQty == 0 ? 0 : gourpItem.Sum(s => s.NetPrice * s.TradingQty) / swap_flow_summary.TradingQty; swap_flow_summary.TradingAmountNetFeeAvg = swap_flow_summary.TradingQty == 0 ? swap_flow_summary.TradingAmountNetAvg : swap_flow_summary.TradingAmountNetAvg + swap_flow_summary.TradingFeePending * tradeSide / swap_flow_summary.TradingQty; swap_flow_summary.SetOpt(UserInfo); DbContext.swap_flow_merge.Add(swap_flow_summary); list.Add(swap_flow_summary); } return list; } /// /// 重置法生成开平仓事件 /// /// public void MergeRestModeCompose(List mergeList, DateTime valueDate, Action? action) { var flowquery = mergeList.GroupBy(g => g.ClientId); var flowCount = flowquery.Count(); if (flowCount == 0) { return; } var swaptrades = DbContext.trade.Where(t => t.TradeType == "收益互换" && t.StructureType == "普通债券类收益互换" && t.TradeDate <= valueDate && t.ValidState != ConsGlobal.InValid && !ConsTrade.TradeCompleteStatus.Contains(t.TradeStatus)).ToList(); var swapTradeIds = swaptrades.Select(s => s.id); var tradeExtends = DbContext.trade_extend.Where(x => swapTradeIds.Contains(x.TradeId)); var clientMarginTemplates = DbContext.client_marginrate.Where(x => x.Type == "品种" && x.ValueDate <= valueDate).OrderByDescending(o => o.ValueDate).ToList(); var restSwapTrades = new List(); foreach (var swaptrade in swaptrades) { swaptrade.trade_extend = tradeExtends.FirstOrDefault(x => x.TradeId == swaptrade.id); if (swaptrade.trade_extend?.ExtendObj.FlowBookMode == (int)FlowBookModeEnum.重置) { restSwapTrades.Add(swaptrade); } } var swapPositions = DbContext.swap_position.Where(x => swapTradeIds.Contains(x.SwapTradeId) && !x.IsInitial && x.PosiQuantity > 0 && !x.Invalid && x.PosiDirection == (int)SwapDirectionEnum.支付).ToList(); var matuirityDate = QdpCalendarHelper.GetNonHolidayDefore(valueDate.AddDays(14)); var floatRatePredicate = PredicateBuilder.Create(x => x.StartDate <= valueDate && x.EndDate >= matuirityDate); var floatRateQuery = DbContext.swap_float_rate.Where(floatRatePredicate); int dealCount = 0; foreach (var groupItem in flowquery) { MergeRestModelItem(groupItem, restSwapTrades, swapPositions, floatRateQuery, clientMarginTemplates, ref dealCount, action); } } /// /// 加权平均法生成开平仓事件 /// /// /// public void MergeAvgModeCompose(List swapFlows, DateTime valueDate, Action? action) { var flowquery = swapFlows.GroupBy(g => g.ClientId); var flowCount = flowquery.Count(); if (flowCount == 0) { return; } var swaptrades = DbContext.trade.Where(t => t.StructureType == "定义文件型债券收益互换" && t.TradeDate <= valueDate && t.ValidState != ConsGlobal.InValid && !ConsTrade.TradeCompleteStatus.Contains(t.TradeStatus)).ToList(); var swapTradeIds = swaptrades.Select(s => s.id); var swapPositions = DbContext.swap_position.Where(x => swapTradeIds.Contains(x.SwapTradeId) && x.PosiDirection > 0 && !x.IsInitial && x.PosiQuantity > 0 && !x.Invalid).ToList(); var matuirityDate = QdpCalendarHelper.GetNonHolidayDefore(valueDate.AddDays(14)); var floatRatePredicate = PredicateBuilder.Create(x => x.StartDate <= valueDate && x.EndDate >= matuirityDate); var floatRateQuery = DbContext.swap_float_rate.Where(floatRatePredicate); int dealCount = 0; foreach (var groupItem in flowquery) { MergeAvgModelItem(groupItem, swaptrades, swapPositions, floatRateQuery, ref dealCount, action); } } public void UpdateSwapFlowState(List swapFlows) { foreach (var item in swapFlows) { item.DataState = (int)SwapFlowDateStateEnum.完成; } DbContext.SaveChanges(); } /// /// 重置法按客户合成持仓 /// /// /// /// /// /// /// /// private void MergeRestModelItem(IGrouping groupItem, List swaptrades, List swapPositions, IQueryable floatRateQuery, List client_Marginrates, ref int dealCount, Action? action) { var clientId = groupItem.Key; var client = DataCacheProvider.GetClientDataSource().GetData(clientId ?? 0); if (client == null) { throw new ServiceException($"找不到id为{clientId}的客户信息"); } if (!client.DerivativesInvestmentVarieties.Contains((int)DerivativesInvestmentVarietiesEnum.场外互换 + "")) { throw new ServiceException($"客户:{client.Name}未设置交易种类“场外互换”,无法生成互换交易!"); } var etradeRule = new EtradingRuleService(UserInfo).GetEtradingRuleAccont(client.BoundSide, client.Number); if (etradeRule == null || string.IsNullOrEmpty(etradeRule.AssetAccount_0)) { throw new ServiceException($"{client.Number}未设置TRS对客簿记账户"); } string clearingAgency = etradeRule.ClearingAgency_0; var asset = DataCacheProvider.GetAssetUnitDataSource().AsQueryable(x => x.Name == etradeRule.AssetAccount_0).FirstOrDefault();//取对客簿记账户 if (asset == null) { throw new ServiceException($"找不到名为{etradeRule.AssetAccount_0}的簿记账户信息"); } if (asset.TraderIdsInt.Count == 0) { throw new ServiceException($"{etradeRule.AssetAccount_0}的簿记账户未设置交易员"); } var clientMarginTemplate = client_Marginrates.FirstOrDefault(x => x.ClientId == clientId); if (clientMarginTemplate == null) { clientMarginTemplate = client_Marginrates.FirstOrDefault(x => x.ClientId == 0); } var underlyingGroup = groupItem.GroupBy(g => g.UnderlyingCode); var clientSwapTrades = swaptrades.Where(x => x.ClientId == clientId).ToList(); var clientSwapTradeIds = clientSwapTrades.Select(s => s.id); var clientSwapPositions = swapPositions.Where(x => clientSwapTradeIds.Contains(x.SwapTradeId));//现有客户持仓 var underlyingCodes = underlyingGroup.Select(s => s.Key).ToList(); var underlyings = DataCacheProvider.GetUnderlyingDataSource().AsQueryable(x => underlyingCodes.Contains(x.UnderlyingCode)); foreach (var underlyingGroupItem in underlyingGroup) { var underlyingCode = underlyingGroupItem.Key; var underlying = underlyings.FirstOrDefault(x => x.UnderlyingCode == underlyingCode); if (underlying == null) { throw new ServiceException($"找不到标的代码为{underlyingCode}的标的信息"); } var floatRate = new SwapFloatRateService(UserInfo).GetSwapFloatRate(floatRateQuery, clientId ?? 0, underlyingCode); var clientSwapPositionList = clientSwapPositions.Where(x => x.UnderlyingCode == underlyingCode).ToList();//现有标的持仓 var hasPayPosition = clientSwapPositionList.Any(); var mergeList = underlyingGroupItem.OrderByDescending(o => o.TradingQty).ToList(); var flowMerge = mergeList.First(); var flowMerge2 = mergeList.Last(); dealCount = dealCount + mergeList.Count(); action?.Invoke(dealCount); flowMerge.DataState = 100; flowMerge2.DataState = 100; bool cashNeedAfter = false;//资金是否需要延后 if (mergeList.Count == 2) { var unwindQty = (flowMerge.TradingQty * (flowMerge.BsType == 1 ? 1 : -1)) + (flowMerge2.TradingQty * (flowMerge2.BsType == 1 ? 1 : -1)); if (unwindQty == 0) { cashNeedAfter = true; } } if (cashNeedAfter) { var flowEvents = DbContext.swap_flow_event.Where(x => x.EventDate == flowMerge.OccurTime && x.PayDate > x.UnwindDate && x.EventType == (int)SwapFlowEventTypeEnum.平仓 && x.DataState == (int)SwapFlowDateStateEnum.完成 && x.ClientId == flowMerge.ClientId && x.PayDirection > 0); var tradeIds = flowEvents.Select(s=>s.SwapTradeId).Distinct(); var trades = DbContext.trade.Where(x=> tradeIds.Contains(x.id)&&x.ValidState!=ConsGlobal.InValid); cashNeedAfter = !trades.Any(); } if (!hasPayPosition)//没有持仓 { DealNoPosition(mergeList, client, asset, underlying, floatRate, clientMarginTemplate, clearingAgency, cashNeedAfter); } else { DealHasPosition(mergeList, client, asset, underlying, floatRate, clientSwapPositionList, clientSwapTrades, clientMarginTemplate, clearingAgency, cashNeedAfter); } } DbContext.SaveChanges(); } /// /// 加权平均法按客户合成持仓 /// /// /// /// /// /// /// /// private void MergeAvgModelItem(IGrouping groupItem, List swaptrades, List swapPositions, IQueryable floatRateQuery, ref int dealCount, Action? action) { var clientId = groupItem.Key; var client = DataCacheProvider.GetClientDataSource().GetData(clientId ?? 0); var etradeRule = new EtradingRuleService(UserInfo).GetEtradingRuleAccont(client.BoundSide, client.Number); string clearingAgency = etradeRule.ClearingAgency_0; var asset = DataCacheProvider.GetAssetUnitDataSource().AsQueryable(x => x.Name == etradeRule.AssetAccount_0).FirstOrDefault();//取对客簿记账户 var underlyingGroup = groupItem.GroupBy(g => g.UnderlyingCode); var clientSwapTrades = swaptrades.Where(x => x.ClientId == clientId).ToList(); var clientSwapTradeIds = clientSwapTrades.Select(s => s.id); var clientSwapPositions = swapPositions.Where(x => clientSwapTradeIds.Contains(x.SwapTradeId));//现有客户持仓 var underlyingCodes = underlyingGroup.Select(s => s.Key).ToList(); var underlyings = DataCacheProvider.GetUnderlyingDataSource().AsQueryable(x => underlyingCodes.Contains(x.UnderlyingCode)); foreach (var underlyingGroupItem in underlyingGroup) { var underlyingCode = underlyingGroupItem.Key; var underlying = underlyings.FirstOrDefault(x => x.UnderlyingCode == underlyingCode); if (underlying == null) { throw new ServiceException($"找不到标的代码为{underlyingCode}的标的信息"); } var floatRate = new SwapFloatRateService(UserInfo).GetSwapFloatRate(floatRateQuery, clientId ?? 0, underlyingCode); var clientSwapPositionList = clientSwapPositions.Where(x => x.UnderlyingCode == underlyingCode).ToList();//现有标的持仓 var hasPayPosition = clientSwapPositionList.Any(); var mergeList = underlyingGroupItem.OrderBy(o => o.OptTime).ToList(); dealCount = dealCount + mergeList.Count(); action?.Invoke(dealCount); if (!hasPayPosition)//没有持仓 { var bsType = mergeList.OrderBy(o => o.OptTime).First().BsType; AvgDealNoPosition(mergeList, client, asset, underlying, floatRate, clearingAgency, bsType); } else { AvgDealHasPosition(mergeList, client, asset, underlying, floatRate, clientSwapPositionList, clientSwapTrades, clearingAgency); } } DbContext.SaveChanges(); } /// /// 将上日没有持仓的流水自动簿记 /// /// /// /// /// /// /// private void DealNoPosition(List mergeList, Client client, AssetUnit asset, underlying_manager underlying, SwapFloatRate floatRate, client_marginrate clientMarginTemplate, string clearingAgency, bool cashNeedAfter) { var mergeOrderList = mergeList.OrderBy(o => o.FirstFlowTime); swap_flow_merge flowMergeMax = mergeOrderList.First();//先开最早的一条 swap_flow_merge flowMergeMin = mergeOrderList.Last(); var swapTradeService = new SwapTradeService(UserInfo); var trade = swapTradeService.NewSwapTrade(flowMergeMax, client, asset, underlying, floatRate, clientMarginTemplate, clearingAgency, cashNeedAfter: cashNeedAfter); flowMergeMax.SwapTradeNo = trade.TradeNumber; flowMergeMin.SwapTradeNo = trade.TradeNumber; if (mergeList.Count == 2)//有两条流水 { var qty = flowMergeMax.TradingQtyAbs - flowMergeMin.TradingQtyAbs;//平仓剩余数量 new SwapDealService(UserInfo).AuotoSwapUnwind(trade.id, flowMergeMin.TradingAmountAvg, flowMergeMin.TradingAmountFeeAvg, flowMergeMin.TradingAmountNetFeeAvg ?? 0, flowMergeMin.TradingAmountNetAvg ?? 0, flowMergeMin.OccurTime, flowMergeMax.TradingQtyAbs, flowMergeMin.TradingQty, flowMergeMin.TradingFeePending); var amount = qty * flowMergeMax.ContractSize;//平仓剩余金额=(平仓流水的成交均价-平仓对象的期初价格不含费)*平仓流水的成交数量*合约乘数 if (qty != 0)//平仓有剩余,开仓 { var qtyAbs = Math.Abs(qty); var flowMergeClone = flowMergeMax.Clone(); if (qty < 0) { flowMergeClone = flowMergeMin.Clone(); } flowMergeClone.TradingAmount = Math.Abs(amount); flowMergeClone.TradingFeePending = flowMergeClone.TradingFeePending * qtyAbs / flowMergeClone.TradingQty;//剩余后付费用 flowMergeClone.TradingQty = qtyAbs; if (qty > 0)//交易有剩余新开仓 { var posi = DbContext.swap_position.FirstOrDefault(x => x.SwapTradeId == trade.id && x.PosiDirection > 0 && !x.IsInitial); SetNewOpenData(flowMergeMin, flowMergeClone, posi); } var trade2 = swapTradeService.NewSwapTrade(flowMergeClone, client, asset, underlying, floatRate, clientMarginTemplate, clearingAgency); flowMergeMax.SwapTradeNo = trade2.TradeNumber; flowMergeMin.SwapTradeNo = trade2.TradeNumber; } } } /// /// 将上日没有持仓的流水自动簿记 /// /// /// /// /// /// /// private trade AvgDealNoPosition(List swapFlows, Client client, AssetUnit asset, underlying_manager underlying, SwapFloatRate floatRate, string clearingAgency, int byType) { var negativeFlows = swapFlows.Where(x => x.BsType != byType).OrderBy(o => o.OptTime).ToList(); var sameFlows = swapFlows.Where(x => x.BsType == byType).OrderBy(o => o.OptTime).ToList(); if (negativeFlows.Count == 0) { return DealOneDirectionFlows(sameFlows, client, asset, underlying, floatRate, clearingAgency); } return DealTwoDirectionFlows(sameFlows, negativeFlows, client, asset, underlying, floatRate, clearingAgency); } /// /// 当前无持仓,且只有一个方向的流水合成簿记 /// /// /// /// /// /// /// private trade DealOneDirectionFlows(List sameFlows, Client client, AssetUnit asset, underlying_manager underlying, SwapFloatRate floatRate, string clearingAgency) { List swapFlowDeals = new List(); foreach (var flow in sameFlows) { SwapFlowDeal swapFlowDeal = GetSwapFlowDeal(flow, flow.TradingQty, (int)OpenCloseEnum.开仓, flow.TradingFee, null); swapFlowDeals.Add(swapFlowDeal); } return NewSwapTrade(swapFlowDeals, client, asset, underlying, floatRate, clearingAgency); } /// /// 当前无持仓,且有2个方向流水合成簿记 /// /// /// /// /// /// /// /// private trade DealTwoDirectionFlows(List sameFlows, List negativeFlows, Client client, AssetUnit asset, underlying_manager underlying, SwapFloatRate floatRate, string clearingAgency) { var swapTradeService = new SwapTradeService(UserInfo); var sameQty = sameFlows.Sum(s => s.TradingQty); var negaQty = negativeFlows.Sum(s => s.TradingQty); //第一次开仓流水拆分明细 List openFirstFlows = new List(); //第一次平仓流水拆分明细 List unwindFirstFlows = new List(); //第二次开仓流水拆分明细,最多可能存在第二次开仓 List openLastFlows = new List(); List negaFlowClones = DataHelper.DeepCopyObject(negativeFlows); List sameFlowClones = DataHelper.DeepCopyObject(sameFlows); unwindFirstFlows = GetRemainderFlows(negaFlowClones, sameQty, null); var unwindQty = unwindFirstFlows.Sum(s => s.TradingQty); //日内轧差全开全平 //同向总数量>=反向总数量,同向新开仓,反向流水全部平仓,同向剩余新开仓 if (sameQty >= negaQty) { openFirstFlows = GetRemainderFlows(sameFlowClones, negaQty, null); openFirstFlows.ForEach(x => { x.OpenFlag = (int)OpenCloseEnum.开仓; }); var openQty = openFirstFlows.Sum(s => s.TradingQty); if (openQty < negaQty) { var sameFlow = sameFlowClones.First(); var unwindFirstQty = negaQty - openQty; var unwindFee = sameFlow.TradingFee * unwindFirstQty / sameFlow.TradingQty; SwapFlowDeal firstOpenFlowDeal = GetSwapFlowDeal(sameFlow, unwindFirstQty, (int)OpenCloseEnum.开仓, unwindFee, null); openFirstFlows.Add(firstOpenFlowDeal); sameFlow.TradingQty = sameFlow.TradingQty - unwindFirstQty; sameFlow.TradingFee = sameFlow.TradingFee - unwindFee; sameFlow.TradingAmount = sameFlow.TradingQty; } //反向流水全部平仓 foreach (var negaFlow in negaFlowClones) { SwapFlowDeal swapFlowDeal = GetSwapFlowDeal(negaFlow, negaFlow.TradingQty, (int)OpenCloseEnum.平仓, negaFlow.TradingFee, null); unwindFirstFlows.Add(swapFlowDeal); } foreach (var sameFlow in sameFlowClones) { SwapFlowDeal swapFlowDeal = GetSwapFlowDeal(sameFlow, sameFlow.TradingQty, (int)OpenCloseEnum.开仓, sameFlow.TradingFee, null); openLastFlows.Add(swapFlowDeal); } } else //同向总数量 < 反向总数量,同向先开仓,反向流水部分平仓,反向剩余新开仓 { if (unwindQty < sameQty) { var negaFlow = negaFlowClones.First(); var unwindFirstQty = sameQty - unwindQty; var unwindFee = negaFlow.TradingFee * unwindFirstQty / negaFlow.TradingQty; SwapFlowDeal swapFirstFlowDeal = GetSwapFlowDeal(negaFlow, unwindFirstQty, (int)OpenCloseEnum.平仓, unwindFee, null); unwindFirstFlows.Add(swapFirstFlowDeal); negaFlow.TradingQty = negaFlow.TradingQty - unwindFirstQty; negaFlow.TradingFee = negaFlow.TradingFee - unwindFee; negaFlow.TradingAmount = negaFlow.TradingQty; } foreach (var flow in sameFlowClones) { SwapFlowDeal swapFlowDeal = GetSwapFlowDeal(flow, flow.TradingQty, (int)OpenCloseEnum.开仓, flow.TradingFee, null); openFirstFlows.Add(swapFlowDeal); } foreach (var minFlow in negaFlowClones) { SwapFlowDeal swapFlowDeal = GetSwapFlowDeal(minFlow, minFlow.TradingQty, (int)OpenCloseEnum.开仓, minFlow.TradingFee, null); openLastFlows.Add(swapFlowDeal); } } var trade = NewSwapTrade(openFirstFlows, client, asset, underlying, floatRate, clearingAgency); unwindFirstFlows.ForEach(x => { x.SwapTradeId = trade.id; x.SwapTradeNo = trade.TradeNumber; }); DbContext.swap_flow_deal.AddRange(unwindFirstFlows); var mergeUnwindFlows = SummaryFlow(unwindFirstFlows); var unwindMergeFlow = mergeUnwindFlows.First(); // 平仓 new SwapDealService(UserInfo).AuotoSwapUnwind(trade.id, unwindMergeFlow.TradingAmountAvg, unwindMergeFlow.TradingAmountFeeAvg, unwindMergeFlow.TradingAmountNetFeeAvg ?? 0, unwindMergeFlow.TradingAmountNetAvg ?? 0, unwindMergeFlow.OccurTime, unwindMergeFlow.TradingQty, unwindMergeFlow.TradingQty, unwindMergeFlow.TradingFeePending); if (openLastFlows.Count > 0) { return NewSwapTrade(openLastFlows, client, asset, underlying, floatRate, clearingAgency); } return trade; } /// /// 流水拆分后新开仓 /// /// /// /// /// /// /// /// private trade NewSwapTrade(List openFlows, Client client, AssetUnit asset, underlying_manager underlying, SwapFloatRate floatRate, string clearingAgency) { var swapTradeService = new SwapTradeService(UserInfo); var mergeFlows = SummaryFlow(openFlows); swap_flow_merge flowMergeFirst = mergeFlows.First(); var trade = swapTradeService.NewSwapTrade(flowMergeFirst, client, asset, underlying, floatRate, null, clearingAgency, LongShortStructType); flowMergeFirst.SwapTradeNo = trade.TradeNumber; flowMergeFirst.SwapTradeId = trade.id; openFlows.ForEach(x => { x.SwapTradeId = trade.id; x.SwapTradeNo = trade.TradeNumber; }); DbContext.swap_flow_deal.AddRange(openFlows); DbContext.SaveChanges(); return trade; } /// /// 获取剩余要开仓流水 /// /// /// private List GetRemainderFlows(List flows, decimal unwindQty, trade td) { decimal openQty = 0; List unwindFirstFlows = new List(); for (int i = 0; i < flows.Count; i++) { var flow = flows[i]; openQty += flow.TradingQty; if (openQty <= unwindQty) { SwapFlowDeal swapFlowDeal = GetSwapFlowDeal(flow, flow.TradingQty, (int)OpenCloseEnum.平仓, flow.TradingFee, td); unwindFirstFlows.Add(swapFlowDeal); flows.Remove(flow); i--; } } return unwindFirstFlows; } /// /// 转换拆分流水 /// /// /// /// /// private SwapFlowDeal GetSwapFlowDeal(swap_flow swapFlow, decimal qty, int openFlag, decimal fee, trade td) { SwapFlowDeal swapFlowDeal = new SwapFlowDeal(); swapFlowDeal.ContractSize = swapFlow.ContractSize; swapFlowDeal.SetOpt(UserInfo); swapFlowDeal.BsType = swapFlow.BsType; swapFlowDeal.ClientId = swapFlow.ClientId ?? 0; swapFlowDeal.ClientName = swapFlow.ClientName; swapFlowDeal.FullPriceFee = swapFlow.TradingAmountFeeAvg; swapFlowDeal.FullPrice = swapFlow.TradingAmountAvg; swapFlowDeal.OccurDate = swapFlow.OccurTime ?? DateTime.Now.Date; swapFlowDeal.HedgeDealType = swapFlow.DealType ?? 0; swapFlowDeal.SettleDate = swapFlow.SettleDate; swapFlowDeal.TrsDealId = swapFlow.trs_deal_id; swapFlowDeal.FlowId = swapFlow.id; swapFlowDeal.HedgeTime = swapFlow.OptTime; swapFlowDeal.OpenFlag = openFlag; swapFlowDeal.TradeingAmount = qty; swapFlowDeal.TradingFee = fee; swapFlowDeal.NetPrice = swapFlow.TradingAmountNet ?? 0; swapFlowDeal.NetPriceFee = swapFlow.TradingAmountNetFee ?? 0; swapFlowDeal.TradingQty = qty; swapFlowDeal.UnderlyingCode = swapFlow.UnderlyingCode; swapFlowDeal.UnderlyingName = swapFlow.UnderlyingName; swapFlowDeal.Ytm = swapFlow.ytm; if (td != null) { swapFlowDeal.SwapTradeId = td.id; swapFlowDeal.SwapTradeNo = td.TradeNumber; } return swapFlowDeal; } /// /// 将上日有持仓的流水自动簿记 /// /// /// /// /// /// /// /// /// private void DealHasPosition(List mergeList, Client client, AssetUnit asset, underlying_manager underlying, SwapFloatRate floatRate, List clientSwapPositionList, List clientSwapTrades, client_marginrate clientMarginTemplate, string clearingAgency, bool cashNeedAfter) { if (mergeList.Count == 1)//只有一条流水情况 { DealSingleFlow(mergeList, clientSwapPositionList, clientSwapTrades, client, asset, underlying, floatRate, clientMarginTemplate, clearingAgency); } else { DealDoubleFlow(mergeList, clientSwapPositionList, clientSwapTrades, client, asset, underlying, floatRate, clientMarginTemplate, clearingAgency, cashNeedAfter); } } /// /// 将上日有持仓的流水自动簿记 /// /// /// /// /// /// /// /// /// private void AvgDealHasPosition(List flowList, Client client, AssetUnit asset, underlying_manager underlying, SwapFloatRate floatRate, List clientSwapPositionList, List clientSwapTrades, string clearingAgency) { var firstFlow = flowList.First(); var negaFlowExist = flowList.Any(x => x.BsType != firstFlow.BsType); if (!negaFlowExist)//只有同向流水情况 { AvgDealSingleFlow(flowList, clientSwapPositionList, clientSwapTrades, client, asset, underlying, floatRate, clearingAgency); } else { AvgDealDoubleFlow(flowList, clientSwapPositionList, clientSwapTrades, client, asset, underlying, floatRate, clearingAgency); } } /// /// 处理单条流水情况 /// /// /// /// /// /// /// /// /// private void DealSingleFlow(List mergeList, List clientSwapPositionList, List clientSwapTrades, Client client, AssetUnit asset, underlying_manager underlying, SwapFloatRate floatRate, client_marginrate clientMarginTemplate, string clearingAgency) { var swapTradeService = new SwapTradeService(UserInfo); swap_flow_merge flowMergeMax = mergeList.First(); swap_flow_merge flowMergeMin = mergeList.Last(); var negativeDirectionPositions = clientSwapPositionList.Where(x => x.PositionType != flowMergeMax.BsType).ToList();//查找反方向交易 var sameDirectionPositions = clientSwapPositionList.Where(x => x.PositionType == flowMergeMax.BsType).ToList();//查找同方向交易 var tradeIds = clientSwapPositionList.Select(x => x.SwapTradeId).ToList(); var sameTradeIds = sameDirectionPositions.Select(x => x.SwapTradeId).ToList(); var sameTrades = clientSwapTrades.Where(x => sameTradeIds.Contains(x.id) && x.TradeDate == flowMergeMax.OccurTime).ToList();//只处理当前清算日期的交易 if (negativeDirectionPositions.Any())//存在反方向交易 { List unwindTradeIds = new List(); var negativeTradeIds = negativeDirectionPositions.Select(x => x.SwapTradeId).ToList(); var negativeTrades = clientSwapTrades.Where(x => negativeTradeIds.Contains(x.id)).OrderBy(o => o.TradeDate).ToList(); var flowMergeClone = flowMergeMax.Clone(); flowMergeMax.SwapTradeNo = flowMergeClone.SwapTradeNo; var dealResult = DealNegativeTrade(negativeTrades, flowMergeClone, negativeDirectionPositions, unwindTradeIds, true, false); if (dealResult.Item3)// 处理完有开仓需求 { flowMergeClone.TradingAmount = dealResult.Item1; flowMergeClone.TradingQty = dealResult.Item2; if (flowMergeClone.BsType != flowMergeMax.BsType)//交易有剩余新开仓 { SetNewOpenData(flowMergeMax, flowMergeClone, dealResult.Item4); } var trade = swapTradeService.NewSwapTrade(flowMergeClone, client, asset, underlying, floatRate, clientMarginTemplate, clearingAgency); flowMergeMax.SwapTradeNo = trade.TradeNumber; } } else //只存在同向交易 { var trade = swapTradeService.NewSwapTrade(flowMergeMax, client, asset, underlying, floatRate, clientMarginTemplate, clearingAgency); flowMergeMax.SwapTradeNo = trade.TradeNumber; } } /// /// 交易平完有剩余重置法新开仓算价格等数据 /// /// /// /// private void SetNewOpenData(swap_flow_merge origin, swap_flow_merge flowMergeClone, swap_position position) { if (position == null) { return; } var ratio = flowMergeClone.BsType == 1 ? 1 : -1; var oriRatio = flowMergeClone.BsType == 1 ? -1 : 1; flowMergeClone.TradingFeePending = flowMergeClone.TradingQty / origin.TradingQty * origin.TradingFeePending; flowMergeClone.TradingAmountAvg = origin.TradingAmountAvg + oriRatio * origin.TradingFeePending * 2 / origin.TradingQty; flowMergeClone.TradingAmountNetAvg = origin.TradingAmountNetAvg + oriRatio * origin.TradingFeePending * 2 / origin.TradingQty; flowMergeClone.TradingAmountFeeAvg = flowMergeClone.TradingAmountAvg + ratio * flowMergeClone.TradingFeePending / flowMergeClone.TradingQty; flowMergeClone.TradingAmountNetFeeAvg = flowMergeClone.TradingAmountNetAvg + ratio * flowMergeClone.TradingFeePending / flowMergeClone.TradingQty; } /// /// 加权平均处理单条流水情况 /// /// /// /// /// /// /// /// /// private void AvgDealSingleFlow(List flowList, List clientSwapPositionList, List clientSwapTrades, Client client, AssetUnit asset, underlying_manager underlying, SwapFloatRate floatRate, string clearingAgency) { swap_flow flowMergeMax = flowList.First(); var negaSwapPositions = clientSwapPositionList.Where(x => x.PositionType != flowMergeMax.BsType).ToList(); List flowClones = DataHelper.DeepCopyObject(flowList); if (negaSwapPositions.Count > 0)//流水与持仓反向 { AvgDealUnwind(flowClones, clientSwapTrades, negaSwapPositions, client, asset, underlying, floatRate, clearingAgency,true); } else //只存在同向交易,同向流水合并新开仓 { DealOneDirectionFlows(flowClones, client, asset, underlying, floatRate, clearingAgency); } } /// /// 处理多条流水情况 /// /// /// /// /// /// /// /// /// public void DealDoubleFlow(List mergeList, List clientSwapPositionList, List clientSwapTrades, Client client, AssetUnit asset, underlying_manager underlying, SwapFloatRate floatRate, client_marginrate clientMarginTemplate, string clearingAgency, bool cashNeedAfter) { var swapTradeService = new SwapTradeService(UserInfo); mergeList = mergeList.OrderBy(o => o.FirstFlowTime).ToList(); var flowMergeFirst = mergeList.First(); var flowMergeLast = mergeList.Last(); var flowMergeFirstClone = flowMergeFirst.Clone(); var flowMergeLastClone = flowMergeLast.Clone(); var sameDirectionPositions = clientSwapPositionList.Where(x => x.PositionType == flowMergeFirstClone.BsType).ToList(); var negDirectionPositions = clientSwapPositionList.Where(x => x.PositionType == flowMergeLastClone.BsType).ToList(); var sameTradeIds = sameDirectionPositions.Select(x => x.SwapTradeId).ToList(); var sameTrades = clientSwapTrades.Where(x => sameTradeIds.Contains(x.id)).ToList();//取出与第一条流水方向相同的交易 var negTradeIds = negDirectionPositions.Select(x => x.SwapTradeId).ToList(); var negTrades = clientSwapTrades.Where(x => negTradeIds.Contains(x.id)).ToList();//取出与第一条流水方向相反的交易 //先处理第一条流水的反向持仓 var firstTrade = DealDoubleFlowDetial(negTrades, negDirectionPositions, flowMergeFirstClone, client, asset, underlying, floatRate, clientMarginTemplate, clearingAgency, true, cashNeedAfter); flowMergeFirst.SwapTradeNo = flowMergeFirstClone.SwapTradeNo; //再处理第二条流水的反向持仓 var lastTrade = DealDoubleFlowDetial(sameTrades, sameDirectionPositions, flowMergeLastClone, client, asset, underlying, floatRate, clientMarginTemplate, clearingAgency, false,false); flowMergeLast.SwapTradeNo = flowMergeLastClone.SwapTradeNo; if (flowMergeFirstClone.BsType != flowMergeLastClone.BsType && firstTrade != null) { var trades = new List { firstTrade }; var positions = DbContext.swap_position.Where(x => x.SwapTradeId == firstTrade.id && x.PosiDirection > 0 && !x.IsInitial && !x.Invalid).ToList(); DealDoubleFlowDetial(trades, positions, flowMergeLastClone, client, asset, underlying, floatRate, clientMarginTemplate, clearingAgency, true,false); flowMergeLast.SwapTradeNo = flowMergeLastClone.SwapTradeNo; } else if (lastTrade==null) { swapTradeService.NewSwapTrade(flowMergeLastClone, client, asset, underlying, floatRate, clientMarginTemplate, clearingAgency); } } /// /// 加权平均处理当前有持仓,切两个方向多条流水情况 /// /// /// /// /// /// /// /// /// public void AvgDealDoubleFlow(List flowList, List clientSwapPositionList, List clientSwapTrades, Client client, AssetUnit asset, underlying_manager underlying, SwapFloatRate floatRate, string clearingAgency) { var swapTradeService = new SwapTradeService(UserInfo); var firstPosi = clientSwapPositionList.First(); var swapPositions = clientSwapPositionList.Where(x => x.PositionType == firstPosi.PositionType).ToList(); var posiQty = swapPositions.Sum(s => s.PosiQuantity); var flowSames = flowList.Where(x => x.BsType == firstPosi.PositionType).ToList(); var flowNegs = flowList.Where(x => x.BsType != firstPosi.PositionType).ToList(); var negaBsType= flowNegs.First().BsType; var sameQty = posiQty + flowSames.Sum(s => s.TradingQty); var flowSameClones = DataHelper.DeepCopyObject(flowSames); var flowNegClones = DataHelper.DeepCopyObject(flowNegs); //先平反向 AvgDealUnwind(flowNegClones, clientSwapTrades, swapPositions, client, asset, underlying, floatRate, clearingAgency,false); var newFlowList = new List(); var openBystype = firstPosi.PositionType; if (flowNegClones.Count > 0) { newFlowList.AddRange(flowNegClones); openBystype = negaBsType; } newFlowList.AddRange(flowSameClones); //有历史持仓按反方向先开仓 AvgDealNoPosition(newFlowList, client, asset, underlying, floatRate, clearingAgency, openBystype); } /// /// 有持仓流水 /// /// /// /// /// /// /// /// /// private void AvgDealUnwind(List swapFlows, List trades, List swapPositions, Client client, AssetUnit asset, underlying_manager underlying, SwapFloatRate floatRate, string clearingAgency, bool needAdd) { var swapTradeService = new SwapTradeService(UserInfo); foreach (var posi in swapPositions) { if (swapFlows.Count == 0) { break; } var td = trades.FirstOrDefault(p => p.id == posi.SwapTradeId); if (td != null) { var posiQty = posi.PosiQuantity; var flowQty = swapFlows.Sum(s => s.TradingQty); var unwindSwapFlowDeals = GetRemainderFlows(swapFlows, posiQty, td); var unwindQty = unwindSwapFlowDeals.Sum(x => x.TradingQty); if (unwindQty < posiQty && swapFlows.Count > 0) { var unwindSwapFlow = swapFlows.First(); var flowUnindQty = posiQty - unwindQty; var unwindFee = unwindSwapFlow.TradingQty == 0 ? 0 : unwindSwapFlow.TradingFee * flowUnindQty / unwindSwapFlow.TradingQty; unwindSwapFlow.TradingQty = unwindSwapFlow.TradingQty - flowUnindQty; unwindSwapFlow.TradingFee = unwindSwapFlow.TradingFee - unwindFee; unwindSwapFlow.TradingAmount = unwindSwapFlow.TradingQty; var unwindFlowDeal = GetSwapFlowDeal(unwindSwapFlow, flowUnindQty, (int)OpenCloseEnum.平仓, unwindFee, td); unwindSwapFlowDeals.Add(unwindFlowDeal); } DbContext.swap_flow_deal.AddRange(unwindSwapFlowDeals); var unwindMergeFlow = SummaryFlow(unwindSwapFlowDeals).First(); // 平仓 new SwapDealService(UserInfo).AuotoSwapUnwind(td.id, unwindMergeFlow.TradingAmountAvg, unwindMergeFlow.TradingAmountFeeAvg, unwindMergeFlow.TradingAmountNetFeeAvg ?? 0, unwindMergeFlow.TradingAmountNetAvg ?? 0, unwindMergeFlow.OccurTime, unwindMergeFlow.TradingQty, posiQty, unwindMergeFlow.TradingFeePending); } } if (swapFlows.Count > 0 && needAdd) { var bsType = swapFlows.First().BsType; AvgDealNoPosition(swapFlows, client, asset, underlying, floatRate, clearingAgency, bsType); } } /// /// 2条流水处理明细 /// /// /// /// /// /// /// /// /// /// /// private trade DealDoubleFlowDetial(List negTrades, List negDirectionPositions, swap_flow_merge flowMergeSameClone, Client client, AssetUnit asset, underlying_manager underlying, SwapFloatRate floatRate, client_marginrate clientMarginTemplate, string clearingAgency, bool needOpen, bool cashNeedAfter ) { var swapTradeService = new SwapTradeService(UserInfo); var flowMergeMax = flowMergeSameClone.Clone(); List unwindTradeIds = new List(); var dealResult = DealNegativeTrade(negTrades, flowMergeSameClone, negDirectionPositions, unwindTradeIds, needOpen, cashNeedAfter); if (dealResult.Item3)// 处理完有开仓需求 { flowMergeSameClone.TradingAmount = dealResult.Item1; flowMergeSameClone.TradingQty = dealResult.Item2; if (flowMergeSameClone.BsType != flowMergeMax.BsType)//交易有剩余新开仓 { SetNewOpenData(flowMergeMax, flowMergeSameClone, dealResult.Item4); } return swapTradeService.NewSwapTrade(flowMergeSameClone, client, asset, underlying, floatRate, clientMarginTemplate, clearingAgency); } return null; } /// /// 处理反方向流水簿记 /// /// /// /// /// /// /// /// /// private (decimal, decimal, bool, swap_position) DealNegativeTrade( List negativeTrades, swap_flow_merge flowMerge, List floatPositions, List unwindTradeIds, bool needOpen, bool cashNeedAfter ) { if (negativeTrades.Count == 0) { return (flowMerge.TradingAmount, flowMerge.TradingQty, needOpen, null); } var swapTradeService = new SwapTradeService(UserInfo); List cloneNegativeTrades = new List(negativeTrades); var first = true; foreach (trade td in negativeTrades) { if (first&& cashNeedAfter) { cashNeedAfter = true; } else { cashNeedAfter = false; } var floatPosition = floatPositions.FirstOrDefault(x => x.SwapTradeId == td.id); if (floatPosition == null) { cloneNegativeTrades.Remove(td); if (cloneNegativeTrades.Count > 0) { return DealNegativeTrade(cloneNegativeTrades, flowMerge, floatPositions, unwindTradeIds, needOpen,false);//继续平下一个簿记 } else //交易平完,流水有剩余 { //新开 return (flowMerge.TradingAmount, flowMerge.TradingQty, needOpen, null); } } //新开 var oldAmount = floatPosition.PosiNetPrice * floatPosition.PosiQuantity; var newAmount = flowMerge.TradingAmountFeeAvg * flowMerge.TradingQty; var newQty = floatPosition.PosiQuantity - flowMerge.TradingQtyAbs; var newQtyAbs = Math.Abs(newQty); flowMerge.SwapTradeNo = td.TradeNumber; // 全平 new SwapDealService(UserInfo).AuotoSwapUnwind(td.id, flowMerge.TradingAmountAvg, flowMerge.TradingAmountFeeAvg, flowMerge.TradingAmountNetFeeAvg ?? 0, flowMerge.TradingAmountNetAvg ?? 0, flowMerge.OccurTime, floatPosition.PosiQuantity, flowMerge.TradingQty, flowMerge.TradingFeePending); unwindTradeIds.Add(td.id); flowMerge.TradingAmount = newQtyAbs * flowMerge.ContractSize; if (newQty > 0) { flowMerge.TradingFeePending = floatPosition.PosiTradingFeePending * newQtyAbs / floatPosition.PosiQuantity; } else { flowMerge.TradingFeePending = flowMerge.TradingFeePending * newQtyAbs / flowMerge.TradingQty; } flowMerge.TradingQty = newQtyAbs; if (newQty < 0)//交易不够平,继续平 { cloneNegativeTrades.Remove(td); if (cloneNegativeTrades.Count > 0) { return DealNegativeTrade(cloneNegativeTrades, flowMerge, floatPositions, unwindTradeIds, needOpen, false);//继续平下一个簿记 } else //交易平完,流水有剩余 { //新开 return (flowMerge.TradingAmount, flowMerge.TradingQty, needOpen, null); } } else if (newQty > 0) //交易平完交易有剩余 { flowMerge.BsType = flowMerge.BsType == 1 ? 2 : 1; return (flowMerge.TradingAmount, flowMerge.TradingQty, true, floatPosition); } else //完全平仓 { return (0, 0, false, null); } } return (0, 0, false, null); } /// /// 定时任务合成持仓 /// /// /// /// /// /// public void MergeSwapPositionAvg(List flowList, List swapPositions, ClientPosition clientPosition, decimal multiplier, int positionType) { var hasPayPosition = swapPositions != null && swapPositions.Count > 0; var posiQty = swapPositions == null ? 0 : swapPositions.Sum(x => x.PosiQuantity); var flowSames = flowList.Where(x => x.BsType == positionType).OrderBy(o => o.OptTime).ToList(); var flowNegatives = flowList.Where(x => x.BsType != positionType).OrderBy(o => o.OptTime).ToList(); var sameQty = flowSames.Sum(s => s.TradingQty); var negaQty = flowNegatives.Sum(s => s.TradingQty); if (sameQty + posiQty < negaQty) { var negaPosiType = flowNegatives.First().BsType; clientPosition.side = negaPosiType - 1; } var posiType = clientPosition.id == 0 ? 0 : clientPosition.side; List maxFlowClones = new List(flowSames); var mergeFlow = new swap_flow_merge(); if (hasPayPosition)//有日终持仓 { var posiFlows = ConvertToSwapFlow(swapPositions, (int)clientPosition.client_id); maxFlowClones.AddRange(posiFlows); maxFlowClones = maxFlowClones.OrderBy(o => o.id).ToList(); sameQty = maxFlowClones.Sum(s => s.TradingQty); } var maxQty = sameQty; var minQty = negaQty; if (clientPosition.side + 1 != positionType) { maxQty = negaQty; minQty = sameQty; maxFlowClones = new List(flowNegatives); } maxFlowClones.ForEach(x => { x.OccurTime = DateTime.Now.Date; }); var unwindDeals = GetRemainderFlows(maxFlowClones, minQty, null); var unwindQty = unwindDeals.Sum(s => s.TradingQty); if (minQty != 0 && unwindQty < maxQty) { var firstFlow = maxFlowClones.First(); var unwindFlowQty = minQty - unwindQty; var unwindFee = firstFlow.TradingFee * unwindFlowQty / firstFlow.TradingQty; firstFlow.TradingQty = firstFlow.TradingQty - unwindFlowQty; firstFlow.TradingFee = firstFlow.TradingFee - unwindFee; firstFlow.TradingAmount = firstFlow.TradingQty; } var mergeList = SummaryFlow(maxFlowClones, false); mergeFlow = mergeList.Count>0? mergeList.First():new swap_flow_merge(); clientPosition.commission = mergeFlow.TradingFee; clientPosition.position_qty = mergeFlow.TradingQty / 10000; clientPosition.position_notional_principal = mergeFlow.TradingQty; clientPosition.deal_full_price_avg = mergeFlow.TradingAmountAvg * multiplier; clientPosition.deal_price_avg = mergeFlow.TradingAmountNetAvg * multiplier; } /// /// 将持仓转换为流水 /// /// /// /// private List ConvertToSwapFlow(List positions, int clientId) { List flows = new List(); foreach (var item in positions) { swap_flow flow = new swap_flow(); flow.TradingAmount = item.PosiNotionalValue; flow.TradingAmountAvg = item.PosiGrossPrice; flow.TradingAmountFeeAvg = item.PosiNetPrice; flow.TradingAmountNet = item.PosiNetNoFeePrice; flow.TradingAmountNetFee = item.PosiNetFeePrice; flow.TradingFee = item.PosiTradingFeePending; flow.TradingQty = item.PosiQuantity; flow.UnderlyingCode = item.UnderlyingCode; flow.OccurTime = DateTime.Now.Date; flow.ContractSize = item.ContractSize; flow.ClientId = clientId; flow.BsType = item.PositionType; flow.SettleDate = item.PosiStartDate; flows.Add(flow); } return flows; } } }