using ClosedXML.Report.Utils; using Confluent.Kafka; using CsvHelper; using Dapper; using DocumentFormat.OpenXml.Drawing; using DocumentFormat.OpenXml.Spreadsheet; using MoreLinq; using Newtonsoft.Json; using NPOI.SS.Formula.Functions; using Org.BouncyCastle.Asn1.Ocsp; using Qdp.ComputeServiceV2.Data.CommonModels.TradeInfos; using Qdp.Foundation.Utilities; 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.SwapModule.Dto; 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); swapFlow.TradingFee = Math.Round(swapFlow.TradingFee, ConsGlobal.MoneyRound, MidpointRounding.AwayFromZero); 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.TradingAmountAvg; 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; } /// /// 检查FR007数据 /// /// /// public void CheckFR007Data(DateTime valueDate) { var existFr007= DbContext.eod_commodity_future_price.Any(s => s.ValueDate==valueDate && s.UnderlyingCode=="FR007"); if (!existFr007) { throw new ServiceException($"{valueDate.ToString("yyyy-MM-dd")}没有FR007数据不能进行簿记"); } } /// /// 校验交易是否收盘 /// /// /// /// 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,DateTime valueDate) { 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, TradingAmount = gourpItem.Sum(s => s.TradingAmount), }; int tradeSide = swap_flow_summary.BsType == (int)EnumDirection.Long ? 1 : -1; swap_flow_summary.TradingFeePending = Math.Round(swap_flow_summary.TradingFeePending, ConsGlobal.MoneyRound, MidpointRounding.AwayFromZero); swap_flow_summary.FirstFlowTime = swapflow.OptTime; swap_flow_summary.SettleDate = gourpItem.Max(s => s.SettleDate); swap_flow_summary.TradingAmountAvg = swap_flow_summary.TradingQty == 0 ? 0 : gourpItem.Sum(s => s.TradingAmountAvg * s.TradingQty) / swap_flow_summary.TradingQty; swap_flow_summary.TradingAmountAvg = Math.Round(swap_flow_summary.TradingAmountAvg, ConsGlobal.PriceRound, MidpointRounding.AwayFromZero); 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.TradingAmountFeeAvg = Math.Round(swap_flow_summary.TradingAmountFeeAvg, ConsGlobal.PriceRound, MidpointRounding.AwayFromZero); swap_flow_summary.TradingAmountNetAvg = swap_flow_summary.TradingQty == 0 ? 0 : gourpItem.Sum(s => s.TradingAmountNet * s.TradingQty) / swap_flow_summary.TradingQty; swap_flow_summary.TradingAmountNetAvg = Math.Round(swap_flow_summary.TradingAmountNetAvg ?? 0, 10); 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.TradingAmountNetFeeAvg = Math.Round(swap_flow_summary.TradingAmountNetFeeAvg ?? 0, 10); 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 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, ref dealCount, action); } } /// /// 加权平均法生成开平仓事件 /// /// /// public Dictionary> MergeAvgModeCompose(List mergeList, DateTime valueDate, Action? action) { var flowquery = mergeList.GroupBy(g => g.ClientId); var flowCount = flowquery.Count(); if (flowCount == 0) { return null; } var swaptrades = DbContext.trade.Where(t => t.TradeType == "收益互换" && 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; Dictionary> clientUmsDic = new Dictionary>(); foreach (var groupItem in flowquery) { MergeAvgModelItem(groupItem, swaptrades, swapPositions, floatRateQuery, ref dealCount, action); clientUmsDic.Add(groupItem.Key ?? 0, groupItem.Select(p => p.UnderlyingCode).Distinct().ToList()); } return clientUmsDic; } /// /// 刷新冻结资金缓存 /// /// public void refreshFrozenCashCache(Dictionary> clientUmsDic) { if (clientUmsDic == null || clientUmsDic.Count == 0) { return; } Task.Run(() => { try { //发送http请求 var baseUrl = Environment.GetEnvironmentVariable("BondOmsInterface_BaseUrl"); if (!string.IsNullOrEmpty(baseUrl)) { var httpHelper = new HttpHelper(baseUrl, null); var url = $"{baseUrl}/riskJob/refreshClientFrozenCash"; foreach (var item in clientUmsDic) { var clientId = item.Key; var underlyingCodes = item.Value; //请求参数 RefreshFrozenCashCacheReq req = new RefreshFrozenCashCacheReq(clientId, underlyingCodes); // http 请求 Web项目接口 var result = httpHelper.PostRequestNoAuth(url, req).Result; if (result != null && !result.success) { LogFactory.GetLogger("刷新冻结资金").Info("刷新冻结资金:" + result.message); } } } } catch (Exception ex) { LogFactory.GetLogger("刷新冻结资金").Error("刷新冻结资金:" + ex.Message); } }); } 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, 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 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, clearingAgency, cashNeedAfter); } else { DealHasPosition(mergeList, client, asset, underlying, floatRate, clientSwapPositionList, clientSwapTrades,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, 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, 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.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.TradingFeePending = Math.Round(flowMergeClone.TradingFeePending, 4, MidpointRounding.AwayFromZero); 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, 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 negativeFlow = swapFlows.Where(x => x.BsType != byType).FirstOrDefault(); var sameFlow = swapFlows.Where(x => x.BsType == byType).FirstOrDefault(); if (negativeFlow==null) { return NewSwapTrade(sameFlow, client, asset, underlying, floatRate,clearingAgency); } return DealTwoDirectionFlows(sameFlow, negativeFlow, client, asset, underlying, floatRate, clearingAgency); } /// /// 当前无持仓,且有2个方向流水合成簿记 /// /// /// /// /// /// /// /// /// /// private trade DealTwoDirectionFlows(swap_flow_merge sameFlow, swap_flow_merge negativeFlow, Client client, AssetUnit asset, underlying_manager underlying, SwapFloatRate floatRate, string clearingAgency) { var sameQty = sameFlow.TradingQty; var negaQty = negativeFlow.TradingQty; swap_flow_merge negaFlowClone = DataHelper.DeepCopyObject(negativeFlow); swap_flow_merge sameFlowClone = DataHelper.DeepCopyObject(sameFlow); //先开数量大的,再用小的平仓 if (sameQty < negaQty) { sameFlowClone= DataHelper.DeepCopyObject(negativeFlow); negaFlowClone = DataHelper.DeepCopyObject(sameFlow); } var trade = NewSwapTrade(sameFlowClone, client, asset, underlying, floatRate, clearingAgency); // 平仓 new SwapDealService(UserInfo).AuotoSwapUnwind(trade.id, negaFlowClone.TradingAmountAvg, negaFlowClone.TradingAmountFeeAvg, negaFlowClone.TradingAmountNetFeeAvg ?? 0, negaFlowClone.TradingAmountNetAvg ?? 0, negaFlowClone.OccurTime, negaFlowClone.TradingQty, negaFlowClone.TradingFeePending); return trade; } /// /// 流水新开仓 /// /// /// /// /// /// /// /// /// private trade NewSwapTrade(swap_flow_merge flowMergeFirst, Client client, AssetUnit asset, underlying_manager underlying, SwapFloatRate floatRate, string clearingAgency) { var swapTradeService = new SwapTradeService(UserInfo); var trade = swapTradeService.NewSwapTrade(flowMergeFirst, client, asset, underlying, floatRate, clearingAgency, LongShortStructType); flowMergeFirst.SwapTradeNo = trade.TradeNumber; flowMergeFirst.SwapTradeId = trade.id; 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, string clearingAgency, bool cashNeedAfter) { if (mergeList.Count == 1)//只有一条流水情况 { DealSingleFlow(mergeList, clientSwapPositionList, clientSwapTrades, client, asset, underlying, floatRate,clearingAgency); } else { DealDoubleFlow(mergeList, clientSwapPositionList, clientSwapTrades, client, asset, underlying, floatRate, 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(); if (flowList.Count==1)//只有一条流水情况 { AvgDealSingleFlow(firstFlow, 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, 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, clearingAgency); flowMergeMax.SwapTradeNo = trade.TradeNumber; } } else //只存在同向交易 { var trade = swapTradeService.NewSwapTrade(flowMergeMax, client, asset, underlying, floatRate, 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; } /// /// 处理多条流水情况 /// /// /// /// /// /// /// /// /// public void DealDoubleFlow(List mergeList, List clientSwapPositionList, List clientSwapTrades, Client client, AssetUnit asset, underlying_manager underlying, SwapFloatRate floatRate, 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, clearingAgency, true, cashNeedAfter); flowMergeFirst.SwapTradeNo = flowMergeFirstClone.SwapTradeNo; //再处理第二条流水的反向持仓 var lastTrade = DealDoubleFlowDetial(sameTrades, sameDirectionPositions, flowMergeLastClone, client, asset, underlying, floatRate, 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, clearingAgency, true,false); flowMergeLast.SwapTradeNo = flowMergeLastClone.SwapTradeNo; } else if (lastTrade==null) { swapTradeService.NewSwapTrade(flowMergeLastClone, client, asset, underlying, floatRate, clearingAgency); } } /// /// 加权平均处理当前有持仓,切两个方向多条流水情况 /// /// /// /// /// /// /// /// /// public void AvgDealSingleFlow(swap_flow_merge flow, 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 flowClone = DataHelper.DeepCopyObject(flow); // 同向新开 if (flow.BsType== firstPosi.PositionType) { NewSwapTrade(flowClone, client, asset, underlying, floatRate, clearingAgency); } else //反向先平仓,有剩余开仓 { AvgDealUnwind(flowClone, clientSwapTrades, swapPositions, client, asset, underlying, floatRate, 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 flowSame = flowList.Where(x => x.BsType == firstPosi.PositionType).First(); var flowNeg = flowList.Where(x => x.BsType != firstPosi.PositionType).First(); var negaBsType= flowNeg.BsType; var sameQty = posiQty + flowSame.TradingQty; var flowSameClone = DataHelper.DeepCopyObject(flowSame); var flowNegClone = DataHelper.DeepCopyObject(flowNeg); //先平反向 var trade= AvgDealUnwind(flowNegClone, clientSwapTrades, swapPositions, client, asset, underlying, floatRate, clearingAgency); var newFlowList = new List(); if (trade!=null) { var flowQty = flowSameClone.TradingQty; var currentPosiQty = flowNegClone.TradingQty; var newOpenQty = currentPosiQty - flowQty; var unwindQty = newOpenQty > 0 ? flowQty : currentPosiQty; var unwindFee = flowSameClone.TradingFeePending * unwindQty / currentPosiQty; unwindFee=Math.Round(unwindFee,ConsGlobal.MoneyRound,MidpointRounding.AwayFromZero); flowSameClone.TradingFeePending= flowSameClone.TradingFeePending - unwindFee; flowQty = flowQty - unwindQty; // 平仓 new SwapDealService(UserInfo).AuotoSwapUnwind(trade.id, flowSameClone.TradingAmountAvg, flowSameClone.TradingAmountFeeAvg, flowSameClone.TradingAmountNetFeeAvg ?? 0, flowSameClone.TradingAmountNetAvg ?? 0, flowSameClone.OccurTime, unwindQty, unwindFee); if (flowQty>0) { flowSameClone.TradingQty = flowQty; flowSameClone.TradingAmount = flowSameClone.TradingQty; NewSwapTrade(flowSameClone, client, asset, underlying, floatRate, clearingAgency); } } else { NewSwapTrade(flowSameClone, client, asset, underlying, floatRate, clearingAgency); } } /// /// 有持仓流水反向平仓 /// /// /// /// /// /// /// /// /// /// /// private trade AvgDealUnwind(swap_flow_merge swapFlow, List trades, List swapPositions, Client client, AssetUnit asset, underlying_manager underlying, SwapFloatRate floatRate, string clearingAgency) { var swapTradeService = new SwapTradeService(UserInfo); var flowQty = swapFlow.TradingQty; foreach (var posi in swapPositions) { if (swapFlow==null|| flowQty == 0) { break; } var td = trades.FirstOrDefault(p => p.id == posi.SwapTradeId); if (td != null) { var posiQty = posi.PosiQuantity; var newOpenQty = posiQty - flowQty; var unwindQty = newOpenQty > 0 ? flowQty : posiQty; var unwindFee = swapFlow.TradingFeePending* unwindQty / flowQty; unwindFee=Math.Round(unwindFee, ConsGlobal.MoneyRound, MidpointRounding.AwayFromZero); flowQty = flowQty - unwindQty; swapFlow.TradingFeePending = swapFlow.TradingFeePending - unwindFee; // 平仓 new SwapDealService(UserInfo).AuotoSwapUnwind(td.id, swapFlow.TradingAmountAvg, swapFlow.TradingAmountFeeAvg, swapFlow.TradingAmountNetFeeAvg ?? 0, swapFlow.TradingAmountNetAvg ?? 0, swapFlow.OccurTime, unwindQty, unwindFee); } } if (flowQty > 0) //平仓完有剩余流水, { swapFlow.TradingQty = flowQty; swapFlow.TradingAmount = swapFlow.TradingQty* swapFlow.TradingAmountAvg; return NewSwapTrade(swapFlow, client, asset, underlying, floatRate, clearingAgency); } return null; } /// /// 2条流水处理明细 /// /// /// /// /// /// /// /// /// /// /// private trade DealDoubleFlowDetial(List negTrades, List negDirectionPositions, swap_flow_merge flowMergeSameClone, Client client, AssetUnit asset, underlying_manager underlying, SwapFloatRate floatRate, 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, 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; var unwindFee= newQty>0? flowMerge.TradingFeePending: flowMerge.TradingFeePending* floatPosition.PosiQuantity / flowMerge.TradingQty; unwindFee = Math.Round(unwindFee, 4, MidpointRounding.AwayFromZero); // 全平 new SwapDealService(UserInfo).AuotoSwapUnwind(td.id, flowMerge.TradingAmountAvg, flowMerge.TradingAmountFeeAvg, flowMerge.TradingAmountNetFeeAvg ?? 0, flowMerge.TradingAmountNetAvg ?? 0, flowMerge.OccurTime, floatPosition.PosiQuantity, unwindFee); unwindTradeIds.Add(td.id); flowMerge.TradingAmount = newQtyAbs * flowMerge.ContractSize; if (newQty > 0) { flowMerge.TradingFeePending = 0; } else { flowMerge.TradingFeePending = flowMerge.TradingFeePending- unwindFee; } 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); } } }