using Qdp.Pricing.Base.Implementations; using YLErp.Abstract.DataProviders; using YLErp.Commons; using YLErp.Modules; using YLErp.Modules.CalculationModule; using YLErp.Modules.DataProviderModule; using YLErp.Modules.MarginModule; using YLErp.QdpModule; namespace YLErp.BLL.MarginCalculation { public partial class MarginCalculationBase { /// /// 预付金计算帮助类 /// protected class RunMarginCalculationHelper { public readonly RunMarginCalculationReq req; readonly IUnderlyingDataProvider underlyingDataProvider; HashSet _underlyingCodeSet; Dictionary _clientDic; readonly MarginParamProvider _mpProvider; public RunMarginCalculationHelper(RunMarginCalculationReq req, IUnderlyingDataProvider underlyingDataProvider) { this.req = req; this.underlyingDataProvider = underlyingDataProvider; _mpProvider = new MarginParamProvider(req.UserInfo, req.settleDate); } #region----数据准备(内部方法)---- /// /// 交易对应客户信息 /// protected void PrepareClient() { if (_clientDic != null) { return; } var clientIds = req.tradeList.Select(t => t.ClientId).ToHashSet(); using (var db = DbContextFactory.GetClientDbContext(req.UserInfo)) { var query = from c in db.client join cl in db.clientlevel on c.LevelId equals cl.id into t_cl from cl in t_cl.DefaultIfEmpty() where clientIds.Contains(c.id) select new InnerClient { ClientId = c.id, Ratio = cl == null ? null : cl.Ratio, Ratio1 = cl == null ? null : cl.Ratio1, AddRatio = cl == null ? null : cl.AddRatio, MarginOptionType = c.MarginOptionType, ProperClientClass = c.ProperClientClass, QuestionnaireScore = c.QuestionnaireScore, RuleT0orT1 = c.RuleT0orT1, BoundSide = c.BoundSide }; _clientDic = query.ToDictionary(n => n.ClientId); } } /// /// /// protected HashSet PrepareUnderlyingCodes() { if (_underlyingCodeSet != null) { return _underlyingCodeSet; } _underlyingCodeSet = new HashSet(StringComparer.OrdinalIgnoreCase); void setUnderlyingCode(trade td) { if (td?.UnderlyingCode == null) { return; } _underlyingCodeSet.Add(td.UnderlyingCode); switch (td.TradeType) { case "彩虹期权": if (td.trade_rainbow_option != null) { _underlyingCodeSet.Add(td.trade_rainbow_option.UnderlyingAssetCode2); } break; case "价差期权": if (td.trade_spread_option != null) { var codes = td.trade_spread_option.UnderlyingAssetCodes(); foreach (var code in codes) { _underlyingCodeSet.Add(code); } } break; case "结构化交易": if (td.SubTrades != null) { foreach (var std in td.SubTrades) { setUnderlyingCode(std); } } break; } } foreach (var td in req.tradeList) { setUnderlyingCode(td); } return _underlyingCodeSet; } #endregion #region----提供数据(公共)---- /// /// 获取标的数据 /// public underlying_manager GetUnderlying(string underlyingCode) { return underlyingDataProvider.GetUnderlying(underlyingCode); } /// /// 获取underlying code集合 /// /// public HashSet GetUnderlyingCodes() { return _underlyingCodeSet; } /// /// 根据交易获取客户数据 /// public InnerClient GetClient(trade trade) { return GetClient(trade.ClientId); } /// /// 根据客户ID获取客户数据 /// public InnerClient GetClient(int clientId) { if (clientId < 1) { return null; } if (_clientDic == null) { PrepareClient(); } return _clientDic.TryGetValue(clientId, out var client) ? client : null; } public List GetClients() { if (_clientDic == null) { PrepareClient(); } return _clientDic.Values.ToList(); } /// /// 获取预付金参数提供 /// public MarginParamProvider GetMarginParamProvider(MarginParamTypeEnum initTypeEnums = MarginParamTypeEnum.None) { if (initTypeEnums != MarginParamTypeEnum.None) { var umCodeSet = PrepareUnderlyingCodes(); _mpProvider.Initialize(umCodeSet, initTypeEnums); } return _mpProvider; } /// /// 根据标的ID获取涨跌停价格 /// public UpdownLimit GetUpDownLimit(string underlyingCode, double price, bool useMinPriceChange = false, double extendLimitRate = 1) { if (_mpProvider.TryGetUpdownLimit(underlyingCode, out var limit, out var isFixed)) { if (!isFixed) { limit *= price; } } else { limit = price * 0.05; } //注意:price价格可能为负值 limit = Math.Abs(limit); if (useMinPriceChange) { var um = underlyingDataProvider.GetUnderlying(underlyingCode); var minPriceChange = um?.PriceTick ?? 0.01; var half = minPriceChange / 2; var diff = limit % minPriceChange; limit -= diff; if (diff >= half) { limit += minPriceChange; } } return new UpdownLimit { UpLimitPrice = price + limit * extendLimitRate, DownLimitPrice = price - limit * extendLimitRate }; } /// /// 根据涨跌停比例获取张跌停价 /// /// /// /// public UpdownLimit GetUpDownLimitByRate(double price, double limitRate) { return new UpdownLimit { UpLimitPrice = price * (1 + limitRate), DownLimitPrice = price * (1 - limitRate) }; } public UpdownLimit GetStockUpDownLimit(string underlyingCode, double price) { var limit = 0.1; double limitPrice; if (underlyingCode.StartsWith("30") || underlyingCode.StartsWith("688")) { limit = 0.2; } limitPrice = price * limit; var minPriceChange = 0.01; var half = minPriceChange / 2; var diff = limitPrice % minPriceChange; limitPrice -= diff; if (diff >= half) { limitPrice += minPriceChange; } return new UpdownLimit { UpLimitPrice = OtcFormatHelper.FormatValue(price + limitPrice, 2), DownLimitPrice = OtcFormatHelper.FormatValue(price - limitPrice, 2) }; } /// /// 获取涨跌停价格字典 /// /// /// /// /// 涨跌停额外系数,默认为1 public void GetUpDownLimitPrices(out IPriceProvider upLimitPrices, out IPriceProvider downLimitPrices, bool useMinPriceChange = false, double extendLimitRate = 1, bool isUsePriceLimit = true) { var umCodeSet = PrepareUnderlyingCodes(); var upLimitPrices2 = new ManualPriceProvider(); var downLimitPrices2 = new ManualPriceProvider(); //根据涨跌幅限制以及当日结算价计算涨停价以及跌停价 foreach (var code in umCodeSet) { var price = req.PriceProvider.GetPrice(code); var updown = isUsePriceLimit ? GetUpDownLimit(code, price, useMinPriceChange, extendLimitRate) : GetUpDownLimitByRate(price, extendLimitRate); upLimitPrices2.SetPrice(code, updown.UpLimitPrice); downLimitPrices2.SetPrice(code, updown.DownLimitPrice); } upLimitPrices = upLimitPrices2; downLimitPrices = downLimitPrices2; } /// /// 获取涨跌停价格字典 /// 可通过SplitNumber控制涨停到跌停的分割数量 /// 例如:涨停24,跌停6,splitNumber=6, /// 则返回的数组的lenght=7,分别为24,21,18,15,12,9,6 /// /// 从涨停到跌停分割多少次 /// /// /// 涨跌停额外系数,默认为1 /// public IPriceProvider[] GetUpDownLimitPrices(int splitNumber, bool useMinPriceChange = false, double extendLimitRate = 1, bool isUsePriceLimit = true) { var umCodeSet = PrepareUnderlyingCodes(); var priceArr = new ManualPriceProvider[splitNumber + 1]; for (int i = 0; i < priceArr.Length; i++) { priceArr[i] = new ManualPriceProvider(); } //根据涨跌幅限制以及当日结算价计算涨停价以及跌停价 foreach (var code in umCodeSet) { var price = req.PriceProvider.GetPrice(code); var updown = isUsePriceLimit ? GetUpDownLimit(code, price, useMinPriceChange, extendLimitRate) : GetUpDownLimitByRate(price, extendLimitRate); var gap = (updown.UpLimitPrice - updown.DownLimitPrice) / splitNumber; for (int i = 0; i < priceArr.Length - 1; i++) { priceArr[i].SetPrice(code, (updown.UpLimitPrice - (gap * i)).FormatValue(10)); } priceArr[priceArr.Length - 1].SetPrice(code, updown.DownLimitPrice); } return priceArr.Cast().ToArray(); } /// /// 获取持仓波动率字典 /// public void GetTradVolRateDic(out Dictionary volRateDic, Func addVol = null) { var tradeVolRateArr = req.tradeList.Select(t => new { tradeId = t.id, volRate = (_mpProvider.GetVolatilityRate(t.UnderlyingCode) ?? 0) + (addVol?.Invoke(t) ?? 0) }).Where(d => d.volRate > 0).ToArray(); volRateDic = tradeVolRateArr.ToDictionary(d => d.tradeId, d => d.volRate); } /// /// 获取持仓波动率字典 /// public void GetMarginTradVolRateDic(out Dictionary volRateDic, Func addVol = null) { var tradeVolRateArr = req.tradeList.Select(t => new { tradeId = t.id, volRate = GetTradeVol(t.TradeNumber, req.settleDate) ?? 0 }).Where(d => d.volRate > 0).ToArray(); volRateDic = tradeVolRateArr.ToDictionary(d => d.tradeId, d => d.volRate); } /// /// 获取持仓波动率字典 /// public void GetUpDownVolRateDic(out Dictionary upVolRateDic, out Dictionary downVolRateDic) { var tradeVolRateArr = req.tradeList.Select(t => new { tradeId = t.id, volRate = _mpProvider.GetVolatilityRate(t.UnderlyingCode) ?? 0 }).Where(d => d.volRate > 0).ToArray(); upVolRateDic = tradeVolRateArr.ToDictionary(d => d.tradeId, d => d.volRate); downVolRateDic = tradeVolRateArr.ToDictionary(d => d.tradeId, d => -d.volRate); } #endregion #region----实用方法(公共)---- /// /// 将BuySell换做相反的方向, /// 交易列表变更为克隆数据,所以不需要再对交易恢复原来的方向 /// /// 强制转换 public void ReverseTradeSide(bool force = false) { if (!force && !req.forOtherSide) { return; } req.tradeList = req.tradeList.Select(n => { var clone = n.Clone(); clone.BuySell = clone.BuySell == "买入" ? "卖出" : "买入"; if (clone.SubTrades != null && clone.SubTrades.Any()) { clone.SubTrades = clone.SubTrades.Select(m => { var xs = m.Clone(); xs.BuySell = xs.BuySell == "买入" ? "卖出" : "买入"; return xs; }).ToArray(); } return clone; }).ToList(); } /// /// /// public void SetFieldsByTradeType() { if (!req.hasOptionInfo) { tradeBLL.SetFieldsByTradeType(req.tradeList); } } /// /// 获取特殊预付金(手动维护或收益互换交易) /// public bool GetSpecialMargin(trade trade, double calcPv, out double value, bool isForOtherSide = false) { value = 0; //首先获取手动维护(包括收益互换交易)的预付金值 using (var db = DbContextFactory.GetYLDbContext()) { var predicate = PredicateBuilder.Create(x => x.TradeId == trade.id && x.Margin != null && string.IsNullOrEmpty(x.VolType)); if (trade.TradeType == "自定义交易") { predicate = predicate.And(x => x.ValueDate <= req.settleDate); } else { //非自定义交易只取当天保存的持仓预付金 predicate = predicate.And(x => x.ValueDate == req.settleDate); } var manual = db.eod_trade_risk_manual.Where(predicate) .OrderByDescending(x => x.ValueDate) .Select(n => new { n.Margin, n.ValueDate }).FirstOrDefault(); if (manual != null && manual.Margin != null) { if (isForOtherSide) { value = 0; } else { value = manual.Margin.Value; } } //自定义交易总是返回true并有一些特殊逻辑判断 if (trade.TradeType == "自定义交易") { //收盘时如果自定义交易还活着且没有维护当日风险,并且收的时系统日期当日的盘,抛出exception if ((manual == null || manual.ValueDate != req.settleDate) && req.CalcMarginType == Enums.CalcMarginTypeEnum.EodMargin && !ConsTrade.TradeCompleteStatus.Contains(trade.TradeStatus) && req.settleDate == valuedateBLL.ValueDate && !PS.Config.Is润和 && !PS.Config.IsMustRiskManual) { throw new Exception($"交易'{trade.TradeNumber}'在{req.settleDate:yyyy-MM-dd}需先进行交易风险维护"); } return true; } //非自定义交易如果有手工维护的预付金则返回true //否则如果是收益互换交易则进行计算,其他交易返回false if (manual != null && manual.Margin != null) { return true; } } //获取收益互换交易的预付金值 if (trade.TradeType == "收益互换") { return true; } return false; } /// /// 创建trade_span实例 /// public trade_span CreateTradeSpan(trade td, underlying_manager un = null) { if (td is null) { throw new ArgumentNullException(nameof(td)); } if (un == null || td.UnderlyingCode != un.UnderlyingCode) { un = DataCacheProvider.GetUnderlyingDataSource().GetData(td.UnderlyingCode); } return new trade_span { TradeId = td.id, ClientId = td.ClientId, ValueDate = req.settleDate, VarietyId = un?.UnderlyingTypeId, UnderlyingId = td.UnderlyingId, UnderlyingCode = td.UnderlyingCode, OptId = req.userId, OptName = req.userName, OptDate = DateTime.Now }; } /// /// 获取计算一组交易的风险指标输入参数 /// public CalculateRisksForTradesReq GetCalculateRisksForTradesReq(IPriceProvider priceProvider, Dictionary addVolRateDic, Dictionary overrideVols, PricingRequest pricingRequest = QdpPricingRequest.PV_ONLY, bool isEodCalc = false, string pricekey = null) { var tlist = pricekey != null && pricekey.Contains("barPrice") ? req.tradeList.Where(l => l.UnderlyingCode == pricekey.Split(',')[1]).ToList() : req.tradeList; var reqConv = new CalculateRisksForTradesReq { valueDate = req.settleDate, tradeList = tlist, priceProvider = priceProvider, pricingRequest = pricingRequest, addVolRateDic = addVolRateDic, volType = req.volType, settlementType = req.settlementType, isUseTradeVol = PS.Config.IsTradeVol, PreciseTimeMode = req.CalcMarginType != Enums.CalcMarginTypeEnum.EodMargin, isAddVolPercent = true, overrideVolsForTrade = overrideVols, isMarginCalc = true, isEodCalc = isEodCalc }; if (req.CalcMarginType == Enums.CalcMarginTypeEnum.EodMargin) { reqConv.calcScenario = Enums.CalcScenarioEnum.EodSettlement; } if (req.CalcMarginType == Enums.CalcMarginTypeEnum.InitialMargin) { reqConv.calcScenario = Enums.CalcScenarioEnum.InitialMargin; } return reqConv; } public double? GetTradeVol(string TradeNumber, DateTime ValueDate) { using (var db = DbContextFactory.GetYLDbContext()) { var tradeVol = db.trade_vol.Where(a => a.IsValid && a.TradeNumber == TradeNumber && a.ValueDate == ValueDate).FirstOrDefault(); if (tradeVol != null) { return tradeVol.Vol; } } return null; } #endregion } #region----内部类---- /// /// /// protected class InnerClient { public int ClientId { get; set; } /// /// 维持预付金系数(R2) /// public double? Ratio { get; set; } /// /// 初始预付金系数(R1) /// public double? Ratio1 { get; set; } /// /// 商品预付金率加点 /// public double? AddRatio { get; set; } /// /// 是否有双向预付金 /// public int? MarginOptionType { get; set; } /// /// 适当性类型 /// public string ProperClientClass { get; set; } /// /// 资信评估分数 /// public int? QuestionnaireScore { get; set; } /// /// 方顿预付金模板选择 /// public string RuleT0orT1 { get; set; } /// /// 资金流向 /// public BoundSideEnum BoundSide { get; set; } public override string ToString() { return ClientId.ToString(); } } /// /// 涨跌停限制 /// protected struct UpdownLimit { /// /// 涨停价 /// public double UpLimitPrice { get; set; } /// /// 跌停价 /// public double DownLimitPrice { get; set; } /// /// 涨跌停幅度:创业板为20%,其他默认为10% /// public double limit { get; set; } } #endregion } }