using YLErp.BLL.Calculation; using YLErp.Helpers; using YLErp.Modules; using YLErp.Modules.PricingModule; using YLErp.QdpModule; namespace YLErp.BLL.MarginCalculation { /// /// 招证 /// public class ZhaoZhengMarginCalculation : MarginCalculationBase { public static readonly ZhaoZhengMarginCalculation Instance; static ZhaoZhengMarginCalculation() { Instance = new ZhaoZhengMarginCalculation(); } protected ZhaoZhengMarginCalculation() { } public override List RunMarginCalculation(RunMarginCalculationReq req) { var result = new List(); if (req.CalcMarginType == Enums.CalcMarginTypeEnum.InitialMargin) { var swapList = req.tradeList.Where(O => O.TradeType == "收益互换"); RunMarginCalculationHelper helper = null; foreach (var item in swapList) { helper ??= new RunMarginCalculationHelper(req, _underlyingDataProvider); var clientRatio = helper.GetClient(item)?.Ratio1 ?? 1.0; var tradeSpan = new trade_span(); var ratio = item.trade_swap.GetSwapRate - item.trade_swap.PaySwapRate; logger.Info($"互换初始预付金:tradeId:{item.id} 名义本金 * (收取预付金比例 - 支付预付金比例) * 客户初始预付金系数"); logger.Info($"互换初始预付金:tradeId:{item.id} {item.OriginalStockEqvNotional} * ({item.trade_swap.GetSwapRate} - {item.trade_swap.PaySwapRate}) * {clientRatio}"); tradeSpan.SetAllSpvAndWorst((item.OriginalStockEqvNotional ?? 0) * ratio * clientRatio); result.Add(tradeSpan); } var optionList = req.tradeList.Where(O => O.TradeType != "收益互换"); req.tradeList = optionList.ToList(); result.AddRange(calcPositionMargin(req)); return result; } else { return calcPositionMargin(req); } } private List calcPositionMargin(RunMarginCalculationReq req) { var helper = new RunMarginCalculationHelper(req, _underlyingDataProvider); var resultMap = new Dictionary(); helper.SetFieldsByTradeType(); var priceArr = helper.GetUpDownLimitPrices(6); //var prices = priceArr.Select(O=> new (string key, IPriceProvider priceProvider){ ("up", upLimitPrices), ("down", downLimitPrices), ("normal", req.PriceProvider) }; var index = 0; var loops = priceArr.Select(n => { index++; return new { pricekey = $"P{index}", priceProvider = n, }; }).ToArray(); foreach (var loop in loops) { var calcReq = helper.GetCalculateRisksForTradesReq(priceProvider: loop.priceProvider, addVolRateDic: null, overrideVols: null, pricingRequest: QdpPricingRequest.BASIC_PRICING); var tradeRiskResult = CalculatorHelper.CalculateRisksForTrades(calcReq); if (tradeRiskResult.Results == null || tradeRiskResult.Results.Count < 1) { continue; } var key = $"{loop.pricekey}"; foreach (var item in tradeRiskResult.Results) { var contains = resultMap.TryGetValue(item.Trade.id, out var tempTradeSpan); if (!contains) { resultMap[item.Trade.id] = tempTradeSpan = helper.CreateTradeSpan(item.Trade); } switch (key) { case "P1": logger.Info($"期权预付金:tradeId:{item.Trade.id} 价格1 = {loop.priceProvider.GetPrice(item.Trade.UnderlyingCode)} Pv = {item.ValueResult.Pv}"); tempTradeSpan.Spv1 = item.ValueResult.Pv; break; case "P2": logger.Info($"期权预付金:tradeId:{item.Trade.id} 价格2 = {loop.priceProvider.GetPrice(item.Trade.UnderlyingCode)} Pv = {item.ValueResult.Pv}"); tempTradeSpan.Spv2 = item.ValueResult.Pv; break; case "P3": logger.Info($"期权预付金:tradeId:{item.Trade.id} 价格3 = {loop.priceProvider.GetPrice(item.Trade.UnderlyingCode)} Pv = {item.ValueResult.Pv}"); tempTradeSpan.Spv3 = item.ValueResult.Pv; break; case "P4"://这里是现价计算的,所以同时保存一下Delta tempTradeSpan.Spv4 = item.ValueResult.Pv; var marginRatio = helper.GetMarginParamProvider().GetMarginRate(item.Trade.UnderlyingCode) ?? 1; tempTradeSpan.DeltaMargin = item.ValueResult.DeltaCash * marginRatio; logger.Info($"期权预付金:tradeId:{item.Trade.id} 价格4 = {loop.priceProvider.GetPrice(item.Trade.UnderlyingCode)} Pv = {item.ValueResult.Pv} Delta = {item.ValueResult.Delta} DeltaCash = {item.ValueResult.DeltaCash} 品种最低预付金率 = Delta * Price * 品种最低预付金比例 = {marginRatio} DeltaMargin = {tempTradeSpan.DeltaMargin}"); break; case "P5": logger.Info($"期权预付金:tradeId:{item.Trade.id} 价格5 = {loop.priceProvider.GetPrice(item.Trade.UnderlyingCode)} Pv = {item.ValueResult.Pv}"); tempTradeSpan.Spv5 = item.ValueResult.Pv; break; case "P6": logger.Info($"期权预付金:tradeId:{item.Trade.id} 价格6 = {loop.priceProvider.GetPrice(item.Trade.UnderlyingCode)} Pv = {item.ValueResult.Pv}"); tempTradeSpan.Spv6 = item.ValueResult.Pv; break; case "P7": logger.Info($"期权预付金:tradeId:{item.Trade.id} 价格7 = {loop.priceProvider.GetPrice(item.Trade.UnderlyingCode)} Pv = {item.ValueResult.Pv}"); tempTradeSpan.Spv7 = item.ValueResult.Pv; break; } if (contains) { tempTradeSpan.SetWorstCastClientPayable(); if (tempTradeSpan.WorstCastClientPayable > 0) { var clientInfo = helper.GetClient(item.Trade); var clientRatio = (req.CalcMarginType == Enums.CalcMarginTypeEnum.InitialMargin ? clientInfo?.Ratio1 : clientInfo?.Ratio) ?? 1.0; if (key == "P7") { var ss = Math.Max(tempTradeSpan.WorstCastClientPayable ?? 0, tempTradeSpan.DeltaMargin ?? 0) * clientRatio; logger.Info($"期权预付金:tradeId:{item.Trade.id} 预付金 = MaxPv > 0 Max(MaxPv, DeltaMargin) * 客户预付金率"); logger.Info($"期权预付金:tradeId:{item.Trade.id} {ss} = {tempTradeSpan.WorstCastClientPayable} > 0 Max({tempTradeSpan.WorstCastClientPayable}, {tempTradeSpan.DeltaMargin}) * {clientRatio}"); } tempTradeSpan.WorstCastClientPayable = Math.Max(tempTradeSpan.WorstCastClientPayable ?? 0, tempTradeSpan.DeltaMargin ?? 0) * clientRatio; } else { if (key == "P7") { logger.Info($"期权预付金:tradeId:{item.Trade.id} 预付金 = MaxPv <= 0"); } tempTradeSpan.WorstCastClientPayable = 0; } } } } return resultMap.Values.ToList(); } public override List CalcClientMargin(CalcClientMarginReq req) { var clientSpanNews = new List(); using (var db = new YLContext()) { var tradeIds = req.tradeSpans.Select(t => t.TradeId).ToList(); var tradeList = db.trade.AsNoTracking().Where(t => tradeIds.Contains(t.id)).ToList(); var tradeSpanInfo = (from tradeSpan in req.tradeSpans join trade in tradeList on tradeSpan.TradeId equals trade.id where tradeSpan.ValueDate == req.settleDate select new { trade, tradeSpan }).ToList(); var clientGroups = tradeSpanInfo.GroupBy(t => t.trade.ClientId); foreach (var clientGroup in clientGroups) { // 手动维护的预付金VarietyId是null,合计的时候需要排除掉,最后再加进来 var varietyGroup = clientGroup.Where(O => O.tradeSpan.VarietyId > 0).GroupBy(t => t.tradeSpan.VarietyId ?? 0).Select(t => new ClientSpan { VarietyId = t.Key, ClientId = clientGroup.Key, ValueDate = req.settleDate, Spv1 = t.Sum(g => g.tradeSpan.Spv1) * (-1), Spv2 = t.Sum(g => g.tradeSpan.Spv2) * (-1), Spv3 = t.Sum(g => g.tradeSpan.Spv3) * (-1), Spv4 = t.Sum(g => g.tradeSpan.Spv4) * (-1), Spv5 = t.Sum(g => g.tradeSpan.Spv5) * (-1), Spv6 = t.Sum(g => g.tradeSpan.Spv6) * (-1), Spv7 = t.Sum(g => g.tradeSpan.Spv7) * (-1), DeltaMargin = -Math.Abs(t.Sum(g => g.tradeSpan.DeltaMargin ?? 0)), PositionWinLoss = t.Sum(g => g.tradeSpan.PositionWinLoss) * (-1), OptId = req.userId, OptName = req.userName, OptDate = DateTime.Now, SpanType = req.SpanType }).ToList(); foreach (var varietyInfo in varietyGroup) { var tradeSpanIds = clientGroup.Where(O => O.tradeSpan.VarietyId == varietyInfo.VarietyId).Select(x => x.tradeSpan.id); var tradeSpansUpdate = db.trade_span.Where(x => tradeSpanIds.Contains(x.id)).ToList(); var tradeSpansReq = req.tradeSpans.Where(x => tradeSpanIds.Contains(x.id)).ToList(); varietyInfo.SetWorstCastClientPayableMin(); if (varietyInfo.WorstCastClientPayable >= 0 || varietyInfo.WorstCastClientPayable.Value < (varietyInfo.DeltaMargin ?? 0)) { if (varietyInfo.WorstCastClientPayable == varietyInfo.Spv1) { tradeSpansUpdate.ForEach(x => x.WorstCastClientPayable = x.Spv1); tradeSpansReq.ForEach(x => x.WorstCastClientPayable = x.Spv1); } else if (varietyInfo.WorstCastClientPayable == varietyInfo.Spv2) { tradeSpansUpdate.ForEach(x => x.WorstCastClientPayable = x.Spv2); tradeSpansReq.ForEach(x => x.WorstCastClientPayable = x.Spv2); } else if (varietyInfo.WorstCastClientPayable == varietyInfo.Spv3) { tradeSpansUpdate.ForEach(x => x.WorstCastClientPayable = x.Spv3); tradeSpansReq.ForEach(x => x.WorstCastClientPayable = x.Spv3); } else if (varietyInfo.WorstCastClientPayable == varietyInfo.Spv4) { tradeSpansUpdate.ForEach(x => x.WorstCastClientPayable = x.Spv4); tradeSpansReq.ForEach(x => x.WorstCastClientPayable = x.Spv4); } else if (varietyInfo.WorstCastClientPayable == varietyInfo.Spv5) { tradeSpansUpdate.ForEach(x => x.WorstCastClientPayable = x.Spv5); tradeSpansReq.ForEach(x => x.WorstCastClientPayable = x.Spv5); } else if (varietyInfo.WorstCastClientPayable == varietyInfo.Spv6) { tradeSpansUpdate.ForEach(x => x.WorstCastClientPayable = x.Spv6); tradeSpansReq.ForEach(x => x.WorstCastClientPayable = x.Spv6); } else { tradeSpansUpdate.ForEach(x => x.WorstCastClientPayable = x.Spv7); tradeSpansReq.ForEach(x => x.WorstCastClientPayable = x.Spv7); } } else { varietyInfo.WorstCastClientPayable = varietyInfo.DeltaMargin ?? 0; tradeSpansUpdate.ForEach(x => x.WorstCastClientPayable = x.DeltaMargin); tradeSpansReq.ForEach(x => x.WorstCastClientPayable = x.DeltaMargin); } if (varietyInfo.WorstCastClientPayable < 0) { var clientRatio = 1.0; var clientLevelId = DataCacheProvider.GetClientDataSource().GetData(varietyInfo.ClientId).LevelId; if (clientLevelId > 0) { clientRatio = DataCacheProvider.GetClientLevelDataSource().GetData(clientLevelId ?? 0).Ratio ?? 1.0; } varietyInfo.WorstCastClientPayable *= clientRatio; } else { varietyInfo.WorstCastClientPayable = varietyInfo.TwoSideMargin = 0; } } var clientSpan = new ClientSpan { ClientId = clientGroup.Key, ValueDate = req.settleDate, Spv1 = varietyGroup.Sum(g => g.Spv1), Spv2 = varietyGroup.Sum(g => g.Spv2), Spv3 = varietyGroup.Sum(g => g.Spv3), Spv4 = varietyGroup.Sum(g => g.Spv4), Spv5 = varietyGroup.Sum(g => g.Spv5), Spv6 = varietyGroup.Sum(g => g.Spv6), Spv7 = varietyGroup.Sum(g => g.Spv7), DeltaMargin = varietyGroup.Sum(g => g.DeltaMargin), //负数代表客户应缴预付金,正数代表客户应收预付金 WorstCastClientPayable = varietyGroup.Sum(g => g.WorstCastClientPayable), MySideMargin = varietyGroup.Sum(g => g.WorstCastClientPayable), TwoSideMargin = varietyGroup.Sum(g => g.TwoSideMargin), OptId = req.userId, OptName = req.userName, OptDate = DateTime.Now, SpanType = req.SpanType, AdditionalWorstCastClientPayable = req.clientAdditionalMarginDic != null && req.clientAdditionalMarginDic.TryGetValue(clientGroup.Key, out var dd) ? dd : 0 }; //单笔预付金算法的交易不参与品种轧差; var singleMarginTrade = clientGroup.Where(O => O.trade.MarginType != DBModels.Enums.MarginTypeEnum.DEFAULT).Select(O => O.tradeSpan); clientSpan.Spv1 += singleMarginTrade.Sum(O => O.Spv1 * (-1)); clientSpan.Spv2 += singleMarginTrade.Sum(O => O.Spv2 * (-1)); clientSpan.Spv3 += singleMarginTrade.Sum(O => O.Spv3 * (-1)); clientSpan.Spv4 += singleMarginTrade.Sum(O => O.Spv4 * (-1)); clientSpan.Spv5 += singleMarginTrade.Sum(O => O.Spv5 * (-1)); clientSpan.Spv6 += singleMarginTrade.Sum(O => O.Spv6 * (-1)); clientSpan.Spv7 += singleMarginTrade.Sum(O => O.Spv7 * (-1)); clientSpan.DeltaMargin += singleMarginTrade.Sum(O => O.DeltaMargin * (-1)); clientSpan.WorstCastClientPayable += singleMarginTrade.Sum(O => O.WorstCastClientPayable * (-1)); clientSpan.MySideMargin += singleMarginTrade.Sum(O => O.WorstCastClientPayable * (-1)); clientSpan.TwoSideMargin += singleMarginTrade.Sum(O => O.TwoSideMargin); clientSpan.MySideMargin = Math.Min(clientSpan.MySideMargin ?? 0, 0); //交易员不支付预付金 clientSpan.WorstCastClientPayable = Math.Min(clientSpan.WorstCastClientPayable ?? 0, 0); //手动维护的预付金,不参与以上算法 var maMargin = clientGroup.Where(O => O.tradeSpan.VarietyId == null).Sum(t => t.tradeSpan.WorstCastClientPayable * (-1)); clientSpan.WorstCastClientPayable += maMargin; clientSpan.MySideMargin += maMargin; clientSpanNews.Add(clientSpan); } //span类型为实时删除所有实时计算的交易的预付金信息 if (req.SpanType == ClientSpan.SpanType_RealTime) { if (req.RefreshClientIds != null) { db.BulkDelete($"{nameof(ClientSpan.ClientId)} in @ids", new { ids = req.RefreshClientIds }); } else { db.BulkDelete($"{nameof(ClientSpan.SpanType)}=@SpanType", new { req.SpanType }); } } else { if (req.ClientIds != null) { var sql = $"{nameof(ClientSpan.ClientId)} in @ids and {nameof(ClientSpan.ValueDate)}='{req.settleDate.ToSqlDate()}' and {nameof(ClientSpan.SpanType)}={req.SpanType} and {nameof(ClientSpan.ModifiedFlag)}=0"; db.BulkDelete(sql, new { ids = req.ClientIds }); } else { var sql = $"{nameof(ClientSpan.ValueDate)}='{req.settleDate.ToSqlDate()}' and {nameof(ClientSpan.SpanType)}={req.SpanType} and {nameof(ClientSpan.ModifiedFlag)}=0"; db.BulkDelete(sql); } var clientSpanOldsWithFlag = db.client_span.Where(t => t.ValueDate == req.settleDate && t.SpanType == req.SpanType && t.ModifiedFlag) .Select(n => new { n.ValueDate, n.ClientId }).ToList(); //筛选出可以修改的clientSpan clientSpanNews = clientSpanNews.Where(c => !clientSpanOldsWithFlag.Any(t => t.ValueDate == c.ValueDate && t.ClientId == c.ClientId)).ToList(); } if (clientSpanNews.Count > 0) { MySqlBulkExtensions.BulkInsert(db, clientSpanNews); } db.SaveChanges(); } return req.tradeSpans; } public override double GetTradeMargin(GetTradeMarginReq req) { using var db = new YLContext(); if (req.trade.TradeType == "结构化交易") { req.trade.SubTrades = db.trade.Where(x => x.ParentTradeId == req.realTradeId).ToList(); } var marginReq = req.GetRunMarginCalculationReq(); if (req.trade.IsGroup == 1) { marginReq.tradeList = db.trade.Where(x => x.ParentTradeId == req.realTradeId).ToList(); } if (req.trade.ParentTradeId > 0) { marginReq.tradeList.AddRange(db.trade.Where(x => x.ParentTradeId == req.trade.ParentTradeId && x.id != req.realTradeId).ToList()); } var tradeMargin = RunMarginCalculation(marginReq); if (null != tradeMargin) { double margin; if (req.trade.IsGroup == 1) { margin = (double)tradeMargin.Sum(x => x.WorstCastClientPayable ?? 0); } else if (req.trade.ParentTradeId > 0) { PriceCalcService.GroupSpansCalc(tradeMargin); margin = (double)(tradeMargin.FirstOrDefault(O => O.TradeId == req.trade.id)?.WorstCastClientPayable ?? 0); } else { margin = (double)(tradeMargin.FirstOrDefault()?.WorstCastClientPayable ?? 0); } return margin; } return 0.0; } } }