using YLErp.Abstract.DataProviders; using YLErp.BLL.Calculation; using YLErp.BLL.Eod; using YLErp.BLL.MarginCalculation; using YLErp.Enums; using YLErp.Helpers; using YLErp.Modules.DataProviderModule; using YLErp.QdpModule; namespace YLErp.BLL { /// /// 浙期 /// public class ZheQiMarginCalculation : MarginCalculationBase { // 定义一个静态变量来保存类的实例 public static readonly ZheQiMarginCalculation Instance; static ZheQiMarginCalculation() { Instance = new ZheQiMarginCalculation(); } // 定义私有构造函数,使外界不能创建该类实例 protected ZheQiMarginCalculation() { } public override List RunMarginCalculation(RunMarginCalculationReq req) { var helper = new RunMarginCalculationHelper(req, _underlyingDataProvider); var resultMap = new Dictionary(); 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", req.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) { // 远期掉期是否走现货算法 bool isStock = false; if (item.Trade.TradeType == "远期") { isStock = uProvider.GetUnderlying(item.Trade.UnderlyingCode).UnderlyingInstrumentType == ConsGlobal.InstrumentType.CommoditySpot; if (!isStock && !string.IsNullOrWhiteSpace(item.Trade.BasisUnderlyingCode)) { isStock = uProvider.GetUnderlying(item.Trade.BasisUnderlyingCode).UnderlyingInstrumentType == ConsGlobal.InstrumentType.CommoditySpot; } } if (isStock && key != "normal_0") { continue; } var pv = item.ValueResult.Pv; var clientRatio = helper.GetClient(item.Trade)?.Ratio ?? 1.0; if (!helper.GetSpecialMargin(item.Trade, pv, out var value, helper.req.forOtherSide)) { 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 (isStock) { // 现货预付金: // 普通掉期开仓固定 15%, // 基差掉期开仓预付金固定 10% // 持仓预付金观察: // 1. -5%*名义本金<单笔持仓盈亏<0, // 一般掉期持仓预付金为15% // 基差掉期持仓预付金为10% // 2. 单笔持仓盈亏<-5%*名义本金 // 一般掉期持仓预付金为: -单笔持仓盈亏+15% // 基差掉期持仓预付金为: -单笔持仓盈亏+10% var config = valuedateBLL.SystemDate.ConfigInfoData; if (helper.req.CalcMarginType == CalcMarginTypeEnum.InitialMargin) { double rate = string.IsNullOrWhiteSpace(item.Trade.BasisUnderlyingCode) ? config.NormalSwapInitMarginRatio : config.BasisSwapInitMarginRatio; rate /= 100; value = rate * item.Trade.StockEqvNotional; logger.Info($"远期预付金计算,交易:{item.Trade.TradeNumber}预付金 = 普通掉期开仓固定 15%,基差掉期开仓预付金固定 10%"); logger.Info($"远期预付金计算,交易:{item.Trade.TradeNumber}预付金 = {rate} * {item.Trade.StockEqvNotional}"); } else { double rate = string.IsNullOrWhiteSpace(item.Trade.BasisUnderlyingCode) ? config.NormalSwapMarginRatio : config.BasisSwapMarginRatio; rate /= 100; var tradePrice = (item.Trade.TradePrice * item.Trade.Notional / item.Trade.OriginalNotional) ?? 0; double pnl = item.ValueResult.Pv + tradePrice; logger.Info($"远期预付金计算,交易:{item.Trade.TradeNumber} pnl = Pv + tradePrice"); logger.Info($"远期预付金计算,交易:{item.Trade.TradeNumber} pnl = {item.ValueResult.Pv} + {tradePrice}"); // 客户持仓盈亏 logger.Info($"远期预付金计算,交易:{item.Trade.TradeNumber}预付金 = 1. -5%*名义本金<单笔持仓盈亏<0,\r\n一般掉期持仓预付金为15%,\r\n基差掉期持仓预付金为10%\r\n2. 单笔持仓盈亏<-5%*名义本金,一般掉期持仓预付金为:\r\n-单笔持仓盈亏+15%,\r\n基差掉期持仓预付金为:\r\n-单笔持仓盈亏+10%"); double clientpnl = -pnl; double rateStockEqvNotional = config.StockEqvNotionalRatio / 100d * item.Trade.StockEqvNotional; if (rateStockEqvNotional < clientpnl && clientpnl < 0) { logger.Info($"远期预付金计算,交易:{item.Trade.TradeNumber} if({config.StockEqvNotionalRatio / 100d} * {item.Trade.StockEqvNotional} < {clientpnl} && {clientpnl} > 0)"); value = rate * item.Trade.StockEqvNotional; logger.Info($"远期预付金计算,交易:{item.Trade.TradeNumber}预付金 = {rate} * {item.Trade.StockEqvNotional}"); } else if (clientpnl < rateStockEqvNotional) { logger.Info($"远期预付金计算,交易:{item.Trade.TradeNumber} if({clientpnl} < {rateStockEqvNotional} > 0)"); value = -clientpnl + rate * item.Trade.StockEqvNotional; logger.Info($"远期预付金计算,交易:{item.Trade.TradeNumber}预付金 = -{clientpnl} + {rate} * {item.Trade.StockEqvNotional}"); } else { value = rate * item.Trade.StockEqvNotional; logger.Info($"远期预付金计算,交易:{item.Trade.TradeNumber}预付金 = {rate} * {item.Trade.StockEqvNotional}"); } } tempTradeSpan.SetAllSpvAndWorst(value); } else { 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 * clientRatio; 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.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), 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.tradeSpan.IsSingleMargin != true).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 = (item.DeltaMargin > 0 ? -(x.DeltaMargin ?? 0) : (x.DeltaMargin ?? 0)) + (x.PositionWinLoss ?? 0)); tradeSpansReq.ForEach(x => x.WorstCastClientPayable = (item.DeltaMargin > 0 ? -(x.DeltaMargin ?? 0) : (x.DeltaMargin ?? 0)) + (x.PositionWinLoss ?? 0)); } } 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.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.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; } } } }