using BaseOUDAL; using Qdp.Pricing.Base.Implementations; using Qdp.Pricing.Library.Equity.Engines.Analytical; using System.Data; using YLErp.Abstract; using YLErp.BLL; using YLErp.Modules.CalculationModule; using YLErp.QdpModule; using YLErp.QdpModule.Constants; namespace YLErp.Modules.VolatilityModule { /// /// 波动率操作帮助类 /// public static class VolatilityHelper { public static volatility GetVol(DateTime date, string volType, string underlyingCode, string userGroup) { return new VolatilityQueryService(OptUserInfo.SystemUser).GetVolatility(userGroup, date, volType, underlyingCode, true); } /// /// 获取结算波动率 /// public static volatility GetEodSettlementVols(OtcTradeBase trade, underlying_manager underlying, DateTime valueDate) { if (PS.Config.IsTradeVol) { return GetSurfaceFromTradeVol(trade, valueDate, isEodSettle: true); } //再非tradevol的情况下,默认就使用财务波动率 var userGroup = UserBLL.GetUserGroup(trade.TraderId); return GetVol(valueDate, "财务", underlying.UnderlyingCode, userGroup); } /// /// 获取隐含波动率 /// public static double GetImpliedVol(DateTime valueDate, OtcTradeBase trade, double? ttmdays, double underlyingPrice, bool isEod) { var riskFreeRate = trade.NoRiskRate ?? (valuedateBLL.GetRiskFreeRateFromCurve(valueDate, trade.ExerciseDate) / 100); var strike = trade.Strike ?? 0; var variety = DataCacheProvider.GetVariety(trade.UnderlyingCode); if (!ttmdays.HasValue || double.IsNaN(ttmdays.Value)) { ttmdays = TradeCalcHelper.CalculateTTMDays(valueDate, trade.ExerciseDate.Value, variety.id, false); } var volSurfaceName = Guid.NewGuid().ToString(); var paramReq = new OptionTradeParamRequest(riskFreeRate) { dividends = null, fixings = null, hasNightMarket = variety != null && variety.HasNightMarket, maturityShift = 0, ParamOverride = n => { n.notional = 1; n.riskFreeRate = riskFreeRate; n.buysell = "买入"; //默认买入,如果填卖出会报错 }, preciseTimeMode = !isEod, timeToMaturityDays = ttmdays.Value, tradeId = null, volSurfaceNames = new[] { volSurfaceName } }; var tp = QdpTradeBuilder.GetVanillaOptionTradeParam(trade, paramReq, false); if (trade.IsUsePremiumRate == true && trade.PremiumRate.HasValue && trade.SpotPrice.HasValue) { trade.TradeSinglePrice = trade.PremiumRate * trade.SpotPrice; } return ImpliedVolCalcService.ImpliedVolFromPremium(trade.TradeSinglePrice ?? 0, valueDate, tp, underlyingPrice); } /// /// 获取交易波动率 /// public static double GetTradeVol(OtcTradeBase trade, DateTime valueDate, bool isEodSettle = false) { using (var db = DbContextFactory.GetYLDbContext()) { if (isEodSettle && trade.id > 0) { var overrideVol = db.eod_trade_vol_override .Where(x => x.valuedate == valueDate && x.tradeid == trade.id) .Select(n => (double?)n.vol).FirstOrDefault(); if (overrideVol.HasValue) { return overrideVol.Value; } } try { TradeVolatility tradeVol = null; if (trade.id > 0) { tradeVol = db.TradeVolatility.AsNoTracking() .Where(x => x.TradeId == trade.id && x.ValueDate <= valueDate) .OrderByDescending(x => x.ValueDate).FirstOrDefault(); } if (tradeVol == null || tradeVol.TradePositionVolatility == null || tradeVol.TradeCloseVolatility == null || tradeVol.NumOfSmoothingDays == null) { var openVol = trade.TradeOpenVolatility ?? 0; var closeVol = trade.TradeCloseVolatility ?? openVol; return GetTradeVol(valueDate, trade.StartDate.Value, trade.ExerciseDate.Value, openVol, closeVol, trade.NumOfSmoothingDays ?? 0); } //新增交易当天的持仓波动率需要划掉一天,修改后的持仓波动率不需要再划一天 return GetTradeVol(valueDate: valueDate, valueStartDate: tradeVol.ValueDate, exerciseDate: trade.ExerciseDate.Value, openVol: tradeVol.TradePositionVolatility ?? 0, closeVol: tradeVol.TradeCloseVolatility ?? 0, mumOfSmoothingDays: tradeVol.NumOfSmoothingDays ?? 0, includeStartDate: tradeVol.IsFromTradeAdd); } catch (Exception ex) { throw new Exception($"交易'{trade.TradeNumber}'获取TradeVol出错:{ex.Messages()}", ex); } } } public static double GetTradeVol(DateTime valueDate, DateTime valueStartDate, DateTime exerciseDate , double openVol, double closeVol, int mumOfSmoothingDays, bool includeStartDate = true) { if (valueDate < valueStartDate) { return openVol; } if (valueDate > exerciseDate) { return closeVol; } var daycountMode = PS.Config.ErpElement.SmoothingDaycountMode == Configuration.Enums.SmoothingDaycountMode.CalendarDay ? Qdp.Pricing.Base.Enums.DayCountMode.CalendarDay : Qdp.Pricing.Base.Enums.DayCountMode.TradingDay; return AnalyticalOptionTradeVolInterp.tradeVolLinearInterp( new Qdp.Foundation.Implementations.Date(valueDate), openVol, closeVol, new Qdp.Foundation.Implementations.Date(valueStartDate), new Qdp.Foundation.Implementations.Date(exerciseDate), mumOfSmoothingDays, daycountMode, CalendarImpl.Get("chn"), includeStartDate); } /// /// 获取交易波动率曲面 /// public static volatility GetSurfaceFromTradeVol(OtcTradeBase trade, DateTime valueDate, bool isEodSettle = false) { var constVol = GetTradeVol(trade, valueDate, isEodSettle); var userGroup = UserBLL.GetUserGroup(trade.TraderId); //生成3*3水平的波动率曲面 return GetDefaultVol(new SingleVolatilityRequest { UserGroup = userGroup, QuotationDate = valueDate, VolType = ConsVolInfos.defVolType, UnderlyingCode = trade.UnderlyingCode, UnderlyingId = trade.UnderlyingId, TradeVolWithBidAsk = false }, constVol); } #region----获取默认波动率---- /// /// 获取默认波动率(不考虑标的是否过期) /// public static IEnumerable GetDefaultVols(SingleVolatilityRequest request, double volValue = ConsVolInfos.defVol) { if (request is null) { throw new ArgumentNullException(nameof(request)); } if (PS.Config.Is光大光子 || !ConsVolInfos.TradeVolTypes.Contains(request.VolType)) { return Enumerable.Empty(); } if (!string.IsNullOrEmpty(request.UnderlyingCode)) { if (!request.UnderlyingId.HasValue || request.UnderlyingId.Value < 1) { request.UnderlyingId = DataCacheProvider.GetUnderlyingDataSource().GetData(request.UnderlyingCode)?.id; } } else if (request.UnderlyingId > 0) { request.UnderlyingCode = DataCacheProvider.GetUnderlyingDataSource().GetData(request.UnderlyingId.Value)?.UnderlyingCode; } var volaty = new VolatilityDefault { VolType = request.VolType, QuotationDate = request.QuotationDate, UserGroup = request.UserGroup, UnderlyingId = request.UnderlyingId, ContractCode = request.UnderlyingCode, VolSurfaceMode = ConsVolInfos.defVolMode, InterpolationMethod = ConsVolMethod.Default, ReviewDownLimit = ConsVolInfos.defReviewDownLimit, ReviewUpLimit = ConsVolInfos.defReviewUpLimit, OptId = 0, OptName = "系统", OptDate = DateTime.Now }; volaty.Data = QdpVolHelper.GenerateFlatSingleVols(volValue).ToJson(); return request.GetVolTypes().Select(n => (VolatilityDefault)volaty.Clone(n)).ToArray(); } /// /// 获取默认波动率 /// public static VolatilityDefault GetDefaultVol(SingleVolatilityRequest request, double volValue = ConsVolInfos.defVol) { var vols = GetDefaultVols(request, volValue); if (vols != null & vols.Any()) { return vols.First(); } //一定要返回值否则某些计算会报错 return new VolatilityDefault { VolSurfaceMode = ConsVolInfos.defVolMode, InterpolationMethod = ConsVolInfos.defInterpolationMethod, VolType = request.VolType, UserGroup = request.UserGroup, QuotationDate = request.QuotationDate, UnderlyingId = request.UnderlyingId, ContractCode = request.UnderlyingCode, Data = QdpVolHelper.GenerateFlatSingleVols(volValue).ToJson(), OptId = 0, OptName = "系统", OptDate = DateTime.Now }; } /// /// 获取默认波动率 /// public static VolatilityDefault GetDefaultVol(string userGroup, DateTime quotationDate, string volType, string underlyingCode, int? underlyingId = null) { return GetDefaultVol(new SingleVolatilityRequest { QuotationDate = quotationDate, TradeVolWithBidAsk = false, UnderlyingCode = underlyingCode, UnderlyingId = underlyingId, UserGroup = userGroup, VolType = volType }); } #endregion #region----从波动率曲面取一个点---- public static double GetInterpolatedVol( VolConstructionType volConstructionType, IVolatility volSurface, DateTime valueDate, string underlyingCode, DateTime exerciseDate, double strike, bool isBuy, bool isCall, double spotPrice, bool isMoneynessOption = true, double timeToMaturityDays = double.NaN, int? skewMapVolVar = null, bool isEodCalc = false) { if (volConstructionType == VolConstructionType.SkewMap) { return SkewMapVolHelper.GetInterpolatedVol( volSurface: volSurface, valueDate: valueDate, underlyingCode: underlyingCode, exerciseDate: exerciseDate, strikePrice: strike, isBuy: isBuy, isCall: isCall, spotPrice: spotPrice, timeToMaturityDays: timeToMaturityDays, skewMapVolVar: skewMapVolVar); } else { return QdpVolHelper.GetInterpolatedVolFromNormalSurface( volSurface.VolTable, new InterpolatedVolReq() { valueDate = valueDate, exerciseDate = exerciseDate, strike = strike, isMoneynessOption = isMoneynessOption, isEodCalc = isEodCalc, volSurfaceType = volSurface.VolSurfaceMode, spot = spotPrice }, volSurface.InterpolationMethod); } } #endregion /// /// 导出用的矩阵 /// public static DataTable GetMatrix(IEnumerable vols) { var dataTable = new DataTable(); var dataColumns = new DataColumn[10]; for (var i = 0; i < dataColumns.Length; i++) { dataColumns[i] = new DataColumn(); } dataTable.Columns.AddRange(dataColumns); var strikeSet = new HashSet(10); var volFormat = PS.Config.ErpElement.VolMoreAccurate ? "0.0###%" : "0.0%"; try { foreach (var vol in vols) { strikeSet.Clear(); //转换并按到期分组 var lookup = vol.VolTable.Select(n => { strikeSet.Add(n.Strike); return new { n.Expire, Strike = n.Strike.OtcFormatUmPrice(true), Vol = n.Vol.ToString(volFormat) }; }).OrderBy(n => new Term(n.Expire)).ToLookup(n => n.Expire); //写入标的和执行价 var strikes = strikeSet.OrderBy(n => n).Select(n => n.OtcFormatUmPrice(true)).ToArray(); var values = strikes.Prepend(vol.ContractCode).ToArray(); while (values.Length > dataTable.Columns.Count) { dataTable.Columns.Add(); } dataTable.Rows.Add(values); //写入到期和波动率 foreach (var g in lookup) { var garr = g.ToArray(); var query = from s in strikes join item in garr on s equals item.Strike into items from item in items select item?.Vol; values = query.ToArray().Prepend(g.Key).ToArray(); dataTable.Rows.Add(values); } dataTable.Rows.Add(); } } catch (Exception ex) { System.Diagnostics.Debug.WriteLine(ex); } return dataTable; } /// /// 获取曲面波动率类型 /// /// 用于计算的波动率类型 /// public static string GetUnderlyingVolType(string calcVolType) { calcVolType = calcVolType.TrimToNull(); if (PS.Config.IsTradeVol) { return calcVolType switch { null or "持仓" or "对冲" or "交易曲面" => "交易", _ => calcVolType }; } return calcVolType switch { null or "持仓" => valuedateBLL.SystemDate.EodSettleVolMode.TrimToNull() ?? "财务", "对冲" or "交易曲面" => "交易", _ => calcVolType, }; } } }