using CxxCalcLib; using Qdp.ComputeService.Data.CommonModels.ValuationParams.Equity; using Qdp.ComputeService.Data.CommonModels.ValuationParams.FixedIncome; using Qdp.ComputeServiceV2.Data.CommonModels.TradeInfos; using Qdp.ComputeServiceV2.Data.CommonModels.TradeInfos.Options; using Qdp.Foundation.Implementations; using Qdp.Pricing.Base.Enums; using Qdp.Pricing.Base.Implementations; using Qdp.Pricing.Base.Interfaces; using Qdp.Pricing.Base.Utilities; using Qdp.Pricing.Ecosystem.Market; using Qdp.Pricing.Library.Common.Base; using Qdp.Pricing.Library.Common.Interfaces; using Qdp.Pricing.Library.Common.Products.Rates.Engines; using Qdp.Pricing.Library.Options.Products.Accumulator.Engines.Analytical; using Qdp.Pricing.Library.Options.Products.Accumulator.Engines.Numerical; using Qdp.Pricing.Library.Options.Products.Airbag; using Qdp.Pricing.Library.Options.Products.Airbag.Engines.Analytical; using Qdp.Pricing.Library.Options.Products.Asian; using Qdp.Pricing.Library.Options.Products.AsianSyntheticSpread.Engines.Analytical; using Qdp.Pricing.Library.Options.Products.Autocall.Phoenix; using Qdp.Pricing.Library.Options.Products.Autocall.Phoenix.Numerical; using Qdp.Pricing.Library.Options.Products.Autocall.Snowball; using Qdp.Pricing.Library.Options.Products.Barrier; using Qdp.Pricing.Library.Options.Products.Binary; using Qdp.Pricing.Library.Options.Products.DoubleSharkFin; using Qdp.Pricing.Library.Options.Products.PayoffEnhance; using Qdp.Pricing.Library.Options.Products.PayoffEnhance.Engines.Analytical; using Qdp.Pricing.Library.Options.Products.RangeAccrual; using Qdp.Pricing.Library.Options.Products.Vanilla; using YLErp.BLL.Calculation; using YLErp.BLL.Calculation.Engine; using YLErp.BLL.Calculation.V2; using YLErp.Commons; using YLErp.Enums; using YLErp.Modules.ApiModule; using YLErp.Modules.CalculationLogModule; using YLErp.Modules.CalculationModule.Abstract; using YLErp.QdpModule; namespace YLErp.Modules.CalculationModule { /// /// 交易风险计算(这个类中的MarketProxy应该是已经准备好波动率和价格的对象) /// public static class TradeRiskCalcUtil { /// /// 获取Qdp数据 /// public static TradeValueResult CalcTradeRisk(OtcTradeBase tradeObj, IOtcTradeValueCalcContext context, out underlying_manager[] underlyings) { if (tradeObj == null) { throw new ArgumentNullException(nameof(tradeObj)); } if (context == null) { throw new ArgumentNullException(nameof(context)); } if (string.IsNullOrWhiteSpace(tradeObj.UnderlyingCode) && tradeObj.TradeType != "现金流交易") { throw new TradeCalcExpception(tradeObj.id, "UnderlyingCode为空"); } if (context.MarketProxy == null) { throw new ArgumentNullException("context.Market"); } return new TradeRiskValueCalc(context, tradeObj).GetTradeValue(out underlyings); } #region----CalcHelper---- class InnerCalcHelper : IDisposable { protected readonly MarketProxy _marketProxy; protected string _dividendCurveName; public InnerCalcHelper(MarketProxy marketProxy) { _marketProxy = marketProxy; } public virtual void Dispose() { if (!string.IsNullOrEmpty(_dividendCurveName)) { _marketProxy.RemoveYieldCurve(_dividendCurveName); _dividendCurveName = null; } } protected void PrepareBase(OptionCalcParam calcParam) where T : OptionTradeParamBase { _dividendCurveName = null; //分红率曲线 if (!calcParam.TradeParam.isForwardTrade) { var dividendRate = calcParam.TradeParam.dividendRate; if (dividendRate > 0) { _dividendCurveName = Guid.NewGuid().ToString(); var dividendCurve = CalculatorHelper.CreateConstantRiskFreeCurve(_dividendCurveName, dividendRate); _marketProxy.SetYieldCurve(_dividendCurveName, dividendCurve); } } } } class SingleUnderlyingCalcHelper : InnerCalcHelper { string volSurfaceName; double spotPrice; public string underlyingTicker { get; private set; } public SingleUnderlyingCalcHelper(MarketProxy marketProxy) : base(marketProxy) { } public SingleUnderlyingCalcHelper Prepare(OptionCalcParam calcParam, bool noUnderlying = false) where T : OptionTradeParamBase { if (calcParam is null) { throw new ArgumentNullException(nameof(calcParam)); } if (!noUnderlying) { if (calcParam.spotPrices == null || !calcParam.spotPrices.Any()) { throw new Exception("期权计算缺少标的现价"); } if (calcParam.underlyingTickers == null || !calcParam.underlyingTickers.Any()) { throw new Exception("期权计算缺少标的"); } spotPrice = calcParam.spotPrices[0]; underlyingTicker = calcParam.underlyingTickers[0]; OptimizeSpotPrice(ref spotPrice, calcParam.TradeParam.GetAbsStrike()); //设置标的价格 _marketProxy.SetStockPrice(underlyingTicker, spotPrice); } if (calcParam.volSurfaceNames == null || !calcParam.volSurfaceNames.Any()) { throw new Exception("期权计算缺少波动率"); } volSurfaceName = calcParam.volSurfaceNames[0]; PrepareBase(calcParam); return this; } public OptionValuationParameters GetValuationParameters() { return new OptionValuationParameters(_marketProxy.DiscountCurveName ?? MarketProxy.ConstantZeroCurve, _dividendCurveName ?? MarketProxy.ConstantZeroCurve, volSurfaceName, underlyingTicker); } } /// /// 计算T+1日Delta /// static double? CalcDeltaT1(TradeBase optionTrade, PrebuiltQdpMarket baseMarket, IEngine engine, OptionValuationParameters parameters) { var variety = DataCacheProvider.GetVariety(parameters.UnderlyingId); var referenceDateAddOne = baseMarket.ReferenceDate.AddDays(1); var nextDay = QdpCalendarHelper.GetNonHoliday(referenceDateAddOne); if (nextDay > optionTrade.MaturityDate.DateTime) { return null; } var market = new PrebuiltQdpMarket(nextDay.ToString("yyyy-MM-dd"), nextDay) { YieldCurves = baseMarket.YieldCurves, StockPrices = baseMarket.StockPrices, VolSurfaces = baseMarket.VolSurfaces, CorrSurfaces = baseMarket.CorrSurfaces }; var isAsianOption = false; if (optionTrade.Instrument is AsianOption asianOption && asianOption.Fixings != null) { isAsianOption = true; if (asianOption.Fixings.TryGetValue(baseMarket.ReferenceDate, out var dd)) { asianOption.Fixings[nextDay] = dd; } else if (baseMarket.StockPrices != null && asianOption.UnderlyingTickers != null && asianOption.UnderlyingTickers.Length > 0 && baseMarket.StockPrices.TryGetValue(asianOption.UnderlyingTickers[0], out dd)) { asianOption.Fixings[baseMarket.ReferenceDate] = dd; if (!PS.Config.Is润和) { asianOption.Fixings[nextDay] = dd; } } } if (optionTrade.Instrument is Qdp.Pricing.Library.Common.Base.OptionBase optionBase) { if (PS.Config.Is润和) { var optionDayCount = CalculatorHelper.GetTradeDayCount(); var timeToMaturityDays = double.NaN; if (isAsianOption) { timeToMaturityDays = TradeCalcHelper.CalculateTTMDays(nextDay, optionTrade.MaturityDate.DateTime, 0, false, serverDateTime: nextDay.AddHours(15)); } else { //当天有夜盘,并且当天还没有进入夜盘(以晚上9点为判断标准),DeltaT+1按照当天晚上9点来计算,否则按照下一个工作日早晨9点来计算 //如果当天时节假日,则按照下一个工作日早晨9点来计算 //ToDo: 关于长假,虽然品种有夜盘,但是放假前一天晚上是没有夜盘,DeltaT+1需要用下一个工作日的早晨9点来计算的逻辑还未实现 if (variety.HasNightMarket && DateTime.Now.Hour < 21 && !QdpCalendarHelper.IsHoliday(baseMarket.ReferenceDate)) { timeToMaturityDays = TradeCalcHelper.CalculateTTMDays(baseMarket.ReferenceDate.DateTime, optionTrade.MaturityDate.DateTime, 0, false, serverDateTime: baseMarket.ReferenceDate.DateTime.AddHours(21)); } else { timeToMaturityDays = TradeCalcHelper.CalculateTTMDays(nextDay, optionTrade.MaturityDate.DateTime, 0, false, serverDateTime: nextDay.AddHours(9)); } } optionBase.PricingToverride = QdpCalendarHelper.CalculateTFromDays(timeToMaturityDays, optionDayCount, optionTrade.StartDate); } else { optionBase.PricingToverride = double.NaN; } optionBase.CommodityFuturesPreciseTimeMode = false; } var result = optionTrade.CalculateRisks(market, PricingRequest.Delta, engine, parameters); if (result.Succeeded) { if (PS.Config.Company == Configuration.CompanyEnum.国元固收) { return result.Delta / 10000; } else { return result.Delta; } } return null; } /// /// 现价和执行价非常接近时QDP计算雪球凤凰时会非常慢而且吃内存,故而需要优化 /// !!!注意不要传入相对行权价 /// static void OptimizeSpotPrice(ref double spotPrice, double strike) { if (spotPrice == 0) { if (Math.Abs(strike) < 1e-5) { spotPrice = strike; } } else if (Math.Abs(1 - (strike / spotPrice)) < 1e-5) { spotPrice = strike; } } #endregion #region----香草期权---- /// /// 计算香草期权的估值与风险 /// public static TradeValueResult GetVanillaOptionValue(MarketProxy marketProxy, OptionCalcParam calcParam) { if (marketProxy is null) { throw new ArgumentNullException(nameof(marketProxy)); } if (calcParam is null) { throw new ArgumentNullException(nameof(calcParam)); } using var helper = new SingleUnderlyingCalcHelper(marketProxy).Prepare(calcParam); var optionTrade = QdpTradeBuilder.GetVanillaOptionTrade(calcParam.TradeParam); // 添加计算日志 AddCalculationLog(optionTrade, calcParam.calcScenario, calcParam?.TradeParam?.tradeNumber); var parameters = helper.GetValuationParameters(); var option = optionTrade.Instrument as VanillaOption; var factory = OptionEngineFactory.GetEngineFactory("VanillaOption"); var engine = factory.GetEngine(calcParam.engineName, option.Exercise); marketProxy.Trace?.WriteData(optionTrade, "QDP 香草"); var result = optionTrade.CalculateRisks(marketProxy.QdpMarket, calcParam.pricingRequest, engine, parameters); if (result.Succeeded) { var tr = new TradeValueResult(result) { UnderlyingCode = helper.underlyingTicker, RoundedPv = OtcFormatHelper.FormatValue(result.Pv / optionTrade.Notional, 2) * optionTrade.Notional }; if (calcParam.CalcDeltaT1) { tr.DeltaT1 = CalcDeltaT1(optionTrade, marketProxy.QdpMarket, engine, parameters); } if (PS.Config.Company == Configuration.CompanyEnum.天风)//?不记得这个算法调整为啥有些地方加了,有些地方没加了,所以不要把这个逻辑移动到 ConvertTradeValueResultOfCompany 里 { tr.GammaCash = 0.5 * result.Gamma * Math.Pow(calcParam.spotPrices[0] * 0.01, 2); } ValueCalculator.ConvertTradeValueResultOfCompany(tr, calcParam.TradeParam.tradeType); return tr; } return null; } #endregion #region----彩虹期权---- /// /// 彩虹期权 /// public static TradeValueResult GetRainbowOptionValue(MarketProxy marketProxy, OptionCalcParam calcParam) { if (marketProxy is null) { throw new ArgumentNullException(nameof(marketProxy)); } if (calcParam is null) { throw new ArgumentNullException(nameof(calcParam)); } var tp = calcParam.TradeParam; //这里需要优化 if (calcParam.volSurfaceNames == null || calcParam.volSurfaceNames.Length != 2) { throw new Exception("缺少波动率"); } var optionTrade = QdpTradeBuilder.GetRainbowOptionTrade(tp); // 添加计算日志 AddCalculationLog(optionTrade, calcParam.calcScenario, calcParam?.TradeParam?.tradeNumber); //设置标的价格 var count = Math.Min(calcParam.underlyingTickers.Length, calcParam.spotPrices.Length); for (var i = 0; i < count; ++i) { marketProxy.SetStockPrice(calcParam.underlyingTickers[i], calcParam.spotPrices[i]); } //设置correlation marketProxy.SetCorrelation(calcParam.underlyingTickers, tp.correlation); string dividendCurveName = null; OptionValuationParameters parameters; if (ConsGlobal.InstrumentType.CalcTypeIsStock(tp.underlyingInstrumentType)) { //设置DividendCurve dividendCurveName = Guid.NewGuid().ToString(); var dividendCurve = CalculatorHelper.CreateConstantRiskFreeCurve(dividendCurveName, tp.dividendRate); marketProxy.SetYieldCurve(dividendCurveName, dividendCurve); parameters = new OptionValuationParameters( marketProxy.DiscountCurveName, new string[] { dividendCurveName, dividendCurveName }, calcParam.volSurfaceNames, new string[] { calcParam.underlyingTickers[0] + "_" + calcParam.underlyingTickers[1] }, null); } else { parameters = new OptionValuationParameters( marketProxy.DiscountCurveName, new string[] { MarketProxy.ConstantZeroCurve, MarketProxy.ConstantZeroCurve }, calcParam.volSurfaceNames, new string[] { calcParam.underlyingTickers[0] + "_" + calcParam.underlyingTickers[1] }, null); } marketProxy.Trace?.WriteData(optionTrade, "QDP 彩虹"); var riskResult = optionTrade.CalculateRisks(marketProxy.QdpMarket, calcParam.pricingRequest, null, parameters); if (!string.IsNullOrEmpty(dividendCurveName)) { marketProxy.RemoveYieldCurve(dividendCurveName); } if (riskResult.Succeeded) { var ret = new TradeValueResult { UnderlyingCode = calcParam.underlyingTickers[0], Pv = riskResult.Pv, Delta = riskResult.asset1Delta, Gamma = riskResult.asset1Gamma, Vega = riskResult.asset1Vega, CalendarDayTheta = riskResult.Theta, TradingDayTheta = riskResult.ThetaPnL, Rho = riskResult.Rho, DeltaCash = riskResult.DeltaCash, GammaCash = riskResult.GammaCash, Vol = riskResult.PricingVol, UnderlyingCode2 = calcParam.underlyingTickers[1], Delta2 = riskResult.asset2Delta, Gamma2 = riskResult.asset2Gamma, DeltaCash2 = riskResult.asset2DeltaCash, GammaCash2 = riskResult.asset2GammaCash, Vega2 = riskResult.asset2Vega, CrossGamma = riskResult.crossGamma, CrossVogga = riskResult.crossVomma, CorrVega = riskResult.correlationVega, RoundedPv = OtcFormatHelper.FormatValue(riskResult.Pv / optionTrade.Notional, 2) * optionTrade.Notional, TimeValue = riskResult.TimeValue }; ValueCalculator.ConvertTradeValueResultOfCompany(ret, calcParam?.TradeParam?.tradeType); return ret; } return null; } #endregion #region----亚式期权---- /// /// 亚式期权 /// public static TradeValueResult GetAsianOptionValue(MarketProxy marketProxy, OptionCalcParam calcParam) { using var helper = new SingleUnderlyingCalcHelper(marketProxy).Prepare(calcParam); var optionTrade = QdpTradeBuilder.GetAsianOptionTrade(calcParam.TradeParam); // 添加计算日志 AddCalculationLog(optionTrade, calcParam.calcScenario, calcParam?.TradeParam?.tradeNumber); var parameters = helper.GetValuationParameters(); var option = optionTrade.Instrument as AsianOption; var factory = OptionEngineFactory.GetEngineFactory("AsianOption"); var engine = factory.GetEngine(calcParam.engineName, option.Exercise, calcParam.TradeParam.payoffType, option, calcParam.TradeParam.strikeStyle); var pricingRequest = calcParam.pricingRequest; if (option.AsianType == AsianType.EnhancedArithmeticAverage) { pricingRequest = QdpPricingRequest.UnionBaiscGreeks(calcParam.pricingRequest); if ((calcParam.pricingRequest & PricingRequest.TimeValue) == PricingRequest.TimeValue) { pricingRequest |= PricingRequest.TimeValue; } } marketProxy.Trace?.WriteData(optionTrade, "QDP 亚式"); var result = optionTrade.CalculateRisks(marketProxy.QdpMarket, pricingRequest, engine, parameters); if (result.Succeeded) { var tr = new TradeValueResult(result) { UnderlyingCode = helper.underlyingTicker, RoundedPv = OtcFormatHelper.FormatValue(result.Pv / optionTrade.Notional, 2) * optionTrade.Notional, }; if (calcParam.CalcDeltaT1) { tr.DeltaT1 = CalcDeltaT1(optionTrade, marketProxy.QdpMarket, engine, parameters); } if (PS.Config.Company == Configuration.CompanyEnum.天风) { tr.GammaCash = 0.5 * result.Gamma * Math.Pow(calcParam.spotPrices[0] * 0.01, 2); } ValueCalculator.ConvertTradeValueResultOfCompany(tr, calcParam?.TradeParam?.tradeType); return tr; } return null; } /// /// 亚式合成价差期权 /// public static TradeValueResult GetAsianSSpreadOptionValue(MarketProxy marketProxy, OptionCalcParam calcParam) { using var helper = new SingleUnderlyingCalcHelper(marketProxy).Prepare(calcParam); var optionTrade = QdpTradeBuilder.GetAsianSSpreadOptionTrade(calcParam.TradeParam); // 添加计算日志 AddCalculationLog(optionTrade, calcParam.calcScenario, calcParam?.TradeParam?.tradeNumber); var parameters = helper.GetValuationParameters(); var engine = new AnalyticalAsianSyntheticNormalSpreadOptionEngine(); marketProxy.Trace?.WriteData(optionTrade, "QDP 亚式合成价差"); var result = optionTrade.CalculateRisks(marketProxy.QdpMarket, calcParam.pricingRequest, engine, parameters); if (result.Succeeded) { var tr = new TradeValueResult(result) { UnderlyingCode = helper.underlyingTicker, RoundedPv = OtcFormatHelper.FormatValue(result.Pv / optionTrade.Notional, 2) * optionTrade.Notional }; if (calcParam.CalcDeltaT1) { tr.DeltaT1 = CalcDeltaT1(optionTrade, marketProxy.QdpMarket, engine, parameters); } if (PS.Config.Company == Configuration.CompanyEnum.天风) { tr.GammaCash = 0.5 * result.Gamma * Math.Pow(calcParam.spotPrices[0] * 0.01, 2); } ValueCalculator.ConvertTradeValueResultOfCompany(tr, calcParam?.TradeParam?.tradeType); return tr; } return null; } #endregion #region----障碍期权---- /// /// 障碍期权 /// public static TradeValueResult GetBarrierOptionValue(MarketProxy marketProxy, OptionCalcParam calcParam) { using var helper = new SingleUnderlyingCalcHelper(marketProxy).Prepare(calcParam); var tp = calcParam.TradeParam; //如果用户自定义的观察日就是每天观察,则忽略自定义观察日 var observationDates = CalendarImpl.Get("chn").BizDaysBetweenDatesExcluStartDay(tp.startDate, tp.exerciseDate) .Union(new[] { tp.exerciseDate }).ToArray(); var barrierObservationDates = QdpHelper.ParseObservationDate(tp.observationDateStr); if (barrierObservationDates != null && (barrierObservationDates.Count(d => d >= observationDates.First() && d <= observationDates.Last()) == observationDates.Length)) { tp.observationDateStr = null; } var pricingRequest = calcParam.pricingRequest; if (!string.IsNullOrWhiteSpace(calcParam.TradeParam.observationDateStr)) { pricingRequest = QdpPricingRequest.UnionBaiscGreeks(calcParam.pricingRequest); } var optionTrade = QdpTradeBuilder.GetBarrierOptionTrade(tp); // 添加计算日志 AddCalculationLog(optionTrade, calcParam.calcScenario, calcParam?.TradeParam?.tradeNumber); var parameters = helper.GetValuationParameters(); var option = optionTrade.Instrument as BarrierOption; var factory = OptionEngineFactory.GetEngineFactory("BarrierOption"); var engine = factory.GetEngine(calcParam.engineName, option.Exercise, option, !string.IsNullOrWhiteSpace(tp.observationDateStr), calcParam.quadratureFastMode); marketProxy.Trace?.WriteData(optionTrade, "QDP 障碍"); var result = optionTrade.CalculateRisks(marketProxy.QdpMarket, pricingRequest, engine, parameters); if (result.Succeeded) { var tr = new TradeValueResult(result) { UnderlyingCode = helper.underlyingTicker, RoundedPv = OtcFormatHelper.FormatValue(result.Pv / optionTrade.Notional, 2) * optionTrade.Notional, }; if (calcParam.CalcDeltaT1) { tr.DeltaT1 = CalcDeltaT1(optionTrade, marketProxy.QdpMarket, engine, parameters); } if (PS.Config.Company == Configuration.CompanyEnum.天风) { tr.GammaCash = 0.5 * result.Gamma * Math.Pow(calcParam.spotPrices[0] * 0.01, 2); } ValueCalculator.ConvertTradeValueResultOfCompany(tr, calcParam?.TradeParam?.tradeType); return tr; } return null; } #endregion #region----二元期权---- private static BinaryOptionReplicationStrategy ConvertReplicationStrategy(string replicationStrategy) { switch (replicationStrategy) { case "Down": return BinaryOptionReplicationStrategy.Down; case "Middle": return BinaryOptionReplicationStrategy.Middle; default: return BinaryOptionReplicationStrategy.None; } } /// /// 二元期权 /// public static TradeValueResult GetBinaryOptionValue(MarketProxy marketProxy, OptionCalcParam calcParam) { if (marketProxy is null) { throw new ArgumentNullException(nameof(marketProxy)); } if (calcParam is null) { throw new ArgumentNullException(nameof(calcParam)); } using var helper = new SingleUnderlyingCalcHelper(marketProxy).Prepare(calcParam); var tp = calcParam.TradeParam; var optionTrade = QdpTradeBuilder.GetBinaryOptionTrade(tp); // 添加计算日志 AddCalculationLog(optionTrade, calcParam.calcScenario, calcParam?.TradeParam?.tradeNumber); var replicationStrategy = ConvertReplicationStrategy(tp.binaryOptionReplicationStrategy); var parameters = helper.GetValuationParameters(); var option = optionTrade.Instrument as BinaryOption; var factory = OptionEngineFactory.GetEngineFactory("BinaryOption"); var engine = factory.GetEngine(calcParam.engineName, option.Exercise, (tp.replicationShiftSize <= 0) ? option.Strike * 0.01 : tp.replicationShiftSize, replicationStrategy, !string.IsNullOrWhiteSpace(tp.observationDateStr), option.RebateAnnualizedAtKO); marketProxy.Trace?.WriteData(optionTrade, "QDP 二元"); var result = optionTrade.CalculateRisks(marketProxy.QdpMarket, calcParam.pricingRequest, engine, parameters); if (result.Succeeded) { var tr = new TradeValueResult(result) { UnderlyingCode = helper.underlyingTicker, RoundedPv = OtcFormatHelper.FormatValue(result.Pv / optionTrade.Notional, 2) * optionTrade.Notional, }; if (calcParam.CalcDeltaT1) { tr.DeltaT1 = CalcDeltaT1(optionTrade, marketProxy.QdpMarket, engine, parameters); } if (PS.Config.Company == Configuration.CompanyEnum.天风) { tr.GammaCash = 0.5 * result.Gamma * Math.Pow(calcParam.spotPrices[0] * 0.01, 2); } ValueCalculator.ConvertTradeValueResultOfCompany(tr, calcParam?.TradeParam?.tradeType); return tr; } return null; } #endregion #region----价差期权---- /// /// 价差期权 /// public static TradeValueResult GetSpreadOptionValue(MarketProxy marketProxy, OptionCalcParam calcParam) { var tp = calcParam.TradeParam; if (calcParam.volSurfaceNames == null) { tp.volSurfaceNames = calcParam.underlyingTickers.Select(x => x.GenerateVolSurfaceKey()).ToArray(); } var optionTrade = QdpTradeBuilder.GetSpreadOptionTrade(tp); // 添加计算日志 AddCalculationLog(optionTrade, calcParam.calcScenario, calcParam?.TradeParam?.tradeNumber); //设置标的价格 var count = Math.Min(calcParam.underlyingTickers.Length, calcParam.spotPrices.Length); for (var i = 0; i < count; ++i) { marketProxy.SetStockPrice(calcParam.underlyingTickers[i], calcParam.spotPrices[i]); } //设置correlation marketProxy.SetCorrelation(new string[] { calcParam.underlyingTickers[0], calcParam.underlyingTickers[1] }, tp.correlations[0]); if (calcParam.underlyingTickers.Length >= 3) { marketProxy.SetCorrelation(new string[] { calcParam.underlyingTickers[0], calcParam.underlyingTickers[2] }, tp.correlations[1]); marketProxy.SetCorrelation(new string[] { calcParam.underlyingTickers[1], calcParam.underlyingTickers[2] }, tp.correlations[2]); if (calcParam.underlyingTickers.Length >= 4) { marketProxy.SetCorrelation(new string[] { calcParam.underlyingTickers[0], calcParam.underlyingTickers[3] }, tp.correlations[3]); marketProxy.SetCorrelation(new string[] { calcParam.underlyingTickers[1], calcParam.underlyingTickers[3] }, tp.correlations[4]); marketProxy.SetCorrelation(new string[] { calcParam.underlyingTickers[2], calcParam.underlyingTickers[3] }, tp.correlations[5]); } } var parameters = QdpTradeBuilder.GetParametersForSpreadOption(calcParam.underlyingTickers, marketProxy.DiscountCurveName, calcParam.volSurfaceNames); var factory = OptionEngineFactory.GetEngineFactory("SpreadOption"); var engine = factory.GetEngine(calcParam.engineName); marketProxy.Trace?.WriteData(optionTrade, "QDP 价差"); var riskResult = optionTrade.CalculateRisks(marketProxy.QdpMarket, calcParam.pricingRequest, engine, parameters); if (riskResult.Succeeded) { var valueResult = new TradeValueResult { Pv = riskResult.Pv, Delta = riskResult.asset1Delta, Gamma = riskResult.asset1Gamma, Vega = riskResult.asset1Vega, CalendarDayTheta = riskResult.Theta, TradingDayTheta = riskResult.ThetaPnL, Rho = riskResult.Rho, DeltaCash = riskResult.DeltaCash, GammaCash = riskResult.GammaCash, Vol = riskResult.PricingVol, Delta2 = riskResult.asset2Delta, Gamma2 = riskResult.asset2Gamma, DeltaCash2 = riskResult.asset2DeltaCash, GammaCash2 = riskResult.asset2GammaCash, Vega2 = riskResult.asset2Vega, Delta3 = riskResult.asset3Delta, Gamma3 = riskResult.asset3Gamma, DeltaCash3 = riskResult.asset3DeltaCash, GammaCash3 = riskResult.asset3GammaCash, Vega3 = riskResult.asset3Vega, Delta4 = riskResult.asset4Delta, Gamma4 = riskResult.asset4Gamma, DeltaCash4 = riskResult.asset4DeltaCash, GammaCash4 = riskResult.asset4GammaCash, Vega4 = riskResult.asset4Vega, CrossGamma = riskResult.crossGamma, CrossVogga = riskResult.crossVomma, CorrVega = riskResult.correlationVega, RoundedPv = OtcFormatHelper.FormatValue(riskResult.Pv / optionTrade.Notional, 2) * optionTrade.Notional, TimeValue = riskResult.TimeValue, UnderlyingCode = calcParam.underlyingTickers[0], UnderlyingCode2 = calcParam.underlyingTickers[1] }; if (calcParam.underlyingTickers.Length > 2) { valueResult.UnderlyingCode3 = calcParam.underlyingTickers[2]; } if (calcParam.underlyingTickers.Length > 3) { valueResult.UnderlyingCode4 = calcParam.underlyingTickers[3]; } ValueCalculator.ConvertTradeValueResultOfCompany(valueResult, calcParam?.TradeParam?.tradeType); return valueResult; } return null; } #endregion #region----合成价差期权---- /// /// 合成价差期权 /// public static TradeValueResult GetSSpreadOptionValue(MarketProxy marketProxy, OptionCalcParam calcParam) { using var helper = new SingleUnderlyingCalcHelper(marketProxy).Prepare(calcParam); var optionTrade = QdpTradeBuilder.GetSSpreadOptionTrade(calcParam.TradeParam); // 添加计算日志 AddCalculationLog(optionTrade, calcParam.calcScenario, calcParam?.TradeParam?.tradeNumber); var parameters = helper.GetValuationParameters(); var option = optionTrade.Instrument as VanillaOption; var factory = OptionEngineFactory.GetEngineFactory("SynthticNormalSpreadOption"); var engine = factory.GetEngine(calcParam.engineName, option.Exercise); marketProxy.Trace?.WriteData(optionTrade, "QDP 合成价差"); var result = optionTrade.CalculateRisks(marketProxy.QdpMarket, calcParam.pricingRequest, engine, parameters); if (result.Succeeded) { var tr = new TradeValueResult(result) { UnderlyingCode = helper.underlyingTicker, RoundedPv = OtcFormatHelper.FormatValue(result.Pv / optionTrade.Notional, 2) * optionTrade.Notional, }; if (calcParam.CalcDeltaT1) { tr.DeltaT1 = CalcDeltaT1(optionTrade, marketProxy.QdpMarket, engine, parameters); } if (PS.Config.Company == Configuration.CompanyEnum.天风) { tr.GammaCash = 0.5 * result.Gamma * Math.Pow(calcParam.spotPrices[0] * 0.01, 2); } ValueCalculator.ConvertTradeValueResultOfCompany(tr, calcParam?.TradeParam?.tradeType); return tr; } return null; } /// /// 合成价差期权 /// public static double[] CalcSSpreadCrossGammas(MarketProxy marketProxy, OptionCalcParam calcParam, double[] coefficients) { using var helper = new SingleUnderlyingCalcHelper(marketProxy).Prepare(calcParam); var optionTrade = QdpTradeBuilder.GetSSpreadOptionTrade(calcParam.TradeParam); // 添加计算日志 AddCalculationLog(optionTrade, calcParam.calcScenario, calcParam?.TradeParam?.tradeNumber); var parameters = helper.GetValuationParameters(); marketProxy.Trace?.WriteData(optionTrade, "QDP 合成价差 CrossGammas"); var result = optionTrade.CalcCrossGammas(marketProxy.QdpMarket, parameters); if (result == null) { return null; } var crossGammas = new List(); // 先获取返回的结果矩阵中的对角线上的元素,对应的是每个标的的Gamma for (var i = 0; i < coefficients.Length; ++i) { crossGammas.Add(result[i, i]); } // 再获取两两对应的Cross Gamma for (var i = 0; i < coefficients.Length - 1; ++i) { for (var j = i + 1; j < coefficients.Length; ++j) { crossGammas.Add(result[i, j]); } } return crossGammas.ToArray(); } #endregion #region ----双鲨期权---- public static TradeValueResult GetDoubleSharkFinOptionValue(MarketProxy marketProxy, OptionCalcParam calcParam) { using var helper = new SingleUnderlyingCalcHelper(marketProxy).Prepare(calcParam); var tp = calcParam.TradeParam; //如果用户自定义的观察日就是每天观察,则忽略自定义观察日 var observationDates = CalendarImpl.Get("chn").BizDaysBetweenDatesExcluStartDay(tp.startDate, tp.exerciseDate) .Union(new[] { tp.exerciseDate }).ToArray(); var barrierObservationDates = QdpHelper.ParseObservationDate(tp.observationDateStr); if (barrierObservationDates != null && (barrierObservationDates.Count(d => d >= observationDates.First() && d <= observationDates.Last()) == observationDates.Length)) { tp.observationDateStr = null; } var optionTrade = QdpTradeBuilder.GetDoubleSharkFinOptionTrade(tp); // 添加计算日志 AddCalculationLog(optionTrade, calcParam.calcScenario, calcParam?.TradeParam?.tradeNumber); var parameters = helper.GetValuationParameters(); var option = optionTrade.Instrument as DoubleSharkFinOption; var factory = OptionEngineFactory.GetEngineFactory("DoubleSharkFinOption"); var engine = factory.GetEngine(calcParam.engineName, option.Exercise, option, false , !string.IsNullOrWhiteSpace(tp.observationDateStr), calcParam.quadratureFastMode); marketProxy.Trace?.WriteData(optionTrade, "QDP 双鲨"); var result = optionTrade.CalculateRisks(marketProxy.QdpMarket, calcParam.pricingRequest, engine, parameters); if (result.Succeeded) { var tr = new TradeValueResult(result) { UnderlyingCode = helper.underlyingTicker, RoundedPv = OtcFormatHelper.FormatValue(result.Pv / optionTrade.Notional, 2) * optionTrade.Notional, }; if (calcParam.CalcDeltaT1) { tr.DeltaT1 = CalcDeltaT1(optionTrade, marketProxy.QdpMarket, engine, parameters); } if (PS.Config.Company == Configuration.CompanyEnum.天风) { tr.GammaCash = 0.5 * result.Gamma * Math.Pow(calcParam.spotPrices[0] * 0.01, 2); } ValueCalculator.ConvertTradeValueResultOfCompany(tr, calcParam?.TradeParam?.tradeType); return tr; } return null; } #endregion #region ----凤凰期权---- public static TradeValueResult GetAutocallOptionValue(MarketProxy marketProxy, OptionCalcParam calcParam) { using var helper = new SingleUnderlyingCalcHelper(marketProxy).Prepare(calcParam); var optionTrade = QdpTradeBuilder.GetAutocallOptionTrade(calcParam.TradeParam); // 添加计算日志 AddCalculationLog(optionTrade, calcParam.calcScenario, calcParam?.TradeParam?.tradeNumber); var parameters = helper.GetValuationParameters(); var option = optionTrade.Instrument as AutoCall; var factory = OptionEngineFactory.GetEngineFactory("AutoCall"); var engine = factory.GetEngine(calcParam.engineName, option.Exercise, calcParam.quadratureFastMode); marketProxy.Trace?.WriteData(optionTrade, "QDP 凤凰"); try { var result = optionTrade.CalculateRisks(marketProxy.QdpMarket, calcParam.pricingRequest, engine, parameters); if (result.Succeeded) { var tr = new TradeValueResult(result) { UnderlyingCode = helper.underlyingTicker, RoundedPv = OtcFormatHelper.FormatValue(result.Pv / optionTrade.Notional, 2) * optionTrade.Notional }; if (calcParam.CalcDeltaT1) { tr.DeltaT1 = CalcDeltaT1(optionTrade, marketProxy.QdpMarket, engine, parameters); } if (PS.Config.Company == Configuration.CompanyEnum.天风) { tr.GammaCash = 0.5 * result.Gamma * Math.Pow(calcParam.spotPrices[0] * 0.01, 2); } ValueCalculator.ConvertTradeValueResultOfCompany(tr, calcParam?.TradeParam?.tradeType); return tr; } } catch (Exception ex) { using var db = DbContextFactory.GetYLDbContext(); if (int.TryParse(calcParam.TradeParam.tradeId.Split('_')[1], out var t)) { var tradeNumber = db.trade.FirstOrDefault(l => l.id == t)?.TradeNumber; throw new Exception($"交易编号:{tradeNumber},错误信息:{ex.Message}"); } else { //todo } } return null; } #endregion #region ----雪球期权---- public static TradeValueResult GetSnowballOptionValue(MarketProxy marketProxy, OptionCalcParam calcParam) { SimpleSnowball option; AnnualizedPremiumSnowball annualOption = null; using var helper = new SingleUnderlyingCalcHelper(marketProxy).Prepare(calcParam); var optionTrade = QdpTradeBuilder.GetSnowballOptionTrade(calcParam.TradeParam); // 添加计算日志 AddCalculationLog(optionTrade, calcParam.calcScenario, calcParam?.TradeParam?.tradeNumber); if (optionTrade.Instrument is AnnualizedPremiumSnowball snowball) { option = annualOption = snowball; annualOption.AnnualizedPremium = calcParam.TradeParam.annualizedPremiumRate ?? 0; } else { option = optionTrade.Instrument as SimpleSnowball; } var parameters = helper.GetValuationParameters(); var factory = OptionEngineFactory.GetEngineFactory("SnowBall"); var engine = factory.GetEngine(calcParam.engineName, option.Exercise, calcParam.quadratureFastMode); marketProxy.Trace?.WriteData(optionTrade, "QDP 雪球"); IPricingResult result; if (annualOption == null) { //加速方法定价的结果没有考虑deltacash和gammacash,所以只有定价可以使用 if (calcParam.calcScenario == Enums.CalcScenarioEnum.Pricing && engine is QuadAutoCallEngine engine1) { //普通雪球期权可以采用加速方法定价 var market = SnowballTrade.ConvertToMarketCondition(optionTrade.TradeId, marketProxy.QdpMarket, parameters); var gridSnapshot = new QuadSnowballGridSnapshot(option, market, engine1); result = gridSnapshot.Calculate(market.SpotPrices.Value.Values.First() , option.BarrierStatus == BarrierStatus.KnockedIn, calcParam.pricingRequest, rebuildGrid: false); } else { result = optionTrade.CalculateRisks(marketProxy.QdpMarket, calcParam.pricingRequest, engine, parameters); } } else { result = optionTrade.CalculateRisks(marketProxy.QdpMarket, calcParam.pricingRequest, engine, parameters); } if (result != null && result.Succeeded) { var tr = new TradeValueResult(result) { UnderlyingCode = helper.underlyingTicker, RoundedPv = OtcFormatHelper.FormatValue(result.Pv / optionTrade.Notional, 2) * optionTrade.Notional }; if (calcParam.CalcDeltaT1) { tr.DeltaT1 = CalcDeltaT1(optionTrade, marketProxy.QdpMarket, engine, parameters); } if (PS.Config.Company == Configuration.CompanyEnum.天风) { tr.GammaCash = 0.5 * result.Gamma * Math.Pow(calcParam.spotPrices[0] * 0.01, 2); } ValueCalculator.ConvertTradeValueResultOfCompany(tr, calcParam?.TradeParam?.tradeType); return tr; } return null; } /// /// 专业版雪球 /// /// /// /// public static TradeValueResult GetSnowballSpecialistOptionValue(MarketProxy marketProxy, OptionCalcParam calcParam, OptionValueCalcRequest request) { return new TradeValueResult { Succeeded = false, ErrorMessage = "不支持专业版雪球" }; //var tradeParams = calcParam.TradeParam; //using var wrapper = new CxxCalcApi(); //// 敲入观察日 //var koParams = tradeParams.koObservationDateStr.Split(";"); //var koObsDateStr = koParams[0]; //var koObsDates = koObsDateStr.Split(",").Select(g => new Date(DateTime.Parse(g))).ToArray(); //var koBarriers = koParams[1].Split(",").Select(g => double.Parse(g)).ToArray(); //var koCoupons = koParams[2].Split(",").Select(g => double.Parse(g)).ToArray(); //var kiBarrier = tradeParams.kiBarrier; //var strike = tradeParams.strike; //if (!tradeParams.isMoneynessOption) //{ // //转为相对价格比例 // koBarriers = koBarriers.Select(g => g / tradeParams.initialSpotPrice).ToArray(); // kiBarrier /= tradeParams.initialSpotPrice; // strike /= tradeParams.initialSpotPrice; //} //// 计算票息年化系数(外部有不同的计算方式) //var dayCount = tradeParams.couponDayCount.ToDayCountImpl(); //var koFractions = koObsDateStr.Split(",").Select(g => //{ // var koObsDate = DateTime.Parse(g); // var koFraction = YLErp.QdpModule.QdpHelper.AnnualizeFactor(tradeParams.startDate, koObsDate, dayCount); // return koFraction; //}).ToArray(); //// 计算入参 //var calcIn = new CalcIn() //{ // // 市场(日历)名称,如China // market = "chn", // // 开始日期 // startDate = tradeParams.startDate.ToString(), // // 估值日期 // valueDate = marketProxy.ValueDate.ToString("yyyy-MM-dd"), // // 5是向上敲出的雪球,6是向下敲出的雪球?? // barrierType = tradeParams.optionType == OptionType.Call ? CxxCalcLib.BarrierType.UP_OUT_DOWN_IN : CxxCalcLib.BarrierType.UP_OUT_DOWN_IN, // // 期初价格 // initialSpot = tradeParams.initialSpotPrice, // // 敲出观察日列表,以逗号分隔 // koObsDateStr = koObsDateStr, // // 敲出障碍价格列表,与敲出观察日对应 // koBarriers = koBarriers, // // 敲出票息列表?? // koCoupons = koCoupons, // // 敲出票息的年化系数列表 // koFractions = koFractions, // // 剩余未观察的敲出观察日个数 // activeKoObsCount = koObsDates.Where(g => g > tradeParams.startDate).Count(), // // 敲出增强收益的参与率,无增强收益填0?? // koPayoffParticipation = 0, // // 敲入障碍价格 // kiBarrier = kiBarrier, // // 敲入后的行权价 // kiStrike = strike, // // 是否仅在到期日进行敲入观察,否则为每日观察 // kiObsOnlyAtMaturity = tradeParams.KIObservationType == KIObservationType.OnlyEndDate, // // 敲入后的期权参与率?? // kiParticipationRate = tradeParams.KIParticipationRate, // // 红利票息 // maturityCoupon = tradeParams.coupon, // // 保本比例,如0.8表示敲入后亏损封顶20% // protectionRatio = tradeParams.PrincipalProtectionRate, // // 初始预付金比例 // initialMarginRatio = tradeParams.PrepaymentRatio, // // 预付金利率 // marginInterestRate = tradeParams.PrepaymentInterestRate, // // 数量?? // amount = tradeParams.notional, // // 是否已敲入 // isKnockedIn = false, // // 一组标的资产价格,每个价格都是一个情景 // spots = new double[] { tradeParams.initialSpotPrice }, // // 情景个数?? // scenarioCount = 1, // // 无风险利率 // r = tradeParams.riskFreeRate, // // 分红率 // q = tradeParams.dividendRate, // // 波动率 // v = request.vols[0], // // 计算指标,逗号隔开 // greekType = "PV,DELTA,GAMMA,VEGA,RHO,THETA" //}; //var result1 = wrapper.CalcPlatform_SnowballScenario(calcIn); //var result = result1.Values.FirstOrDefault(); //return new TradeValueResult(true) //{ // Pv = result.PV, // Delta = result.DELTA, // Gamma = result.GAMMA, // Vega = result.VEGA, // Rho = result.RHO, // CalendarDayTheta = result.THETA, // TradingDayTheta = result.THETA, //}; } #endregion #region ----气囊结构---- /// /// 气囊结构 /// public static TradeValueResult GetAirbagOptionValue(MarketProxy marketProxy, OptionCalcParam calcParam) { using var helper = new SingleUnderlyingCalcHelper(marketProxy).Prepare(calcParam); var optionTrade = QdpTradeBuilder.GetAirbagOptionTrade(calcParam.TradeParam); // 添加计算日志 AddCalculationLog(optionTrade, calcParam.calcScenario, calcParam?.TradeParam?.tradeNumber); var parameters = helper.GetValuationParameters(); var option = optionTrade.Instrument as Airbag; var engine = new AnalyticalAirbagReplicationEngine(); marketProxy.Trace?.WriteData(optionTrade, "QDP 气囊"); var result = optionTrade.CalculateRisks(marketProxy.QdpMarket, calcParam.pricingRequest, engine, parameters); if (result.Succeeded) { var tr = new TradeValueResult(result) { UnderlyingCode = helper.underlyingTicker, RoundedPv = OtcFormatHelper.FormatValue(result.Pv / optionTrade.Notional, 2) * optionTrade.Notional }; if (calcParam.CalcDeltaT1) { tr.DeltaT1 = CalcDeltaT1(optionTrade, marketProxy.QdpMarket, engine, parameters); } if (PS.Config.Company == Configuration.CompanyEnum.天风) { tr.GammaCash = 0.5 * result.Gamma * Math.Pow(calcParam.spotPrices[0] * 0.01, 2); } ValueCalculator.ConvertTradeValueResultOfCompany(tr, calcParam?.TradeParam?.tradeType); return tr; } return null; } #endregion #region ----收益增强结构---- public static TradeValueResult GetUnderlyingEnhanceValue(MarketProxy marketProxy, OptionCalcParam calcParam) { using var helper = new SingleUnderlyingCalcHelper(marketProxy).Prepare(calcParam); var optionTrade = QdpTradeBuilder.GetUnderlyingEnhanceTrade(calcParam.TradeParam); // 添加计算日志 AddCalculationLog(optionTrade, calcParam.calcScenario, calcParam?.TradeParam?.tradeNumber); var parameters = helper.GetValuationParameters(); var option = optionTrade.Instrument as UnderlyingPayoffEnhance; var engine = new AnalyticalUnderlyingPayoffEnhanceEngine(calcAsOptions: true); marketProxy.Trace?.WriteData(optionTrade, "QDP 收益增强"); var result = optionTrade.CalculateRisks(marketProxy.QdpMarket, calcParam.pricingRequest, engine, parameters); if (result.Succeeded) { var tr = new TradeValueResult(result) { UnderlyingCode = helper.underlyingTicker, RoundedPv = OtcFormatHelper.FormatValue(result.Pv / optionTrade.Notional, 2) * optionTrade.Notional }; if (calcParam.CalcDeltaT1) { tr.DeltaT1 = CalcDeltaT1(optionTrade, marketProxy.QdpMarket, engine, parameters); } if (PS.Config.Company == Configuration.CompanyEnum.天风) { tr.GammaCash = 0.5 * result.Gamma * Math.Pow(calcParam.spotPrices[0] * 0.01, 2); } ValueCalculator.ConvertTradeValueResultOfCompany(tr, calcParam?.TradeParam?.tradeType); return tr; } return null; } #endregion #region----区间累积---- public static TradeValueResult GetRangeAccrualValue(MarketProxy marketProxy, OptionCalcParam calcParam) { using var helper = new SingleUnderlyingCalcHelper(marketProxy).Prepare(calcParam); var optionTrade = QdpTradeBuilder.GetRangeAccrualTrade(calcParam.TradeParam); // 添加计算日志 AddCalculationLog(optionTrade, calcParam.calcScenario, calcParam?.TradeParam?.tradeNumber); var parameters = helper.GetValuationParameters(); var option = optionTrade.Instrument as RangeAccrual; var factory = OptionEngineFactory.GetEngineFactory("RangeAccrual"); var engine = factory.GetEngine(calcParam.engineName, option.Exercise); marketProxy.Trace?.WriteData(optionTrade, "QDP 区间累积"); var result = optionTrade.CalculateRisks(marketProxy.QdpMarket, calcParam.pricingRequest, engine, parameters); if (result.Succeeded) { var tr = new TradeValueResult(result) { UnderlyingCode = helper.underlyingTicker, RoundedPv = OtcFormatHelper.FormatValue(result.Pv / optionTrade.Notional, 2) * optionTrade.Notional, }; if (calcParam.CalcDeltaT1) { tr.DeltaT1 = CalcDeltaT1(optionTrade, marketProxy.QdpMarket, engine, parameters); } if (PS.Config.Company == Configuration.CompanyEnum.天风) { tr.GammaCash = 0.5 * result.Gamma * Math.Pow(calcParam.spotPrices[0] * 0.01, 2); } ValueCalculator.ConvertTradeValueResultOfCompany(tr, calcParam?.TradeParam?.tradeType); return tr; } return null; } #endregion #region ----累计期权---- /// /// /// /// /// /// 累计期权交易的notional,和param里面的notional不同,param里面的notional代表的是沽购数量 /// public static TradeValueResult GetAccumulatorOptionValue(MarketProxy marketProxy, OptionCalcParam calcParam, double notional) { using var helper = new SingleUnderlyingCalcHelper(marketProxy).Prepare(calcParam); //当到期日和定价日一样,并且当前时间大于3点,ttm传0;qdp里面 ttm等于0和不等于0逻辑处理不一样。 if (calcParam.TradeParam.exerciseDate.DateTime == marketProxy.ValueDate && DateTime.Now.Hour >= 15) { calcParam.TradeParam.timeToMaturityDays = 0; } var optionTrade = QdpTradeBuilder.GetAccumulatorOptionTrade(calcParam.TradeParam); // 添加计算日志 AddCalculationLog(optionTrade, calcParam.calcScenario, calcParam?.TradeParam?.tradeNumber); var parameters = helper.GetValuationParameters(); IEngine engine; if (optionTrade is SegmentedAccumulatorOptionTrade) { engine = new AnalyticalSegmentedAccumulatorOptionEngine(); } else { if (calcParam.TradeParam.EarlyTerminate) { engine = new QuadAccumulatorOptionEngine(); } else { engine = new AnalyticalAccumulatorOptionEngine(); } } calcParam.pricingRequest = PricingRequest.Pv | PricingRequest.Delta | PricingRequest.Gamma | PricingRequest.Vega | PricingRequest.Rho | PricingRequest.Theta; marketProxy.Trace?.WriteData(optionTrade, "QDP 累计期权"); var result = optionTrade.CalculateRisks(marketProxy.QdpMarket, calcParam.pricingRequest, engine, parameters); if (result.Succeeded) { var tr = new TradeValueResult(result) { UnderlyingCode = helper.underlyingTicker, RoundedPv = OtcFormatHelper.FormatValue(result.Pv / notional, 2) * notional }; if (calcParam.CalcDeltaT1) { tr.DeltaT1 = CalcDeltaT1(optionTrade, marketProxy.QdpMarket, engine, parameters); } if (PS.Config.Company == Configuration.CompanyEnum.天风) { tr.GammaCash = 0.5 * result.Gamma * Math.Pow(calcParam.spotPrices[0] * 0.01, 2); } ValueCalculator.ConvertTradeValueResultOfCompany(tr, calcParam?.TradeParam?.tradeType); return tr; } return null; } #endregion #region ----现金流---- public static TradeValueResult GetCashFlowValue(MarketProxy marketProxy, OptionCalcParam calcParam, double stockEqvNotional) { if (marketProxy is null) { throw new ArgumentNullException(nameof(marketProxy)); } if (calcParam is null) { throw new ArgumentNullException(nameof(calcParam)); } calcParam.TradeParam.volSurfaceNames = new string[] { "cashflow_0" }; //现金流交易在Qdp里的对应的类(Deposit)不接受常量无风险利率作为输入,只能以折现曲线的形式传入定价 //现金流交易的无风险利率使用market中设置的全局值 marketProxy.SetYieldCurve(marketProxy.DiscountCurveName, QdpHelper.CreateRiskFreeCurve(marketProxy.DiscountCurveName, calcParam.TradeParam.riskFreeRate, marketProxy.CurveDayCount)); using var helper = new SingleUnderlyingCalcHelper(marketProxy).Prepare(calcParam, true); var optionTrade = QdpTradeBuilder.GetCashFlowTrade(calcParam.TradeParam, stockEqvNotional); // 添加计算日志 AddCalculationLog(optionTrade, calcParam.calcScenario, calcParam?.TradeParam?.tradeNumber); var discountCurveName = marketProxy.DiscountCurveName ?? MarketProxy.ConstantZeroCurve; var parameters = new SimpleCfValuationParameters(discountCurveName, discountCurveName, discountCurveName); IEngine engine = new DepositEngine(); var result = optionTrade.CalculateRisks(marketProxy.QdpMarket, calcParam.pricingRequest, engine, parameters); TradeValueResult tradeValue = null; if (result.Succeeded) { tradeValue = new TradeValueResult(result);// { RoundedPv = result.Pv }; if (calcParam.TradeParam.DepositType == CashflowDepositTypeEnum.资金收益) { tradeValue.Pv += stockEqvNotional * calcParam.TradeParam.PrepayRatio * (calcParam.TradeParam.buysell == TradeType.Buy.ToString() ? 1 : -1); } tradeValue.RoundedPv = tradeValue.Pv; ValueCalculator.ConvertTradeValueResultOfCompany(tradeValue, calcParam?.TradeParam?.tradeType); } return tradeValue; } #endregion #region----自定义交易---- /// /// 计算自定义交易期权价值 /// 默认取维护的风险值 /// 没维护过风险值且ExternalAPIEnable功能开启时,会调用外部接口计算 /// /// 估值日期 /// 交易 /// 现价 /// 是否为自定义交易 /// 调用外部接口计算时,使用的波动率类型,默认为交易波动率 /// 调用外部接口计算时,使用的波动率,该值忽略时使用波动率类型取值 /// public static (eod_trade_risk_manual manual, TradeValueResult optionValue) GetManualOptionValue( DateTime valueDate, trade td, double spotPrice, double volValue, bool isCustomerTrade, CalcScenarioEnum calcScenario, string volType = "持仓", SettlementTypeEnum settlementType = SettlementTypeEnum.ClosePrice, double overrideVol = double.NaN, bool isSettle = false) { using var db = DbContextFactory.GetYLDbContext(); if (volType == "交易") { volType = "持仓"; } var tradeId = td.id; //分类维护的风险数据优先级高于普通的风险数据 var manual = db.eod_trade_risk_manual.Where(x => (x.ValueDate <= valueDate && isCustomerTrade || x.ValueDate == valueDate) && x.TradeId == tradeId && x.VolType == volType && x.SettlementType == settlementType) .OrderByDescending(x => x.ValueDate).FirstOrDefault(); if (manual == null) { manual = db.eod_trade_risk_manual.Where(x => (x.ValueDate <= valueDate && isCustomerTrade || x.ValueDate == valueDate) && x.TradeId == tradeId && string.IsNullOrEmpty(x.VolType)) .OrderByDescending(x => x.ValueDate).FirstOrDefault(); } else { //如果分类维护的数据不全,通过单一数据补充 var singleManual = db.eod_trade_risk_manual.Where(x => (x.ValueDate <= valueDate && isCustomerTrade || x.ValueDate == valueDate) && x.TradeId == tradeId && string.IsNullOrEmpty(x.VolType)) .OrderByDescending(x => x.ValueDate).FirstOrDefault(); if (singleManual != null) { manual.Pv ??= singleManual.Pv; manual.Delta ??= singleManual.Delta; manual.DeltaCash ??= singleManual.DeltaCash; manual.Gamma ??= singleManual.Gamma; manual.GammaCash ??= singleManual.GammaCash; manual.Theta ??= singleManual.Theta; manual.Vega ??= singleManual.Vega; manual.VegaCash ??= singleManual.VegaCash; manual.Rho ??= singleManual.Rho; } } TradeValueResult optionValue = null; if (manual == null && isCustomerTrade && PS.Config.ErpElement.ExternalAPIForCustomCalcEnable) { var apiValue = TradeCalcApiHelper.CalculateCustomizedTrade(valueDate, td, spotPrice, volValue, isSettle, calcScenario, TradeCalcApiHelper.PV, TradeCalcApiHelper.DELTA, TradeCalcApiHelper.GAMMA, TradeCalcApiHelper.VEGA, TradeCalcApiHelper.THETA, TradeCalcApiHelper.RHO); if (apiValue.Success) { manual = new eod_trade_risk_manual() { ValueDate = valueDate, TradeId = td.id, TradeNumber = td.TradeNumber, Pv = apiValue.Content.Pv, Delta = apiValue.Content.Delta, Gamma = apiValue.Content.Gamma, Vega = apiValue.Content.Vega, Theta = apiValue.Content.Theta, Rho = apiValue.Content.Rho, DeltaCash = apiValue.Content.Delta * spotPrice, GammaCash = apiValue.Content.Gamma * Math.Pow(spotPrice, 2) / 100, VegaCash = apiValue.Content.Vega * spotPrice, Vol = td.Vol, }; optionValue = apiValue.Content; optionValue.FromManual = false; } //抛出异常信息返回前端 else { throw new Exception(apiValue.Msg); } } else { optionValue = new TradeValueResult { FromManual = true }; if (manual != null) { manual.DeltaCash ??= (manual.Delta * spotPrice); manual.GammaCash ??= (manual.Gamma * Math.Pow(spotPrice, 2) / 100); manual.VegaCash ??= (manual.Vega * spotPrice); optionValue = new TradeValueResult { Pv = manual.Pv ?? 0, RoundedPv = manual.Pv ?? 0, Delta = manual.Delta ?? 0, Gamma = manual.Gamma ?? 0, Vega = manual.Vega ?? 0, TradingDayTheta = manual.Theta ?? 0, CalendarDayTheta = manual.Theta ?? 0, Rho = manual.Rho ?? 0, DeltaCash = manual.DeltaCash ?? 0, GammaCash = manual.GammaCash ?? 0, VegaCash = manual.VegaCash ?? 0, FromManual = true, Vol = manual.Vol ?? 0 }; } } return (manual, optionValue); } //如果当天有维护过部分风险值,则直接获取风险值,未维护的风险值部分则通过系统计算 //系统计算结果和手动维护值合并 public static void GetOptionValueWithManual(eod_trade_risk_manual manual, TradeValueResult result) { if (manual != null) { result.Pv = manual.Pv ?? result.Pv; result.RoundedPv = manual.Pv ?? result.RoundedPv; result.Delta = manual.Delta ?? result.Delta; result.DeltaCash = manual.DeltaCash ?? result.DeltaCash; result.Gamma = manual.Gamma ?? result.Gamma; result.GammaCash = manual.GammaCash ?? result.GammaCash; result.Vega = manual.Vega ?? result.Vega; result.TradingDayTheta = manual.Theta ?? result.TradingDayTheta; result.CalendarDayTheta = manual.Theta ?? result.CalendarDayTheta; result.Rho = manual.Rho ?? result.Rho; } } #endregion private static void AddCalculationLog(TradeBase trade, CalcScenarioEnum scenarioEnum, string tradeNumber) { try { var option = trade.Instrument as OptionBase; // 添加计算日志 var log = new CalculationLog() { CreateTime = DateTime.Now, Scenario = scenarioEnum, LogObject = trade, TradeNumber = tradeNumber, Notional = trade.Notional, Exercise = option?.Exercise.ToString(), OptionType = option?.OptionType.ToString(), TradeDate = trade.TradeDate?.ToString(), MaturityDate = trade.MaturityDate?.ToString(), InitialSpotPrice = option?.InitialSpotPrice.ToString(), Strike = option?.Strike.ToString(), }; ICalculationLogService calcLogService = new CalculationLogService(); calcLogService.AddLog(log, scenarioEnum); } catch (Exception ex) { Console.WriteLine(ex.Message); } } } }