using Qdp.Foundation.Implementations; using Qdp.Pricing.Base.Implementations; using Qdp.Pricing.Base.Utilities; using Qdp.Pricing.Library.Common.MathMethods.VolTermStructure; using Qdp.Pricing.Library.Equity.Engines.Analytical; using YLErp.Abstract; using YLErp.BLL.Calculation; using YLErp.Commons; using YLErp.Models; using YLErp.Modules.CalculationModule; using YLErp.QdpModule.Constants; namespace YLErp.QdpModule { /// /// 波动率曲面帮助类 /// public static class QdpVolHelper { public const string VOL_SURFACE_SUFFIX = "_VolSurface"; /// /// 从交易的TradeVol构造一个波动率曲面 /// public static IVolatility GetSurfaceFromTradeVol(DateTime valueDate, ITradeVolLinearParam para) { if (para == null) { throw new ArgumentNullException(nameof(para)); } double constVol; if (valueDate < para.GetStartDate()) { constVol = para.GetTradeOpenVol(); } else if (valueDate > para.GetMaturityDate()) { constVol = para.GetTradeCloseVol(); } else { var daycountMode = PS.Config.ErpElement.SmoothingDaycountMode == Configuration.Enums.SmoothingDaycountMode.CalendarDay ? Qdp.Pricing.Base.Enums.DayCountMode.CalendarDay : Qdp.Pricing.Base.Enums.DayCountMode.TradingDay; constVol = AnalyticalOptionTradeVolInterp.tradeVolLinearInterp( new Date(valueDate), para.GetTradeOpenVol(), para.GetTradeCloseVol(), new Date(para.GetStartDate()), new Date(para.GetMaturityDate()), para.GetNumOfSmoothingDays(), daycountMode, CalendarImpl.Get("chn"), para.IsIncludeStartDate()); } //生成3*3水平的波动率曲面 return GenerateFlatSurface(OtcFormatHelper.FormatValue(constVol, 4)); } /// /// 生成平面波动率 /// public static IVolatility GenerateFlatSurface(double constVol) { return GenerateFlatSurface(constVol, ConsVolInfos.DefaultVolStrikeList, ConsVolInfos.DefaultVolTenorList); } /// /// 生成平面波动率 /// public static IVolatility GenerateFlatSurface(double constVol, IEnumerable strikes, IEnumerable expires) { if (strikes == null) { throw new ArgumentNullException(nameof(strikes)); } if (expires == null) { throw new ArgumentNullException(nameof(expires)); } var volTable = new List(); foreach (var strike in strikes) { foreach (var expire in expires) { volTable.Add(new SingleVol { Expire = expire, Strike = strike, Vol = constVol }); } } return new VolatilityImpl() { VolSurfaceMode = "StrikeVol", InterpolationMethod = ConsVolMethod.BiLinear, VolTable = volTable }; } /// /// /// public static List GenerateFlatSingleVols(double vol) { var volTable = new List(20); foreach (var strike in ConsVolInfos.DefaultMoneynessVolStrikeList) { foreach (var expire in ConsVolInfos.DefaultVolTenorList) { volTable.Add(new SingleVol(strike, expire, vol)); } } return volTable; } /// /// 根据一组波动率,算出所有的strike /// public static List GetStrikes(IEnumerable vols) { if (vols == null) { return new List(0); } return vols.GroupBy(v => v.Strike).OrderBy(v => v.Key).Select(v => v.Key).ToList(); } /// /// 从波动率曲面上找到某点的波动率 /// /// 某点的到期日 /// 某点的行权价 /// 波动率 public static double GetInterpolatedVol( ImpliedVolSurface volSurface, DateTime exerciseDate, double strike, bool isMoneynessOption, bool isEodCalc, double spot = 0.0, double? timeFraction = null) { double vol; if (PS.Config.Is润和 && !isEodCalc) { vol = volSurface.GetValue(timeFraction.Value, strike); } else { if (isMoneynessOption) { vol = volSurface.GetValue(new Date(exerciseDate), strike * spot, spot); } else { vol = volSurface.GetValue(new Date(exerciseDate), strike, spot); } } return PS.Config.Is润和 ? OtcFormatHelper.FormatValue(vol, 4) : vol; } /// /// 从波动率曲面上找到某点的波动率 /// /// 某点的到期日 /// 某点的行权价 /// 波动率 public static double GetInterpolatedVol( ImpliedVolSurface volSurface, Date exerciseDate, double strike, string isMoneynessOption, double spot = 0.0) { if (isMoneynessOption == "是" || isMoneynessOption == "True") { return volSurface.GetValue(exerciseDate, strike * spot, spot); } else { return volSurface.GetValue(exerciseDate, strike, spot); } } /// /// interpolatevol qdp中 根据波动率曲面得到一个波动率数值 /// public static double GetInterpolatedVolFromNormalSurface(IEnumerable vols, InterpolatedVolReq volReq, string interpolationMethod = ConsVolMethod.Default) { if (vols == null || !vols.Any()) { return double.NaN; } var surfaceWrap = new VolSurfaceBuilder { interpolation = interpolationMethod, volSurfaceName = "tempVolSurface", volSurfaceType = volReq.volSurfaceType }.SetVectors(vols).Build(volReq.valueDate); double? timeFraction = null; double strike = 0; if (PS.Config.Is润和 && !volReq.isEodCalc) { var days = TradeCalcHelper.CalculateTTMDays(volReq.valueDate, volReq.exerciseDate, 0, false); var daysInYear = (int)CalculatorHelper.GetTradeDayCount().ToDayCountImpl().DaysInYear(); timeFraction = days / daysInYear; strike = volReq.volSurfaceType == "StrikeVol" ? (volReq.isMoneynessOption ? (volReq.strike * volReq.spot) : volReq.strike) : (volReq.isMoneynessOption ? volReq.strike : (volReq.strike / volReq.spot)); } else { strike = volReq.strike; } return GetInterpolatedVol( surfaceWrap.VolSurface, volReq.exerciseDate, strike, volReq.isMoneynessOption, volReq.isEodCalc, volReq.spot, timeFraction); } /// /// /// public static string GetVolSurfaceName(string referenceId, string secondUnderlyingCode = null) { if (string.IsNullOrEmpty(referenceId)) { throw new ArgumentException($"“{nameof(referenceId)}”不能是 Null 或为空。", nameof(referenceId)); } return string.IsNullOrWhiteSpace(secondUnderlyingCode) ? $"{referenceId}{VOL_SURFACE_SUFFIX}" : $"{referenceId}_{secondUnderlyingCode}{VOL_SURFACE_SUFFIX}"; } /// /// /// public static string GenerateVolSurfaceKey(this string underlyingCode) { return underlyingCode + VOL_SURFACE_SUFFIX; } /// /// 用自定义波动率值构造一个水平的波动率曲面 /// public static IVolatility GetDefaultVolatility(double vol) { var vols = new List { new SingleVol { Strike = 1, Expire = "1D", Vol = vol }, new SingleVol { Strike = 1, Expire = "1Y", Vol = vol } }; return new VolatilityImpl { VolTable = vols, VolSurfaceMode = ConsVolInfos.defVolMode, InterpolationMethod = ConsVolInfos.defInterpolationMethod }; } } /// /// /// public class InterpolatedVolReq { public DateTime valueDate { get; set; } public DateTime exerciseDate { get; set; } public double strike { get; set; } public bool isMoneynessOption { get; set; } public string volSurfaceType { get; set; } = "StrikeVol"; public double spot { get; set; } public bool isEodCalc { get; set; } = false; } }