using YLErp.Abstract.DataProviders; using YLErp.BLL.Calculation; using YLErp.BLL.Eod; using YLErp.Helpers; using YLErp.Modules; using YLErp.Modules.DataProviderModule; using YLErp.QdpModule; namespace YLErp.BLL.MarginCalculation { public class HuaAnMarginCalculation : MarginCalculationBase { // 定义一个静态变量来保存类的实例 public static readonly HuaAnMarginCalculation Instance; static HuaAnMarginCalculation() { Instance = new HuaAnMarginCalculation(); } // 定义私有构造函数,使外界不能创建该类实例 private HuaAnMarginCalculation() { } public override List RunMarginCalculation(RunMarginCalculationReq req) { var resultList = new List(); if (req.CalcMarginType == Enums.CalcMarginTypeEnum.InitialMargin) { resultList = calcInitialMargin(req); } else { resultList = calcPositionMargin(req); } return resultList; } private List calcPositionMargin(RunMarginCalculationReq req) { var resultMap = new Dictionary(); req.PriceProvider = new EodPriceProvider(req.settleDate).GetPriceProvider(SettlementTypeEnum.SettlePrice); var _helper = new RunMarginCalculationHelper(req, _underlyingDataProvider); _helper.GetUpDownLimitPrices(out var upPrices, out var downPrices); _helper.GetUpDownVolRateDic(out var upVolRateDic, out _); var ForwordOrSwapList = req.tradeList.Where(O => !resultMap.Keys.Contains(O.id) && O.TradeType != "收益互换" && O.TradeType != "远期"); var ForwordOrSwapcalcReq = _helper.GetCalculateRisksForTradesReq( req.PriceProvider, null, null, Qdp.Pricing.Base.Implementations.PricingRequest.Pv); ForwordOrSwapcalcReq.tradeList = req.tradeList.Where(O => O.TradeType == "远期" || O.TradeType == "收益互换"); var ForwordOrSwapRusult = CalculatorHelper.CalculateRisksForTrades(ForwordOrSwapcalcReq); foreach (var f in ForwordOrSwapRusult.Results.Where(l => l.Trade.TradeType == "远期" || l.Trade.TradeType == "收益互换")) { double? marginRate2 = 0; if (!string.IsNullOrEmpty(f.Trade.BasisUnderlyingCode) && f.Trade.TradeType == "远期") { marginRate2 = GetUnderlyingCodeMarginRate(f.Trade.BasisUnderlyingCode); } double? marginRate1 = GetUnderlyingCodeMarginRate(f.Trade.UnderlyingCode); var positionPnl = EodOperationBase.GetPositionPnl(f.ValueResult.Pv, f.Trade.TradePrice ?? 0.0, f.Trade.Notional, f.Trade.OriginalNotional ?? 0, f.Trade.BuySell); var marginRate = ((marginRate1 > marginRate2 ? marginRate1 : marginRate2) ?? 0); var margin = (f.Trade.TradeType == "远期" ? f.Trade.SpotPrice : req.PriceProvider.GetPrice(f.Trade.UnderlyingCode)) * f.Trade.Notional * marginRate + positionPnl; var sp = _helper.CreateTradeSpan(f.Trade).SetAllSpvAndWorst(Math.Max((margin ?? 0), 0)); sp.SetWorstCastClientPayable(); resultMap[sp.TradeId] = sp; } var allList = req.tradeList.Where(O => !resultMap.Keys.Contains(O.id) && O.TradeType != "收益互换" && O.TradeType != "远期"); var calcReq = _helper.GetCalculateRisksForTradesReq( req.PriceProvider, null, null, Qdp.Pricing.Base.Implementations.PricingRequest.Delta); calcReq.tradeList = allList; var tradeRiskResult = CalculatorHelper.CalculateRisksForTrades(calcReq); var sellList = tradeRiskResult.Results.Where(O => O.Trade.ParentTradeId == 0 && O.Trade.IsGroup == 0 && (O.Trade.TradeType.Contains("香草") || O.Trade.TradeType.Contains("亚式")) && O.Trade.BuySell == "卖出"); foreach (var item in sellList) { var sp = _helper.CreateTradeSpan(item.Trade).SetAllSpvAndWorst(0); sp.SetWorstCastClientPayable(); resultMap[sp.TradeId] = sp; } //1、交易员角度买入香草和亚式期权 //先按标的分类,在标的i下 //Span算法:先算出品种i下每笔交易的span算法预付金,然后合计 //Delta算法:先算出每一笔交易的delta算法预付金 = max(期权的总的delta* 收盘价*交易所预付金比例,0),然后合计算出该品种的delta算法预付金 //品种i的预付金 = max(span算法预付金,Delta算法预付金) //香草和亚式的总的预付金 var VanillaOrAsianList = tradeRiskResult.Results.Where(O => !resultMap.Keys.Contains(O.Trade.id) && (O.Trade.ParentTradeId == 0 || O.Trade.IsGroup == 2) && (O.Trade.TradeType.Contains("香草") || O.Trade.TradeType.Contains("亚式")) && O.Trade.BuySell == "买入"); foreach (var i in VanillaOrAsianList) { var deltaMargin = i.ValueResult.Delta * i.Trade.SpotPrice * GetUnderlyingCodeMarginRate(i.Trade.UnderlyingCode); var sp = marginCalculation(req, new List { i.Trade }).First(); var spanMargin = sp.WorstCastClientPayable; deltaMargin = deltaMargin > 0 ? deltaMargin : 0; var margin = Math.Max((deltaMargin ?? 0), (spanMargin ?? 0)); sp.DeltaMargin = deltaMargin; sp.WorstCastClientPayable = margin; resultMap[sp.TradeId] = sp; } //买入宽跨式(跨式)期权组合 var StraddleTradeIds = req.tradeList.Where(O => (O.StructureType ?? O.TradeType).Contains("跨式")).Select(O => O.id); var StraddleResultDict = tradeRiskResult.Results.Where(O => StraddleTradeIds.Contains(O.Trade.id)).GroupBy(O => O.Trade.ParentTradeId).ToDictionary(K => K.Key, V => V.ToList()); foreach (var straddle in StraddleResultDict) { //获取多个腿的 deltaInitialMargin var deltaInitialMargin = straddle.Value.Where(l => l.Trade.BuySell == "买入").ToDictionary(k => k.Trade.id, O => O.ValueResult.Delta * req.PriceProvider.GetPrice(O.Trade.UnderlyingCode) * GetUnderlyingCodeMarginRate(O.ValueResult.UnderlyingCode)); //获取多个腿的 spanInitialMargin var spanInitialMargin = marginCalculation(req, straddle.Value.Where(l => l.Trade.BuySell == "买入").Select(l => l.Trade).ToList()).ToDictionary(k => k.TradeId, v => v.WorstCastClientPayable); double margin = 0; int selfLegTradeId = 0; deltaInitialMargin.ToList().Union(spanInitialMargin.ToList()).ToList().ForEach(x => { if (margin < x.Value) { margin = x.Value ?? 0; selfLegTradeId = x.Key; } }); //另外一条腿的权利金总额 var straddleCalcReq = _helper.GetCalculateRisksForTradesReq( req.PriceProvider, null, null, Qdp.Pricing.Base.Implementations.PricingRequest.Pv); straddleCalcReq.tradeList = new List { straddle.Value.Where(l => l.Trade.id != selfLegTradeId).Select(l => l.Trade).FirstOrDefault() }; var straddleRusult = CalculatorHelper.CalculateRisksForTrades(straddleCalcReq); var t = straddleRusult.Results.FirstOrDefault().Trade; double? otherLegAmount = t.BuySell == "买入" ? Math.Abs(t.TradePrice ?? 0) : 0; var partialNotionalRate = t.Notional / t.OriginalNotional; for (int i = 0; i < straddle.Value.Count; i++) { if (straddle.Value[i].Trade.id == selfLegTradeId) { var sp = _helper.CreateTradeSpan(straddle.Value[i].Trade).SetAllSpvAndWorst(margin); sp.SetWorstCastClientPayable(); resultMap[straddle.Value[i].Trade.id] = sp; } else { var sp = _helper.CreateTradeSpan(straddle.Value[i].Trade).SetAllSpvAndWorst((otherLegAmount ?? 0) * (partialNotionalRate ?? 0)); sp.SetWorstCastClientPayable(); resultMap[straddle.Value[i].Trade.id] = sp; } } } //其它奇异期权 持仓预付金:用span算 var ExoticOptionsList = req.tradeList.Where(O => !resultMap.Keys.Contains(O.id) && O.TradeType != "收益互换" && (O.ParentTradeId == 0 || O.IsGroup == 2) && !O.TradeType.Contains("香草") && !O.TradeType.Contains("亚式")); //牛市价差、熊市价差期权 var NiuxiongTradeIds = req.tradeList.Where(O => ((O.StructureType ?? O.TradeType).Contains("牛市") || (O.StructureType ?? O.TradeType).Contains("熊市"))).Select(O => O.id); var NiuxiongResultDict = tradeRiskResult.Results.Where(O => NiuxiongTradeIds.Contains(O.Trade.id)).GroupBy(O => O.Trade.ParentTradeId).ToDictionary(K => K.Key, V => V.ToList()); foreach (var buyNiuxiong in NiuxiongResultDict) { var margin = buyNiuxiong.Value.Max(O => Math.Max(Math.Abs(O.ValueResult.Delta) / O.Trade.Notional, 0.5) * req.PriceProvider.GetPrice(O.Trade.UnderlyingCode) * O.Trade.Notional * GetUnderlyingCodeMarginRate(O.Underlyings.Select(l => l.UnderlyingCode).FirstOrDefault())) * 0.5; for (int i = 0; i < buyNiuxiong.Value.Count; i++) { if (buyNiuxiong.Value[i].Trade.BuySell == "买入") { var sp = _helper.CreateTradeSpan(buyNiuxiong.Value[i].Trade).SetAllSpvAndWorst(margin ?? 0); sp.SetWorstCastClientPayable(); resultMap[buyNiuxiong.Value[i].Trade.id] = sp; } else { var sp = _helper.CreateTradeSpan(buyNiuxiong.Value[i].Trade).SetAllSpvAndWorst(0); sp.SetWorstCastClientPayable(); resultMap[buyNiuxiong.Value[i].Trade.id] = sp; } } } //除了宽跨式(跨式)期权、牛熊市价差期权以外的其他组合期权 var OtherTradeList = req.tradeList.Where(O => (O.ParentTradeId > 0 && O.IsGroup == 0) && !(O.StructureType ?? O.TradeType).Contains("牛市") && !(O.StructureType ?? O.TradeType).Contains("熊市") && !(O.StructureType ?? O.TradeType).Contains("跨式")).ToList(); var tradeList = ExoticOptionsList.Union(OtherTradeList).ToList(); if (tradeList.Count > 0) { var resultspan = marginCalculation(req, tradeList); foreach (var i in resultspan) { if (resultMap.ContainsKey(i.TradeId)) { resultMap[i.TradeId] = i; } else { resultMap.Add(i.TradeId, i); } } } return resultMap.Values.ToList(); } /// /// 获取标的现价 /// /// /// /// /// public static double GetUnderlyingPrice(RunMarginCalculationReq req, string underlyingCode, DateTime tradeDate, SettlementTypeEnum typeEnum = SettlementTypeEnum.SettlePrice) { double price = 0; var udm = DataCacheProvider.GetUnderlyingDataSource().GetData(underlyingCode); if (udm != null) { var priceProvider = new EodPriceProvider(tradeDate).GetPriceProvider(typeEnum); price = priceProvider.GetPrice(underlyingCode); } return price; } public List calcInitialMargin(RunMarginCalculationReq req) { var resultMap = new Dictionary(); if (req.tradeList == null || !req.tradeList.Any()) { return new List(); } var tradeSpanList = new List(); var _helper = new RunMarginCalculationHelper(req, _underlyingDataProvider); _helper.GetUpDownLimitPrices(out var upPrices, out var downPrices); _helper.GetUpDownVolRateDic(out var upVolRateDic, out _); //收益互换交易单独计算trade_span var swap = req.tradeList.Where(O => O.TradeType == "收益互换"); tradeBLL.SetFieldsByTradeType(swap); foreach (var item in swap) { var margin = 0d; var un = DataCacheProvider.GetUnderlyingDataSource().GetData(item.UnderlyingCode); if (un != null) { margin = (double)(item.StockEqvNotional * (item.trade_swap?.GetMarginRate ?? 0)); } var sp = _helper.CreateTradeSpan(item).SetAllSpvAndWorst(margin); sp.SetWorstCastClientPayable(); resultMap[sp.TradeId] = sp; } //远期计算span if (req.tradeList.Any(t => t.TradeType == "远期")) { var forwardTradeList = req.tradeList.Where(t => t.TradeType == "远期").ToList(); foreach (var t in forwardTradeList) { double? marginRate2 = 0; if (!string.IsNullOrEmpty(t.BasisUnderlyingCode)) { marginRate2 = GetUnderlyingCodeMarginRate(t.BasisUnderlyingCode); } double? marginRate1 = GetUnderlyingCodeMarginRate(t.UnderlyingCode); var margin = t.OriginalNotional * (t.SpotPrice ?? 0) * (marginRate1 > marginRate2 ? marginRate1 : marginRate2); var sp = _helper.CreateTradeSpan(t).SetAllSpvAndWorst(margin ?? 0); sp.SetWorstCastClientPayable(); resultMap[sp.TradeId] = sp; } } var allList = req.tradeList.Where(O => !resultMap.Keys.Contains(O.id) && O.TradeType != "收益互换" && O.TradeType != "远期"); var calcReq = _helper.GetCalculateRisksForTradesReq( req.PriceProvider, null, null, Qdp.Pricing.Base.Implementations.PricingRequest.Delta); calcReq.tradeList = allList; var tradeRiskResult = CalculatorHelper.CalculateRisksForTrades(calcReq); var sellList = tradeRiskResult.Results.Where(O => (O.Trade.CalcId == null || (O.Trade.CalcId != null && !O.Trade.CalcId.Contains("-"))) && O.Trade.IsGroup == 0 && (O.Trade.TradeType.Contains("香草") || O.Trade.TradeType.Contains("亚式")) && O.Trade.BuySell == "卖出"); foreach (var item in sellList) { var sp = _helper.CreateTradeSpan(item.Trade).SetAllSpvAndWorst(0); sp.SetWorstCastClientPayable(); resultMap[sp.TradeId] = sp; } //1、买入香草和亚式期权 初始预付金:基于delta算法,max(期权的总Delta *期初价格*交易所预付金比例,0) var VanillaOrAsianList = tradeRiskResult.Results.Where(O => (O.Trade.CalcId == null || (O.Trade.CalcId != null && !O.Trade.CalcId.Contains("-"))) && !resultMap.Keys.Contains(O.Trade.id) && (O.Trade.TradeType.Contains("香草") || O.Trade.TradeType.Contains("亚式")) && O.Trade.BuySell == "买入"); foreach (var item in VanillaOrAsianList) { var deltaMargin = item.ValueResult.Delta * item.Trade.SpotPrice * GetUnderlyingCodeMarginRate(item.Trade.UnderlyingCode); var sp = marginCalculation(req, new List { item.Trade }).First(); var spanMargin = sp.WorstCastClientPayable; deltaMargin = deltaMargin > 0 ? deltaMargin : 0; var margin = Math.Max((deltaMargin ?? 0), (spanMargin ?? 0)); sp.DeltaMargin = deltaMargin; sp.WorstCastClientPayable = margin; resultMap[sp.TradeId] = sp; } //买入宽跨式(跨式)期权组合 var StraddleTradeIds = req.tradeList.Where(O => (O.StructureType ?? O.TradeType).Contains("跨式")).Select(O => O.id); var StraddleResultDict = tradeRiskResult.Results.Where(O => StraddleTradeIds.Contains(O.Trade.id)).GroupBy(O => O.Trade.ParentTradeId).ToDictionary(K => K.Key, V => V.ToList()); foreach (var item in StraddleResultDict) { //获取多个腿的 deltaInitialMargin var deltaInitialMargin = item.Value.Where(l => l.Trade.BuySell == "买入").ToDictionary(k => k.Trade.id, O => O.ValueResult.Delta * O.ValueResult.SpotPrice * GetUnderlyingCodeMarginRate(O.ValueResult.UnderlyingCode)); //获取多个腿的 spanInitialMargin var spanInitialMargin = marginCalculation(req, item.Value.Where(l => l.Trade.BuySell == "买入").Select(l => l.Trade).ToList()).ToDictionary(k => k.TradeId, v => v.WorstCastClientPayable); double margin = 0; int selfLegTradeId = 0; deltaInitialMargin.ToList().Union(spanInitialMargin.ToList()).ToList().ForEach(x => { if (margin < x.Value) { margin = x.Value ?? 0; selfLegTradeId = x.Key; } }); if (item.Value.Where(l => l.Trade.id != selfLegTradeId).Select(l => l.Trade).FirstOrDefault() != null) { var straddleCalcReq = _helper.GetCalculateRisksForTradesReq( req.PriceProvider, null, null, Qdp.Pricing.Base.Implementations.PricingRequest.Pv); straddleCalcReq.tradeList = new List { item.Value.Where(l => l.Trade.id != selfLegTradeId).Select(l => l.Trade).FirstOrDefault() }; var straddleRusult = CalculatorHelper.CalculateRisksForTrades(straddleCalcReq); var t = straddleRusult.Results.FirstOrDefault().Trade; double? otherLegAmount = t.BuySell == "买入" ? Math.Abs(t.TradePrice ?? 0) : 0; for (int i = 0; i < item.Value.Count; i++) { if (item.Value[i].Trade.id == selfLegTradeId) { var sp = _helper.CreateTradeSpan(item.Value[i].Trade).SetAllSpvAndWorst(margin); sp.SetWorstCastClientPayable(); resultMap[item.Value[i].Trade.id] = sp; } else { var sp = _helper.CreateTradeSpan(item.Value[i].Trade).SetAllSpvAndWorst((otherLegAmount ?? 0)); sp.SetWorstCastClientPayable(); resultMap[item.Value[i].Trade.id] = sp; } } } else { for (int i = 0; i < item.Value.Count; i++) { if (item.Value[i].Trade.id == selfLegTradeId) { var sp = _helper.CreateTradeSpan(item.Value[i].Trade).SetAllSpvAndWorst(margin); sp.SetWorstCastClientPayable(); resultMap[item.Value[i].Trade.id] = sp; } } } } //其它奇异期权 初始预付金:用span算 var ExoticOptionsList = req.tradeList.Where(O => !(O.CalcId ?? "").Contains("-") && !resultMap.Keys.Contains(O.id) && O.TradeType != "收益互换" && O.TradeType != "远期" && !O.TradeType.Contains("香草") && !O.TradeType.Contains("亚式") && O.BuySell == "买入"); //牛市价差、熊市价差期权 var NiuxiongTradeIds = req.tradeList.Where(O => ((O.StructureType ?? O.TradeType).Contains("牛市") || (O.StructureType ?? O.TradeType).Contains("熊市"))).Select(l => l.id); var NiuxiongResultDict = tradeRiskResult.Results.Where(O => NiuxiongTradeIds.Contains(O.Trade.id)).GroupBy(O => O.Trade.ParentTradeId).ToDictionary(K => K.Key, V => V.ToList()); foreach (var buyNiuxiong in NiuxiongResultDict) { var margin = buyNiuxiong.Value.Max(O => Math.Max((Math.Abs(O.ValueResult.Delta) / O.Trade.OriginalNotional ?? 0), 0.5) * O.Trade.SpotPrice * O.Trade.OriginalNotional * GetUnderlyingCodeMarginRate(O.Trade.UnderlyingCode)) * 0.5; for (int i = 0; i < buyNiuxiong.Value.Count; i++) { if (buyNiuxiong.Value[i].Trade.BuySell == "买入") { var sp = _helper.CreateTradeSpan(buyNiuxiong.Value[i].Trade).SetAllSpvAndWorst(margin ?? 0); sp.SetWorstCastClientPayable(); resultMap[buyNiuxiong.Value[i].Trade.id] = sp; } else { var sp = _helper.CreateTradeSpan(buyNiuxiong.Value[i].Trade).SetAllSpvAndWorst(0); sp.SetWorstCastClientPayable(); resultMap[buyNiuxiong.Value[i].Trade.id] = sp; } } } //除了宽跨式(跨式)期权、牛熊市价差期权以外的其他组合期权 var OtherTradeList = req.tradeList.Where(O => (O.CalcId ?? "").Contains("-") && !(O.StructureType ?? O.TradeType).Contains("牛市") && !(O.StructureType ?? O.TradeType).Contains("熊市") && !(O.StructureType ?? O.TradeType).Contains("跨式")); var tradeList = ExoticOptionsList.Union(OtherTradeList).ToList(); if (tradeList.Count > 0) { var resultspan = marginCalculation(req, tradeList); foreach (var i in resultspan) { if (resultMap.ContainsKey(i.TradeId)) { resultMap[i.TradeId] = i; } else { resultMap.Add(i.TradeId, i); } } } return resultMap.Values.ToList(); } /// /// 获取预付金比率 /// /// /// public double? GetUnderlyingCodeMarginRate(string UnderlyingCode) { double? MarginRate = 0; var un = DataCacheProvider.GetUnderlyingDataSource().GetData(UnderlyingCode); if (un != null) { MarginRate = DataCacheProvider.GetUnderlyingDataSource().GetData(UnderlyingCode).MarginRate ?? DataCacheProvider.GetVarietyDataSource().GetData(UnderlyingCode).Margin; } return MarginRate; } public List marginCalculation(RunMarginCalculationReq req, List trades) { var thisReq = req.Clone(); var helper = new RunMarginCalculationHelper(thisReq, _underlyingDataProvider); var resultMap = new Dictionary(); if (trades != null && trades.Any()) { //处理香草,亚式,跨式 thisReq.tradeList = trades; } helper.SetFieldsByTradeType(); helper.GetUpDownLimitPrices(out var upLimitPrices, out var downLimitPrices); helper.GetTradVolRateDic(out var tradeVolRateDic); var vols = new[] { null, tradeVolRateDic }; var prices = new (string key, IPriceProvider priceProvider)[] { ("up", upLimitPrices), ("down", downLimitPrices), ("normal", thisReq.PriceProvider) }; var loops = prices.SelectMany(n => vols.Select(m => new { pricekey = n.key, priceProvider = n.priceProvider, addVolRateDic = m })).ToArray(); var uProvider = new UnderlyingDataProvider(); foreach (var loop in loops) { var calcReq = helper.GetCalculateRisksForTradesReq(priceProvider: loop.priceProvider, addVolRateDic: loop.addVolRateDic, overrideVols: null, pricingRequest: QdpPricingRequest.BASIC_PRICING); var tradeRiskResult = CalculatorHelper.CalculateRisksForTrades(calcReq); if (tradeRiskResult.Results == null || tradeRiskResult.Results.Count < 1) { continue; } var key = $"{loop.pricekey}_{(loop.addVolRateDic == null ? 0 : 1)}"; if (key == "normal_1") continue; foreach (var item in tradeRiskResult.Results) { if ((item.Trade.TradeType == "远期" || item.Trade.TradeType == "收益互换")) { continue; } 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 "normal_0": if (!(item.Trade.StructureType ?? item.Trade.TradeType).Contains("跨式")) { var um = uProvider.GetUnderlying(item.Trade.UnderlyingCode); var variety = uProvider.GetVariety(item.Trade.UnderlyingCode); var marginRate = ((um?.MarginRate ?? 0) > 0 ? um.MarginRate : variety?.Margin) ?? 0; double deltaCash = 0; if ((item.Trade.BuySell == "买入" && item.Trade.OptionType == "看涨") || (item.Trade.BuySell == "卖出" && item.Trade.OptionType == "看跌")) { deltaCash = Math.Abs(item.ValueResult.DeltaCash); } else { deltaCash = -Math.Abs(item.ValueResult.DeltaCash); } value = deltaCash * marginRate; tempTradeSpan.DeltaMargin = value; tempTradeSpan.PositionWinLoss = EodOperationBase.GetPositionPnl(item.ValueResult.Pv, item.Trade.TradePrice ?? 0.0, item.Trade.Notional, item.Trade.OriginalNotional ?? 0, item.Trade.BuySell); } break; case "down_0": tempTradeSpan.Spv3 = value; break; case "down_1": tempTradeSpan.Spv4 = value; break; } if (contains) { tempTradeSpan.SetWorstCastClientPayable(); if (tempTradeSpan.WorstCastClientPayable > 0) { tempTradeSpan.WorstCastClientPayable = Math.Max(tempTradeSpan.WorstCastClientPayable.Value, Math.Abs(tempTradeSpan.DeltaMargin ?? 0) + (tempTradeSpan.PositionWinLoss ?? 0)); } else { 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 tradeTemplates = db.trade_margin_template.AsNoTracking().Where(t => tradeIds.Contains(t.TradeId) && t.ValueDate <= req.settleDate); var groupQuery = from tt in tradeTemplates group tt by tt.TradeId into tts select new { TradeId = tts.Key, ValueDate = tts.Max(n => n.ValueDate) }; var groupTemplates = from gq in groupQuery join tt in tradeTemplates on new { gq.TradeId, gq.ValueDate } equals new { tt.TradeId, tt.ValueDate } select tt; var tradeSpanInfo = (from tradeSpan in req.tradeSpans join trade in tradeList on tradeSpan.TradeId equals trade.id join tradetemplate in groupTemplates on tradeSpan.TradeId equals tradetemplate.TradeId into templates from tradetemplate in templates.DefaultIfEmpty() where tradeSpan.ValueDate == req.settleDate select new { trade, tradeSpan, tradetemplate }).ToList(); if (req.tradeSpans != null && req.tradeSpans.Count > 0) { var spans = req.tradeSpansOtherSide != null ? req.tradeSpansOtherSide.Where(n => n != null).ToArray() : Enumerable.Empty(); var tradeSpanInfoOtherSide = (from tradeSpan in spans 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).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), DeltaMargin = t.Sum(g => g.tradeSpan.DeltaMargin) * (-1), PositionWinLoss = t.Sum(g => g.tradeSpan.PositionWinLoss) * (-1), OptId = req.userId, OptName = req.userName, OptDate = DateTime.Now, SpanType = req.SpanType }).ToList(); foreach (var item in underlyingGroup) { var tradeSpanIds = clientGroup.Where(O => O.trade.MarginType == DBModels.Enums.MarginTypeEnum.DEFAULT).Select(x => x.tradeSpan.id); var tradeSpansUpdate = db.trade_span.Where(x => tradeSpanIds.Contains(x.id) && x.ClientId == item.ClientId && x.UnderlyingId == item.UnderlyingId && x.ValueDate == req.settleDate).ToList(); var tradeSpansReq = req.tradeSpans.Where(x => tradeSpanIds.Contains(x.id) && x.ClientId == item.ClientId && x.UnderlyingId == item.UnderlyingId && x.ValueDate == req.settleDate).ToList(); var minSpv = Math.Min(Math.Min(Math.Min(item.Spv1 ?? 0, item.Spv2 ?? 0), item.Spv3 ?? 0), item.Spv4 ?? 0); if (minSpv < 0) { item.WorstCastClientPayable = Math.Max(Math.Abs(minSpv), Math.Abs(item.DeltaMargin ?? 0) - (item.PositionWinLoss ?? 0)) * (-1); item.TwoSideMargin = item.WorstCastClientPayable; if (Math.Abs(minSpv) > Math.Abs(item.DeltaMargin ?? 0) - (item.PositionWinLoss ?? 0)) { //更新tradeSpan,使得每笔交易的持仓预付金和客户预付金计算用的Spv组保持一致 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 { tradeSpansUpdate.ForEach(x => x.WorstCastClientPayable = x.DeltaMargin); tradeSpansReq.ForEach(x => x.WorstCastClientPayable = x.DeltaMargin); } } else { item.WorstCastClientPayable = 0; item.TwoSideMargin = 0; tradeSpansUpdate.ForEach(x => x.WorstCastClientPayable = 0); tradeSpansReq.ForEach(x => x.WorstCastClientPayable = 0); } } 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), DeltaMargin = underlyingGroup.Sum(g => g.DeltaMargin), //负数代表客户应缴预付金,正数代表客户应收预付金 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).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.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); 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(); } 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; } } } }