using Qdp.Pricing.Base.Implementations; using System.Text.RegularExpressions; using YLErp.BLL; using YLErp.BLL.Calculation.V2; using YLErp.BLL.Eod; using YLErp.BLL.MarginCalculation; using YLErp.Commons; using YLErp.Configuration; using YLErp.DBModels.Helpers; using YLErp.Enums; using YLErp.Model; using YLErp.Modules.CalculationModule; using YLErp.Modules.DataProviderModule; using YLErp.Modules.EodModule; using YLErp.Modules.PricingModule.Models; using YLErp.Modules.TradeModule; using YLErp.QdpModule; using static iTextSharp.text.pdf.AcroFields; namespace YLErp.Modules.PricingModule { /// /// 计算服务 /// public class PriceCalcService : YLBaseService { public PriceCalcService(OptUserInfo userInfo) : base(userInfo) { } /// /// 定价计算调用 /// public List GetOptionCalculatorV2(trade trade, underlying_manager underlying, OptionCalcParams para, CalcScenarioEnum calcScenario) { if (trade is null) { throw new ArgumentNullException(nameof(trade)); } if (underlying is null) { throw new ArgumentNullException(nameof(underlying)); } if (para is null) { throw new ArgumentNullException(nameof(para)); } if (trade.TradeType != "合成价差期权" && trade.TradeType != "现金流交易" && trade.SpotPrice <= 0) { throw new Exception("请输入正确的标的价格"); } if (!trade.TradeDate.HasValue) { throw new Exception("请输入正确的交易日期"); } if (PS.Config.Is润和) { if (trade.TTMDays.HasValue) { var days = Math.Floor(trade.TTMDays.Value - 0.625) + 1; //underlying.QuotationDate = trade.ExerciseDate.Value.AddDays(-days); underlying.QuotationDate = QdpCalendarHelper.AddDate(trade.ExerciseDate.Value, -(int)days); } else { underlying.QuotationDate = DateTime.Today; } } else if (para.ValueDate.HasValue) { GetAccQuotationDate(trade, para); underlying.QuotationDate = para.ValueDate.Value; } else { GetAccQuotationDate(trade, para); underlying.QuotationDate = trade.TradeDate.Value; if (underlying.QuotationDate != valuedateBLL.ValueDate.Date) { underlying.QuotationDate = AdjustQuotationDate(underlying.QuotationDate.Value, trade.TradeType); } } var QRList = new List(); var callputPara = new CallPutResultCalcParams(para); try { //根据报价方式为波动率调整时,要用波动率ask,bid值计算call,put //波动率调整不需要rule if (para.QuotationType == "波动率调整") { //补丁 波动率调整不需要rule callputPara.rules = GetRules(0); trade.BuySell = "卖出"; var askovrs = GetCallPutResultV2(trade, underlying, callputPara.SetVol(para.AskVol), CalcScenarioEnum.Pricing); trade.BuySell = "买入"; var bidovrs = GetCallPutResultV2(trade, underlying, callputPara.SetVol(para.BidVol), CalcScenarioEnum.Pricing); QRList.Add(bidovrs[0]); QRList.Add(askovrs[0]); //call 针对波动率调整,TradePriceBid 和 TradePriceAsk应该一样(因为rule为0) QRList[0].TradePriceBid = bidovrs[0].TradePriceBid;//askovrs[0].TradePriceBid; //系统买入 QRList[0].RoundedTradePriceBid = bidovrs[0].RoundedTradePriceBid; QRList[0].TradePriceAsk = askovrs[0].TradePriceAsk; //系统卖出 QRList[0].RoundedTradePriceAsk = askovrs[0].RoundedTradePriceAsk; //put QRList[1].TradePriceBid = bidovrs[1].TradePriceBid; //系统买入 QRList[1].RoundedTradePriceBid = bidovrs[1].RoundedTradePriceBid; QRList[1].TradePriceAsk = askovrs[1].TradePriceAsk; //系统卖出 QRList[1].RoundedTradePriceAsk = askovrs[1].RoundedTradePriceAsk; } else { //报价参数 callputPara.rules = GetRules(underlying.id); callputPara.callVol = PS.Config.IsTradeVol ? trade.TradeOpenVolatility ?? 0 : trade.Vol ?? 0; trade.BuySell = "买入"; var bidovrs = GetCallPutResultV2(trade, underlying, callputPara, CalcScenarioEnum.Pricing); //不用再计算一遍,直接把买入的计算结果取反 //trade.BuySell = "卖出"; //var askovrs = GetCallPutResultV2(trade, underlying, paraBuilder.Build(vol, vol)); var bid = bidovrs[0]; if (bid.Vega != 0 && bid.VegaCash == 0) { bid.VegaCash = bid.Vega * (trade.SpotPrice ?? 0); } var ask = new TradeValueResult { Pv = -bid.Pv, Delta = -bid.Delta, Gamma = -bid.Gamma, GammaCash = -bid.GammaCash, DeltaCash = -bid.DeltaCash, VegaCash = -bid.VegaCash, Vega = -bid.Vega, CalendarDayTheta = -bid.CalendarDayTheta, TradingDayTheta = -bid.TradingDayTheta, Rho = -bid.Rho, UnderlyingCode = bid.UnderlyingCode, Strike = bid.Strike, Vol = bid.Vol, RoundedPv = -bid.RoundedPv, KnockOutPayoff = -bid.KnockOutPayoff, IsKnockOut = bid.IsKnockOut, }; QRList.AddRange(new[] { bid, ask }); } return QRList; } catch (Exception ex) { LogFactory.GetLogger("定价计算").Error(ex); var message = Regex.Replace(ex.Message, "exercise date (.*?) is not in KOObsDates", "到期日 $1 不在敲出观察日列表中"); throw new Exception("计算出错," + message); } } /// /// 获取累计包含首日定价日期 /// /// /// private static void GetAccQuotationDate(trade trade, OptionCalcParams para) { if (trade.TradeType == "累计期权" && trade.trade_accumulator_option != null && !string.IsNullOrWhiteSpace(trade.trade_accumulator_option.KOObservationDates)) { var ko = QdpHelper.ParseAutocallCustomizedInfo(trade.trade_accumulator_option.KOObservationDates); if (ko.Item1.Length > 0 && ko.Item1.Select(l => l.DateTime).ToArray().Contains(trade.TradeDate.Value)) { para.ValueDate = QdpCalendarHelper.GetNonHolidayDefore((para.ValueDate ?? trade.TradeDate).Value.AddDays(-1)); } } } private static List GetCallPutResultV2(trade trade, underlying_manager underlying, CallPutResultCalcParams para, CalcScenarioEnum calcScenario) { if (trade is null) { throw new ArgumentNullException(nameof(trade)); } if (underlying is null) { throw new ArgumentNullException(nameof(underlying)); } if (para is null) { throw new ArgumentNullException(nameof(para)); } //交易日等于系统日期,是当天报价,要使用精确时间模式 //trade.TradeDate必然有值,在外部调用的方法中判断 var isPreciseTimeMode = underlying.QuotationDate.Value.Date == valuedateBLL.ValueDate.Date; if (trade.TradeType == "气囊结构" || trade.TradeType == "收益增强结构" || trade.TradeType == "区间累积期权" || (trade.TradeType == "二元期权" && trade.ExerciseMode == "American")) { trade.OptionType = "看涨"; } TradeValueResult pricingResult; var valueDate = underlying.QuotationDate.Value; var spotPrice = para.UnderlyingPrice ?? trade.SpotPrice ?? 0; if (trade.TradeType == "自定义交易") { var result = TradeRiskCalcUtil.GetManualOptionValue(valueDate, trade, spotPrice, para.callVol, true, calcScenario); if (result.manual == null) { throw new Exception(PS.Config.ErpElement.ExternalAPIForCustomCalcEnable ? "失败:接口计算失败;" : "失败:无法调用计算接口,请开启相应配置"); } return new List { result.optionValue, result.optionValue }; } string fixing = null; if (trade.TradeType == "亚式期权" || trade.StructureType == "亚式熊市价差") { if (!string.IsNullOrEmpty(para.Fixings)) { fixing = para.Fixings; } else { var avgStartDate = trade.trade_asian_option.AveragingPeriodStartDate ?? trade.TradeDate.Value; if (valueDate > avgStartDate) { fixing = FixingService.GetFixingString(valueDate, trade, avgStartDate, trade.trade_asian_option.ObservationDates); fixing = FixingService.AddOrReplaceLastDateSpotPrice(fixing, valueDate, spotPrice); if (PS.Config.Is润和 && DateTime.Now.Hour < 15) { var index = fixing.IndexOf(valueDate.ToString("yyyy-MM-dd")); if (index >= 0) { fixing = fixing.Remove(index).TrimEnd(';'); } } } } } else if (trade.TradeType == "区间累积期权") { fixing = !string.IsNullOrEmpty(para.Fixings) ? para.Fixings : FixingService.GetFixingString(valueDate: valueDate.AddDays(-1), otcTrade: trade, startDate: trade.StartDate ?? trade.TradeDate.Value, observationDates: trade.trade_rangeaccrual.ObservationDates); } if ("V2".Equals(para.CalcVersion, StringComparison.OrdinalIgnoreCase) || trade.TradeType == "累计期权" || "结构化产品".Equals(trade.TradeType) || trade.IsSnowballSpecialist()) { var request = new OptionValueCalcRequest(valuedateBLL.SysRiskFreeRate()) { vols = new[] { para.callVol }, spotPrices = new[] { spotPrice }, engineName = para.EngineName, preciseTimeMode = isPreciseTimeMode, pricingRequest = para.IsCalcGreeks ? QdpPricingRequest.BASIC_GREEKS : QdpPricingRequest.PRICE_GREEKS, quadratureFastMode = true, calcScenario = CalcScenarioEnum.Pricing, fixings = fixing, timeToMaturityDays = trade.TTMDays }; pricingResult = OptionCalculatorV2.GetOptionValueResult(valueDate, trade, request, out _); } else { pricingResult = ValueCalculator.GetOptionValueResultV2( para.UserId, underlying, trade, new double[] { para.callVol }, new double[] { spotPrice }, fixing: fixing, preciseTimeMode: isPreciseTimeMode, engineName: para.EngineName, request: para.IsCalcGreeks ? QdpPricingRequest.BASIC_GREEKS : QdpPricingRequest.PRICE_GREEKS, calcScenario: CalcScenarioEnum.Pricing, quadratureFastMode: true); } if (pricingResult == null) { throw new Exception("失败:此交易无法计算 "); } //报价结果 var qr = new QuotationResult(); OptionQuote(ref pricingResult, para.rules.Where(r => underlying_parameter.callQuoteTypes.Contains(r.Type)).ToList(), trade.Notional, trade.TradeType, ref qr); var payoffService = new TradeKnockOutPayoffCalcService(valueDate); var payoffResult = payoffService.GetKnockOutPayoff(trade, spotPrice); if (payoffResult != null) { pricingResult.IsKnockOut = payoffResult.IsKnockOut; pricingResult.KnockOutPayoff = payoffResult.Payoff; } return new List { pricingResult, pricingResult }; } /// /// 计算期权权利金 /// public IEnumerable CalcOptionPrice(IEnumerable trades, bool calcMargin, CalcScenarioEnum calcScenario , Func calcGreeks = null, string calcVersion = null) { var results = new List(); var tdGroups = trades.GroupBy(n => n.CalcId.Split('-')[0]).ToArray(); var priceProvider = new ManualPriceProvider(); var req = new Lazy(() => new RunMarginCalculationReq(UserInfo) { forOtherSide = false, hasOptionInfo = false, CalcMarginType = CalcMarginTypeEnum.InitialMargin, settleDate = DateTime.Now, PriceProvider = priceProvider }); foreach (var tdGroup in tdGroups) { var tdList = tdGroup.Select(td => { if (!td.TradeDate.HasValue) { throw new ServiceException("交易日期 必须填写"); } if (!td.ExerciseDate.HasValue) { throw new ServiceException("到期日期 必须填写"); } var tdConv = TradeConverter.ConvertOptionTrade(td); tdConv.CalcId = td.CalcId; return new { tdConv, td.IsTTMSystem, td.ValueDate, td.UnderlyingPrice, EngineName = ConsTrade.GetEngineName(td.EngineName), }; }).ToArray(); //结构化交易(用于预付金计算) if (tdList.Length > 1) { var index = -2; foreach (var item in tdList) { item.tdConv.id = index--; item.tdConv.ParentTradeId = -1; } } var tradeValueDict = new Dictionary(); //计算pv foreach (var item in tdList) { var td = item.tdConv; var buySell = td.BuySell; var underlying = DataCacheProvider.GetUnderlyingDataSource().GetData(td.UnderlyingCode); if (td.TradeType == "现金流交易") { underlying = new underlying_manager(); } if (underlying == null) { throw new ServiceException($"没有找到标的信息,标的代码:{td.UnderlyingCode}"); } if (PS.Config.Is润和 && item.IsTTMSystem) { td.TTMDays = TradeCalcHelper.CalculateTTMDays(DateTime.Today, td.ExerciseDate.Value, underlying.UnderlyingTypeId, PS.Config.ErpElement.PrecisionOfMinuteInQuote); } else if (!td.TTMDays.HasValue && td.TradeType != "现金流交易") { td.TTMDays = TradeCalcHelper.CalculateTTMDays(td.TradeDate.Value, td.ExerciseDate.Value, underlying.UnderlyingTypeId, PS.Config.ErpElement.PrecisionOfMinuteInQuote); } //这个地方会把交易方向改变 var QRList = GetOptionCalculatorV2(td, underlying, new OptionCalcParams { UserId = UserId.ToString(), IsCalcGreeks = calcGreeks == null || calcGreeks(td), EngineName = item.EngineName, CalcVersion = calcVersion, UnderlyingPrice = item.UnderlyingPrice, ValueDate = item.ValueDate, }, calcScenario); var calcResult = buySell == "卖出" ? QRList[1] : QRList[0]; calcResult.BuySell = buySell; //期权本身的价值,不包含买卖方向 td.TradePrice = QRList[0].Pv; td.AccurateTradePrice = QRList[0].Pv; if (PS.Config.Is润和) { var tdmid = td.Clone(); tdmid.Vol = tdmid.MidVol; tdmid.TradeOpenVolatility = tdmid.MidVol; //这个地方会把交易方向改变 var QRList2 = GetOptionCalculatorV2(tdmid, underlying, new OptionCalcParams { UserId = UserId.ToString(), IsCalcGreeks = calcGreeks == null || calcGreeks(tdmid), EngineName = item.EngineName, CalcVersion = calcVersion, UnderlyingPrice = item.UnderlyingPrice, ValueDate = item.ValueDate }, calcScenario); var calcResult2 = buySell == "卖出" ? QRList2[1] : QRList2[0]; td.Day1Pnl = calcResult2.Pv - calcResult.Pv; calcResult2.Pv = calcResult.Pv; tradeValueDict[td.id] = (calcResult2, underlying); } else { tradeValueDict[td.id] = (calcResult, underlying); } //if (PS.Config.Company == Configuration.CompanyEnum.天示) //{ // calcResult.Pv = calcResult.Pv + td.PrincipalSum * (buySell == "卖出" ? -1 : 1); //} //恢复原有的交易方向,确保预付金计算正确 td.BuySell = buySell; } List tradeSpans = null; //计算预付金 if (calcMargin) { var tdCloneList = tdList.Where(n => n.tdConv.TradeType != "现金流交易").Select(n => { var tdClone = n.tdConv.Clone(); tdClone.OptId = UserId; tdClone.VolType = "报价Bid"; tdClone.TradeCloseVolatility = null; priceProvider.SetPrice(tdClone.UnderlyingCode, tdClone.SpotPrice ?? 0); return tdClone; }).ToList(); //结构化交易预付金计算可能出现的问题:结算日期或者期初标的价格不一致 //要正确处理上述问题,需要对现有代码做重新设计和开发,故暂时忽略上述情况 if (tdCloneList.Count() > 0) { req.Value.settleDate = tdCloneList[0].TradeDate ?? valuedateBLL.ValueDate; req.Value.CalcMarginType = CalcMarginTypeEnum.InitialMargin; tradeSpans = MarginDefault.RunMarginCalculation(req.Value.Clone(tdCloneList)).ToList(); if (PS.Config.Is国投 || PS.Config.Is润和 || PS.Config.Is华安 || PS.Config.Is招证) { GroupSpansCalc(tradeSpans); } if (PS.Config.Company == CompanyEnum.东吴) { foreach (var ts in tradeSpans) { if (ts.WorstCastClientPayable < 0) { ts.WorstCastClientPayable = 0; } } } } } var greeksHandleService = new GLMSGreeksHandleService(); foreach (var item in tdList) { var td = item.tdConv; var initialMargin = 0d; if (tradeSpans != null) { initialMargin = tradeSpans.FirstOrDefault(n => n.TradeId == td.id)?.WorstCastClientPayable ?? 0; } var (calcResult, underlying) = tradeValueDict[td.id]; if (calcResult != null) { if (calcResult.IsKnockOut) { calcResult.DeltaContainsKnockOut = 0; calcResult.GammaContainsKnockOut = 0; calcResult.PvContainsKnockOut = calcResult.KnockOutPayoff; } else { calcResult.PvContainsKnockOut = calcResult.Pv; calcResult.DeltaContainsKnockOut = calcResult.Delta; calcResult.GammaContainsKnockOut = calcResult.Gamma; } } greeksHandleService.InitData(item.ValueDate ?? td.TradeDate ?? DateTime.Today, new List { td.UnderlyingCode }); greeksHandleService.Handle(td, calcResult, underlying); results.Add(new CalcOptionPriceResult { BuySell = td.BuySell, CalcId = td.CalcId, calcResult = calcResult, initialMargin = initialMargin, countRatio = underlying.CountRatio, contractSize = underlying.ContractSize, Day1Pnl = td.Day1Pnl ?? 0, TradePrice = td.TradePrice, AccurateTradePrice = td.AccurateTradePrice, TTMDays = td.TTMDays ?? 0 }); } if (PS.Config.Is华安) { if (tradeSpans != null) { if (tradeSpans.Sum(x => x.WorstCastClientPayable) < 0 || results.Where(r => tdList.Select(td => td.tdConv.CalcId).Contains(r.CalcId)).Sum(t => t.TradePrice * (t.BuySell == "卖出" ? 1 : -1)) > 0) { tradeSpans.ForEach(x => x.WorstCastClientPayable = 0); results.ForEach(x => x.initialMargin = 0); } } } } if (PS.Config.Is润和) { var TotalMarginDic = results.GroupBy(l => l.CalcId.Split('-')[0]).ToDictionary(l => l.Key, l => new { totalMargin = l.Sum(i => i.initialMargin), totalTradePrice = l.Sum(i => i.AccurateTradePrice * (i.BuySell == "卖出" ? 1 : -1)) }); foreach (var item in TotalMarginDic) { if (item.Value.totalMargin > 0 && item.Value.totalTradePrice + item.Value.totalMargin > 0) { var modifyHasNeedOffsetModel = results.Where(l => l.CalcId.Split('-')[0] == item.Key); modifyHasNeedOffsetModel.ToList().ForEach(l => l.hasInitialMargin = true); } else { var modifyHasNeedOffsetModel = results.Where(l => l.CalcId.Split('-')[0] == item.Key); modifyHasNeedOffsetModel.ToList().ForEach(l => l.hasInitialMargin = false); } } } return results; } /// /// 分组取最大 /// /// public static void GroupSpansCalc(List tradeSpans) { double? spv1 = tradeSpans.Sum(O => O.Spv1 ?? 0); double? spv2 = tradeSpans.Sum(O => O.Spv2 ?? 0); double? spv3 = tradeSpans.Sum(O => O.Spv3 ?? 0); double? spv4 = tradeSpans.Sum(O => O.Spv4 ?? 0); double? spv5 = tradeSpans.Sum(O => O.Spv5 ?? 0); double? spv6 = tradeSpans.Sum(O => O.Spv6 ?? 0); double? spv7 = tradeSpans.Sum(O => O.Spv7 ?? 0); double? spv8 = tradeSpans.Sum(O => O.Spv8 ?? 0); double? deltaMargin = tradeSpans.Sum(O => O.DeltaMargin ?? 0); //默认使用了某个spv不会赋 null if (tradeSpans.Any(x => x.Spv1 == null)) spv1 = null; if (tradeSpans.Any(x => x.Spv2 == null)) spv2 = null; if (tradeSpans.Any(x => x.Spv3 == null)) spv3 = null; if (tradeSpans.Any(x => x.Spv4 == null)) spv4 = null; if (tradeSpans.Any(x => x.Spv5 == null)) spv5 = null; if (tradeSpans.Any(x => x.Spv6 == null)) spv6 = null; if (tradeSpans.Any(x => x.Spv7 == null)) spv7 = null; if (tradeSpans.Any(x => x.Spv8 == null)) spv8 = null; if (tradeSpans.Any(x => x.DeltaMargin == null)) deltaMargin = null; var spvArr = new[] { spv1, spv2, spv3, spv4, spv5, spv6, spv7, spv8, deltaMargin }; var maxSpv = spvArr.Max(); var index = Array.IndexOf(spvArr, maxSpv); tradeSpans.ForEach(x => { var clientRatio = 1.0; if (PS.Config.Company == CompanyEnum.招证 && x.ClientId != 0) { var client = DataCacheProvider.GetClientDataSource().GetData(x?.ClientId.Value ?? 0); var clientLevel = DataCacheProvider.GetClientLevelDataSource().GetData(client.LevelId ?? 0); clientRatio = clientLevel?.Ratio1 ?? 1.0; } x.WorstCastClientPayable = new[] { x.Spv1, x.Spv2, x.Spv3, x.Spv4, x.Spv5, x.Spv6, x.Spv7, x.Spv8, x.DeltaMargin }[index] * clientRatio; }); } /// /// 计算期权权利金 /// public CalcOptionPriceResult CalcOptionPrice(OtcOptionTradeFull trade, bool calcMargin, CalcScenarioEnum calcScenario , bool calcGreeks = true, string calcVersion = "V1", string fixings = null) { if (!trade.TradeDate.HasValue) { throw new ServiceException("交易日期 必须填写"); } if (!trade.ExerciseDate.HasValue) { throw new ServiceException("到期日期 必须填写"); } var underlying = DataCacheProvider.GetUnderlyingDataSource().GetData(trade.UnderlyingCode); if (trade.TradeType == "现金流交易") { underlying = new underlying_manager(); } if (underlying == null) { throw new ServiceException($"没有找到标的信息,标的代码:{trade.UnderlyingCode}"); } var tdConv = TradeConverter.ConvertOptionTrade(trade); tdConv.EnableSetFieldsByTradeType = false; //由于新增交易时,页面交易数量还未根据沽购数量进行观察日倍数乘积处理,所以,如果后台处理了,得到的PV页面处理成权利金时会除以数量,就会有问题,需要和前台同步 if (tdConv.TradeType == "累计期权" && tdConv.id > 0) { var customObservDates = QdpHelper.ParseObservationDate(tdConv.trade_accumulator_option.KOObservationDates); customObservDates ??= CalendarImpl.Get("chn").BizDaysBetweenDatesExcluStartDay(tdConv.TradeDate.Value, tdConv.ExerciseDate.Value).ToArray(); tdConv.TradeAmount = tdConv.trade_accumulator_option.AccumuTradeAmount * customObservDates.Length; tdConv.Notional = tdConv.TradeAmount * underlying.CountRatio; tdConv.StockEqvNotionalReal = tdConv.Notional * (tdConv.SpotPrice ?? 0); tdConv.StockEqvNotional = TradeHelper.GetStockEqvNotional(tdConv.StockEqvNotionalReal, tdConv.ParticipationRate, tdConv.AnnualizeFactor); } var buySell = tdConv.BuySell; if (PS.Config.Is润和) { tdConv.TTMDays = TradeCalcHelper.CalculateTTMDays(DateTime.Today, tdConv.ExerciseDate.Value, underlying.UnderlyingTypeId, PS.Config.ErpElement.PrecisionOfMinuteInQuote); } else if (!tdConv.TTMDays.HasValue) { tdConv.TTMDays = TradeCalcHelper.CalculateTTMDays(trade.TradeDate.Value, trade.ExerciseDate.Value, underlying.UnderlyingTypeId, PS.Config.ErpElement.PrecisionOfMinuteInQuote); } var initialMargin = 0d; if (calcMargin) { var tdClone = tdConv.Clone(); tdClone.OptId = UserId; tdClone.BuySell = "买入"; tdClone.VolType = "报价Bid"; tdClone.TradeCloseVolatility = null; initialMargin = MarginDefault.GetInitialMargin(tdClone, tdConv.id, true); } var QRList = GetOptionCalculatorV2(tdConv, underlying, new OptionCalcParams { UserId = UserId.ToString(), IsCalcGreeks = calcGreeks, EngineName = ConsTrade.GetEngineName(trade.EngineName), UnderlyingPrice = trade.UnderlyingPrice, ValueDate = trade.ValueDate, CalcVersion = calcVersion ?? "V1", Fixings = fixings }, calcScenario); var calcResult = buySell == "卖出" ? QRList[1] : QRList[0]; calcResult.BuySell = buySell; if (PS.Config.Is润和) { var tdmid = tdConv.Clone(); tdmid.Vol = tdmid.MidVol; tdmid.TradeOpenVolatility = tdmid.MidVol; //这个地方会把交易方向改变 var QRList2 = GetOptionCalculatorV2(tdmid, underlying, new OptionCalcParams { UserId = UserId.ToString(), IsCalcGreeks = calcGreeks, EngineName = ConsTrade.GetEngineName(trade.EngineName), UnderlyingPrice = trade.UnderlyingPrice, ValueDate = trade.ValueDate, CalcVersion = calcVersion ?? "V1" }, calcScenario); var calcResult2 = buySell == "卖出" ? QRList2[1] : QRList2[0]; trade.Day1Pnl = calcResult2.Pv - calcResult.Pv; } return new CalcOptionPriceResult { CalcId = trade.CalcId, calcResult = calcResult, initialMargin = initialMargin, countRatio = underlying.CountRatio, contractSize = underlying.ContractSize, Day1Pnl = trade.Day1Pnl ?? 0, }; } /// /// 根据标的价格和波动率对交易进行试算 /// /// /// public CalcTradesResult CalcTrades(CalcTradesRequest request) { var result = new CalcTradesResult(); var eodTrades = DbContext.eod_trade.Where(x => request.TradeIds.Contains(x.TradeId) && x.ValueDate == request.ValueDate).ToList(); var trades = DbContext.trade.Where(x => request.TradeIds.Contains(x.id)).ToList(); request.TradeIds.ForEach(x => { trade trade = new trade(); var eodTrade = eodTrades.FirstOrDefault(y => y.TradeId == x); if(eodTrade != null) { trade = eodTrade.trade; } else { trade = trades.FirstOrDefault(y => y.id == x); new TradeExtendService(OptUser, DbContext).SetTradeExtend(new[] { trade }, tracking: true); } if(trade.TradeType != "自定义交易" && trade.TradeType != "现金流交易") { double? price = 0; if (request.TradePricesDic.ContainsKey(x)) { price = request.TradePricesDic.FirstOrDefault(y => y.Key == x).Value; } else if (request.PricesDic.ContainsKey(trade.UnderlyingCode)) { price = request.PricesDic.FirstOrDefault(y => y.Key == trade.UnderlyingCode).Value; } TradeValueResult optionValue = new TradeValueResult(); if (trade.TradeType == "远期") { if (price != null) { optionValue = ForwardradeCalcService.CalcValue(trade, price.Value); } } else { double? vol = 0; if (request.VolsDic.ContainsKey(x)) { vol = request.VolsDic.FirstOrDefault(z => z.Key == x).Value; } if (price != null && vol != null) { double ttm; var udm = DataCacheProvider.GetUnderlyingDataSource().GetData(trade.UnderlyingCode); if (PS.Config.Is厦门象屿 && trade.SettlementType == SettlementTypeEnum.ReferencePrice) { ttm = TradeCalcHelper.CalculateTTMDaysForXiangYu(request.ValueDate, trade.ExerciseDate.Value, udm.UnderlyingTypeId, trade.ExerciseDate.Value == valuedateBLL.ValueDate); } else { ttm = TradeCalcHelper.CalculateTTMDays(request.ValueDate, trade.ExerciseDate.Value, udm.UnderlyingTypeId, trade.ExerciseDate.Value == valuedateBLL.ValueDate); } var calcRequest = new OptionValueCalcRequest(valuedateBLL.SysRiskFreeRate()) { vols = new[] { vol.Value }, spotPrices = new[] { price.Value }, engineName = null, preciseTimeMode = request.ValueDate == valuedateBLL.ValueDate.Date, pricingRequest = QdpPricingRequest.BASIC_PRICING, timeToMaturityDays = ttm, calcScenario = CalcScenarioEnum.Pricing }; optionValue = OptionCalculatorV2.GetOptionValueResult(request.ValueDate, trade, calcRequest, out _); } } var pnl = EodOperationBase.GetPositionPnl(optionValue.Pv, trade.TradePrice ?? 0.0, trade.Notional, trade.OriginalNotional ?? 0, trade.BuySell); var singlePV = TradeHelper.GetTradeSinglePriceByTradePrice(optionValue.Pv, trade.Notional, PS.Config.ErpElement.IsPVIncludePrincipal ? trade.PrincipalSum() : 0, trade.BuySell, trade.TradeType, false); result.TradeOptionValueDic.Add(x, new CalcTradeResult() { Pv = optionValue.Pv, SinglePv = singlePV, Pnl = pnl, Delta = optionValue.Delta }); } }); return result; } public CalcTradesResult CalcSwapTrades(CalcTradesRequest request) { var result = new CalcTradesResult(); var eodTrades = DbContext.eod_swap.Where(x => request.TradeIds.Contains(x.SwapTradeId) && x.ValueDate == request.ValueDate).ToList(); var trades = DbContext.trade.Where(x => request.TradeIds.Contains(x.id)).ToList(); request.TradeIds.ForEach(x => { trade trade = new trade(); trade = trades.FirstOrDefault(y => y.id == x); double? price = request.PricesDic.FirstOrDefault(y => y.Key == trade.UnderlyingCode).Value; if (price != null) { var optionValue = PayoffSwapCalcService.CalcValueSingle(trade, request.ValueDate, null, false, price.Value); var pnl = EodOperationBase.GetPositionPnl(optionValue.Pv, trade.TradePrice ?? 0.0, trade.Notional, trade.OriginalNotional ?? 0, trade.BuySell); var singlePV = TradeHelper.GetTradeSinglePriceByTradePrice(optionValue.Pv, trade.Notional, PS.Config.ErpElement.IsPVIncludePrincipal ? trade.PrincipalSum() : 0, trade.BuySell, trade.TradeType, false); result.TradeOptionValueDic.Add(x, new CalcTradeResult() { Pv = optionValue.Pv, SinglePv = singlePV, Pnl = pnl, Delta = optionValue.Delta }); } }); return result; } //报价参数 private underlying_parameter[] GetRules(int underlyingId) { underlying_parameter[] rules = null; if (underlyingId > 0) { rules = DbContext.underlying_parameter.Where(u => u.UnderlyingId == underlyingId).ToArray(); } //补丁 波动率调整不需要rule if (rules == null || rules.Length == 0) { var arr = new string[] { underlying_parameter.CallAsk, underlying_parameter.CallBid, underlying_parameter.PutAsk, underlying_parameter.PutBid }; rules = arr.Select(n => new underlying_parameter() { Type = n, Delta = 0.0, Gamma = 0.0, Vega = 0.0, Theta = 0.0, Rho = 0.0, Other = 0.0 }).ToArray(); } return rules; } /// /// 计算保本雪球的年化期权费率 /// public double CalcSnowballAnnualPremium(OtcOptionTradeFull trade) { if (!trade.TradeDate.HasValue) { throw new ServiceException("交易日期 必须填写"); } if (!trade.ExerciseDate.HasValue) { throw new ServiceException("到期日期 必须填写"); } var underlying = DataCacheProvider.GetUnderlyingDataSource().GetData(trade.UnderlyingCode); if (underlying == null) { throw new ServiceException($"没有找到标的信息,标的代码:{trade.UnderlyingCode}"); } var tdConv = TradeConverter.ConvertOptionTrade(trade); tdConv.EnableSetFieldsByTradeType = false; if (!tdConv.TTMDays.HasValue) { tdConv.TTMDays = TradeCalcHelper.CalculateTTMDays(trade.TradeDate.Value, trade.ExerciseDate.Value, underlying.UnderlyingTypeId, PS.Config.ErpElement.PrecisionOfMinuteInQuote); } if (tdConv.SpotPrice <= 0) { throw new Exception("请输入正确的标的价格"); } underlying.QuotationDate = trade.TradeDate; if (underlying.QuotationDate != valuedateBLL.ValueDate.Date) { underlying.QuotationDate = AdjustQuotationDate(underlying.QuotationDate.Value, tdConv.TradeType); } //交易日等于系统日期,是当天报价,要使用精确时间模式 var isPreciseTimeMode = tdConv.TradeDate.Value.Date == valuedateBLL.ValueDate.Date; var callVol = PS.Config.IsTradeVol ? trade.TradeOpenVolatility ?? 0 : trade.Vol ?? 0; return ValueCalculator.CalculateSnowballAnnualPremium( UserId.ToString(), underlying, tdConv, new double[] { callVol }, new double[] { tdConv.SpotPrice ?? 0 }, preciseTimeMode: isPreciseTimeMode, engineName: trade.EngineName, quadratureFastMode: true); } /// /// 反算雪球票息 /// public double CalcSnowballKORebate(OtcOptionTradeFull trade) { if (!trade.TradeDate.HasValue) { throw new ServiceException("交易日期 必须填写"); } if (!trade.ExerciseDate.HasValue) { throw new ServiceException("到期日期 必须填写"); } var underlying = DataCacheProvider.GetUnderlyingDataSource().GetData(trade.UnderlyingCode); if (underlying == null) { throw new ServiceException($"没有找到标的信息,标的代码:{trade.UnderlyingCode}"); } var tdConv = TradeConverter.ConvertOptionTrade(trade); tdConv.EnableSetFieldsByTradeType = false; if (!tdConv.TTMDays.HasValue) { tdConv.TTMDays = TradeCalcHelper.CalculateTTMDays(trade.TradeDate.Value, trade.ExerciseDate.Value, underlying.UnderlyingTypeId, PS.Config.ErpElement.PrecisionOfMinuteInQuote); } if (tdConv.SpotPrice <= 0) { throw new Exception("请输入正确的标的价格"); } var valueDate = trade.TradeDate.Value; if (valueDate != valuedateBLL.ValueDate.Date) { valueDate = AdjustQuotationDate(valueDate, tdConv.TradeType); } //交易日等于系统日期,是当天报价,要使用精确时间模式 var isPreciseTimeMode = tdConv.TradeDate.Value.Date == valuedateBLL.ValueDate.Date; var callVol = PS.Config.IsTradeVol ? trade.TradeOpenVolatility ?? 0 : trade.Vol ?? 0; var koRebate = ValueCalculator.CalculateSnowballKORebateV2( trade: tdConv, snowball: tdConv.trade_snowball, valueDate: valueDate, vols: new double[] { callVol }, spotPrices: new double[] { tdConv.SpotPrice ?? 0 }, preciseTimeMode: isPreciseTimeMode, timeToMaturityDays: tdConv.TTMDays.Value, initialMarginRate: trade.InitialAdvance.HasValue ? trade.InitialAdvance.Value : 0.0); // 预付预付金比例 return koRebate; } /// /// 反算专业版雪球票息、波动率等 /// public double CalcSnowballSpecialistTargetValue(OtcOptionTradeFull trade, int calcTarget) { if (!trade.TradeDate.HasValue) { throw new ServiceException("交易日期 必须填写"); } if (!trade.ExerciseDate.HasValue) { throw new ServiceException("到期日期 必须填写"); } var underlying = DataCacheProvider.GetUnderlyingDataSource().GetData(trade.UnderlyingCode); if (underlying == null) { throw new ServiceException($"没有找到标的信息,标的代码:{trade.UnderlyingCode}"); } var snowballSpecialistOptionCalculator = new SnowballSpecialistOptionCalculator(); var tdConv = TradeConverter.ConvertOptionTrade(trade); var breakevenSnowballTrade = snowballSpecialistOptionCalculator.GetBreakevenTrade(tdConv); var QRList = GetOptionCalculatorV2(breakevenSnowballTrade, underlying, new OptionCalcParams { UserId = UserId.ToString(), IsCalcGreeks = false, EngineName = ConsTrade.GetEngineName(trade.EngineName), UnderlyingPrice = trade.UnderlyingPrice, ValueDate = trade.ValueDate, CalcVersion = "V1", Fixings = null }, CalcScenarioEnum.Pricing); var breakevenSnowballResult = QRList[0]; tdConv.TradePrice -= breakevenSnowballResult.Pv; tdConv.trade_snowball.PrepaymentRatio = 0; if (tdConv.SpotPrice <= 0) { throw new Exception("请输入正确的标的价格"); } var valueDate = trade.TradeDate.Value; if (valueDate != valuedateBLL.ValueDate.Date) { valueDate = AdjustQuotationDate(valueDate, tdConv.TradeType); } //交易日等于系统日期,是当天报价,要使用精确时间模式 var callVol = PS.Config.IsTradeVol ? trade.TradeOpenVolatility ?? 0 : trade.Vol ?? 0; var targetValue = snowballSpecialistOptionCalculator.CalcTargets(valueDate, trade.UnderlyingPrice ?? 0, callVol, calcTarget, tdConv); //var koRebate = ValueCalculator.CalculateSnowballKORebateV2( // trade: tdConv, // snowball: tdConv.trade_snowball, // valueDate: valueDate, // vols: new double[] { callVol }, // spotPrices: new double[] { tdConv.SpotPrice ?? 0 }, // preciseTimeMode: isPreciseTimeMode, // timeToMaturityDays: tdConv.TTMDays.Value, // initialMarginRate: trade.InitialAdvance.HasValue ? trade.InitialAdvance.Value : 0.0); // 预付预付金比例 return targetValue; } /// /// 反算凤凰票息 /// /// /// public double CalcPhoenixCouponRate(OtcOptionTradeFull trade) { if (!trade.TradeDate.HasValue) { throw new ServiceException("交易日期 必须填写"); } if (!trade.ExerciseDate.HasValue) { throw new ServiceException("到期日期 必须填写"); } var underlying = DataCacheProvider.GetUnderlyingDataSource().GetData(trade.UnderlyingCode); if (underlying == null) { throw new ServiceException($"没有找到标的信息,标的代码:{trade.UnderlyingCode}"); } var tdConv = TradeConverter.ConvertOptionTrade(trade); tdConv.EnableSetFieldsByTradeType = false; if (!tdConv.TTMDays.HasValue) { tdConv.TTMDays = TradeCalcHelper.CalculateTTMDays(trade.TradeDate.Value, trade.ExerciseDate.Value, underlying.UnderlyingTypeId, PS.Config.ErpElement.PrecisionOfMinuteInQuote); } if (tdConv.SpotPrice <= 0) { throw new Exception("请输入正确的标的价格"); } underlying.QuotationDate = trade.TradeDate; if (underlying.QuotationDate != valuedateBLL.ValueDate.Date) { underlying.QuotationDate = AdjustQuotationDate(underlying.QuotationDate.Value, tdConv.TradeType); } //交易日等于系统日期,是当天报价,要使用精确时间模式 var isPreciseTimeMode = tdConv.TradeDate.Value.Date == valuedateBLL.ValueDate.Date; var callVol = PS.Config.IsTradeVol ? trade.TradeOpenVolatility ?? 0 : trade.Vol ?? 0; return ValueCalculator.CalculatePhoenixCouponRate( underlying, tdConv, new double[] { callVol }, new double[] { tdConv.SpotPrice ?? 0 }, preciseTimeMode: isPreciseTimeMode, engineName: trade.EngineName); } /// /// 在期权定价时调整定价日 /// private DateTime AdjustQuotationDate(DateTime quotationDate, string tradeType) { // 对凤凰雪球不做报价日调整 if (tradeType == "雪球期权" || tradeType == "凤凰期权") { return quotationDate; } //解决定价、新增、修改定价不一致的问题 if (tradeType == "区间累积期权" || tradeType == "累计期权") { return quotationDate; } //为了暂时修复一个QDP计算方式与客户需求不匹配的情况 //在非精确模式下,QDP不包括交易日当天的时间价值,但根据报价需求,需要将交易日当天的时间价值计算在内, //所以要将交易日向前移动一天。未来QDP支持传递TTM来计算时,可以直接在TTM上加1,而不用移动交易日 //注意:当前这个临时修改必须在波动率插值之前调用,这样才能正确设置波动率日期 var calendar = CalendarImpl.Get("chn"); return calendar.PrevBizDay(quotationDate).DateTime; } /// /// 获取根据rule的补充数据 /// private static void OptionQuote(ref TradeValueResult valueResult, List quoteRules, double notional, string tradeType, ref QuotationResult qr) { foreach (var rule in quoteRules) { OptionQuote(ref valueResult, rule, notional, tradeType, ref qr); } } /// /// 根据规则 调整报价数据 /// private static double OptionQuote(ref TradeValueResult valueResult, underlying_parameter quoteRule, double notional, string tradeType, ref QuotationResult qr) { var result = double.NaN; if (valueResult != null && quoteRule != null) { result = valueResult.Pv + ((quoteRule.Delta ?? 0.0) * NumberHelper.Normalize(valueResult.Delta)) + ((quoteRule.Gamma ?? 0.0) * NumberHelper.Normalize(valueResult.Gamma)) + ((quoteRule.Vega ?? 0.0) * NumberHelper.Normalize(valueResult.Vega)) + (quoteRule.Other ?? 0.0); } else if (valueResult != null && quoteRule == null) { result = valueResult.Pv; } var roundResult = notional == 0 ? OtcFormatHelper.FormatValue(result, 2) : (OtcFormatHelper.FormatValue(result / notional, 2) * notional); //valueResult.TradePrice = result; var tempRet = Math.Abs(result); var tempRoundRet = Math.Abs(roundResult); if (tradeType == "凤凰期权" || tradeType == "雪球期权") { tempRet = result; tempRoundRet = roundResult; } //设置 报价结果 switch (quoteRule.Type) { case underlying_parameter.CallAsk: qr.CallAsk = tempRet; valueResult.TradePriceAsk = tempRet; valueResult.RoundedTradePriceAsk = Math.Abs(tempRoundRet); break; case underlying_parameter.CallBid: qr.CallBid = tempRet; valueResult.TradePriceBid = tempRet; valueResult.RoundedTradePriceBid = Math.Abs(tempRoundRet); break; case underlying_parameter.PutAsk: qr.PutAsk = tempRet; valueResult.TradePriceAsk = tempRet; valueResult.RoundedTradePriceAsk = Math.Abs(tempRoundRet); break; case underlying_parameter.PutBid: qr.PutBid = tempRet; valueResult.TradePriceBid = tempRet; valueResult.RoundedTradePriceBid = Math.Abs(tempRoundRet); break; } return result; } } }