using YLErp.BLL.Calculation; using YLErp.BLL.Eod; using YLErp.DBModels.Helpers; using YLErp.Enums; using YLErp.Helpers; using YLErp.QdpModule; namespace YLErp.BLL.MarginCalculation { /// /// 渤海荣盛 /// public class BHRSMarginCalculation : MarginCalculationBase { // 定义一个静态变量来保存类的实例 public static readonly BHRSMarginCalculation Instance; static BHRSMarginCalculation() { Instance = new BHRSMarginCalculation(); } // 定义私有构造函数,使外界不能创建该类实例 private BHRSMarginCalculation() { } public override List RunMarginCalculation(RunMarginCalculationReq req) { var tradeSpans = new List(); if (req?.tradeList == null || !req.tradeList.Any()) { return tradeSpans; } var helper = new RunMarginCalculationHelper(req, _underlyingDataProvider); var tempStockTradeList = req.tradeList.Where(t => t.UnderlyingInstrumentType == ConsGlobal.InstrumentType.Stock).ToList(); if (tempStockTradeList.Any()) { var stockTradeSpanlist = StockMarginCalculation(req.Clone(tempStockTradeList), helper); if (stockTradeSpanlist.Count > 0) { tradeSpans.AddRange(stockTradeSpanlist); } } var tempFutureTradeList = req.tradeList.Where(t => t.UnderlyingInstrumentType == ConsGlobal.InstrumentType.CommodityFutures).ToList(); if (tempFutureTradeList.Any()) { var tradeList = tempFutureTradeList.Where(a => a.TradeType != "远期").ToList(); if (tradeList.Any()) { req = req.Clone(tradeList); var futureTradeSpanlist = FutureMarginCalculation(req, helper); if (futureTradeSpanlist.Count > 0) { tradeSpans.AddRange(futureTradeSpanlist); } } var forwardTradeList = tempFutureTradeList.Where(a => a.TradeType == "远期").ToList(); if (forwardTradeList.Any()) { req = req.Clone(forwardTradeList); var futureTradeSpanlist = ForwardMarginCalculation(req, helper); if (futureTradeSpanlist.Count > 0) { tradeSpans.AddRange(futureTradeSpanlist); } } } return tradeSpans; } /// /// 股票类期权计算预付金 /// private List StockMarginCalculation(RunMarginCalculationReq req, RunMarginCalculationHelper helper) { var tradeSpans = new List(); if (req?.tradeList == null || !req.tradeList.Any()) { return tradeSpans; } var marginRation = valuedateBLL.SystemDate.MarginRatio ?? 0.15; foreach (var t in req.tradeList) { var client = helper.GetClient(t.ClientId); if (client == null) { continue; } double twoSideMargin; if (helper.GetSpecialMargin(t, 0, out var value)) { twoSideMargin = value; } else { var clientRatio = client?.Ratio ?? 1.0; //如果是股票去名义本金,如果是期货取:份额 * 即期价格 var baseValue = (t.StockEqvNotional == 0 ? TradeHelper.GetStockEqvNotional(t.Notional * t.SpotPrice, t.ParticipationRate, t.AnnualizeFactor) : t.StockEqvNotional) * marginRation; value = baseValue * (t.BuySell == "买入" ? 1 : (PS.Config.ErpElement.TwoSideMargin && client.MarginOptionType == 1 ? -1 : 0)) * clientRatio; twoSideMargin = baseValue * (t.BuySell == "买入" ? 1 : -1) * clientRatio; } tradeSpans.Add(new trade_span { TradeId = t.id, OptDate = DateTime.Now, OptId = req.userId, OptName = req.userName, ClientId = client.ClientId, UnderlyingId = t.UnderlyingId, UnderlyingCode = t.UnderlyingCode, ValueDate = req.settleDate, Spv1 = value, Spv2 = value, Spv3 = value, Spv4 = value, WorstCastClientPayable = value, TwoSideMargin = twoSideMargin }); } return tradeSpans; } /// /// 商品期权计算预付金 /// private List FutureMarginCalculation(RunMarginCalculationReq req, RunMarginCalculationHelper helper) { var futureTradeList = req.tradeList; var tradeSpans = new List(); using (var db = new YLContext()) { if (!req.hasOptionInfo) { tradeBLL.SetFieldsByTradeType(futureTradeList); } var tradeRiskResult = CalculatorHelper.CalculateRisksForTrades( valueDate: req.settleDate, tradeList: futureTradeList, calcScenario: req.GetCalcScenario(), priceProvider: req.PriceProvider, pricingRequest: QdpPricingRequest.BASIC_GREEKS, addVolRateDic: null, volType: req.volType, settlementType: req.settlementType, isUseTradeVol: PS.Config.IsTradeVol, preciseTimeMode: req.CalcMarginType != CalcMarginTypeEnum.EodMargin); logger.Info($"商品期货预付金计算,交易入:[{futureTradeList.Count}条],返回[{tradeRiskResult.Results.Count}],错误:{tradeRiskResult.ErrorMessage}"); //计算预付金包含错误信息时弹出错误信息 if (!string.IsNullOrWhiteSpace(tradeRiskResult.ErrorMessage)) { throw new Exception(tradeRiskResult.ErrorMessage); } if (tradeRiskResult.Results.Count < 1) { return tradeSpans; } var vegaCoefficient = valuedateBLL.SystemDate.FutureMarginVegaCoefficient ?? 3; var omegaCoefficient = valuedateBLL.SystemDate.FutureMarginOmegaCoefficient ?? 2.5; var alphaRate = valuedateBLL.SystemDate.FutureMarginAlphaRate ?? 1; foreach (var item in tradeRiskResult.Results) { var tempVariety = _underlyingDataProvider.GetVariety(item.Trade.UnderlyingCode); double closePrice = 0.0; switch (req.CalcMarginType) { case CalcMarginTypeEnum.None: case CalcMarginTypeEnum.EodMargin: closePrice = req.PriceProvider.GetPrice(item.Trade.UnderlyingCode); break; case CalcMarginTypeEnum.InitialMargin: closePrice = item.Trade.SpotPrice ?? 0; break; default: break; } var client = helper.GetClient(item.Trade.ClientId); if (item.ValueResult == null) { throw new Exception("PV结算结果为null"); } var deltaMerge = item.ValueResult.Delta; var vegaMerge = item.ValueResult.Vega; var gammaMerge = item.ValueResult.Gamma; var pvMerge = item.ValueResult.Pv; var PositionPnl = req.CalcMarginType == CalcMarginTypeEnum.InitialMargin ? 0 : pvMerge - (EodOperationBase.GetPositionCost(item.Trade.TradePrice ?? 0, item.Trade.Notional, item.Trade.OriginalNotional ?? 0, item.Trade.BuySell)); PositionPnl = PositionPnl > 0 ? PositionPnl : 0; bool isTwoSide = HasTwoSideMargin(item.Trade.ClientId); //逐笔预付金算法 var margin = MarginAlgorithm(deltaMerge, pvMerge, closePrice, isTwoSide, tempVariety.Margin ?? 0, item.Trade, PositionPnl); if (item.Trade.TradeType == "自定义交易") { if (helper.GetSpecialMargin(item.Trade, 0, out var value)) { margin = value; } } tradeSpans.Add(new trade_span { TradeId = item.Trade.id,//默认记录为第一条交易记录中 OptDate = DateTime.Now, OptId = req.userId, OptName = req.userName, ClientId = item.Trade.ClientId, ValueDate = req.settleDate, UnderlyingId = item.Trade.UnderlyingId, UnderlyingCode = item.Trade.UnderlyingCode, DeltaMargin = margin, WorstCastClientPayable = margin, Comment = $"delta:{deltaMerge},closePrice:{closePrice},pv:{pvMerge}" + $",gamma{gammaMerge},交易员方向:{item.Trade.BuySell},持仓盈亏:{PositionPnl},预付金率:{tempVariety.Margin},单双向:{client.MarginOptionType}" }); } return tradeSpans; } } private List ForwardMarginCalculation(RunMarginCalculationReq req, RunMarginCalculationHelper helper) { var futureTradeList = req.tradeList; var tradeSpans = new List(); using (var db = new YLContext()) { if (!req.hasOptionInfo) { tradeBLL.SetFieldsByTradeType(futureTradeList); } var tradeRiskResult = CalculatorHelper.CalculateRisksForTrades( valueDate: req.settleDate, tradeList: futureTradeList, calcScenario: req.GetCalcScenario(), priceProvider: req.PriceProvider, pricingRequest: QdpPricingRequest.BASIC_GREEKS, addVolRateDic: null, volType: req.volType, isUseTradeVol: PS.Config.IsTradeVol, preciseTimeMode: req.CalcMarginType != CalcMarginTypeEnum.EodMargin); logger.Info($"远期预付金计算,交易入:[{futureTradeList.Count}条],返回[{tradeRiskResult.Results.Count}],错误:{tradeRiskResult.ErrorMessage}"); //计算预付金包含错误信息时弹出错误信息 if (!string.IsNullOrWhiteSpace(tradeRiskResult.ErrorMessage)) { throw new Exception(tradeRiskResult.ErrorMessage); } if (tradeRiskResult.Results.Count < 1) { return tradeSpans; } var vegaCoefficient = valuedateBLL.SystemDate.FutureMarginVegaCoefficient ?? 3; var omegaCoefficient = valuedateBLL.SystemDate.FutureMarginOmegaCoefficient ?? 2.5; var alphaRate = valuedateBLL.SystemDate.FutureMarginAlphaRate ?? 1; //如果持仓中的所有远期delta方向相同,那么无视delta方向,每笔远期交易都按照delta绝对值计算margin; bool IsDeltaMerge = tradeRiskResult.Results.All(a => a.ValueResult.Delta >= 0) || tradeRiskResult.Results.All(a => a.ValueResult.Delta <= 0); var tradeIds = futureTradeList.Where(O => O.id > 0).Select(O => O.id); var tradeCashDict = db.trade_cash.Where(O => tradeIds.Contains(O.TradeId) && O.ValidState != "InValid" && !O.IsDeleted && O.Action != ClientCashInCashOut.系统操作_期权费) .AsEnumerable() .GroupBy(O => O.TradeId) .ToDictionary(K => K.Key, V => V.ToList()); var marginCostDict = db.eod_forward_margin.Where(x => x.ValueDate == req.settleDate && tradeIds.Contains(x.TradeId)) .AsEnumerable() .GroupBy(O => O.TradeId) .ToDictionary(K => K.Key, V => V.FirstOrDefault()?.MarginCost ?? 0); foreach (var item in tradeRiskResult.Results) { var tempVariety = _underlyingDataProvider.GetVariety(item.Trade.UnderlyingCode); double closePrice = 0.0; switch (req.CalcMarginType) { case CalcMarginTypeEnum.None: case CalcMarginTypeEnum.EodMargin: closePrice = req.PriceProvider.GetPrice(item.Trade.UnderlyingCode); break; case CalcMarginTypeEnum.InitialMargin: closePrice = item.Trade.SpotPrice ?? 0; break; default: break; } if (item.ValueResult == null) { throw new Exception("PV结算结果为null"); } var deltaMerge = item.ValueResult.Delta; var vegaMerge = item.ValueResult.Vega; var gammaMerge = item.ValueResult.Gamma; var pvMerge = item.ValueResult.Pv; var marginCost = marginCostDict.TryGetValue(item.Trade.id, out var margincost) ? margincost : 0; var unwindTradeCashList = tradeCashDict.TryGetValue(item.Trade.id, out var cashList) ? cashList : new List(); //平仓比例 double unwindRatio = unwindTradeCashList.Any() ? unwindTradeCashList.Sum(a => a.UnwindNotional.Value) / item.Trade.OriginalNotional.Value : 0; //持仓比例 double positionRatio = 1 - unwindRatio; //持仓盈亏 = 持仓市值+开仓总费用 * 持仓比例 var pnl = pvMerge + item.Trade.TradePrice.Value * positionRatio + marginCost; logger.Info($"远期预付金计算,交易:{item.Trade.TradeNumber}持仓盈亏 = 持仓市值 + 开仓总费用 * 持仓比例"); logger.Info($"远期预付金计算,交易:{item.Trade.TradeNumber}PnL = {pvMerge} + {item.Trade.TradePrice.Value} * {positionRatio} + {marginCost}"); pnl = req.CalcMarginType == CalcMarginTypeEnum.InitialMargin ? 0 : Math.Max(0, pnl); deltaMerge = IsDeltaMerge ? Math.Abs(deltaMerge) : deltaMerge; //预付金计算公式: delta * 标的收盘价 * 交易所预付金率 + 客户亏损部分 double value = deltaMerge * closePrice * (tempVariety.Margin ?? 0) + pnl; logger.Info($"远期预付金计算,交易:{item.Trade.TradeNumber}预付金计算公式: delta * 标的收盘价 * 交易所预付金率 + 客户亏损部分"); logger.Info($"远期预付金计算,交易:{item.Trade.TradeNumber}预付金 = {deltaMerge} * {closePrice} * {(tempVariety.Margin ?? 0)} + {pnl}"); tradeSpans.Add(new trade_span { TradeId = item.Trade.id,//默认记录为第一条交易记录中 OptDate = DateTime.Now, OptId = req.userId, OptName = req.userName, ClientId = item.Trade.ClientId, ValueDate = req.settleDate, UnderlyingId = item.Trade.UnderlyingId, UnderlyingCode = item.Trade.UnderlyingCode, DeltaMargin = value, WorstCastClientPayable = value, Comment = $"远期交易,delta:{deltaMerge},closePrice:{closePrice},pv:{pvMerge}" + $",gamma{gammaMerge},交易员方向:{item.Trade.BuySell},预付金率:{tempVariety.Margin}" }); } } return 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 tradeMargin = RunMarginCalculation(req.GetRunMarginCalculationReq()); if (null != tradeMargin) { return tradeMargin.FirstOrDefault()?.WorstCastClientPayable ?? 0.0; } return 0.0; } 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.AsEnumerable().GroupBy(t => t.ClientId); foreach (var clientGroup in clientGroups) { List clientSpans = new List(); var tradeSpanGroup = clientGroup.ToList(); var tradeIds = tradeSpanGroup.Select(a => a.TradeId).ToList(); var notForwardTrades = db.trade.Where(a => tradeIds.Contains(a.id) && a.TradeType != "远期").ToList(); #region 非远期 if (notForwardTrades.Any()) { var notForwardTradeIds = notForwardTrades.Select(a => a.id); //双向追保,客户只有买入交易 则预付金为0 if (notForwardTrades.All(a => a.BuySell == "卖出") && HasTwoSideMargin(clientGroup.Key ?? 0)) { var tradeSpansUpdate = db.trade_span.Where(x => notForwardTradeIds.Contains(x.TradeId) && x.ClientId == clientGroup.Key && x.ValueDate == req.settleDate).ToList(); var tradeSpansReq = req.tradeSpans.Where(x => notForwardTradeIds.Contains(x.TradeId) && x.ClientId == clientGroup.Key && x.ValueDate == req.settleDate).ToList(); tradeSpansUpdate.ForEach(x => x.WorstCastClientPayable = 0); tradeSpansReq.ForEach(x => x.WorstCastClientPayable = 0); var clientSpan = new ClientSpan { ClientId = clientGroup.Key ?? 0, ValueDate = req.settleDate, DeltaMargin = 0, WorstCastClientPayable = 0 }; clientSpans.Add(clientSpan); } else { var umTradeSpanGroup = tradeSpanGroup.Where(x => notForwardTradeIds.Contains(x.TradeId) && x.ClientId == clientGroup.Key && x.ValueDate == req.settleDate).AsEnumerable().GroupBy(a => a.UnderlyingId); var umClientSpans = new List(); foreach (var umTrade in umTradeSpanGroup) { var umClientSpan = new ClientSpan { UnderlyingId = umTrade.Key, ClientId = clientGroup.Key ?? 0, ValueDate = req.settleDate, DeltaMargin = umTrade.Sum(u => u.DeltaMargin) * (-1) }; umClientSpans.Add(umClientSpan); } var notForwardClientSpan = new ClientSpan { ClientId = clientGroup.Key ?? 0, ValueDate = req.settleDate, //负数代表客户应缴预付金,正数代表客户应收预付金 DeltaMargin = umClientSpans.Sum(g => g.DeltaMargin), WorstCastClientPayable = umClientSpans.Sum(g => g.DeltaMargin) }; clientSpans.Add(notForwardClientSpan); } } #endregion #region 远期 var forwardTrades = db.trade.Where(a => tradeIds.Contains(a.id) && a.TradeType == "远期").ToList(); if (forwardTrades.Any()) { var forwardTradeIds = forwardTrades.Select(a => a.id); var forwardTradeSpans = tradeSpanGroup.Where(x => forwardTradeIds.Contains(x.TradeId) && x.ClientId == clientGroup.Key && x.ValueDate == req.settleDate); var margin = Math.Abs((forwardTradeSpans.Sum(g => g.DeltaMargin) ?? 0)) * -1; var forwardClientSpan = new ClientSpan { ClientId = clientGroup.Key ?? 0, ValueDate = req.settleDate, //负数代表客户应缴预付金,正数代表客户应收预付金 DeltaMargin = margin, WorstCastClientPayable = margin }; clientSpans.Add(forwardClientSpan); var tradeSpansUpdate = db.trade_span.Where(x => forwardTradeIds.Contains(x.TradeId) && x.ClientId == clientGroup.Key && x.ValueDate == req.settleDate).ToList(); var tradeSpansReq = req.tradeSpans.Where(x => forwardTradeIds.Contains(x.TradeId) && x.ClientId == clientGroup.Key && x.ValueDate == req.settleDate).ToList(); if (margin > 0) { tradeSpansUpdate.ForEach(a => a.WorstCastClientPayable = 0); tradeSpansReq.ForEach(a => a.WorstCastClientPayable = 0); } } #endregion var item = new ClientSpan { ClientId = clientGroup.Key ?? 0, ValueDate = req.settleDate, //负数代表客户应缴预付金,正数代表客户应收预付金 DeltaMargin = clientSpans.Sum(g => g.DeltaMargin), WorstCastClientPayable = clientSpans.Sum(g => g.DeltaMargin), 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(item); } } //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 double? MarginAlgorithm(double deltaMerge, double pvMerge, double closePrice, bool isTwoSide, double Margin, trade tradeModel, double PositionPnl) { //交易员买入 delta为正,卖出为负 deltaMerge = tradeModel.BuySell == "买入" ? Math.Abs(deltaMerge) : -Math.Abs(deltaMerge); var value = 0.0; // 预付金 = delta(交易员方向delta) * 标的收盘价 * 交易所预付金率 + 持亏(交易员方向) value = deltaMerge * closePrice * Margin + PositionPnl; if (tradeModel.BuySell == "卖出" && !isTwoSide)//单向 客户买入 { value = 0; } return value; } } }