using YLErp.Abstract.DataProviders; using YLErp.BLL.Calculation; using YLErp.Enums; using YLErp.Helpers; using YLErp.Model.Enum; using YLErp.Modules.CalculationModule; using YLErp.QdpModule; namespace YLErp.BLL.MarginCalculation { /// /// 伴兴预付金计算 /// public class BXMarginCalculation : MarginCalculationBase { // 定义一个静态变量来保存类的实例 public static readonly BXMarginCalculation Instance; static BXMarginCalculation() { Instance = new BXMarginCalculation(); } // 定义私有构造函数,使外界不能创建该类实例 protected BXMarginCalculation() { } public override List RunMarginCalculation(RunMarginCalculationReq req) { var resultMap = new List(); var list2 = new List(req.tradeList.Count); foreach (var td in req.tradeList) { if (td.InitialMargin != null && td.CalcFlag == (int)CalcFlagEnum.IgnoreMarginCalc) { var initMargin = td.InitialMargin * (td.Notional / td.OriginalNotional); var ts = new trade_span() { TradeId = td.id, ClientId = td.ClientId, ValueDate = req.settleDate, UnderlyingId = td.UnderlyingId, UnderlyingCode = td.UnderlyingCode, OptId = req.userId, OptName = req.userName, OptDate = DateTime.Now, Spv1 = initMargin, Spv2 = initMargin, Spv3 = initMargin, Spv4 = initMargin, }; ts.SetWorstCastClientPayable(); resultMap.Add(ts); } else { list2.Add(td); } } resultMap.AddRange(marginCalculation(req.Clone(list2))); return resultMap; } private List marginCalculation(RunMarginCalculationReq req) { var resultMap = new Dictionary(); var helper = new RunMarginCalculationHelper(req, _underlyingDataProvider); helper.SetFieldsByTradeType(); using (var db = new YLContext()) { helper.GetTradVolRateDic(out var tradeVolRateDic, t => { var pclass = helper.GetClient(t.ClientId)?.ProperClientClass; return pclass != null && pclass.Contains("普通投资者") ? 0.02 : 0; }); helper.GetUpDownLimitPrices(out var upLimitPrices, out var downLimitPrices); var prices = new (string, IPriceProvider)[] { ("up", upLimitPrices), ("down", downLimitPrices) }; foreach (var price in prices) { foreach (var addVolRateDic in new[] { null, tradeVolRateDic }) { var key = $"{price.Item1}_{(addVolRateDic == null ? 0 : 1)}"; var tradeRiskResult = CalculatorHelper.CalculateRisksForTrades( valueDate: req.settleDate, calcScenario: req.GetCalcScenario(), tradeList: req.tradeList, priceProvider: price.Item2, pricingRequest: QdpPricingRequest.PV_ONLY, addVolRateDic: addVolRateDic, volType: req.volType, isUseTradeVol: PS.Config.IsTradeVol, preciseTimeMode: req.CalcMarginType != CalcMarginTypeEnum.EodMargin, isAddVolPercent: false); if (tradeRiskResult.Results != null && tradeRiskResult.Results.Count > 0) { foreach (var item in tradeRiskResult.Results) { var client = helper.GetClient(item.Trade); var clientRatio = client?.Ratio ?? 1.0; var pv = item.ValueResult.Pv; //if (item.Trade.TradeType == "收益互换") //{ // var getclosePrice = req.PriceProvider.GetPrice(item.Trade.trade_swap.GetUnderlyingCode); // UpdownLimit getprice = helper.GetUpDownLimit(item.Trade.trade_swap.GetUnderlyingCode, getclosePrice); // var payclosePrice = req.PriceProvider.GetPrice(item.Trade.trade_swap.PayUnderlyingCode); // UpdownLimit payprice = helper.GetUpDownLimit(item.Trade.trade_swap.PayUnderlyingCode, payclosePrice); // TradeValueResult optionValue = null; // if (key == "up_0") // { // var priceProvidr = new ManualPriceProvider(); // priceProvidr.SetPrice(item.Trade.trade_swap.GetUnderlyingCode, getprice.UpLimitPrice); // priceProvidr.SetPrice(item.Trade.trade_swap.PayUnderlyingCode, payprice.UpLimitPrice); // optionValue = PayoffSwapCalcService.CalcValue(item.Trade, item.Trade.trade_swap, req.settleDate, null, req.CalcMarginType == DBModels.Enums.CalcMarginTypeEnum.EodMargin); // optionValue = PayoffSwapCalcService.CalcValue(item.Trade, item.Trade.trade_swap, req.settleDate, priceProvidr, req.CalcMarginType == DBModels.Enums.CalcMarginTypeEnum.EodMargin); // } // else if (key == "down_0") // { // var priceProvidr = new ManualPriceProvider(); // priceProvidr.SetPrice(item.Trade.trade_swap.GetUnderlyingCode, getprice.DownLimitPrice); // priceProvidr.SetPrice(item.Trade.trade_swap.PayUnderlyingCode, payprice.DownLimitPrice); // optionValue = PayoffSwapCalcService.CalcValue(item.Trade, item.Trade.trade_swap, req.settleDate, priceProvidr, req.CalcMarginType == DBModels.Enums.CalcMarginTypeEnum.EodMargin); // } // pv = optionValue == null ? 0 : optionValue.Pv; //} if (!helper.GetSpecialMargin(item.Trade, pv, out var value)) { if (item.Trade.CalcFlag == (int)CalcFlagEnum.IgnoreMarginCalc) { value = item.Trade.InitialMargin ?? 0; } else { value = GetMargin(item.Trade, item.ValueResult, 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(); 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.对手方单向追保) { value2 = item.Trade.BuySell == "卖出" ? Math.Min(value2, 0) : 0; } tempTradeSpan.WorstCastClientPayable = value2; } } } } } return resultMap.Values.ToList(); } } private double GetMargin(trade trade, TradeValueResult valueResult, double clientRatio) { var value = 0.0; //预付金不再传入结构化主交易数据 //if (trade.TradeType == "结构化交易") //{ // if (trade.StructureType != null && trade.StructureType.Contains("跨式")) // { // value = valueResult.MaxAbsPv; // } // else // { // value = valueResult.SellPv; // } //} if (trade.TradeType != "自定义交易") { value = valueResult.Pv; } return (double.IsNaN(value) ? 0 : value) * clientRatio; } public override List CalcClientMargin(CalcClientMarginReq req) { var clientSpanNews = new List(); using (var db = new YLContext()) { if (req.tradeSpans != null && req.tradeSpans.Count > 0) { var clientGroups = req.tradeSpans.GroupBy(t => t.ClientId); foreach (var clientGroup in clientGroups) { var clientSpan = new ClientSpan { ClientId = clientGroup.Key ?? 0, ValueDate = req.settleDate, //负数代表客户应缴预付金,正数代表客户应收预付金 Spv1 = -clientGroup.Sum(g => g.Spv1), Spv2 = -clientGroup.Sum(g => g.Spv2), Spv3 = -clientGroup.Sum(g => g.Spv3), Spv4 = -clientGroup.Sum(g => g.Spv4), WorstCastClientPayable = -clientGroup.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 ?? 0, out var dd) ? dd : 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).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; if (trade.TradeType == "结构化交易" && trade.id > 0) { using (var db = new YLContext()) { trade.SubTrades = db.trade.Where(x => x.ParentTradeId == trade.id).ToList(); } } var tradeMargin = RunMarginCalculation(req.GetRunMarginCalculationReq()); if (null != tradeMargin) { return tradeMargin.FirstOrDefault()?.WorstCastClientPayable ?? 0.0; } return 0.0; } } }