using Qdp.Foundation.Implementations; using Qdp.Pricing.Base.Implementations; using YLErp.BLL; using YLErp.BLL.Calculation; using YLErp.BLL.Calculation.V2; using YLErp.BLL.Calculation.V2.Parameter; using YLErp.Modules.CalculationModule; using YLErp.Modules.ClientModule; using YLErp.Modules.VolatilityModule; using YLErp.QdpModule; namespace YLErp.Modules.TQuoteModule { /// /// 自定义手机报价 /// public class CustomizedQuoteService { public static HandleResult> GetHolidaysBetween(DateTime startDate, DateTime endDate) { var result = new List(); var days = (endDate - startDate).Days; var calendar = CalendarImpl.Get("chn"); for (var i = 0; i < days; i++) { var date = startDate.AddDays(i); if (date >= endDate) { break; } if (calendar.IsHoliday(new Date(date))) { result.Add(date); } } return new HandleResult>(result); } /// /// /// public static CustomizedQuoteResult CustomizedQuoteV2(CustomizedQuoteRequest request, string userGroup = "") { if (request.MaturityDate < request.ValueDate) { return new CustomizedQuoteResult() { Info = "到期日小于估值日", StatusCode = -1 }; } var qdpMarketId = Guid.NewGuid().ToString(); var marketProxy = QdpMarketManager.Instance.GetPrebuiltMarketProxy(qdpMarketId); var valueDate = request.ValueDate; var maturityDate = request.MaturityDate; volatility bidVol = null, askVol = null; underlying_manager underlying = null; Variety variety = null; using (var db = new YLContext()) { underlying = DataCacheProvider.GetUnderlyingDataSource().GetData(request.UnderlyingCode); if (underlying != null) { variety = db.variety.FirstOrDefault(x => x.VarietyCode != null && x.id == underlying.UnderlyingTypeId); if (request.BidVol <= 0 && request.AskVol <= 0) { underlying.QuotationDate = request.ValueDate; if (PS.Config.ErpElement.SkewMapVolConstruction) { bidVol = askVol = VolatilityHelper.GetVol(DateTime.Today, "交易", underlying.UnderlyingCode, userGroup ?? ""); } else { bidVol = VolatilityHelper.GetVol(DateTime.Today, "报价Bid", underlying.UnderlyingCode, userGroup ?? ""); askVol = VolatilityHelper.GetVol(DateTime.Today, "报价Ask", underlying.UnderlyingCode, userGroup ?? ""); } } } } //如果用户未指定标的,构造一个虚拟标的 if (string.IsNullOrWhiteSpace(request.UnderlyingCode)) { underlying = new underlying_manager() { UnderlyingCode = "dummy_code", UnderlyingInstrumentType = "CommodityFutures", QuotationDate = request.ValueDate, Price = request.SpotPrice, PrevClosePrice = request.SpotPrice, MaturityDate = request.MaturityDate }; } //根据用户设置的bid/ask天数调整规则来分别调整到期日 //TODO: bidMaturityShift和askMaturityShift应该从某数据库表读取 var bidMaturityShift = 0; var askMaturityShift = 0; var otherInfo = ""; var client_param = ClientPricingParamService.GetPricingParam(request.ValueDate.Date, request.MaturityDate.Date); if (client_param != null) { askMaturityShift = client_param.ask_tuning_day ?? 0; bidMaturityShift = client_param.bid_tuning_day ?? 0; otherInfo = client_param.ToJson();//$"ask到期日偏离{askMaturityShift}天,bid到期日偏离{bidMaturityShift}天"; } var dayCount = CalculatorHelper.GetTradeDayCount(); var bidMaturityDate = QdpCalendarHelper.ShiftDate(maturityDate, dayCount, bidMaturityShift).DateTime; var askMaturityDate = QdpCalendarHelper.ShiftDate(maturityDate, dayCount, askMaturityShift).DateTime; if (underlying == null) { return null; } // 如果未传入最新价格,则使用系统里的最新价格; 如果未传入无风险利率,则使用系统里的无风险利率 var spotPrice = (request.SpotPrice <= 0) ? (underlying.Price ?? 0) : request.SpotPrice; var riskFreeRate = (request.RiskFreeRate <= 0) ? (valuedateBLL.RiskFreeRate / 100.0) : request.RiskFreeRate; var strike = (request.Strike <= 0) ? spotPrice : request.Strike; //使用全局的DiscountCurve以提高计算效率 var discountCurveName = Guid.NewGuid().ToString(); var discountCurve = CalculatorHelper.CreateConstantRiskFreeCurve(discountCurveName, riskFreeRate); marketProxy.AddYieldCurve(discountCurveName, valueDate, discountCurve); var trade = new trade() { TradeType = "香草期权", TradeDate = request.ValueDate, ExerciseMode = request.Exercise, Strike = strike, Notional = request.Notional }; var parameter = new VanillaOptionParameter() { ValueDate = request.ValueDate, DiscountCurveName = discountCurveName, SpotPrices = new Dictionary() { { underlying.UnderlyingCode, spotPrice } }, HasNightMarket = variety.HasNightMarket, PreciseTimeMode = request.commodityFuturesPreciseTimeMode }; trade.OptionType = "看涨"; trade.MaturityDate = bidMaturityDate; trade.ExerciseDate = bidMaturityDate; parameter.Volatility = bidVol == null ? request.BidVol : VolatilityHelper.GetInterpolatedVol( volConstructionType: PS.Config.ErpElement.SkewMapVolConstruction ? VolConstructionType.SkewMap : VolConstructionType.Normal, volSurface: bidVol, valueDate: valueDate, underlyingCode: underlying.UnderlyingCode, exerciseDate: bidMaturityDate, strike: strike, isBuy: true, isCall: true, spotPrice: spotPrice, isMoneynessOption: false); var callBidResult = ValueCalculator.CalculateTradeValue(qdpMarketId, trade, underlying, parameter); trade.OptionType = "看跌"; parameter.Volatility = bidVol == null ? request.BidVol : VolatilityHelper.GetInterpolatedVol( volConstructionType: PS.Config.ErpElement.SkewMapVolConstruction ? VolConstructionType.SkewMap : VolConstructionType.Normal, volSurface: bidVol, valueDate: valueDate, underlyingCode: underlying.UnderlyingCode, exerciseDate: bidMaturityDate, strike: strike, isBuy: true, isCall: false, spotPrice: spotPrice, isMoneynessOption: false); var putBidResult = ValueCalculator.CalculateTradeValue(qdpMarketId, trade, underlying, parameter); trade.OptionType = "看涨"; trade.MaturityDate = askMaturityDate; trade.ExerciseDate = askMaturityDate; parameter.Volatility = askVol == null ? request.AskVol : VolatilityHelper.GetInterpolatedVol( volConstructionType: PS.Config.ErpElement.SkewMapVolConstruction ? VolConstructionType.SkewMap : VolConstructionType.Normal, volSurface: askVol, valueDate: valueDate, underlyingCode: underlying.UnderlyingCode, exerciseDate: askMaturityDate, strike: strike, isBuy: false, isCall: true, spotPrice: spotPrice, isMoneynessOption: false); var callAskResult = ValueCalculator.CalculateTradeValue(qdpMarketId, trade, underlying, parameter); trade.OptionType = "看跌"; parameter.Volatility = askVol == null ? request.AskVol : VolatilityHelper.GetInterpolatedVol( volConstructionType: PS.Config.ErpElement.SkewMapVolConstruction ? VolConstructionType.SkewMap : VolConstructionType.Normal, volSurface: askVol, valueDate: valueDate, underlyingCode: underlying.UnderlyingCode, exerciseDate: askMaturityDate, strike: strike, isBuy: false, isCall: false, spotPrice: spotPrice, isMoneynessOption: false); var putAskResult = ValueCalculator.CalculateTradeValue(qdpMarketId, trade, underlying, parameter); var valueResult = new CustomizedQuoteResult() { CallAskPrice = callAskResult.Pv / request.Notional, CallBidPrice = callBidResult.Pv / request.Notional, Strike = strike, PutAskPrice = putAskResult.Pv / request.Notional, PutBidPrice = putBidResult.Pv / request.Notional, BidVol = callBidResult.Vol, AskVol = callAskResult.Vol, RiskFreeRate = riskFreeRate }; valueResult.LatestPrice = underlying.Price.Value; if (underlying.PrevClosePrice.HasValue && underlying.PrevClosePrice.Value > 0.0) { valueResult.Change = underlying.Price.Value - underlying.PrevClosePrice.Value; valueResult.ChangePercent = valueResult.Change / underlying.PrevClosePrice.Value; } else { valueResult.Change = 0.0; valueResult.ChangePercent = 0.0; } QdpMarketManager.Instance.RemovePrebuiltMarketProxy(qdpMarketId); return valueResult; } /// /// /// public static double Pricing(OptionStrategyCodeParts part, string userGroup) { var valueDate = DateTime.Today; var maturityDate = QdpHelper.getMaturityDate(valueDate, part.Maturity); var exercisType = "European"; var underlying = DataCacheProvider.GetUnderlyingDataSource().GetData(part.UnderlyingCode); if (underlying == null) { throw new Exception("未找到标的" + part.UnderlyingCode); } var variety = DataCacheProvider.GetVarietyDataSource().GetData(underlying.UnderlyingTypeId); if (variety == null) { throw new Exception("未找到标的品种" + underlying.UnderlyingType); } var vol = VolatilityHelper.GetVol(DateTime.Today, part.IsSell ? "报价Bid" : "报价Ask", underlying.UnderlyingCode, userGroup); if (vol == null) { throw new Exception("未找到波动率曲面"); } var riskFreeRate = valuedateBLL.RiskFreeRate / 100.0; using (var mp = new MarketProxy(valueDate, riskFreeRate)) { var volsurfaceName = mp.SaveVolSurface(vol); var tdParam = new VanillaOptionTradeParam { underlyingTickers = new[] { part.UnderlyingCode }, underlyingInstrumentType = underlying.UnderlyingInstrumentType, strike = part.Strike, startDate = valueDate, endDate = maturityDate, optionType = QdpConverter.ConvertOptionType(part.OptionType), exerciseType = exercisType, initialSpotPrice = underlying.Price ?? 0, notional = part.Notional, volSurfaceNames = new[] { volsurfaceName }, riskFreeRate = riskFreeRate, buysell = part.IsSell ? "Sell" : "Buy", exerciseDate = maturityDate, hasNightMarket = variety.HasNightMarket, preciseTimeMode = true, participationRate = 1.0, principalRate = 0.0, isAnnualized = false, annualizedFactor = 1.0 }; var result = TradeRiskCalcUtil.GetVanillaOptionValue(mp, new OptionCalcParam(tdParam) { spotPrices = new[] { underlying.Price ?? 0 }, pricingRequest = PricingRequest.Pv }); return result.Pv; } } } }