using YLErp.Abstract.DataProviders; using YLErp.BLL.Calculation; using YLErp.Enums; using YLErp.Helpers; using YLErp.Model.Enum; using YLErp.QdpModule; namespace YLErp.BLL.MarginCalculation { public class ZhongLiangMarginCalculation : MarginCalculationBase { public static readonly ZhongLiangMarginCalculation Instance; static ZhongLiangMarginCalculation() { Instance = new ZhongLiangMarginCalculation(); } public ZhongLiangMarginCalculation() { } public override List RunMarginCalculation(RunMarginCalculationReq req) { if (req?.tradeList == null || !req.tradeList.Any()) { return new List(); } var calcTradeList = req.tradeList.ToList(); var resultMap = new Dictionary(); var helper = new RunMarginCalculationHelper(req, _underlyingDataProvider); helper.SetFieldsByTradeType(); helper.GetUpDownLimitPrices(out var upLimitPrices, out var downLimitPrices); helper.GetUpDownVolRateDic(out var tradeVolRateDicUp, out var tradeVolRateDicDown); var prices = new (string, IPriceProvider)[] { ("up", upLimitPrices), ("down", downLimitPrices), ("normal", req.PriceProvider) }; var volRates = new (int, Dictionary)[] { (0, null), (1, tradeVolRateDicUp), (2, tradeVolRateDicDown) }; foreach (var price in prices) { foreach (var itemDic in volRates) { var key = $"{price.Item1}_{itemDic.Item1}"; if (key == "normal_0") { continue; } var tradeRiskResult = CalculatorHelper.CalculateRisksForTrades( valueDate: req.settleDate, tradeList: calcTradeList, calcScenario: req.GetCalcScenario(), priceProvider: price.Item2, pricingRequest: QdpPricingRequest.PV_ONLY, addVolRateDic: itemDic.Item2, volType: req.volType, isUseTradeVol: PS.Config.IsTradeVol, preciseTimeMode: req.CalcMarginType != CalcMarginTypeEnum.EodMargin, isAddVolPercent: false); if (tradeRiskResult.Results == null || tradeRiskResult.Results.Count < 1) { continue; } foreach (var item in tradeRiskResult.Results) { var client = helper.GetClient(item.Trade.ClientId); var clientRatio = client?.Ratio ?? 1.0; var clientRatio1 = client?.Ratio1 ?? 1.0; var contains = resultMap.TryGetValue(item.Trade.id, out var tempTradeSpan); var pv = item.ValueResult.Pv; if (!helper.GetSpecialMargin(item.Trade, pv, out var value)) { if (req.CalcMarginType == CalcMarginTypeEnum.InitialMargin) { value = double.IsNaN(pv) ? 0 : pv * clientRatio1; } else { value = double.IsNaN(pv) ? 0 : pv * clientRatio; } } if (!contains) { resultMap[item.Trade.id] = tempTradeSpan = helper.CreateTradeSpan(item.Trade); if (item.Trade.TradeType == "雪球期权" && item.Trade.BuySell == "买入") { resultMap[item.Trade.id].IsSingleMargin = true; resultMap[item.Trade.id].Comment = "雪球买入"; } } switch (key) { case "up_1": tempTradeSpan.Spv1 = value; break; case "up_0": tempTradeSpan.Spv2 = value; break; case "up_2": tempTradeSpan.Spv3 = value; break; case "normal_1": tempTradeSpan.Spv4 = value; break; case "normal_2": tempTradeSpan.Spv5 = value; break; case "down_1": tempTradeSpan.Spv6 = value; break; case "down_0": tempTradeSpan.Spv7 = value; break; case "down_2": tempTradeSpan.Spv8 = value; break; } if (contains) { tempTradeSpan.SetWorstCastClientPayable(); if (client != null) { var value2 = (double)tempTradeSpan.WorstCastClientPayable; if (client.MarginOptionType == (int)MarginOptionEnum.单向追保) { value2 = item.Trade.BuySell == "卖出" ? 0 : Math.Max(value2, 0); } else if (client.MarginOptionType == (int)MarginOptionEnum.对手方单向追保 || client.MarginOptionType == (int)MarginOptionEnum.其他) { value2 = 0; } else if (client.MarginOptionType == (int)MarginOptionEnum.双向追保) { value2 = Math.Max(value2, 0); } tempTradeSpan.WorstCastClientPayable = value2; } } } } } return resultMap.Values.ToList(); } public override List CalcClientMargin(CalcClientMarginReq req) { var clientSpanNews = new List(); var runMargin = new RunMarginCalculationReq(req.UserInfo); runMargin.settleDate = req.settleDate; var helper = new RunMarginCalculationHelper(runMargin, _underlyingDataProvider); 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 underlyingIdList = tradeList.Select(t => t.UnderlyingId).ToList(); var underlyingList = db.underlying_manager.AsNoTracking().Where(t => underlyingIdList.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) { var varietyGroups = clientGroup.GroupBy(t => t.trade.UnderlyingId).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), Spv8 = t.Sum(g => g.tradeSpan.Spv8) * (-1), OptId = req.userId, OptName = req.userName, OptDate = DateTime.Now, SpanType = req.SpanType }).ToList(); foreach (var item in varietyGroups) { item.WorstCastClientPayable = Math.Min(Math.Min(Math.Min(Math.Min(Math.Min(Math.Min(Math.Min(item.Spv1 ?? 0, item.Spv2 ?? 0), item.Spv3 ?? 0), item.Spv4 ?? 0), item.Spv5 ?? 0), item.Spv6 ?? 0), item.Spv7 ?? 0), item.Spv8 ?? 0); #region 更新tradeSpan,使得每笔交易的持仓预付金和客户预付金计算用的Spv组保持一致 var tradeIdList = clientGroup.Select(x => x.tradeSpan.TradeId); var tradeSpansUpdate = db.trade_span.Where(x => tradeIdList.Contains(x.TradeId) && x.ClientId == item.ClientId && x.ValueDate == req.settleDate).ToList(); var tradeSpansReq = req.tradeSpans.Where(x => tradeIdList.Contains(x.TradeId) && x.ClientId == item.ClientId && x.ValueDate == req.settleDate).ToList(); if (item.WorstCastClientPayable == item.Spv1) { tradeSpansUpdate.ForEach(x => x.WorstCastClientPayable = x.Spv1); tradeSpansReq.ForEach(x => x.WorstCastClientPayable = x.Spv1); } else if (item.WorstCastClientPayable == item.Spv2) { tradeSpansUpdate.ForEach(x => x.WorstCastClientPayable = x.Spv2); tradeSpansReq.ForEach(x => x.WorstCastClientPayable = x.Spv2); } else if (item.WorstCastClientPayable == item.Spv3) { tradeSpansUpdate.ForEach(x => x.WorstCastClientPayable = x.Spv3); tradeSpansReq.ForEach(x => x.WorstCastClientPayable = x.Spv3); } else if (item.WorstCastClientPayable == item.Spv4) { tradeSpansUpdate.ForEach(x => x.WorstCastClientPayable = x.Spv4); tradeSpansReq.ForEach(x => x.WorstCastClientPayable = x.Spv4); } else if (item.WorstCastClientPayable == item.Spv5) { tradeSpansUpdate.ForEach(x => x.WorstCastClientPayable = x.Spv5); tradeSpansReq.ForEach(x => x.WorstCastClientPayable = x.Spv5); } else if (item.WorstCastClientPayable == item.Spv6) { tradeSpansUpdate.ForEach(x => x.WorstCastClientPayable = x.Spv6); tradeSpansReq.ForEach(x => x.WorstCastClientPayable = x.Spv6); } else if (item.WorstCastClientPayable == item.Spv7) { tradeSpansUpdate.ForEach(x => x.WorstCastClientPayable = x.Spv7); tradeSpansReq.ForEach(x => x.WorstCastClientPayable = x.Spv7); } else { tradeSpansUpdate.ForEach(x => x.WorstCastClientPayable = x.Spv8); tradeSpansReq.ForEach(x => x.WorstCastClientPayable = x.Spv8); } #endregion } var clientSpan = new ClientSpan { ClientId = clientGroup.Key, ValueDate = req.settleDate, Spv1 = varietyGroups.Sum(g => g.Spv1), Spv2 = varietyGroups.Sum(g => g.Spv2), Spv3 = varietyGroups.Sum(g => g.Spv3), Spv4 = varietyGroups.Sum(g => g.Spv4), Spv5 = varietyGroups.Sum(g => g.Spv5), Spv6 = varietyGroups.Sum(g => g.Spv6), Spv7 = varietyGroups.Sum(g => g.Spv7), Spv8 = varietyGroups.Sum(g => g.Spv8), //负数代表客户应缴预付金,正数代表客户应收预付金 WorstCastClientPayable = varietyGroups.Sum(g => g.WorstCastClientPayable), 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 }; if (!HasTwoSideMargin(clientGroup.Key) && varietyGroups.Sum(g => g.WorstCastClientPayable) > 0) { clientSpan.WorstCastClientPayable = 0; foreach (var item in varietyGroups) { var tradeIdList = clientGroup.Select(x => x.tradeSpan.TradeId); var tradeSpansUpdate = db.trade_span.Where(x => tradeIdList.Contains(x.TradeId) && x.ClientId == item.ClientId && x.ValueDate == req.settleDate).ToList(); var tradeSpansReq = req.tradeSpans.Where(x => tradeIdList.Contains(x.TradeId) && x.ClientId == item.ClientId && x.ValueDate == req.settleDate).ToList(); tradeSpansUpdate.ForEach(x => x.WorstCastClientPayable = 0); tradeSpansReq.ForEach(x => x.WorstCastClientPayable = 0); } } 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) { var trade = req.trade; using (var db = new YLContext()) { if (trade.TradeType == "结构化交易") { trade.SubTrades = db.trade.Where(x => x.ParentTradeId == trade.id).ToList(); } } var tradeMargin = RunMarginCalculation(req.GetRunMarginCalculationReq()); if (null != tradeMargin && tradeMargin.FirstOrDefault() != null) { var margin = tradeMargin.FirstOrDefault().WorstCastClientPayable ?? 0.0; return margin; } return 0.0; } } }