using Qdp.Pricing.Base.Implementations; using YLErp.Abstract.DataProviders; using YLErp.BLL.Calculation; using YLErp.Enums; using YLErp.Helpers; using YLErp.Modules.CalculationModule; using YLErp.Modules.DataProviderModule; using YLErp.Modules.MarginModule; namespace YLErp.BLL.MarginCalculation { /// /// 国泰君安预付金计算 /// public class XMXYMarginCalculation : MarginCalculationBase { // 定义一个静态变量来保存类的实例 public static readonly XMXYMarginCalculation Instance; static XMXYMarginCalculation() { Instance = new XMXYMarginCalculation(); } // 定义私有构造函数,使外界不能创建该类实例 protected XMXYMarginCalculation() { } public override List RunMarginCalculation(RunMarginCalculationReq req) { if (req.tradeList == null || !req.tradeList.Any()) { return new List(); } var helper = new RunMarginCalculationHelper(req, _underlyingDataProvider); var tradeSpans = RunMarginCalculationForCommodity(helper); using (var db = new YLContext()) { var tradeIds = req.tradeList.Select(x => x.id); var tradeMarginTemplates = db.trade_margin_template.Where(x => tradeIds.Contains(x.TradeId)).ToList(); tradeSpans.ForEach(x => { var trade = req.tradeList.FirstOrDefault(y => y.id == x.TradeId); var tradeMarginTemplate = tradeMarginTemplates.Where(y => y.TradeId == x.TradeId && x.ValueDate <= req.settleDate).OrderByDescending(y => y.ValueDate).FirstOrDefault(); if (tradeMarginTemplate != null) { var marginTemplate = db.margin_template_v2.Find(tradeMarginTemplate.MarginTemplateId); if (marginTemplate != null) { switch (marginTemplate.RuleType) { case (int)MarginRuleTypeEnum.无预付金: CalcAsNo(x); break; case (int)MarginRuleTypeEnum.交易所基本规则: var PriceProvider = new EodPriceProvider(req.settleDate).GetPriceProvider(SettlementTypeEnum.ClosePrice); CalcAsExchangeBasicRule(x, trade, req, PriceProvider); break; } } } }); } return tradeSpans; } private void CalcAsNo(trade_span span) { span.Spv1 = 0; span.Spv2 = 0; span.Spv3 = 0; span.Spv4 = 0; span.WorstCastClientPayable = 0; span.MaxlossMargin = 0; span.IsSingleMargin = true; } private void CalcAsExchangeBasicRule(trade_span span, trade trade, RunMarginCalculationReq req, IEodPriceProviderWrap priceProvider) { if (trade.BuySell == "买入") { var helper = new RunMarginCalculationHelper(req, _underlyingDataProvider); var mpProvider = helper.GetMarginParamProvider(MarginParamTypeEnum.MarginRate | MarginParamTypeEnum.UpDownLimit); mpProvider.TryGetMarginRate(trade.UnderlyingCode, out var marginRate); var price = req.PriceProvider.GetPrice(trade.UnderlyingCode); if (req.CalcMarginType == CalcMarginTypeEnum.EodMargin) { price = new EodPriceProvider(req.settleDate).GetPrice(trade.UnderlyingCode, SettlementTypeEnum.ClosePrice); } double RoundedPv = 0; var reqConv = new CalculateRisksForTradesReq { valueDate = req.settleDate, tradeList = new List { trade }, priceProvider = priceProvider,//req.PriceProvider, pricingRequest = PricingRequest.Pv | PricingRequest.Delta | PricingRequest.Gamma, addVolRateDic = null, volType = req.volType, isUseTradeVol = PS.Config.IsTradeVol, PreciseTimeMode = req.CalcMarginType != Enums.CalcMarginTypeEnum.EodMargin, isAddVolPercent = true, overrideVolsForTrade = null, isMarginCalc = true }; if (req.CalcMarginType == Enums.CalcMarginTypeEnum.EodMargin) { reqConv.calcScenario = Enums.CalcScenarioEnum.EodSettlement; } var tradeRiskResult = CalculatorHelper.CalculateRisksForTrades(reqConv); RoundedPv = tradeRiskResult.Results.Where(x => x.Trade.id == trade.id).FirstOrDefault().ValueResult.Pv; var margin1 = RoundedPv + 0.5 * price * trade.TradeAmount * marginRate; var margin2 = RoundedPv + price * trade.TradeAmount * marginRate - (trade.OptionType == "看涨" ? Math.Max((trade.ActualStrike ?? 0) - price, 0) : Math.Max(price - (trade.ActualStrike ?? 0), 0)) * trade.Notional / 2; //var margin1 = price * trade.Notional + 0.5 * trade.StockEqvNotional * marginRate; //var margin2 = price * trade.Notional + trade.StockEqvNotional * marginRate - (trade.OptionType == "看涨" ? Math.Max((trade.ActualStrike ?? 0) - price, 0) : Math.Max(price - (trade.ActualStrike ?? 0), 0)) * trade.Notional / 2; var value = Math.Max(margin1, margin2); span.Spv1 = value; span.Spv2 = value; span.Spv3 = value; span.Spv4 = value; span.WorstCastClientPayable = value; span.MaxlossMargin = value; span.IsSingleMargin = true; } } //商品类预付金计算 private static List RunMarginCalculationForCommodity(RunMarginCalculationHelper helper) { var resultMap = new Dictionary(); //为了算客户角度的一个预付金数值 helper.ReverseTradeSide(); helper.SetFieldsByTradeType(); helper.GetUpDownLimitPrices(out var upLimitPrices, out var downLimitPrices); helper.GetTradVolRateDic(out var tradeVolRateDic); var vols = new[] { null, tradeVolRateDic }; var prices = new (string, IPriceProvider)[] { ("up", upLimitPrices), ("down", downLimitPrices) }; var loops = prices.SelectMany(n => vols.Select(m => new { pricekey = n.Item1, priceProvider = n.Item2, addVolRateDic = m })).ToArray(); foreach (var loop in loops) { var calcReq = helper.GetCalculateRisksForTradesReq(priceProvider: loop.priceProvider, addVolRateDic: loop.addVolRateDic, overrideVols: null); var tradeRiskResult = CalculatorHelper.CalculateRisksForTrades(calcReq); if (tradeRiskResult.Results == null || tradeRiskResult.Results.Count < 1) { continue; } var key = $"{loop.pricekey}_{(loop.addVolRateDic == null ? 0 : 1)}"; foreach (var item in tradeRiskResult.Results) { var pv = item.ValueResult.Pv; if (!helper.GetSpecialMargin(item.Trade, pv, out var value, helper.req.forOtherSide)) { var clientRatio = helper.GetClient(item.Trade)?.Ratio ?? 1.0; value = double.IsNaN(pv) ? 0 : 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_0": tempTradeSpan.Spv1 = value; break; case "up_1": tempTradeSpan.Spv2 = value; break; case "down_0": tempTradeSpan.Spv3 = value; break; case "down_1": tempTradeSpan.Spv4 = value; break; } if (contains) { tempTradeSpan.SetWorstCastClientPayable(); tempTradeSpan.MaxlossMargin = tempTradeSpan.WorstCastClientPayable; } } } 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(); if (req.tradeSpans != null && req.tradeSpans.Count > 0) { var tradeSpanInfoOtherSide = (from tradeSpan in req.tradeSpansOtherSide 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 underlyingGroup = clientGroup.Where(O => O.trade.MarginType == DBModels.Enums.MarginTypeEnum.DEFAULT && O.tradeSpan.IsSingleMargin != true).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), 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(item.Spv1 ?? 0, item.Spv2 ?? 0), item.Spv3 ?? 0), item.Spv4 ?? 0); item.TwoSideMargin = Math.Min(Math.Min(Math.Min(item.Spv1 ?? 0, item.Spv2 ?? 0), item.Spv3 ?? 0), item.Spv4 ?? 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.UnderlyingId == item.UnderlyingId && x.ValueDate == req.settleDate).ToList(); var tradeSpansReq = req.tradeSpans.Where(x => tradeIdList.Contains(x.TradeId) && 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); 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); } #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), //负数代表客户应缴预付金,正数代表客户应收预付金 WorstCastClientPayable = underlyingGroup.Sum(g => g.WorstCastClientPayable), MySideMargin = underlyingGroup.Sum(g => g.WorstCastClientPayable), TwoSideMargin = underlyingGroup.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 || O.tradeSpan.IsSingleMargin == true).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.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); clientSpanNews.Add(clientSpan); } //处理从客户角度的预付金计算(将交易买卖方向反向处理) var clientGroupsOtherSide = tradeSpanInfoOtherSide.GroupBy(t => t.trade.ClientId); foreach (var clientGroup in clientGroupsOtherSide) { var underlyingGroup = clientGroup.Where(O => O.trade.MarginType == DBModels.Enums.MarginTypeEnum.DEFAULT).GroupBy(t => t.trade.UnderlyingId).Select(t => new ClientSpan { 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), OptId = req.userId, OptName = req.userName, OptDate = DateTime.Now, SpanType = req.SpanType }).ToList(); foreach (var item in underlyingGroup) { item.WorstCastClientPayable = new[] { item.Spv1 ?? 0, item.Spv2 ?? 0, item.Spv3 ?? 0, item.Spv4 ?? 0 }.Min(); } var clientSpan = clientSpanNews.FirstOrDefault(x => x.ClientId == clientGroup.Key && x.ValueDate == req.settleDate); clientSpan.OtherSideMargin = underlyingGroup.Sum(g => g.WorstCastClientPayable); var singleMarginTrade = clientGroup.Where(O => O.trade.MarginType != DBModels.Enums.MarginTypeEnum.DEFAULT).Select(O => O.tradeSpan); clientSpan.OtherSideMargin += singleMarginTrade.Sum(O => O.WorstCastClientPayable * (-1)); clientSpan.OtherSideMargin = Math.Min(clientSpan.OtherSideMargin ?? 0, 0); if (HasTwoSideMargin(clientGroup.Key)) { clientSpan.WorstCastClientPayable = clientSpan.MySideMargin - clientSpan.OtherSideMargin; } } } //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.trade.id).ToList(); } var marginReq = req.GetRunMarginCalculationReq(); if (req.trade.IsGroup == 1) { marginReq.tradeList = db.trade.Where(x => x.ParentTradeId == req.trade.id).ToList(); } var tradeMargin = RunMarginCalculation(marginReq); if (null != tradeMargin) { var margin = req.trade.IsGroup == 1 ? tradeMargin.Sum(x => x.WorstCastClientPayable ?? 0) : (tradeMargin.FirstOrDefault()?.WorstCastClientPayable ?? 0); return margin; } return 0.0; } } } }