using YLErp.BLL.Calculation; using YLErp.Enums; using YLErp.Helpers; using YLErp.Modules.DataProviderModule; using YLErp.Modules.MarginModule; using YLErp.QdpModule; namespace YLErp.BLL.MarginCalculation { /// /// 瑞达预付金计算 /// public class RDMarginCalculation : MarginCalculationBase { // 定义一个静态变量来保存类的实例 public static readonly RDMarginCalculation Instance; static RDMarginCalculation() { Instance = new RDMarginCalculation(); } // 定义私有构造函数,使外界不能创建该类实例 protected RDMarginCalculation() { } public override List RunMarginCalculation(RunMarginCalculationReq req) { if (req?.tradeList == null || !req.tradeList.Any()) { return new List(); } var resultMap = new Dictionary(); var helper = new RunMarginCalculationHelper(req, _underlyingDataProvider); helper.SetFieldsByTradeType(); var mpProvider = helper.GetMarginParamProvider(); var tradeVolatilityRateDic = new Dictionary(); var calcTradeList = req.tradeList.ToList(); var twoMarginClietIds = new HashSet(); foreach (var t in req.tradeList) { var volRate = 0d; var price = req.PriceProvider.GetPrice(t.UnderlyingCode); if (t.OptionType == "看涨" && price / t.ActualStrike < 0.9 || t.OptionType == "看跌" && price / t.ActualStrike > 1.1) { volRate = 0.2; } else { volRate = mpProvider.GetVolatilityRate(t.UnderlyingCode) ?? 0; } if (volRate > 0) { tradeVolatilityRateDic[t.id] = volRate; } //if (t.id > 0 && t.TradeType != "掉期" // && !twoMarginClietIds.Contains(t.ClientId) // && !twoMarginClietIds.Contains(~t.ClientId)) //{ // var client = helper.GetClient(t.ClientId); // if (client != null && client.MarginOptionType == (int)MarginOptionEnum.双向追保) // { // twoMarginClietIds.Add(t.ClientId); // } // else // { // twoMarginClietIds.Add(~t.ClientId); // } //} } var upLimitPrices = new ManualPriceProvider(); var upLimitPricesTwoThirds = new ManualPriceProvider(); var upLimitPricesOneThird = new ManualPriceProvider(); var downLimitPrices = new ManualPriceProvider(); var downLimitPricesTwoThirds = new ManualPriceProvider(); var downLimitPricesOneThird = new ManualPriceProvider(); var normalLimitPrices = new ManualPriceProvider(); //根据涨跌幅限制以及当日结算价计算涨停价以及跌停价 foreach (var t in req.tradeList) { if (upLimitPrices.Contains(t.UnderlyingCode)) { continue; } if (!mpProvider.TryGetUpdownLimit(t.UnderlyingCode, out var updownLimit, out var isFixed)) { updownLimit = 0.05; } var price = req.PriceProvider.GetPrice(t.UnderlyingCode); if (isFixed) { upLimitPrices.SetPrice(t.UnderlyingCode, price + updownLimit); upLimitPricesTwoThirds.SetPrice(t.UnderlyingCode, price + updownLimit * 2.0 / 3.0); upLimitPricesOneThird.SetPrice(t.UnderlyingCode, price + updownLimit / 3.0); downLimitPrices.SetPrice(t.UnderlyingCode, price - updownLimit); downLimitPricesTwoThirds.SetPrice(t.UnderlyingCode, price - updownLimit * 2.0 / 3.0); downLimitPricesOneThird.SetPrice(t.UnderlyingCode, price - updownLimit / 3.0); } else { upLimitPrices.SetPrice(t.UnderlyingCode, price * (price > 0 ? (1 + updownLimit) : (1 - updownLimit))); upLimitPricesTwoThirds.SetPrice(t.UnderlyingCode, price * (price > 0 ? (1 + updownLimit * 2.0 / 3.0) : (1 - updownLimit * 2.0 / 3.0))); upLimitPricesOneThird.SetPrice(t.UnderlyingCode, price * (price > 0 ? (1 + updownLimit / 3.0) : (1 - updownLimit / 3.0))); downLimitPrices.SetPrice(t.UnderlyingCode, price * (price > 0 ? (1 - updownLimit) : (1 + updownLimit))); downLimitPricesTwoThirds.SetPrice(t.UnderlyingCode, price * (price > 0 ? (1 - updownLimit * 2.0 / 3.0) : (1 + updownLimit * 2.0 / 3.0))); downLimitPricesOneThird.SetPrice(t.UnderlyingCode, price * (price > 0 ? (1 - updownLimit / 3.0) : (1 + updownLimit / 3.0))); } //Normal normalLimitPrices.SetPrice(t.UnderlyingCode, price); } var prices = new[] { ("up", upLimitPrices),("upTwoThirds", upLimitPricesTwoThirds),("upOneThird", upLimitPricesOneThird), ("down", downLimitPrices),("downTwoThirds", downLimitPricesTwoThirds),("downOneThird", downLimitPricesOneThird), ("normal", normalLimitPrices) }; foreach (var price in prices) { foreach (var addVolRateDic in new[] { null, tradeVolatilityRateDic }) { var key = $"{price.Item1}_{(addVolRateDic == null ? 0 : 1)}"; var tradeRiskResult = CalculatorHelper.CalculateRisksForTrades( valueDate: req.settleDate, tradeList: calcTradeList, calcScenario: req.GetCalcScenario(), priceProvider: price.Item2, pricingRequest: QdpPricingRequest.BASIC_PRICING, addVolRateDic: addVolRateDic, volType: req.volType, isUseTradeVol: PS.Config.IsTradeVol, preciseTimeMode: req.CalcMarginType != CalcMarginTypeEnum.EodMargin, isAddVolPercent: false); if (tradeRiskResult.Results == null || !tradeRiskResult.Results.Any()) { continue; } foreach (var item in tradeRiskResult.Results) { if (item.Trade.StructureType == "掉期") { continue; } var pv = double.IsNaN(item.ValueResult.Pv) ? 0 : item.ValueResult.Pv; if (!helper.GetSpecialMargin(item.Trade, pv, out var value)) { var clientRatio = helper.GetClient(item.Trade)?.Ratio ?? 1.0; value = pv * clientRatio; } var contains = resultMap.TryGetValue(item.Trade.id, out var tempTradeSpan); if (!contains) { resultMap[item.Trade.id] = tempTradeSpan = helper.CreateTradeSpan(item.Trade); } switch (key) { case "up_1": tempTradeSpan.Spv1 = value; break; case "upTwoThirds_1": tempTradeSpan.Spv2 = value; break; case "upOneThird_1": tempTradeSpan.Spv3 = value; break; case "normal_1": tempTradeSpan.Spv4 = value; break; case "down_1": tempTradeSpan.Spv5 = value; break; case "downTwoThirds_1": tempTradeSpan.Spv6 = value; break; case "downOneThird_1": tempTradeSpan.Spv7 = value; break; case "normal_0": tempTradeSpan.Delta = item.ValueResult.Delta; break; } if (contains) { tempTradeSpan.SetWorstCastClientPayable(); } resultMap[item.Trade.id].UnderlyingPrice = req.PriceProvider.GetPrice(item.Trade.UnderlyingCode); } } if (price.Item1 == "normal") { var lastSettleDate = QdpCalendarHelper.GetNonHolidayDefore(req.settleDate.AddDays(-1)); var tradeRiskResultNormal = CalculatorHelper.CalculateRisksForTrades( valueDate: lastSettleDate, tradeList: calcTradeList, calcScenario: req.GetCalcScenario(), priceProvider: price.Item2, pricingRequest: QdpPricingRequest.BASIC_PRICING, addVolRateDic: null, volType: req.volType, isUseTradeVol: PS.Config.IsTradeVol, preciseTimeMode: req.CalcMarginType != CalcMarginTypeEnum.EodMargin, isAddVolPercent: false); if (tradeRiskResultNormal.Results != null && tradeRiskResultNormal.Results.Count > 0) { foreach (var item in tradeRiskResultNormal.Results) { if (item.Trade.StructureType != "掉期") { var pv = double.IsNaN(item.ValueResult.Pv) ? 0 : item.ValueResult.Pv; if (!helper.GetSpecialMargin(item.Trade, pv, out var value)) { var clientRatio = helper.GetClient(item.Trade)?.Ratio ?? 1.0; value = pv * clientRatio; } resultMap.TryGetValue(item.Trade.id, out var tempTradeSpan); tempTradeSpan.Spv = value; } } } } } //foreach (var item in resultMap.Values) //{ // if (item.ClientId > 0 && twoMarginClietIds.Contains(item.ClientId.Value)) // { // var clone = item.Clone(); // clone.ReverseSpv().SetWorstCastClientPayable(); // item.WorstCastClientPayable += clone.WorstCastClientPayable ?? 0; // item.SetAllSpv(item.WorstCastClientPayable ?? 0); // } //} var tradeListDQ = req.tradeList.Where(x => x.StructureType == "掉期").ToList(); tradeListDQ.ForEach(x => { var contains = resultMap.TryGetValue(x.id, out var tempTradeSpan); if (!contains) { resultMap[x.id] = tempTradeSpan = helper.CreateTradeSpan(x); } var eodPrice = req.PriceProvider.GetPrice(x.UnderlyingCode); if (x.SpotPrice != null && x.SpotPrice != 0 && (Math.Abs(eodPrice / x.SpotPrice.Value) > 1.05 && x.BuySell == "买入" || Math.Abs(eodPrice / x.SpotPrice.Value) < 0.95 && x.BuySell == "卖出")) { tempTradeSpan.WorstCastClientPayable = tempTradeSpan.DeltaMargin = (0.15 + Math.Abs((eodPrice / x.SpotPrice.Value) - 1)) * eodPrice * x.TradeAmount; } else { tempTradeSpan.WorstCastClientPayable = tempTradeSpan.DeltaMargin = 0.15 * eodPrice * x.TradeAmount; } }); return resultMap.Values.Where(n => n.TradeId >= 0).ToList(); } public override List CalcClientMargin(CalcClientMarginReq req) { var clientSpanNews = new List(); using (var db = new YLContext()) { var underlyingCodes = req.tradeSpans.Select(t => t.UnderlyingCode).ToHashSet(); var mpProvider = new MarginParamProvider(req.UserInfo, req.settleDate).Initialize(underlyingCodes, MarginParamTypeEnum.MarginRate); //删除 if (req.tradeSpans != null && req.tradeSpans.Count > 0) { var tradeIds = req.tradeSpans.Select(t => t.TradeId).ToList(); var tradeList = db.trade.AsNoTracking().Where(t => tradeIds.Contains(t.id)).ToList(); //var clientIds = req.tradeSpans.Select(t => t.ClientId).Distinct().ToList(); //var clientList = db.client.Where(x => clientIds.Contains(x.id)).ToList(); var tradeSpanInfos = (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 tradeSpanInfosWithoutDQ = tradeSpanInfos.Where(x => x.trade.StructureType != "掉期"); var tradeSpanInfosDQ = tradeSpanInfos.Where(x => x.trade.StructureType == "掉期"); var clientGroups = tradeSpanInfosWithoutDQ.GroupBy(t => t.trade.ClientId); foreach (var clientGroup in clientGroups) { #region Span Margin Method var underlyingGroup = clientGroup.GroupBy(t => t.trade.UnderlyingId).Select(t => new ClientSpan { UnderlyingId = 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), OptId = req.userId, OptName = req.userName, OptDate = DateTime.Now, SpanType = req.SpanType }).ToList(); foreach (var item in underlyingGroup) { item.WorstCastClientPayable = 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); #region 更新tradeSpan,使得每笔交易的持仓预付金和客户预付金计算用的Spv组保持一致 var tradeSpansUpdate = db.trade_span.Where(x => x.ClientId == item.ClientId && x.UnderlyingId == item.UnderlyingId && x.ValueDate == req.settleDate).ToList(); if (item.WorstCastClientPayable == item.Spv1) { tradeSpansUpdate.ForEach(x => x.WorstCastClientPayable = x.Spv1); } else if (item.WorstCastClientPayable == item.Spv2) { tradeSpansUpdate.ForEach(x => x.WorstCastClientPayable = x.Spv2); } else if (item.WorstCastClientPayable == item.Spv3) { tradeSpansUpdate.ForEach(x => x.WorstCastClientPayable = x.Spv3); } else if (item.WorstCastClientPayable == item.Spv4) { tradeSpansUpdate.ForEach(x => x.WorstCastClientPayable = x.Spv4); } else if (item.WorstCastClientPayable == item.Spv5) { tradeSpansUpdate.ForEach(x => x.WorstCastClientPayable = x.Spv5); } else if (item.WorstCastClientPayable == item.Spv6) { tradeSpansUpdate.ForEach(x => x.WorstCastClientPayable = x.Spv6); } else { tradeSpansUpdate.ForEach(x => x.WorstCastClientPayable = x.Spv7); } #endregion } #endregion #region Delta Margin Method var varietyGroup = clientGroup.GroupBy(t => t.tradeSpan.VarietyId).Select(t => new ClientSpan { VarietyId = t.Key, ClientId = clientGroup.Key, ValueDate = req.settleDate, MaxMarginRate = t.Max(g => mpProvider.TryGetMarginRate(g.tradeSpan.UnderlyingCode, out var marginRate) ? marginRate : 0), MaxSpotPrice = t.Max(g => g.tradeSpan.UnderlyingPrice), PvForDelta = t.Sum(g => g.tradeSpan.Spv) * (-1), DeltaSell = t.Sum(g => -g.tradeSpan.Delta * (g.trade.BuySell == "买入" || g.trade.TradeType == "远期" ? 1 : 0)), DeltaBuy = t.Sum(g => -g.tradeSpan.Delta * (g.trade.BuySell == "卖出" && g.tradeSpan.Delta < 0 && g.trade.TradeType != "远期" ? 1 : 0)), DeltaBuyMinus = t.Sum(g => -g.tradeSpan.Delta * (g.trade.BuySell == "卖出" && g.tradeSpan.Delta > 0 && g.trade.TradeType != "远期" ? 1 : 0)), OptId = req.userId, OptName = req.userName, OptDate = DateTime.Now, SpanType = req.SpanType }).ToList(); foreach (var item in varietyGroup) { #region 更新tradeSpan,通过Delta品种级别求和算法,给tradeSpan的deltaMargin赋值 var tradeSpansUpdate = db.trade_span.Where(x => x.ClientId == item.ClientId && x.VarietyId == item.VarietyId && x.ValueDate == req.settleDate).ToList(); //DeltaSell为客户角度卖出,且为客户角度看Delta if (item.DeltaSell > 0) { tradeSpansUpdate.ForEach(x => { var tradeSpanInfo = tradeSpanInfosWithoutDQ.FirstOrDefault(y => y.trade.id == x.TradeId); var trade = tradeSpanInfo != null ? tradeSpanInfo.trade : null; //客户角度卖出的交易 //客户角度买入的交易,客户角度Delta<0,即交易员角度Delta>0 if (trade != null && (trade.BuySell == "买入" || trade.TradeType == "远期" || x.Delta > 0)) { x.DeltaMargin = -x.Delta * item.MaxSpotPrice * item.MaxMarginRate + x.Spv; } }); item.DeltaMargin = -Math.Max((item.DeltaSell ?? 0) + (item.DeltaBuyMinus ?? 0), 0) * item.MaxSpotPrice * item.MaxMarginRate + item.PvForDelta; } else { tradeSpansUpdate.ForEach(x => { var tradeSpanInfo = tradeSpanInfosWithoutDQ.FirstOrDefault(y => y.trade.id == x.TradeId); var trade = tradeSpanInfo?.trade; //客户角度卖出的交易 //客户角度买入的交易,客户角度Delta>0,即交易员角度Delta<0 if (trade != null && (trade.BuySell == "买入" || trade.TradeType == "远期" || x.Delta < 0)) { x.DeltaMargin = x.Delta * item.MaxSpotPrice * item.MaxMarginRate + x.Spv; } }); item.DeltaMargin = Math.Min((item.DeltaSell ?? 0) + (item.DeltaBuy ?? 0), 0) * item.MaxSpotPrice * item.MaxMarginRate + item.PvForDelta; } #endregion } #endregion var clientSpan = new ClientSpan { ClientId = clientGroup.Key, ValueDate = req.settleDate, Spv1 = underlyingGroup.Sum(g => g.Spv1), Spv2 = underlyingGroup.Sum(g => g.Spv2), Spv3 = underlyingGroup.Sum(g => g.Spv3), Spv4 = underlyingGroup.Sum(g => g.Spv4), Spv5 = underlyingGroup.Sum(g => g.Spv5), Spv6 = underlyingGroup.Sum(g => g.Spv6), Spv7 = underlyingGroup.Sum(g => g.Spv7), DeltaMargin = varietyGroup.Sum(g => g.DeltaMargin), //负数代表客户应缴预付金,正数代表客户应收预付金 WorstCastClientPayable = underlyingGroup.Sum(g => g.WorstCastClientPayable) < 0 ? underlyingGroup.Sum(g => g.WorstCastClientPayable) : 0, 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 (clientSpan.WorstCastClientPayable > clientSpan.DeltaMargin) { clientSpan.WorstCastClientPayable = clientSpan.DeltaMargin; //取delta预付金作为最终预付金 var tradeSpansUpdate = db.trade_span.Where(x => x.ClientId == clientGroup.Key && x.ValueDate == req.settleDate).ToList(); tradeSpansUpdate.ForEach(x => x.WorstCastClientPayable = x.DeltaMargin); } var worstCastClientPayableDQ = tradeSpanInfosDQ.Where(x => x.tradeSpan.ClientId == clientSpan.ClientId).Sum(x => x.tradeSpan.WorstCastClientPayable * -1); clientSpan.WorstCastClientPayable += worstCastClientPayableDQ; clientSpanNews.Add(clientSpan); } var clientIdsDQ = tradeSpanInfosDQ.Select(x => x.trade.ClientId).ToHashSet(); foreach (var clientId in clientIdsDQ) { if (!clientSpanNews.Select(x => x.ClientId).Contains(clientId)) { var clientSpan = new ClientSpan { ClientId = clientId, ValueDate = req.settleDate, WorstCastClientPayable = 0, OptId = req.userId, OptName = req.userName, OptDate = DateTime.Now, SpanType = req.SpanType }; var worstCastClientPayableDQ = tradeSpanInfosDQ.Where(x => x.tradeSpan.ClientId == clientId).Sum(x => x.tradeSpan.WorstCastClientPayable * -1); clientSpan.WorstCastClientPayable += worstCastClientPayableDQ; 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 (tradeMargin != null && tradeMargin.FirstOrDefault() != null && trade.BuySell == "买入") { var margin = tradeMargin.FirstOrDefault().WorstCastClientPayable ?? 0.0; var mpProvider = new MarginParamProvider(OptUserInfo.SystemUser, valuedateBLL.ValueDate.Date).Initialize(new List { trade.UnderlyingCode }.ToHashSet(), MarginParamTypeEnum.MarginRate); mpProvider.TryGetMarginRate(trade.UnderlyingCode, out var marginRate); var deltaMargin = Math.Abs(tradeMargin.FirstOrDefault().Delta ?? 0) * (trade.SpotPrice ?? 0) * marginRate; return margin > deltaMargin ? margin : deltaMargin; } return 0.0; } } }