using NPOI.Util; using Qdp.Pricing.Base.Implementations; using YLErp.Abstract; using YLErp.Abstract.DataProviders; using YLErp.BLL; using YLErp.BLL.Calculation; using YLErp.DBModels.Enums; using YLErp.Enums; using YLErp.Modules.CalculationModule.Abstract; using YLErp.Modules.VolatilityModule; using YLErp.QdpModule; namespace YLErp.Modules.CalculationModule { /// /// 期权价值计算 /// public class TradeRiskValueCalc { readonly OtcTradeBase _trade; readonly IOtcTradeValueCalcContext _context; readonly IPriceProvider _underlyingPriceProvider; readonly int _maturityShift; underlying_manager _underlying; underlying_manager[] _spreadUnderlyings; readonly IOptionCalcDataProvider _dataProvider; readonly IUnderlyingDataProvider _underlyingDataProvider; readonly ITradeExtendDataProvider _tradeExtendDataProvider; readonly ITradeKnockOutPayoffCalcService _tradeKnockOutPayoffCalcService; public TradeRiskValueCalc(IOtcTradeValueCalcContext context, OtcTradeBase trade) { _trade = trade ?? throw new ArgumentNullException(nameof(trade)); _context = context ?? throw new ArgumentNullException(nameof(context)); _dataProvider = CalcCheckHelper.CheckOptionCalcDataProvider(context.DataProvider); _underlyingDataProvider = _dataProvider.UnderlyingDataProvider; _tradeExtendDataProvider = _dataProvider.TradeExtendDataProvider; _underlyingPriceProvider = _dataProvider.UnderlyingPriceProvider; _maturityShift = 0; _tradeKnockOutPayoffCalcService = new TradeKnockOutPayoffCalcService(_tradeExtendDataProvider, _context.ValueDate); } /// /// 计算衍生品价值 /// public TradeValueResult GetTradeValue(out underlying_manager[] underlyingArr) { TradeValueResult result; if (_trade.TradeType == "现金流交易") { result = InnerGetCashFlowTradeValue(); if (_context.CalcDeltaT1) { result.DeltaT1 = result.Delta; } } else { result = InnerGetTradeValue(null); } underlyingArr = _spreadUnderlyings ?? Array.Empty(); Array.Resize(ref underlyingArr, underlyingArr.Length + 1); underlyingArr[0] = _underlying; return result; } /// /// 计算衍生品价值 /// private TradeValueResult InnerGetTradeValue(underlying_manager unly) { //underlying _underlying = unly; if (_underlying == null) { _underlying = _underlyingDataProvider.GetUnderlying(_trade.UnderlyingCode); if (_underlying == null) { throw new TradeCalcExpception(_trade.id, $"未获取到标的数据:{_trade.UnderlyingCode}"); } _underlying.QuotationDate = _context.ValueDate; } //结构化交易 if (_trade.TradeType == "结构化交易") { throw new ServiceException($"不支持'结构化交易'主交易的计算,tradeId:{_trade.id},tradeNumber:{_trade.TradeNumber}"); } //准备计算 _spreadUnderlyings = null; //stock MaturityDate if (_underlying.UnderlyingInstrumentType == "Stock") { //股票默认到期日为行权日 if (_trade.ExerciseDate != null) { _trade.MaturityDate = _trade.ExerciseDate; } } //标的即期价格 var blGetPrice = _underlyingPriceProvider.TryGetPrice(_trade.UnderlyingCode, out var spotPrice); _underlying.Price = spotPrice; //如果当天有维护过风险值,则直接获取风险值即可,未维护的风险值则通过系统计算 var result = TradeRiskCalcUtil.GetManualOptionValue(_context.ValueDate, (trade)_trade, spotPrice, 0, false, _context.CalcScenario, volType: _context.VolType, isSettle: _context.CalcScenario == Enums.CalcScenarioEnum.EodSettlement); var optionValue = result.manual != null ? result.optionValue : null; //没有自定义风险维护值 或者 自定风险维护没有涵盖PV和所有希腊值(部分维护场景) //部分维护场景下,需要系统计算出未赋值的属性,进行合并返还 if (result.manual == null || result.manual != null && !result.manual.IsPVAndAllGreek) { //开始计算 switch (_trade.TradeType) { case "远期": if (PS.Config.ErpElement.ForwardTradePriceModel == Configuration.Enums.ForwardTradePriceModel.STANDARD && !string.IsNullOrWhiteSpace(_trade.BasisUnderlyingCode)) { _underlyingPriceProvider.TryGetPrice(_trade.BasisUnderlyingCode, out var BasiseodPrice); spotPrice -= BasiseodPrice; } optionValue = ForwardradeCalcService.CalcValue(_trade, spotPrice); break; case "信用债": case "商品期货": case "股票": { var pv = spotPrice * _trade.Notional; optionValue = new TradeValueResult { Pv = pv, Delta = _trade.Notional, DeltaCash = pv }; } break; case "自定义交易": { var positionVol = VolatilityHelper.GetTradeVol((trade)_trade, _context.ValueDate, _context.CalcScenario == CalcScenarioEnum.EodSettlement); optionValue = TradeRiskCalcUtil.GetManualOptionValue(_context.ValueDate, (trade)_trade, spotPrice, positionVol, true, _context.CalcScenario, isSettle: _context.CalcScenario == CalcScenarioEnum.EodSettlement).optionValue; } break; case "收益互换": { var trade_swap = _tradeExtendDataProvider.GetTrade_Swap(_trade.id); optionValue = PayoffSwapCalcService.CalcValue(_trade, _context.ValueDate, _underlyingPriceProvider, false); } break; } //计算期权估值时包含了DeltaT1,所以不需要再次赋值 if (optionValue == null) { if (!blGetPrice) { if (_trade.TradeType == "场内期权") { throw new ServiceException($"场内期权计算失败,期权代码:{_trade.ExchangeOptionCode},错误信息:期权标的'{_trade.UnderlyingCode}'缺少价格"); } else { throw new ServiceException($"{_trade.TradeType}计算失败,交易编号:{_trade.TradeNumber},标的:{_trade.UnderlyingCode},错误信息:缺少标的价格"); } } optionValue = InnerGetOptionValue(spotPrice); } else if (_context.CalcDeltaT1) { optionValue.DeltaT1 = optionValue.Delta; } } //系统计算结果和手动维护值合并 TradeRiskCalcUtil.GetOptionValueWithManual(result.manual, optionValue); if (optionValue != null) { optionValue.Strike = _trade.Strike ?? 0; optionValue.SpotPrice = spotPrice; optionValue.VegaCash = optionValue.VegaCash.IsNormalize() ? optionValue.VegaCash : (optionValue.Vega * optionValue.SpotPrice).Normalize(); optionValue.NPv = optionValue.Pv; optionValue.NRoundedPv = optionValue.RoundedPv; if (PS.Config.ErpElement.IsPVIncludePrincipal) { optionValue.Pv += _trade.PrincipalSum() * (_trade.BuySell == "卖出" ? -1 : 1); optionValue.RoundedPv += _trade.PrincipalSum() * (_trade.BuySell == "卖出" ? -1 : 1); } } return optionValue; } /// /// 获取场外期权价值计算结果 /// private TradeValueResult InnerGetOptionValue(double spotPrice) { var pricingRequest = _context.GetPricingRequest(_trade); if (pricingRequest == PricingRequest.None) { return new TradeValueResult { TradeId = _trade.id, UnderlyingCode = _trade.UnderlyingCode, BuySell = _trade.BuySell, UnderlyingId = _trade.UnderlyingId, ErrorMessage = "PricingRequest.None" }; } var qdpTradeId = _context.MarketProxy.NextRequestId() + "_" + (_trade.id > 0 ? _trade.id.ToString() : string.IsNullOrWhiteSpace(_trade.ExchangeOptionCode) ? _trade.UnderlyingCode : _trade.ExchangeOptionCode); //准备波动率 if (!_context.PrepareVolatility(qdpTradeId, _trade, spotPrice, out var volsurfaceNames)) { return new TradeValueResult(false) { UnderlyingCode = _underlying.UnderlyingCode, ErrorMessage = "没有波动率", FailReason = TradeValueFailReason.missingVol }; } TradeValueResult result = null; var tpRequest = new OptionTradeParamRequest(_context.SysRiskFreeRate) { tradeId = qdpTradeId, preciseTimeMode = _context.IsPreciseTimeMode, fixings = null, maturityShift = _maturityShift, volSurfaceNames = volsurfaceNames, hasNightMarket = false, isEodCalc = _context.IsEodCalc, timeToMaturityDays = double.NaN, ParamOverride = p => { p.riskFreeRate = _context.GetRiskFreeRate(_trade); p.dividendRate = _context.GetDividendRate(_trade); } }; var nextDay = QdpCalendarHelper.BizDayShift(valuedateBLL.ValueDate, 1); //象屿最后一个交易日实时计算时TTM需要和平仓时算法一致 if (PS.Config.Is厦门象屿 && (_context.CalcScenario == CalcScenarioEnum.RealtimePosition || _context.CalcScenario == CalcScenarioEnum.RealtimeRisk) && _trade.SettlementType == SettlementTypeEnum.ReferencePrice) { tpRequest.timeToMaturityDays = TradeCalcHelper.CalculateTTMDays(valuedateBLL.ValueDate, _trade.ExerciseDate.Value, _underlying.UnderlyingTypeId, false); } var variety = new Variety(); if (_underlying.IsFutures()) { variety = DataCacheProvider.GetVarietyDataSource().GetData(_underlying.UnderlyingTypeId); tpRequest.hasNightMarket = variety != null && variety.HasNightMarket; } OptionCalcParam getOptionCalcParam(T tradeParam, double[] spotPrices = null) where T : OptionTradeParamBase { return new OptionCalcParam(tradeParam) { engineName = null, pricingRequest = pricingRequest, spotPrices = spotPrices ?? new[] { spotPrice }, calcScenario = _context.CalcScenario, CalcDeltaT1 = _context.CalcDeltaT1 }; } var marketProxy = _context.MarketProxy; //东证润和是精确时间模式参与计算,收盘的话,刚好是整数天,所以不需要特殊处理 if (PS.Config.Is润和 && !tpRequest.isEodCalc && double.IsNaN(tpRequest.timeToMaturityDays)) { tpRequest.timeToMaturityDays = TradeCalcHelper.CalculateTTMDays(marketProxy.ValueDate, _trade.ExerciseDate.Value, 0, false); } //开始计算 switch (_trade.TradeType) { case "香草期权": { var tradeParam = QdpTradeBuilder.GetVanillaOptionTradeParam(_trade, tpRequest, false); result = TradeRiskCalcUtil.GetVanillaOptionValue(marketProxy, getOptionCalcParam(tradeParam)); } break; case "Risky期权": { var riskyOption = _tradeExtendDataProvider.GetTrade_Risky_Option(_trade.id); result = GetOptionRisky(_trade, riskyOption, tpRequest, _underlying, marketProxy, pricingRequest, spotPrice, _context.CalcScenario, _context.CalcDeltaT1); } break; case "彩虹期权": { var tradeParam = GetRainbowOptionTradeParam(tpRequest, out var spotPrices); if (tradeParam == null) { return new TradeValueResult { UnderlyingCode = _underlying.UnderlyingCode }; } result = TradeRiskCalcUtil.GetRainbowOptionValue(marketProxy, getOptionCalcParam(tradeParam, spotPrices)); } break; case "亚式期权": { if (!PS.Config.Is润和 && !tpRequest.isEodCalc && double.IsNaN(tpRequest.timeToMaturityDays)) { tpRequest.timeToMaturityDays = TradeCalcHelper.CalculateTTMDays(marketProxy.ValueDate, _trade.ExerciseDate.Value, variety.id, false); } var tradeParam = GetAsianOptionCalcParam(tpRequest, spotPrice); result = TradeRiskCalcUtil.GetAsianOptionValue(marketProxy, getOptionCalcParam(tradeParam)); } break; case "障碍期权": { var barrierOption = _tradeExtendDataProvider.GetTrade_Barrier_Option(_trade.id) ?? throw new TradeCalcExpception(_trade.id, "未获取到对应的障碍期权数据"); var tradeParam = QdpTradeBuilder.GetBarrierOptionTradeParam(_trade, barrierOption, tpRequest); result = TradeRiskCalcUtil.GetBarrierOptionValue(marketProxy, getOptionCalcParam(tradeParam)); } break; case "二元期权": { var binaryOption = _tradeExtendDataProvider.GetTrade_Binary_Option(_trade.id) ?? throw new TradeCalcExpception(_trade.id, "未获取到对应的二元期权数据"); var tradeParam = QdpTradeBuilder.GetBinaryOptionTradeParam(_trade, binaryOption, tpRequest); result = TradeRiskCalcUtil.GetBinaryOptionValue(marketProxy, getOptionCalcParam(tradeParam)); } break; case "价差期权": { var tradeParam = GetSpreadOptionCalcParam(tpRequest, out var spotPrices); if (tradeParam == null) { return new TradeValueResult { UnderlyingCode = _underlying.UnderlyingCode }; } result = TradeRiskCalcUtil.GetSpreadOptionValue(marketProxy, getOptionCalcParam(tradeParam, spotPrices)); } break; case "场内期权": { var tradeParam = QdpTradeBuilder.GetVanillaOptionTradeParam(_trade, tpRequest, true); result = TradeRiskCalcUtil.GetVanillaOptionValue(marketProxy, getOptionCalcParam(tradeParam)); } break; case "合成价差期权": { var tradeParam = QdpTradeBuilder.GetSSpreadOptionTradeParam(_trade, tpRequest); result = TradeRiskCalcUtil.GetSSpreadOptionValue(marketProxy, getOptionCalcParam(tradeParam)); } break; case "亚式合成价差期权": { var tradeParam = GetAsianOptionCalcParam(tpRequest, spotPrice); result = TradeRiskCalcUtil.GetAsianSSpreadOptionValue(marketProxy, getOptionCalcParam(tradeParam)); } break; case "双鲨期权": { var dbsharkfinOption = _tradeExtendDataProvider.GetTrade_Double_SharkFin_Option(_trade.id) ?? throw new TradeCalcExpception(_trade.id, "未获取到对应的双鲨期权数据"); var tradeParam = QdpTradeBuilder.GetDoubleSharkFinOptionTradeParam(_trade, dbsharkfinOption, tpRequest); result = TradeRiskCalcUtil.GetDoubleSharkFinOptionValue(marketProxy, getOptionCalcParam(tradeParam)); } break; case "凤凰期权": { var autocallOption = _tradeExtendDataProvider.GetTrade_Autocall_Option(_trade.id) ?? throw new TradeCalcExpception(_trade.id, "未获取到对应的凤凰期权数据"); autocallOption.HappenedObservations = _tradeExtendDataProvider.GetTrade_HappenedObservations(_trade.id); var tradeParam = QdpTradeBuilder.GetAutocallOptionTradeParam(_trade, autocallOption, tpRequest); try { result = TradeRiskCalcUtil.GetAutocallOptionValue(marketProxy, getOptionCalcParam(tradeParam)); } catch (Exception ex) { if (string.IsNullOrEmpty(_trade.TradeNumber)) { throw; } throw new Exception($"凤凰期权'{_trade.TradeNumber}'计算出错:{ex.Message}"); } } break; case "雪球期权": { var snowball = _tradeExtendDataProvider.GetTrade_Snowball_Option(_trade.id) ?? throw new TradeCalcExpception(_trade.id, "未获取到对应的雪球期权数据"); if (snowball?.PrepaymentUsed ?? false) { var snowballSpecialistOptionCalculator = new SnowballSpecialistOptionCalculator(); var vol = marketProxy.QdpMarket.VolSurfaces[qdpTradeId].ValueOnGrids[0, 0]; var trade = (trade)_trade; trade.trade_snowball = snowball; if (snowball.PrepaymentRatio > 0) { var specialSnowballTrade = snowballSpecialistOptionCalculator.GetSpecialTrade(trade); var specialSnowballResult = snowballSpecialistOptionCalculator.CalcOptionValue(marketProxy.ValueDate, spotPrice, vol, _context.CalcScenario, specialSnowballTrade); var breakevenSnowballTrade = snowballSpecialistOptionCalculator.GetBreakevenTrade(trade); var tradeParam = QdpTradeBuilder.GetSnowballTradeParam(breakevenSnowballTrade, breakevenSnowballTrade.trade_snowball, tpRequest); //因为构建tradeParam的时候,会用全局变量_trade的NoRiskRate,所以这边需要覆盖一下 tradeParam.riskFreeRate = breakevenSnowballTrade.NoRiskRate ?? 0; var breakevenSnowballResult = TradeRiskCalcUtil.GetSnowballOptionValue(marketProxy, getOptionCalcParam(tradeParam)); result = snowballSpecialistOptionCalculator.MergeTradeValueResult(specialSnowballResult, breakevenSnowballResult); } else { result = snowballSpecialistOptionCalculator.CalcOptionValue(marketProxy.ValueDate, spotPrice, vol, _context.CalcScenario, trade); } } else { var tradeParam = QdpTradeBuilder.GetSnowballTradeParam(_trade, snowball, tpRequest); result = TradeRiskCalcUtil.GetSnowballOptionValue(marketProxy, getOptionCalcParam(tradeParam)); } } break; case "气囊结构": { var airbag = _tradeExtendDataProvider.GetTrade_Airbag(_trade.id) ?? throw new TradeCalcExpception(_trade.id, "未获取到对应的气囊结构期权数据"); var tradeParam = QdpTradeBuilder.GetAirbagOptionTradeParam(_trade, airbag, tpRequest); result = TradeRiskCalcUtil.GetAirbagOptionValue(marketProxy, getOptionCalcParam(tradeParam)); } break; case "收益增强结构": { var underlyingEnhance = _tradeExtendDataProvider.GetTrade_UnderlyingEnhance(_trade.id) ?? throw new TradeCalcExpception(_trade.id, "未获取到对应的收益增强结构期权数据"); var tradeParam = QdpTradeBuilder.GetUnderlyingEnhanceTradeParam(_trade, underlyingEnhance, tpRequest); result = TradeRiskCalcUtil.GetUnderlyingEnhanceValue(marketProxy, getOptionCalcParam(tradeParam)); } break; case "区间累积期权": { var rangeAccrual = _tradeExtendDataProvider.GetTrade_RangeAccrual(_trade.id) ?? throw new TradeCalcExpception(_trade.id, "未获取到对应的区间累积期权数据"); //有些时候会传入大于估值日期的票息(比如实时风险算昨日pv的时候) rangeAccrual.HappenedObservations = _tradeExtendDataProvider.GetTrade_HappenedObservations(_trade.id) ?.Where(n => n.EndDate < _context.ValueDate)?.ToList(); var tradeParam = QdpTradeBuilder.GetRangeAccrualTradeParam(_trade, rangeAccrual, tpRequest); result = TradeRiskCalcUtil.GetRangeAccrualValue(marketProxy, getOptionCalcParam(tradeParam)); } break; case "累计期权": { var accumulator = _tradeExtendDataProvider.GetTrade_Accumulator_Option(_trade.id) ?? throw new TradeCalcExpception(_trade.id, "未获取到对应的累计期权数据"); var tradeParam = QdpTradeBuilder.GetAccumulatorOptionTradeParam(_trade, accumulator, tpRequest); result = TradeRiskCalcUtil.GetAccumulatorOptionValue(marketProxy, getOptionCalcParam(tradeParam), _trade.Notional); } break; default: result = new TradeValueResult(false) { ErrorMessage = "未知的期权结构" }; break; } if (result != null) { // 通过当前价格计算出来的交易是否敲出以及敲出payoff var knockOutPayoffResult = _tradeKnockOutPayoffCalcService.GetKnockOutPayoff((trade)_trade, spotPrice); if (knockOutPayoffResult != null) { result.IsKnockOut = knockOutPayoffResult.IsKnockOut; result.KnockOutPayoff = knockOutPayoffResult.Payoff; } } return result; } /// /// 获取现金流价值计算结果 /// private TradeValueResult InnerGetCashFlowTradeValue() { var qdpTradeId = _context.MarketProxy.NextRequestId() + "_" + (_trade.id > 0 ? _trade.id.ToString() : string.IsNullOrWhiteSpace(_trade.ExchangeOptionCode) ? _trade.UnderlyingCode : _trade.ExchangeOptionCode); _context.MarketProxy.SetVolSurface(qdpTradeId, QdpModule.QdpVolHelper.GetDefaultVolatility(0.3)); var tpRequest = new OptionTradeParamRequest(_context.SysRiskFreeRate) { tradeId = qdpTradeId, preciseTimeMode = _context.IsPreciseTimeMode, fixings = null, maturityShift = _maturityShift, volSurfaceNames = new[] { qdpTradeId }, hasNightMarket = false, timeToMaturityDays = double.NaN, ParamOverride = p => { p.riskFreeRate = _context.GetRiskFreeRate(_trade); p.dividendRate = _context.GetDividendRate(_trade); } }; var rangeAccrual = _tradeExtendDataProvider.GetTrade_CashFlow(_trade.id) ?? throw new TradeCalcExpception(_trade.id, "未获取到对应的现金流交易数据"); var tradeParam = QdpTradeBuilder.GetCashFlowTradeParam(_trade, rangeAccrual, tpRequest); var calcParam = new OptionCalcParam(tradeParam) { engineName = null, pricingRequest = _context.GetPricingRequest(_trade), calcScenario = _context.CalcScenario }; return TradeRiskCalcUtil.GetCashFlowValue(_context.MarketProxy, calcParam, _trade.StockEqvNotional); } private static TradeValueResult GetOptionRisky( OtcTradeBase td, trade_risky_option risky_Option, OptionTradeParamRequest request, underlying_manager underlyings, MarketProxy marketProxy, PricingRequest pricingRequest, double spotPrice, CalcScenarioEnum CalcScenario, bool calcDeltaT) { OptionCalcParam getOptionCalcParam(T tradeParam, double[] spotPrices = null) where T : OptionTradeParamBase { return new OptionCalcParam(tradeParam) { engineName = null, pricingRequest = pricingRequest, spotPrices = spotPrices ?? new[] { spotPrice }, calcScenario = CalcScenario, CalcDeltaT1 = calcDeltaT }; } var result = new TradeValueResult(); var tradeclone = td.Copy(); tradeclone.TradeAmount = tradeclone.TradeAmount = TradeCalcHelper.GetTradeAmountV(td, td.TradeAmount, 1); tradeclone.Notional = tradeclone.Notional = TradeCalcHelper.GetTradeAmountV(td, td.Notional, underlyings.CountRatio); var td1 = tradeclone.Copy(); var td2 = tradeclone.Copy(); if (risky_Option.ParticipationRate2 != 0) { td2.Strike = risky_Option.Strike2; td2.ParticipationRate = risky_Option.ParticipationRate2; td2.TradeAmount = TradeCalcHelper.GetTradeAmount(td2, td2.TradeAmount, 1); td2.Notional = TradeCalcHelper.GetTradeAmount(td2, td2.Notional, underlyings.CountRatio); var tradeParam2 = QdpTradeBuilder.GetVanillaOptionTradeParam(td2, request, false); result = TradeRiskCalcUtil.GetVanillaOptionValue(marketProxy, getOptionCalcParam(tradeParam2)); } if (risky_Option.ParticipationRate1 != 0) { td1.Strike = risky_Option.Strike1; td1.ParticipationRate = risky_Option.ParticipationRate1; td1.TradeAmount = TradeCalcHelper.GetTradeAmount(td1, td1.TradeAmount, 1); td1.Notional = TradeCalcHelper.GetTradeAmount(td1, td1.Notional, underlyings.CountRatio); td1.OptionType = "看跌"; var tradeParam1 = QdpTradeBuilder.GetVanillaOptionTradeParam(td1, request, false); var singleresult1 = TradeRiskCalcUtil.GetVanillaOptionValue(marketProxy, getOptionCalcParam(tradeParam1)); result.Pv -= singleresult1.Pv; result.Delta -= singleresult1.Delta; result.Gamma -= singleresult1.Gamma; result.Vega -= singleresult1.Vega; result.CalendarDayTheta -= singleresult1.CalendarDayTheta; result.TradingDayTheta -= singleresult1.TradingDayTheta; result.Rho -= singleresult1.Rho; result.DeltaInLots -= singleresult1.DeltaInLots; result.DeltaCash -= singleresult1.DeltaCash; result.GammaCash -= singleresult1.GammaCash; result.VegaCash -= singleresult1.VegaCash; result.RoundedPv -= singleresult1.RoundedPv; result.DDeltaDVol -= singleresult1.DDeltaDVol; result.DDeltaDt -= singleresult1.DDeltaDt; result.DVegaDVol -= singleresult1.DVegaDVol; result.DVegaDt -= singleresult1.DVegaDt; result.DeltaT1 -= singleresult1.DeltaT1; result.ErrorMessage += singleresult1.ErrorMessage; result.Vol = singleresult1.Vol; result.Succeeded = singleresult1.Succeeded && result.Succeeded; } var td3 = tradeclone.Copy(); td3.Strike = risky_Option.Strike3; //decimal 为了解决精度问题: 0.2-0.3=0.0999999999 var participationRate3 = (decimal)risky_Option.ParticipationRate3 - (decimal)risky_Option.ParticipationRate2; if (participationRate3 != 0) { td3.ParticipationRate = (double?)Math.Abs(participationRate3); td3.TradeAmount = TradeCalcHelper.GetTradeAmount(td3, td3.TradeAmount, 1); td3.Notional = TradeCalcHelper.GetTradeAmount(td3, td3.Notional, underlyings.CountRatio); var tradeParam3 = QdpTradeBuilder.GetVanillaOptionTradeParam(td3, request, false); var singleresult3 = TradeRiskCalcUtil.GetVanillaOptionValue(marketProxy, getOptionCalcParam(tradeParam3)); if (participationRate3 < 0) { result.Pv -= singleresult3.Pv; result.Delta -= singleresult3.Delta; result.Gamma -= singleresult3.Gamma; result.Vega -= singleresult3.Vega; result.CalendarDayTheta -= singleresult3.CalendarDayTheta; result.TradingDayTheta -= singleresult3.TradingDayTheta; result.Rho -= singleresult3.Rho; result.DeltaInLots -= singleresult3.DeltaInLots; result.DeltaCash -= singleresult3.DeltaCash; result.GammaCash -= singleresult3.GammaCash; result.VegaCash -= singleresult3.VegaCash; result.RoundedPv -= singleresult3.RoundedPv; result.DDeltaDVol -= singleresult3.DDeltaDVol; result.DDeltaDt -= singleresult3.DDeltaDt; result.DVegaDVol -= singleresult3.DVegaDVol; result.DVegaDt -= singleresult3.DVegaDt; result.DeltaT1 -= singleresult3.DeltaT1; result.ErrorMessage += singleresult3.ErrorMessage; result.Vol = singleresult3.Vol; } else { result.Pv += singleresult3.Pv; result.Delta += singleresult3.Delta; result.Gamma += singleresult3.Gamma; result.Vega += singleresult3.Vega; result.CalendarDayTheta += singleresult3.CalendarDayTheta; result.TradingDayTheta += singleresult3.TradingDayTheta; result.Rho += singleresult3.Rho; result.DeltaInLots += singleresult3.DeltaInLots; result.DeltaCash += singleresult3.DeltaCash; result.GammaCash += singleresult3.GammaCash; result.VegaCash += singleresult3.VegaCash; result.RoundedPv += singleresult3.RoundedPv; result.DDeltaDVol += singleresult3.DDeltaDVol; result.DDeltaDt += singleresult3.DDeltaDt; result.DVegaDVol += singleresult3.DVegaDVol; result.DVegaDt += singleresult3.DVegaDt; result.DeltaT1 += singleresult3.DeltaT1; result.ErrorMessage += singleresult3.ErrorMessage; result.Vol = singleresult3.Vol; } result.Succeeded = singleresult3.Succeeded && result.Succeeded; } return result; } #region----期权计算参数---- /// /// 彩虹期权(如果准备波动率失败,返回null) /// private RainbowOptionTradeParam GetRainbowOptionTradeParam(OptionTradeParamRequest request, out double[] spotPrices) { var rainbowOption = _tradeExtendDataProvider.GetTrade_Rainbow_Option(_trade.id) ?? throw new TradeCalcExpception(_trade.id, "未获取到对应的彩虹期权数据"); _underlyingPriceProvider.TryGetPrice(rainbowOption.UnderlyingAssetCode, out var price1); _underlyingPriceProvider.TryGetPrice(rainbowOption.UnderlyingAssetCode2, out var price2); spotPrices = new double[2] { price1, price2 }; //彩虹期权,需要两个标的的初始化 return QdpTradeBuilder.GetRainbowOptionTradeParam(_trade, rainbowOption, request); } /// /// 亚式期权 /// private AsianOptionTradeParam GetAsianOptionCalcParam(OptionTradeParamRequest request, double spotPrice) { var asianOption = _tradeExtendDataProvider.GetTrade_Asian_Option(_trade.id) ?? throw new TradeCalcExpception(_trade.id, "未获取到对应的亚式期权数据"); //主要是某些计算场景(比如实时)的fixing用了缓存提升性能所以才从context中获取 request.fixings = _context.GetFixingString(_trade, asianOption, spotPrice); request.fixings = AsianOptionFixingService.CheckAsiaFixings(_trade, asianOption, request.fixings, spotPrice); return QdpTradeBuilder.GetAsianOptionTradeParam(_trade, asianOption, request); } /// /// 价差期权 /// private SpreadOptionTradeParam GetSpreadOptionCalcParam(OptionTradeParamRequest request, out double[] spotPrices) { var spreadOption = _tradeExtendDataProvider.GetTrade_Spread_Option(_trade.id) ?? throw new TradeCalcExpception(_trade.id, "未获取到对应的价差期权数据"); var spreadOptionInput = GetMarketInputForSpreadOption(spreadOption); spotPrices = spreadOptionInput.SpotPrices.ToArray(); var correlations = spreadOptionInput.Correlations.ToArray(); _spreadUnderlyings = spreadOptionInput.Underlyings; return QdpTradeBuilder.GetSpreadOptionTradeParam(_trade, spreadOption, request, correlations); } #endregion /// /// 为价差期权的多个标的获取现价和相关性数据 /// private SpreadOptionPricingInput GetMarketInputForSpreadOption(trade_spread_option spreadOption) { if (spreadOption == null) { throw new ArgumentNullException(nameof(spreadOption)); } var unlyArr = new underlying_manager[4]; unlyArr[0] = _underlyingDataProvider.GetUnderlying(_trade.UnderlyingCode); unlyArr[1] = _underlyingDataProvider.GetUnderlying(spreadOption.UnderlyingAssetCode2); if (!string.IsNullOrEmpty(spreadOption.UnderlyingAssetCode3)) { unlyArr[2] = _underlyingDataProvider.GetUnderlying(spreadOption.UnderlyingAssetCode3); if (!string.IsNullOrEmpty(spreadOption.UnderlyingAssetCode4)) { unlyArr[3] = _underlyingDataProvider.GetUnderlying(spreadOption.UnderlyingAssetCode4); } } unlyArr = unlyArr.Where(n => n != null).ToArray(); var spotPrices = new List(); var correlations = new List(); for (var i = 0; i < unlyArr.Length; i++) { var unly = unlyArr[i]; if (unly == null) { break; } for (var j = 0; j < i; j++) { correlations.Add(_context.GetCorrelation(unlyArr[j].id, unly.id)); } _underlyingPriceProvider.TryGetPrice(unly.UnderlyingCode, out var price); spotPrices.Add(price); unly.Price = price; } return new SpreadOptionPricingInput() { Underlyings = unlyArr, SpotPrices = spotPrices.ToArray(), Correlations = correlations.ToArray() }; } } }