using YLErp.BLL.Calculation; using YLErp.Enums; using YLErp.Helpers; using YLErp.Modules; using YLErp.Modules.DataProviderModule; using YLErp.Modules.MarginModule; using YLErp.QdpModule; namespace YLErp.BLL.MarginCalculation { /// /// 格林大华 /// public class GLDHMarginCalculation : MarginCalculationBase { // 定义一个静态变量来保存类的实例 public static readonly GLDHMarginCalculation Instance; static GLDHMarginCalculation() { Instance = new GLDHMarginCalculation(); } // 定义私有构造函数,使外界不能创建该类实例 private GLDHMarginCalculation() { } 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 calcInitialMargin(RunMarginCalculationReq req) { Dictionary resultMap = new Dictionary(); var _helper = new RunMarginCalculationHelper(req, _underlyingDataProvider); var marginProvider = _helper.GetMarginParamProvider(Modules.MarginModule.MarginParamTypeEnum.MarginRate); var tradeTypes = new List() { "远期", "收益互换" }; var forword = req.tradeList.Where(O => O.TradeType == "远期"); foreach (var item in forword) { var margin = item.Notional * (item.SpotPrice ?? 0) * (marginProvider.TryGetMarginRate(item.UnderlyingCode, out var m) ? m : 0); var sp = _helper.CreateTradeSpan(item).SetAllSpvAndWorst(margin); LogFactory.GetLogger().Debug($"{margin}={item.Notional}*{item.SpotPrice}* {(marginProvider.TryGetMarginRate(item.UnderlyingCode, out var ms) ? ms : 0)})"); sp.SetWorstCastClientPayable(); resultMap[sp.TradeId] = sp; } var swap = req.tradeList.Where(O => O.TradeType == "收益互换"); tradeBLL.SetFieldsByTradeType(swap); foreach (var item in swap) { var rate = (item.trade_swap?.GetMarginRate ?? 0) - (item.trade_swap?.PayMarginRate ?? 0); var margin = 0d; if (rate > 0) { margin = item.StockEqvNotional * rate; var client = DataCacheProvider.GetClientDataSource().GetData(item.ClientId); var clientLevel = DataCacheProvider.GetClientLevelDataSource().GetData(client.LevelId ?? 0); var clientRatio = clientLevel?.Ratio ?? 1.0; margin *= clientRatio; } var sp = _helper.CreateTradeSpan(item).SetAllSpvAndWorst(margin); sp.SetWorstCastClientPayable(); resultMap[sp.TradeId] = sp; } //所有客户买入的交易都不收取预付金,包含买入单腿,跨式期权,风险反转期权以及其他多腿组合期权(买入牛市价差或熊市价差时,卖出的那条腿也不收取预付金) var sellList = req.tradeList.Where(O => !tradeTypes.Contains(O.TradeType) && O.BuySell == "卖出"); foreach (var item in sellList) { var sp = _helper.CreateTradeSpan(item).SetAllSpvAndWorst(0); sp.SetWorstCastClientPayable(); resultMap[sp.TradeId] = sp; } var buyList = req.tradeList.Where(O => !tradeTypes.Contains(O.TradeType) && !resultMap.Keys.Contains(O.id)); var calcReq = _helper.GetCalculateRisksForTradesReq( req.PriceProvider, null, null, Qdp.Pricing.Base.Implementations.PricingRequest.Delta); calcReq.tradeList = buyList; var tradeRiskResult = CalculatorHelper.CalculateRisksForTrades(calcReq); //客户卖出跨式期权 //初始预付金 = max(D1,D2)×S×交易所期货预付金率×数量; //其中 D1,D2 分别为跨式期权两腿的 Delta 绝对值。 var tradeIds = buyList.Where(O => (O.StructureType ?? O.TradeType).Contains("跨式") && O.BuySell == "买入").Select(O => O.id); var resultDict = tradeRiskResult.Results.Where(O => tradeIds.Contains(O.Trade.id)).GroupBy(O => O.Trade.ParentTradeId).ToDictionary(K => K.Key, V => V.ToList()); foreach (var item in resultDict) { var margin = item.Value .Max(O => Math.Abs(O.ValueResult.DeltaCash) * (marginProvider.TryGetMarginRate(O.Trade.UnderlyingCode, out var m) ? m : 0)); var sp = _helper.CreateTradeSpan(item.Value[0].Trade).SetAllSpvAndWorst(margin); sp.SetWorstCastClientPayable(); resultMap[item.Value[0].Trade.id] = sp; for (int i = 1; i < item.Value.Count; i++) { sp = _helper.CreateTradeSpan(item.Value[i].Trade).SetAllSpvAndWorst(0); sp.SetWorstCastClientPayable(); resultMap[item.Value[i].Trade.id] = sp; } } //客户卖出单腿期权 //初始预付金 = D×S×交易所期货预付金率×数量; tradeIds = buyList.Where(O => !((O.StructureType ?? O.TradeType).Contains("跨式")) && O.BuySell == "买入").Select(O => O.id); var resultList = tradeRiskResult.Results.Where(O => tradeIds.Contains(O.Trade.id)); resultList.ToList().ForEach(O => { var margin = Math.Abs(O.ValueResult.DeltaCash * (marginProvider.TryGetMarginRate(O.Trade.UnderlyingCode, out var m) ? m : 0)); LogFactory.GetLogger().Info("初始预付金 = D×S×交易所期货预付金率×数量;"); LogFactory.GetLogger().Info($"{margin}=Math.Abs({O.ValueResult.DeltaCash} * {(marginProvider.TryGetMarginRate(O.Trade.UnderlyingCode, out var ms) ? ms : 0)})"); var sp = _helper.CreateTradeSpan(O.Trade).SetAllSpvAndWorst(margin); sp.SetWorstCastClientPayable(); resultMap[sp.TradeId] = sp; }); return resultMap.Values.ToList(); } /// /// 计算每笔预付金 /// /// /// private List calcPositionMargin(RunMarginCalculationReq req) { var resultMap = new Dictionary(); var tradeTypes = new List() { "远期", "收益互换" }; //结算s req.PriceProvider = new EodPriceProvider(req.settleDate).GetPriceProvider(SettlementTypeEnum.SettlePrice); //收盘 var closePriceProvider = new EodPriceProvider(req.settleDate).GetPriceProvider(SettlementTypeEnum.ClosePrice); var _helper = new RunMarginCalculationHelper(req, _underlyingDataProvider); var marginProvider = _helper.GetMarginParamProvider(Modules.MarginModule.MarginParamTypeEnum.MarginRate); //获取涨跌停价格字典 _helper.GetUpDownLimitPrices(out var upPrices, out var downPrices); //获取持仓波动率字典 _helper.GetUpDownVolRateDic(out var upVolRateDic, out _); var clientGroup = req.tradeList.GroupBy(O => new { O.ClientId, O.UnderlyingCode }).ToDictionary(K => K.Key, V => V.ToList()); foreach (var item in clientGroup) { var forword = req.tradeList.Where(O => O.TradeType == "远期"); foreach (var t in forword) { var margin = t.Notional * (t.SpotPrice ?? 0) * (marginProvider.TryGetMarginRate(t.UnderlyingCode, out var m) ? m : 0); LogFactory.GetLogger().Info($"该交易{t.TradeNumber}id{t.id};远期" + $"{margin} ={t.Notional * (t.SpotPrice ?? 0) * (marginProvider.TryGetMarginRate(t.UnderlyingCode, out var s) ? s : 0)}"); var sp = _helper.CreateTradeSpan(t).SetAllSpvAndWorst(margin); sp.SetWorstCastClientPayable(); resultMap[sp.TradeId] = sp; } var swap = req.tradeList.Where(O => O.TradeType == "收益互换"); tradeBLL.SetFieldsByTradeType(swap); foreach (var t in swap) { var rate = (t.trade_swap?.GetMarginRate ?? 0) - (t.trade_swap?.PayMarginRate ?? 0); var margin = 0d; if (rate > 0) { margin = t.StockEqvNotional * (marginProvider.TryGetMarginRate(t.UnderlyingCode, out var m) ? m : 0); LogFactory.GetLogger().Info($"该交易{t.TradeNumber}id{t.id}收益互换{margin}"); var client = DataCacheProvider.GetClientDataSource().GetData(t.ClientId); var clientLevel = DataCacheProvider.GetClientLevelDataSource().GetData(client.LevelId ?? 0); var clientRatio = clientLevel?.Ratio ?? 1.0; margin *= clientRatio; LogFactory.GetLogger().Info($"该交易 margin *= clientRatio {t.TradeNumber} id{t.id}收益互换{margin}"); } var sp = _helper.CreateTradeSpan(t).SetAllSpvAndWorst(margin); sp.SetWorstCastClientPayable(); resultMap[sp.TradeId] = sp; } /*计算总维持预付金时,先分标的计算,再进行汇总计算。 * 如果客户在标的 i 上只有买权合约,则该标的不计入预付金计算范围; * 如果客户在在标的 i 上有卖权合约,则该标的的所有合约都纳入预付金计算范围。 */ var option = item.Value.Where(O => !tradeTypes.Contains(O.TradeType)); if (option.All(O => O.BuySell == "卖出")) { foreach (var t in option) { var sp = _helper.CreateTradeSpan(t).SetAllSpvAndWorst(0); sp.SetWorstCastClientPayable(); resultMap[sp.TradeId] = sp; } } else { var prices = new Dictionary>(); var umCodeList = option.Select(O => O.UnderlyingCode).ToHashSet(); foreach (var code in umCodeList) { prices[1] = new Dictionary(); prices[2] = new Dictionary(); prices[3] = new Dictionary(); prices[4] = new Dictionary(); prices[5] = new Dictionary(); prices[6] = new Dictionary(); prices[7] = new Dictionary(); prices[8] = new Dictionary(); var upPrice = upPrices.GetPrice(code); var downPrice = downPrices.GetPrice(code); LogFactory.GetLogger().Info($"涨跌价格:{upPrice} {downPrice}"); var interval = (upPrice - downPrice) / 6; var settlePrice = req.PriceProvider.GetPrice(code); LogFactory.GetLogger().Info($"settlePrice:{settlePrice} interval:=(upPrice - downPrice) / 6={interval}={(upPrice - downPrice)} / {6}"); prices[1][code] = settlePrice - 3 * interval; LogFactory.GetLogger().Info($"格林持仓预付金1:prices[1][{code}] = settlePrice - 3 * interval;{prices[1][code]}={settlePrice}- 3 * {interval}"); prices[2][code] = settlePrice - 2 * interval; LogFactory.GetLogger().Info($"格林持仓预付金2:prices[2][{code}] = settlePrice - 2 * interval:{prices[2][code]}={settlePrice}- 2 * {interval}"); prices[3][code] = settlePrice - 1 * interval; LogFactory.GetLogger().Info($"格林持仓预付金3:prices[3][{code}] = settlePrice - 1 * interval:{prices[3][code]}={settlePrice}- 1 * {interval}"); prices[4][code] = settlePrice; LogFactory.GetLogger().Info($"格林持仓预付金4:prices[4][{code}] = settlePrice:{prices[4][code] = settlePrice}"); prices[5][code] = settlePrice + 1 * interval; LogFactory.GetLogger().Info($"格林持仓预付金5:prices[5][{code}] = settlePrice - 1 * interval:{prices[5][code]}={settlePrice}+ 1 * {interval}"); prices[6][code] = settlePrice + 2 * interval; LogFactory.GetLogger().Info($"格林持仓预付金6:prices[6][{code}] = settlePrice - 2 * interval:{prices[6][code]}={settlePrice}+ 2 * {interval}"); prices[7][code] = settlePrice + 3 * interval; LogFactory.GetLogger().Info($"格林持仓预付金7:prices[7][{code}] = settlePrice - 3 * interval:{prices[7][code]} ={settlePrice}+3 * {interval}"); prices[8][code] = closePriceProvider.GetPrice(code); LogFactory.GetLogger().Info($"格林持仓预付金8收盘价算Detal:{prices[8][code] = closePriceProvider.GetPrice(code)}"); } foreach (var p in prices) { var tradeRiskResult = CalculatorHelper.CalculateRisksForTrades( valueDate: req.settleDate, calcScenario: req.GetCalcScenario(), tradeList: item.Value, priceProvider: new ManualPriceProvider(p.Value), pricingRequest: QdpPricingRequest.BASIC_PRICING, addVolRateDic: p.Key < 8 ? upVolRateDic : null, //1~7为PM,波动率应当上浮,第8个为DM,波动率不用变化 volType: req.volType, isUseTradeVol: PS.Config.IsTradeVol, preciseTimeMode: req.CalcMarginType != CalcMarginTypeEnum.EodMargin, isAddVolPercent: true); foreach (var risk in tradeRiskResult.Results) { var code = risk.Trade.UnderlyingCode; var pric = p.Value[risk.Trade.UnderlyingCode]; LogFactory.GetLogger().Info($"格林持仓预付金该交易编号:{risk.Trade.TradeNumber} id:{risk.Trade.id}标的:{code} 交易价格为:{pric}"); if (!resultMap.TryGetValue(risk.Trade.id, out var tradeSpan)) { resultMap[risk.Trade.id] = tradeSpan = _helper.CreateTradeSpan(risk.Trade); } switch (p.Key) { case 1: tradeSpan.Spv1 = risk.ValueResult.Pv; LogFactory.GetLogger().Info($"格林持仓预付金该交易的PV1为:{tradeSpan.Spv1}"); break; case 2: tradeSpan.Spv2 = risk.ValueResult.Pv; LogFactory.GetLogger().Info($"格林持仓预付金该交易的PV2为:{tradeSpan.Spv2}"); break; case 3: tradeSpan.Spv3 = risk.ValueResult.Pv; LogFactory.GetLogger().Info($"格林持仓预付金该交易的PV3为:{tradeSpan.Spv3}"); break; case 4: tradeSpan.Spv4 = risk.ValueResult.Pv; LogFactory.GetLogger().Info($"格林持仓预付金该交易的PV4为:{tradeSpan.Spv4}"); break; case 5: tradeSpan.Spv5 = risk.ValueResult.Pv; LogFactory.GetLogger().Info($"格林持仓预付金该交易的PV5为:{tradeSpan.Spv5}"); break; case 6: tradeSpan.Spv6 = risk.ValueResult.Pv; LogFactory.GetLogger().Info($"格林持仓预付金该交易的PV6为:{tradeSpan.Spv6}"); break; case 7: tradeSpan.Spv7 = risk.ValueResult.Pv; LogFactory.GetLogger().Info($"格林持仓预付金该交易的PV7为:{tradeSpan.Spv7}"); break; case 8: tradeSpan.Spv8 = risk.ValueResult.Pv; LogFactory.GetLogger().Info($"格林持仓预付金该交易的PV8为:{tradeSpan.Spv8}"); tradeSpan.DeltaMargin = risk.ValueResult.DeltaCash; break; default: break; } //第八个pv不参与pm计算 if (p.Key < 8) { tradeSpan.SetWorstCastClientPayable(); } } } } } return resultMap.Values.ToList(); } /// /// 计算每个客户预付金 /// /// /// public override List CalcClientMargin(CalcClientMarginReq req) { var clientSpanNews = new List(); var _mpProvider = new MarginParamProvider(req.UserInfo, req.settleDate); using (var db = new YLContext()) { 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 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 umCode = tradeList.Select(O => O.UnderlyingCode).ToHashSet(); var marginProvider = _mpProvider.Initialize(umCode, MarginParamTypeEnum.MarginRate); var clientGroups = tradeSpanInfo.GroupBy(t => t.trade.ClientId); foreach (var item in clientGroups) { var tradeTypes = new List() { "远期", "收益互换" }; var forword = item.Where(O => O.trade.TradeType == "远期"); var swap = item.Where(O => O.trade.TradeType == "收益互换"); var worstCastClientPayable_forward = -forword.Sum(O => O.tradeSpan.WorstCastClientPayable ?? 0); LogFactory.GetLogger().Info($"格林维持预付金远期为:{worstCastClientPayable_forward}"); var worstCastClientPayable_swap = -swap.Sum(O => O.tradeSpan.WorstCastClientPayable ?? 0); LogFactory.GetLogger().Info($"格林维持预付金收益互换为:{worstCastClientPayable_swap}"); var worstCastClientPayable_option = 0d; var option = item.Where(O => !tradeTypes.Contains(O.trade.TradeType)); if (option.Any(O => O.trade.BuySell == "买入")) { var dm = option.Sum(O => { var margin = (marginProvider.TryGetMarginRate(O.trade.UnderlyingCode, out var m) ? m : 0); var a = Math.Abs((O.tradeSpan.DeltaMargin ?? 0) * margin) - O.tradeSpan.Spv8 ?? 0; LogFactory.GetLogger().Info($"格林维持保证的dm为:{a}"); return Math.Abs((O.tradeSpan.DeltaMargin ?? 0) * margin) - O.tradeSpan.Spv8 ?? 0; }); dm = Math.Max(dm, 0); LogFactory.GetLogger().Info($"格林维持保证最大的dm:{dm}"); //计算标的 i 在上 7 种情形下客户持仓的持仓价值 MVi,k(k=1,2,3…7)(客户卖出期权,期权价值取负值), //则客户在标的 i 上隔日最大亏损 Li = -min(Vi,k),对第 i 个标的取维持预付金 PMi = Max(Li,0); //var pm = option.GroupBy(O => O.trade.UnderlyingCode).Sum(O => Math.Max(O.Sum(B => B.tradeSpan.WorstCastClientPayable ?? 0), 0)); var underlyingGroup = option.GroupBy(O => O.trade.UnderlyingCode); var pmList = new List(); foreach (var unItem in underlyingGroup) { var list = new List() { unItem.Sum(O => O.tradeSpan.Spv1??0), unItem.Sum(O => O.tradeSpan.Spv2??0), unItem.Sum(O => O.tradeSpan.Spv3??0), unItem.Sum(O => O.tradeSpan.Spv4??0), unItem.Sum(O => O.tradeSpan.Spv5??0), unItem.Sum(O => O.tradeSpan.Spv6??0), unItem.Sum(O => O.tradeSpan.Spv7??0), }; LogFactory.GetLogger().Info($"格林维持预付金 7 种情形下客户持仓值为1:" + $"{unItem.Sum(O => O.tradeSpan.Spv1 ?? 0)}" + " 2:" + $"{unItem.Sum(O => O.tradeSpan.Spv2 ?? 0)}" + " 3:" + $"{unItem.Sum(O => O.tradeSpan.Spv3 ?? 0)}" + " 4:" + $"{unItem.Sum(O => O.tradeSpan.Spv4 ?? 0)}" + " 5:" + $"{unItem.Sum(O => O.tradeSpan.Spv5 ?? 0)}" + " 6:" + $"{unItem.Sum(O => O.tradeSpan.Spv6 ?? 0)}" + " 7:" + $"{unItem.Sum(O => O.tradeSpan.Spv7 ?? 0)} 代码角度若为卖出期权则为负数(-min)所以最小的加-则为正数"); var pmi = list.Max(O => O); LogFactory.GetLogger().Info($"格林维持预付金PM为Max(Li,0){pmi}"); pmList.Add(pmi); } var pm = pmList.Sum(); worstCastClientPayable_option = Math.Max(dm, pm); LogFactory.GetLogger().Info($"格林维持保证标的i维持预付金M=Max(dm,pm){worstCastClientPayable_option}={Math.Max(dm, pm)}"); LogFactory.GetLogger().Info($"格林维持保证标的i维持预付金M=Max(dm,pm){worstCastClientPayable_option}"); #region 更新tradeSpan,使得每笔交易的持仓预付金和客户预付金计算用的Spv组保持一致 var sumDelta = option.Sum(O => Math.Abs(O.tradeSpan.DeltaMargin ?? 0)); tradeIds = option.Select(x => x.trade.id).ToList(); var tradeSpansUpdate = db.trade_span.Where(x => tradeIds.Contains(x.TradeId) && x.ClientId == item.Key && x.ValueDate == req.settleDate).ToList(); var tradeSpansReq = req.tradeSpans.Where(x => tradeIds.Contains(x.TradeId) && x.ClientId == item.Key && x.ValueDate == req.settleDate).ToList(); tradeSpansUpdate.ForEach(x => x.WorstCastClientPayable = worstCastClientPayable_option * Math.Abs(x.DeltaMargin ?? 0) / sumDelta); tradeSpansReq.ForEach(x => x.WorstCastClientPayable = worstCastClientPayable_option * Math.Abs(x.DeltaMargin ?? 0) / sumDelta); #endregion } var worstCastClientPayable = -worstCastClientPayable_option + worstCastClientPayable_forward + worstCastClientPayable_swap; LogFactory.GetLogger().Info($"{worstCastClientPayable = -worstCastClientPayable_option + worstCastClientPayable_forward + worstCastClientPayable_swap}"); var clientSpan = new ClientSpan { ClientId = item.Key, ValueDate = req.settleDate, Spv1 = worstCastClientPayable, Spv2 = worstCastClientPayable, Spv3 = worstCastClientPayable, Spv4 = worstCastClientPayable, //负数代表客户应缴预付金,正数代表客户应收预付金 WorstCastClientPayable = worstCastClientPayable, SwapWorstCastClientPayable = worstCastClientPayable_swap, MySideMargin = worstCastClientPayable, OptId = req.userId, OptName = req.userName, OptDate = DateTime.Now, SpanType = req.SpanType, AdditionalWorstCastClientPayable = req.clientAdditionalMarginDic != null && req.clientAdditionalMarginDic.TryGetValue(item.Key, 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)}={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.client_span.AddRange(clientSpanNews); } db.SaveChanges(); } return req.tradeSpans; } public override double GetTradeMargin(GetTradeMarginReq req) { var trade = req.trade; using (YLContext 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) { return tradeMargin.FirstOrDefault()?.WorstCastClientPayable ?? 0.0; } return 0.0; } } }