using Qdp.Pricing.Base.Implementations; using Qdp.Pricing.Library.Equity.Engines.Analytical; using YLErp.Abstract; using YLErp.Commons; using YLErp.Enums; using YLErp.Modules.CalculationModule.Abstract; using YLErp.Modules.VolatilityModule; using YLErp.QdpModule; namespace YLErp.Modules.CalculationModule { /// /// 期权估值计算上下文 /// public class OptionValueCalcContext : IOtcTradeValueCalcContext { /// /// /// protected OptionValueCalcContext(DateTime valueDate, IOtcTradeValueCalcContext baseContext) { if (baseContext == null) { throw new ArgumentNullException(nameof(baseContext)); } ValueDate = valueDate; VolType = baseContext.VolType; IsEodCalc = baseContext.IsEodCalc; DataProvider = baseContext.DataProvider; AddingVolRate = baseContext.AddingVolRate; ErrorHandler = baseContext.ErrorHandler; SysRiskFreeRate = baseContext.SysRiskFreeRate; CalcScenario = baseContext.CalcScenario; UserGroup = baseContext.UserGroup; CalcDeltaT1 = baseContext.CalcDeltaT1; MarketProxy = new MarketProxy(valueDate, SysRiskFreeRate) { Trace = baseContext.Trace }; } /// /// /// public OptionValueCalcContext(string volType, bool isEodCalc, DateTime valueDate, double sysRiskFreeRate, IOptionCalcDataProvider dataProvider) { DataProvider = CalcCheckHelper.CheckOptionCalcDataProvider(dataProvider); VolType = volType; IsEodCalc = isEodCalc; ValueDate = valueDate; SysRiskFreeRate = sysRiskFreeRate; MarketProxy = new MarketProxy(valueDate, sysRiskFreeRate); } #region----属性定义---- /// /// 计算场景枚举 /// public CalcScenarioEnum CalcScenario { get; set; } /// /// 波动率类型(默认:'对冲') /// public string VolType { get; } = "对冲"; public bool IsEodCalc { get; } /// /// 波动率用户组 /// public string UserGroup { get; set; } /// /// 是否精确时间模式 /// public virtual bool IsPreciseTimeMode => CalcScenario != CalcScenarioEnum.EodSettlement; /// /// 波动率加点值 /// public double AddingVolRate { get; set; } /// /// 是否计算T+1日Delta /// public bool CalcDeltaT1 { get; set; } //-------------------------------- /// /// 估值日 /// public DateTime ValueDate { get; } /// /// /// public MarketProxy MarketProxy { get; } /// /// 期权计算数据提供接口 /// public IOptionCalcDataProvider DataProvider { get; protected set; } /// /// 错误处理接口 /// public IErrorHandler ErrorHandler { get; set; } /// /// 系统全局无风险利率 /// public double SysRiskFreeRate { get; } /// /// 用于计算跟踪 /// public TraceWrap Trace { get => MarketProxy.Trace; set => MarketProxy.Trace = value; } public string SkipTradeTypes { get; set; } #endregion #region----方法定义---- /// /// 获取期权计算模式 /// public virtual PricingRequest GetPricingRequest(OtcTradeBase trade) { if (trade is null) { throw new ArgumentNullException(nameof(trade)); } if (!string.IsNullOrEmpty(SkipTradeTypes) && SkipTradeTypes.Contains(trade.TradeType)) { return PricingRequest.None; } if (CalcScenario == CalcScenarioEnum.RealtimeRisk && !PS.Config.ErpElement.CalcAutocallGreeksInRisk && (trade.TradeType == "凤凰期权" || trade.TradeType == "雪球期权")) { return QdpPricingRequest.BASIC_PRICING; } var pr = QdpPricingRequest.BASIC_GREEKS | PricingRequest.TimeValue; if (trade.TradeType == "亚式期权") { pr |= PricingRequest.SA_Delta; } //20220118:上期做了定制处理,可以返回DDeltaDt指标值 return PS.Config.Company != Configuration.CompanyEnum.上期资本 ? pr : pr | PricingRequest.DDeltaDt; } /// /// /// public double GetCorrelation(int underlyingId1, int underlyingId2) { var correlation = DataProvider.UnderlyingDataProvider.GetCorrelation(underlyingId1, underlyingId2); return correlation == null || correlation.Correlation == null ? 0.0 : correlation.Correlation.Value; } /// /// 根据交易品种ID获取此交易品种是否存在夜盘 /// public bool HasNightMarket(int varietyId) { var variety = DataProvider.UnderlyingDataProvider.GetVariety(varietyId); if (variety != null) { return variety.HasNightMarket; } return false; } #region----波动率---- //TODO:PrepareVolatility欠缺多标的交易的波动率处理 /// /// 准备波动率(仅适用于场外期权和场内期权交易) /// public bool PrepareVolatility(string qdpTradeId, OtcTradeBase tradeObj, double spotPrice, out string[] volsurfaceNames) { volsurfaceNames = new[] { qdpTradeId }; if (tradeObj.TradeType == "场内期权") { bool prepareExOptionSavedVol() { var vol = DataProvider.VolatilityDataProvider.GetExOptionSavedVol(tradeObj.ExchangeOptionCode, ValueDate); if (vol != null) { var volitality = QdpVolHelper.GenerateFlatSurface(vol.Value); MarketProxy.SetVolSurface(qdpTradeId, volitality, AddingVolRate); return true; } return false; } if (PS.Config.Company == Configuration.CompanyEnum.光大光子) { return prepareExOptionSavedVol(); } if (VolType != "交易曲面") //交易曲面时获取交易Mid波动率 { if (prepareExOptionSavedVol()) { return true; } if (PS.Config.ErpElement.ExchangeOptionVolType == Configuration.Enums.ExchangeOptionVolType.ImpliedVol) { double volValue = tradeObj.Vol ?? 0; if (!tradeObj.Vol.IsNormalize()) { if (!tradeObj.TradeSinglePrice.HasValue) { throw new ServiceException($"计算场内期权隐含波动率失败,期权代码:{tradeObj.ExchangeOptionCode},错误信息:期权价格未获取到"); } try { volValue = VolatilityHelper.GetImpliedVol(ValueDate, tradeObj, null, spotPrice, IsEodCalc); } catch (Exception ex) { throw new ServiceException($"计算场内期权隐含波动率失败,期权代码:{tradeObj.ExchangeOptionCode},期权价格:{tradeObj.TradeSinglePrice:0.####},错误信息:{ex.Message}"); } } MarketProxy.SetVolSurface(qdpTradeId, volValue); return true; } } } else //场外期权 { if (VolType == "对冲") { var vol = DataProvider.VolatilityDataProvider.GetOtcHedgingVol(tradeObj.id, ValueDate) ?? tradeObj.TradeSavedVol; if (vol != null) { var volitality = QdpVolHelper.GenerateFlatSurface(vol.Value); if (volitality == null) { return false; } MarketProxy.SetVolSurface(qdpTradeId, volitality, AddingVolRate); return true; } } //只有在tradeVol模式下才应从开平仓波动率中插值,否则应去曲面上插值 if (VolType == "持仓" && PS.Config.IsTradeVol) { //场外期权交易使用tradingVol var volitality = GetTradingVolatility(tradeObj, false); if (volitality == null) { return false; } MarketProxy.SetVolSurface(qdpTradeId, volitality, AddingVolRate); return true; } } //设置标的波动率 var volitalityU = GetUnderlyingVolatility(tradeObj, VolType, tradeObj.UnderlyingCode, spotPrice); if (volitalityU == null) { return false; } MarketProxy.SetVolSurface(qdpTradeId, volitalityU, AddingVolRate); return true; } //获取交易波动率(如果是结算,先查找结算波动率) private IVolatility GetTradingVolatility(OtcTradeBase tradeObj, bool isEod) { double? vol = null; if (isEod) { vol = DataProvider.VolatilityDataProvider.GetOtcEodOverrideVol(tradeObj.id, ValueDate); } if (vol == null) { var tradeVol = DataProvider.VolatilityDataProvider.GetOtcPositionVol(tradeObj.id, ValueDate); var daycountMode = PS.Config.ErpElement.SmoothingDaycountMode == Configuration.Enums.SmoothingDaycountMode.CalendarDay ? Qdp.Pricing.Base.Enums.DayCountMode.CalendarDay : Qdp.Pricing.Base.Enums.DayCountMode.TradingDay; if (tradeVol == null) { vol = AnalyticalOptionTradeVolInterp.tradeVolLinearInterp( new Qdp.Foundation.Implementations.Date(ValueDate), tradeObj.TradeOpenVolatility ?? 0, tradeObj.TradeCloseVolatility ?? 0, new Qdp.Foundation.Implementations.Date(tradeObj.StartDate.Value), new Qdp.Foundation.Implementations.Date(tradeObj.ExerciseDate.Value), tradeObj.NumOfSmoothingDays ?? 0, daycountMode, CalendarImpl.Get("chn")); } else { //新增交易当天的持仓波动率需要划掉一天,修改后的持仓波动率不需要再划一天 vol = AnalyticalOptionTradeVolInterp.tradeVolLinearInterp( new Qdp.Foundation.Implementations.Date(ValueDate), tradeVol.OpenVol, tradeVol.CloseVol, new Qdp.Foundation.Implementations.Date(tradeVol.ValueDate), new Qdp.Foundation.Implementations.Date(tradeObj.ExerciseDate.Value), tradeVol.SmoothingDays, daycountMode, CalendarImpl.Get("chn"), tradeVol.IsFirst); } } return QdpVolHelper.GenerateFlatSurface(OtcFormatHelper.FormatValue(vol.Value, PS.Config.ErpElement.VolMoreAccurate ? 6 : 4)); } /// /// 获取vol 没有则按照默认值新增 /// private IVolatility GetUnderlyingVolatility(OtcTradeBase tradeObj, string voltype, string underlyingCode, double spotPrice, string volmode = "MoneynessVol") { voltype = VolatilityHelper.GetUnderlyingVolType(voltype); var vol = DataProvider.VolatilityDataProvider.GetUnderlyingVol(PS.Config.Is润和 && !IsEodCalc ? DateTime.Today : ValueDate, voltype, underlyingCode, UserGroup); if (vol != null) { var constVol = VolatilityHelper.GetInterpolatedVol( volConstructionType: PS.Config.ErpElement.SkewMapVolConstruction ? VolConstructionType.SkewMap : VolConstructionType.Normal, volSurface: vol, valueDate: PS.Config.Is润和 && !IsEodCalc ? DateTime.Today : ValueDate, underlyingCode: underlyingCode, exerciseDate: tradeObj.ExerciseDate.Value, strike: tradeObj.Strike ?? 0, isBuy: tradeObj.BuySell == "买入", isCall: ConsGlobal.CallPut.IsCall(tradeObj.CallPut), spotPrice: spotPrice, isMoneynessOption: tradeObj.IsMoneynessOption == "是", isEodCalc: IsEodCalc); return QdpVolHelper.GenerateFlatSurface(constVol); } var defVol = VolatilityHelper.GetDefaultVol(new SingleVolatilityRequest { QuotationDate = ValueDate, UnderlyingCode = underlyingCode, VolType = voltype, TradeVolWithBidAsk = false, UserGroup = UserGroup }); defVol.VolSurfaceMode = volmode; return defVol; } /// /// 获取交易的无风险利率 /// public virtual double GetRiskFreeRate(OtcTradeBase trade) { return trade.NoRiskRate ?? SysRiskFreeRate; } /// /// 获取交易的分红率 /// public virtual double GetDividendRate(OtcTradeBase trade) { if (trade.TradeType == "场内期权" && !string.IsNullOrWhiteSpace(trade.ExchangeOptionCode)) { var exOption = DataProvider.UnderlyingDataProvider.GetExchange_List_Option(trade.ExchangeOptionCode); var underlying = DataProvider.UnderlyingDataProvider.GetUnderlying(exOption?.UnderlyingCode); return underlying?.DividendRate ?? SysRiskFreeRate; } return trade.DividendRate ?? GetRiskFreeRate(trade); } #endregion #endregion bool _disposed; public void Dispose() { Dispose(true); GC.SuppressFinalize(this); } protected virtual void Dispose(bool disposing) { if (_disposed) { return; } if (disposing) { MarketProxy.Dispose(); } _disposed = true; } public virtual string GetFixingString(OtcTradeBase trade, trade_asian_option asianOption, double spotPrice) { var fixing = AsianOptionFixingService.GetFixingString(ValueDate, trade, asianOption); if (CalcScenario == CalcScenarioEnum.RealtimeRisk && IsPreciseTimeMode) { //修复实时计算中fixing最后一天的价格不是实时价格 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(';'); } } return fixing; } public override string ToString() { return $"{ValueDate:yyyy-MM-dd}--{VolType}"; } } }