using Qdp.Pricing.Base.Implementations; using System; using System.Collections.Generic; using System.Linq; using System.Linq.Dynamic.Core; using System.Text; using System.Threading.Tasks; using YLErp.Abstract.DataProviders; using YLErp.BLL.Calculation; using YLErp.BLL.MarginCalculation.DongWu; using YLErp.DBModels; using YLErp.Enums; using YLErp.Helpers; using YLErp.Model; using YLErp.Model.Enum; using YLErp.Models; using YLErp.Modules; using YLErp.Modules.CalculationModule; using YLErp.Modules.DataProviderModule; using YLErp.Modules.MarginModule; using YLErp.QdpModule; namespace YLErp.BLL.MarginCalculation { /// /// 东吴预付金计算 /// TODO :东吴预付金计算待解决问题: /// 1.接入收盘测试并测试 /// 2.接入定价预付金计算并测试 /// 3.接入实时预付金计算并测试 /// public class DongWuMarginCalculation : MarginCalculationBase { private const string ExtensionNodeName = "DongWu_Margin"; private const string Margin1PreName = "Margin1"; private const string Margin3PreName = "Margin3"; private const int PriceCount = 21; private const double CoverMarginRate = 1.4; // 定义一个静态变量来保存类的实例 public static readonly DongWuMarginCalculation Instance; static DongWuMarginCalculation() { Instance = new DongWuMarginCalculation(); } protected DongWuMarginCalculation() { } #region Override Base Method /// /// 计算交易预付金 /// /// /// public override List RunMarginCalculation(RunMarginCalculationReq req) { // 交易预付金计算结果 var resultMap = new Dictionary(); var helper = new RunMarginCalculationHelper(req, _underlyingDataProvider); // 为了算客户角度的一个预付金数值 helper.ReverseTradeSide(); // 设置期权详情 helper.SetFieldsByTradeType(); //标的集合 helper.GetMarginParamProvider(MarginParamTypeEnum.MarginRate | MarginParamTypeEnum.UpDownLimit); var umCodeSet = helper.GetUnderlyingCodes(); // 预付金参数管理 var marginParamProviderManager = new MarginParamProviderManager(umCodeSet); marginParamProviderManager.Initial(req); // 创建价格 var priceProviders = new Dictionary>(); foreach (var underlyingCode in umCodeSet) { // 生成Margin1 价格 double? settlePrice = null; if (req.CalcMarginType != CalcMarginTypeEnum.EodMargin) { // 如果是实时预付金计算,用前一天的结算价 var settlementDayEodPrice = EodPriceQueryService.GetEodPrice(req.settleDate.AddDays(-1), underlyingCode); settlePrice = settlementDayEodPrice?.SettlePrice ?? 0; } IPriceFactory priceProviderFactory1 = new PriceFactory(req.settleDate, settlePrice, underlyingCode, new UpDownLimitMargin1Provider(req.settleDate, underlyingCode, marginParamProviderManager)); var unPriceProviders1 = priceProviderFactory1.Generate(priceCount: PriceCount, preName: Margin1PreName); // 生成Margin3 价格 IPriceFactory priceProviderFactory3 = new PriceFactory(req.settleDate, settlePrice, underlyingCode, new UpDownLimitMargin3Provider(req.settleDate, underlyingCode)); var unPriceProviders3 = priceProviderFactory3.Generate(priceCount: PriceCount, preName: Margin3PreName); priceProviders.Add(underlyingCode, new List() { unPriceProviders1, unPriceProviders3 }); } foreach (var trade in req.tradeList) { // 获取标的价格 if (!priceProviders.TryGetValue(trade.UnderlyingCode, out List priceProviderWraps)) { continue; } // 计算交易PV结果 var pVJsons = new List(); //计算pv方法 Action> calcPv = (unPriceProviders) => { foreach (var priceProvider in unPriceProviders) { var cloneTrade = trade.Clone(); var tradeRiskResult = CalculateRisksForTrades(req, priceProvider.Value, cloneTrade); var pVJson = new PVJson(); pVJson.TradeId = trade.id; pVJson.UnderlyingId = trade.UnderlyingId; var un = DataCacheProvider.GetUnderlyingDataSource().GetData(trade.UnderlyingCode); pVJson.VarietyId = un.UnderlyingTypeId; pVJson.Name = priceProvider.Key; pVJson.ClientId = trade.ClientId; pVJson.Value = tradeRiskResult.Results.FirstOrDefault().ValueResult.Pv; pVJsons.Add(pVJson); } }; // 计算 Margin1 trade pv var priceProviderWrap1 = priceProviderWraps.FirstOrDefault(g => g.Name == Margin1PreName); calcPv(priceProviderWrap1.priceProviders); // 计算 Margin3 trade pv var priceProviderWrap3 = priceProviderWraps.FirstOrDefault(g => g.Name == Margin3PreName); calcPv(priceProviderWrap3.priceProviders); // Create TradeSpan var contains = resultMap.TryGetValue(trade.id, out var tempTradeSpan); if (!contains) { resultMap[trade.id] = tempTradeSpan = helper.CreateTradeSpan(trade); } tempTradeSpan.WorstCastClientPayable = pVJsons.Max(l => l.Value); PriceJson priceJson = new PriceJson() { Pvs = pVJsons, UpLimitRate = priceProviderWrap1.UpLimitRate, DownLimitRate = priceProviderWrap1.DownLimitRate, UpLimitRate3 = priceProviderWrap3.UpLimitRate, DownLimitRate3 = priceProviderWrap3.DownLimitRate, IsCover = priceProviderWrap1.IsCover, IsTouch = priceProviderWrap1.IsTouch }; tempTradeSpan.SetExtensions(ExtensionNodeName, priceJson); } return resultMap.Values.ToList(); } /// /// 计算客户预付金 /// /// /// public override List CalcClientMargin(CalcClientMarginReq req) { // 客户预付金计算结果 var clientSpanNews = new List(); var updateTradeSpans = new List(); if (req.tradeSpans != null && req.tradeSpans.Count > 0) { var tradeIds = req.tradeSpans.Select(t => t.TradeId).ToList(); var tradeList = GetTrades(tradeIds); var tradeSpanInfo = (from tradeSpan in req.tradeSpans join trade in tradeList on tradeSpan.TradeId equals trade.id //2023-02-24:排除掉现金流交易,现金流交易应该不需要预付金 where tradeSpan.ValueDate == req.settleDate && trade.TradeType != "现金流交易" select new { PriceJson = tradeSpan.GetFromExtensions(ExtensionNodeName), trade, tradeSpan }).ToList(); var varietyCoverStatus = tradeSpanInfo.GroupBy(g => g.tradeSpan.VarietyId ?? 0).ToDictionary(g => g.Key, g => g.Any(b => b.PriceJson != null && b.PriceJson.IsCover)); var varietyTouchStatus = tradeSpanInfo.GroupBy(g => g.tradeSpan.VarietyId ?? 0).ToDictionary(g => g.Key, g => g.Any(b => b.PriceJson != null && b.PriceJson.IsTouch)); var clientGroups = tradeSpanInfo.GroupBy(t => t.trade.ClientId); foreach (var clientGroup in clientGroups) { var pVJsons1 = new List(); var pVJsons3 = new List(); foreach (var tradeInfo in clientGroup) { if (tradeInfo.PriceJson != null) { // margin1 pVJsons1.AddRange(tradeInfo.PriceJson.Pvs.Where(b => b.Name.StartsWith(Margin1PreName)).Select( g => new DongwuPVJson { TradeSpanId = tradeInfo.tradeSpan.id, ClientId = g.ClientId, Describe = g.Describe, Name = g.Name, TradeId = g.TradeId, UnderlyingId = g.UnderlyingId, Value = g.Value, VarietyId = g.VarietyId }).ToList()); // margin3 pVJsons3.AddRange(tradeInfo.PriceJson.Pvs.Where(b => b.Name.StartsWith(Margin3PreName)).Select( g => new DongwuPVJson { TradeSpanId = tradeInfo.tradeSpan.id, ClientId = g.ClientId, Describe = g.Describe, Name = g.Name, TradeId = g.TradeId, UnderlyingId = g.UnderlyingId, Value = g.Value, VarietyId = g.VarietyId }).ToList()); } } // 计算单个客户预付金 var clientPv = CalcClientMargin(clientGroup.Key, pVJsons1, pVJsons3, (int varietyId) => { varietyCoverStatus.TryGetValue(varietyId, out bool isCover); return isCover; }, (int varietyId) => { varietyTouchStatus.TryGetValue(varietyId, out bool isTouch); return isTouch; }, (List UpdatePVJsons) => { updateTradeSpans.AddRange(UpdatePVJsons.Select(g => new trade_span() { id = g.TradeSpanId, WorstCastClientPayable = g.Value })); }); // 计算客户维度预付金 var clientSpan = new ClientSpan { ClientId = clientGroup.Key, ValueDate = req.settleDate, DeltaMargin = tradeSpanInfo.Where(g => g.trade.ClientId == clientGroup.Key).Sum(g => g.tradeSpan.DeltaMargin * -1), // 负数代表客户应缴预付金,正数代表客户应收预付金 WorstCastClientPayable = -clientPv, 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 }; clientSpan.PVJsonList = new List(); clientSpan.PVJsons = JsonHelper.ToJson(clientSpan.PVJsonList); clientSpanNews.Add(clientSpan); } } SaveClientSpan(req, clientSpanNews, updateTradeSpans); return req.tradeSpans; } /// /// 获取交易预付金计算结果 /// /// /// public override double GetTradeMargin(GetTradeMarginReq req) { var trade = req.trade; using (var 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 && tradeMargin.FirstOrDefault() != null) { var margin = tradeMargin.FirstOrDefault().WorstCastClientPayable ?? 0.0; return Math.Max(margin, 0); } return 0.0; } #endregion #region /// /// 东吴-计算客户预付金逻辑 /// /// /// /// private double CalcClientMargin(int clientId, List pVJsons1, List pVJsons3, Func isCoverFunc, Func isTouchFunc, Action> updateTradespansAction) { /* 计算客户预付金: 1.计算标的预付金 2.计算品种预付金 3.计算客户预付金 */ // 计算品种维度预付金方法 var calcVarietyMargin = (List pVJsons) => { var varietyMarginDic = pVJsons .GroupBy(g => new { g.VarietyId, g.UnderlyingId, g.Name }).Select(g => new { // 同品种、同标的、同价格合计pv UnderlyingId = g.Key.UnderlyingId, VarietyId = g.Key.VarietyId, Name = g.Key.Name, // 前缀+价格,例如:Margin1_3.00 UnPriceSumPv = g.Sum(b => b.Value), Tradespans = g.ToList() }) .GroupBy(g => new { g.VarietyId, g.UnderlyingId }).Select(g => new { // 同品种、同标的取最大pv UnderlyingId = g.Key.UnderlyingId, VarietyId = g.Key.VarietyId, UnPv = g.Max(g => g.UnPriceSumPv), Tradespans = g.Where(a => a.UnPriceSumPv == g.Max(g => g.UnPriceSumPv)).First().Tradespans }) .GroupBy(g => g.VarietyId).Select(g => new { // 同品种轧差pv VarietyId = g.Key, VarietyPv = g.Sum(b => b.UnPv), Tradespans = g.SelectMany(a => a.Tradespans).ToList() }).ToDictionary(g => g.VarietyId, g => g); return varietyMarginDic; }; // 计算品种维度margin1预付金 var varietyMargin1Dic = calcVarietyMargin(pVJsons1); // 计算品种维度margin3预付金 var varietyMargin3Dic = calcVarietyMargin(pVJsons3); // 品种Pv=Max(Margin1,Margin3),如果有触板的品种需要乘1.4 Dictionary varietyPv = new Dictionary(); foreach (var varietyId in varietyMargin1Dic.Keys) { var clientMargin1 = varietyMargin1Dic[varietyId]; var clientMargin3 = varietyMargin3Dic[varietyId]; var maxPv = 0.00; List updatePvJsons; if (clientMargin1.VarietyPv > clientMargin3.VarietyPv) { maxPv = clientMargin1.VarietyPv; updatePvJsons = clientMargin1.Tradespans; } else { maxPv = clientMargin3.VarietyPv; updatePvJsons = clientMargin3.Tradespans; } bool isCover = isCoverFunc(varietyId); bool isTouch = isTouchFunc(varietyId); if (!isCover && isTouch) { maxPv = CoverMarginRate * maxPv; } varietyPv.Add(varietyId, maxPv); // 回写trade_span预付金值 updateTradespansAction(updatePvJsons); } // 计算客户维度pv=合计所有品种的pv var clientPv = varietyPv.Values.Sum(); var client = DataCacheProvider.GetClientDataSource().GetData(clientId); if (client.MarginOptionType == (int)MarginOptionEnum.单向追保) { // 单项预付金如果小于0,则显示0 clientPv = Math.Max(clientPv, 0); } return clientPv; } /// /// 计算交易风险 /// /// /// /// /// private TradeRiskResult CalculateRisksForTrades(RunMarginCalculationReq req, IPriceProvider priceProvider, trade trade) { var reqConv = new CalculateRisksForTradesReq { valueDate = req.settleDate, tradeList = new List() { trade }, priceProvider = priceProvider, pricingRequest = QdpPricingRequest.PV_ONLY, volType = req.volType, isUseTradeVol = PS.Config.IsTradeVol, PreciseTimeMode = req.CalcMarginType != Enums.CalcMarginTypeEnum.EodMargin, isAddVolPercent = true, isMarginCalc = true }; if (req.CalcMarginType == Enums.CalcMarginTypeEnum.EodMargin) { reqConv.calcScenario = Enums.CalcScenarioEnum.EodSettlement; } if (req.CalcMarginType == Enums.CalcMarginTypeEnum.InitialMargin) { reqConv.calcScenario = Enums.CalcScenarioEnum.InitialMargin; } return CalculatorHelper.CalculateRisksForTrades(reqConv); } /// /// 查询交易 /// /// /// private List GetTrades(List tradeIds) { using (var db = new YLContext()) { var tradeList = db.trade.AsNoTracking().Where(t => tradeIds.Contains(t.id)).ToList(); return tradeList; } } /// /// 批量保存客户预付金 /// /// /// private void SaveClientSpan(CalcClientMarginReq req, List clientSpanNews, List updateTradeSpans) { using (var db = new YLContext()) { // 回写更新交易预付金 var tradespanids = updateTradeSpans.Select(t => t.id).ToList(); var trade_spans = db.trade_span.Where(g => tradespanids.Contains(g.id)).ToList(); var updatetradeSpanData = (from updatetradeSpan in updateTradeSpans join tradeSpan in trade_spans on updatetradeSpan.id equals tradeSpan.id select new { updatetradeSpan, tradeSpan }); foreach (var item in updatetradeSpanData) { item.tradeSpan.WorstCastClientPayable = item.updatetradeSpan.WorstCastClientPayable; } //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(); } } #endregion } public class DongwuPVJson : PVJson { public int TradeSpanId { get; set; } } }