using Qdp.Foundation.Implementations; using Qdp.Pricing.Base.Implementations; using Qdp.Pricing.Base.Interfaces; using Qdp.Pricing.Base.Utilities; using Qdp.Pricing.Library.Common.Base; using Qdp.Pricing.Library.Options.Products.Accumulator; using Qdp.Pricing.Library.Options.Products.Airbag; using Qdp.Pricing.Library.Options.Products.Asian; using Qdp.Pricing.Library.Options.Products.AsianSyntheticSpread; using Qdp.Pricing.Library.Options.Products.Autocall.Phoenix; using Qdp.Pricing.Library.Options.Products.Autocall.Snowball; using Qdp.Pricing.Library.Options.Products.Barrier; using Qdp.Pricing.Library.Options.Products.Binary; using Qdp.Pricing.Library.Options.Products.DoubleSharkFin; using Qdp.Pricing.Library.Options.Products.PayoffEnhance; using Qdp.Pricing.Library.Options.Products.Rainbow; using Qdp.Pricing.Library.Options.Products.RangeAccrual; using Qdp.Pricing.Library.Options.Products.Spread; using Qdp.Pricing.Library.Options.Products.SyntheticSpread; using Qdp.Pricing.Library.Options.Products.Vanilla; using YLErp.BLL; using YLErp.BLL.Calculation.V2; using YLErp.Modules.TradeModule; using YLErp.Modules.VolatilityModule; using YLErp.QdpModule; namespace YLErp.Modules.CalculationModule { public class OptionTradeAnalysisService : YLBaseService { public OptionTradeAnalysisService(OptUserInfo userInfo) : base(userInfo) { } /// /// 为一组期权交易计算到期时在不同价格条件下的总体payoff情况 /// /// /// public List GetTradesPayoffLine(IEnumerable optionTrades) { if (optionTrades == null) { return null; } var curvePoints = new List(); var keyPricePoints = new List(); if (optionTrades.Count(t => t.TradeType == "雪球期权" || t.TradeType == "凤凰期权") > 0) { throw new Exception($"暂时不支持Autocall的分析"); } var underlyingIds = optionTrades.Select(n => n.UnderlyingId).ToHashSet(); if (underlyingIds.Count > 1) { throw new Exception($"待分析的组合交易需要有相同的标的资产"); } var underlying = DbContext.underlying_manager.AsNoTracking().FirstOrDefault(n => underlyingIds.Contains(n.id)); if (underlying == null) { throw new Exception($"找不到标的资产{optionTrades.First().UnderlyingCode}的信息,id为{optionTrades.First().UnderlyingId}"); } var options = new List(); foreach (var otcTrade in optionTrades) { var trade = TradeConverter.ConvertOptionTrade(otcTrade); if (otcTrade.TradeType == "Risky期权") { var _options = GetToQdpOptionRisk(trade, underlying); if (_options != null) { options.AddRange(_options); } } else { var option = ToQdpOption(trade, underlying); if (option != null) { options.Add(option); } } var points = GetKeyPoints(trade); if (points != null) { keyPricePoints.AddRange(points); } } keyPricePoints = keyPricePoints.Distinct().ToList(); keyPricePoints.Sort(); keyPricePoints.Insert(0, keyPricePoints.First() * 0.8); keyPricePoints.Add(keyPricePoints.Last() * 1.2); var prices = new double[] { 0.0 }; foreach (var price in keyPricePoints) { prices[0] = price; curvePoints.Add(new CurvePoint() { X = price, Y = options.Sum(x => { if (x is BarrierOption barrier) { //障碍期权在GetPayoff方法中会更改BarrierStatus值,所以需要重置 barrier.BarrierStatus = Qdp.Pricing.Base.Enums.BarrierStatus.Monitoring; } return x.GetPayoff(prices)[0].PaymentAmount; }) }); } return curvePoints; } /// /// 为一组期权交易计算不同价格条件下的Pv /// public List GetTradesPvLine(IEnumerable optionTrades) { if (optionTrades == null) { return null; } if (!optionTrades.Any()) { return new List(0); } var curvePoints = new List(); var keyPricePoints = new List(); if (optionTrades.Count(t => t.TradeType == "雪球期权" || t.TradeType == "凤凰期权") > 0) { throw new Exception($"暂时不支持Autocall的分析"); } var underlyingCount = optionTrades.Select(n => n.UnderlyingCode?.ToLowerInvariant()).Distinct().Count(); if (underlyingCount > 1) { throw new Exception($"待分析的组合交易需要有相同的标的资产"); } var trades = new List(); foreach (var otcTrade in optionTrades) { var trade = TradeConverter.ConvertOptionTrade(otcTrade); if (trade != null) { trades.Add(trade); } var points = GetKeyPoints(trade); if (points != null) { keyPricePoints.AddRange(points); } } return GetTradesPvLineForKeyPoints(optionTrades.First().TradeDate.Value, trades, keyPricePoints); } private List GetTradesPvLineForKeyPoints(DateTime valueDate, IEnumerable trades, List keyPoints) { var curvePoints = new List(); if (trades == null || trades.Count() == 0) { return curvePoints; } var startPrice = keyPoints.Min() > 0 ? keyPoints.Min() * 0.8 : keyPoints.Min() * 1.2; var endPrice = keyPoints.Min() > 0 ? keyPoints.Max() * 1.2 : keyPoints.Max() * 0.8; var step = (endPrice - startPrice) / 20.0; var price = startPrice; var calcReq = new OptionValueCalcRequest(valuedateBLL.SysRiskFreeRate()) { correlations = null, engineName = null, maturityShift = 0, ParamOverride = null, preciseTimeMode = false, pricingRequest = PricingRequest.Pv }; while (price < endPrice + step) { var pv = 0.0; foreach (var trade in trades) { //场内期权交易不会有开仓波动率,因此根据其交易价格计算出隐含波动率 if (trade.TradeType == "场内期权" && !trade.TradeOpenVolatility.HasValue) { trade.VolType = "交易"; trade.TradeOpenVolatility = VolatilityHelper.GetImpliedVol(trade.TradeDate ?? valuedateBLL.ValueDate, trade, trade.TTMDays, trade.SpotPrice ?? 0, true); } if (!PS.Config.IsTradeVol && !trade.TradeOpenVolatility.HasValue) { trade.TradeOpenVolatility = trade.Vol; } if (!trade.TradeOpenVolatility.HasValue || double.IsNaN(trade.TradeOpenVolatility.Value)) { throw new Exception("无法获取开仓波动率"); } calcReq.spotPrices = new[] { price }; calcReq.vols = new[] { trade.TradeOpenVolatility.Value }; var result = OptionCalculatorV2.GetOptionValueResult(valueDate, trade, calcReq, out _); if (double.IsNaN(result.Pv)) { throw new Exception($"计算此交易失败:{trade.UnderlyingCode},{trade.TradeType}"); } pv += result.Pv; } curvePoints.Add(new CurvePoint() { X = price, Y = pv }); price += step; } return curvePoints; } public List GetTradesPvLine2(int tradeId) { var trade = DbContext.trade.FirstOrDefault(t => t.id == tradeId); if (trade == null) { return null; } List trades; if (trade.TradeType == "结构化交易") { trades = DbContext.trade.Where(t => t.ParentTradeId == trade.id).ToList(); } else { trades = new List { trade }; } var tradebll = new tradeBLL(); trades.ForEach(t => tradeBLL.SetFieldsByTradeType(t)); return GetTradesPvLine2(valuedateBLL.ValueDate, trades); } private List GetTradesPvLine2(DateTime valueDate, List optionTrades) { if (optionTrades == null) { return null; } if (optionTrades.Count(t => t.TradeType == "雪球期权" || t.TradeType == "凤凰期权") > 0) { throw new Exception($"暂时不支持Autocall的分析"); } var underlyingCount = optionTrades.Select(n => n.UnderlyingCode.ToLowerInvariant()).Distinct().Count(); if (underlyingCount > 1) { throw new Exception($"待分析的组合交易需要有相同的标的资产"); } var keyPricePoints = new List(); optionTrades.ForEach(t => { keyPricePoints.AddRange(GetKeyPoints(t)); }); return GetTradesPvLineForKeyPoints(valueDate, optionTrades, keyPricePoints); } /// /// 计算交易在一半ttm时候的Pv曲线 /// public List GetTradesHalflifePvLine(IEnumerable optionTrades) { var dayCount = valuedateBLL.TradeDayCount.ToDayCountImpl(); foreach (var trade in optionTrades) { trade.ExerciseDate = AdjustToHalfLifeMaturity(dayCount, trade.TradeDate, trade.ExerciseDate); trade.TTMDays = double.NaN; } return GetTradesPvLine(optionTrades); } /// /// 返回当前、一半ttm、以及在到期时的Pv曲线 /// /// public List> GetTradePvLifeLine(IEnumerable optionTrades) { var results = new List>(); var pvLine = GetTradesPvLine(optionTrades); results.Add(pvLine); var payoffPoints = GetTradesPayoffLine(optionTrades); //按Pv曲线的X点对齐 //int start = -1, end = 0; //var payoffLine = new List(); //for (var i = 0; i < pvLine.Count; ++i) //{ // if (pvLine[i].X == payoffPoints[end].X) // { // payoffLine.Add(payoffPoints[end]); // ++start; // ++end; // } // else // { // payoffLine.Add(new CurvePoint() { X = pvLine[i].X, Y = simpleInterpolate(payoffPoints[start].X, payoffPoints[end].X, payoffPoints[start].Y, payoffPoints[end].Y, pvLine[i].X) }); // } //} results.Add(payoffPoints); results.Add(GetTradesHalflifePvLine(optionTrades)); return results; } private DateTime? AdjustToHalfLifeMaturity(IDayCount dayCount, DateTime? tradeDate, DateTime? maturityDate) { var qdpStart = new Date(tradeDate.Value); var qdpEnd = new Date(maturityDate.Value); var half = dayCount.CalcDayCountFraction(qdpStart, qdpEnd) / 2.0; qdpEnd = dayCount.CalcEndDateFromDayCountFraction(qdpStart, half, null, null); return qdpEnd.DateTime; } /// /// 为一组期权交易计算随时间变化的希腊字母变化 /// /// /// public Dictionary> GetTradesGreeksForLifetime(IEnumerable optionTrades) { if (optionTrades == null) { return null; } var curvePoints = new List(); if (optionTrades.Count(t => t.TradeType == "雪球期权" || t.TradeType == "凤凰期权") > 0) { throw new Exception($"暂时不支持Autocall的分析"); } var underlyingIds = optionTrades.Select(n => n.UnderlyingId).ToHashSet(); if (underlyingIds.Count > 1) { throw new Exception($"待分析的组合交易需要有相同的标的资产"); } var underlying = DbContext.underlying_manager.AsNoTracking().FirstOrDefault(n => underlyingIds.Contains(n.id)); if (underlying == null) { throw new Exception($"找不到标的资产{optionTrades.First().UnderlyingCode}的信息,id为{optionTrades.First().UnderlyingId}"); } var options = new List(); var trades = new List(); foreach (var otcTrade in optionTrades) { var trade = TradeConverter.ConvertOptionTrade(otcTrade); if (trade != null) { trades.Add(trade); } } var minStart = trades.Min(t => t.TradeDate.Value); //避开最后一天的计算,在到期日当天会出现一些跟时间相关的结果,会混淆曲线的整体趋势 var maxMaturity = CalendarImpl.Get("chn").PrevBizDay(new Date(trades.Max(t => t.ExerciseDate.Value))); var valueDates = CalendarImpl.Get("chn").BizDaysBetweenDatesInclEndDay(new Date(minStart), new Date(maxMaturity)); var userId = Guid.NewGuid().ToString(); var results = new Dictionary>(); results["Pv"] = new List(); results["Delta"] = new List(); results["Gamma"] = new List(); results["Vega"] = new List(); results["Theta"] = new List(); for (var i = 0; i < valueDates.Count; ++i) { double pv = 0.0, delta = 0.0, gamma = 0.0, vega = 0.0, theta = 0.0; foreach (var trade in trades) { trade.TradeDate = valueDates[i].DateTime; trade.TTMDays = double.NaN; underlying.QuotationDate = trade.TradeDate; if (!PS.Config.IsTradeVol && !trade.TradeOpenVolatility.HasValue) { trade.TradeOpenVolatility = trade.Vol; } if (!trade.TradeOpenVolatility.HasValue || double.IsNaN(trade.TradeOpenVolatility.Value)) { throw new Exception("无法获取开仓波动率"); } var result = ValueCalculator.GetOptionValueResultV2( userId, underlying, trade, new double[] { trade.TradeOpenVolatility.Value }, new double[] { trade.SpotPrice.Value }, request: QdpPricingRequest.BASIC_GREEKS); if (double.IsNaN(result.Pv)) { throw new Exception($"计算此交易失败:{trade.UnderlyingCode},{trade.TradeType}"); } pv += result.Pv; delta += result.Delta; gamma += result.Gamma; vega += result.Vega; theta += result.Theta; } results["Pv"].Add(new CurvePoint() { X = i, Y = pv }); results["Delta"].Add(new CurvePoint() { X = i, Y = delta }); results["Gamma"].Add(new CurvePoint() { X = i, Y = gamma }); results["Vega"].Add(new CurvePoint() { X = i, Y = vega }); results["Theta"].Add(new CurvePoint() { X = i, Y = theta }); } return results; } /// /// 获得一笔期权影响payoff的价格点,如行权价、障碍价格等 /// /// /// private IEnumerable GetKeyPoints(trade trade) { switch (trade.TradeType) { case "香草期权": case "场内期权": case "亚式期权": case "彩虹期权": case "合成价差期权": case "亚式合成价差期权": case "收益增强结构": case "区间累积期权": case "价差期权": case "凤凰期权": case "雪球期权": case "气囊结构": return new double[] { trade.IsMoneynessOptionData ? trade.Strike.Value * trade.SpotPrice.Value : trade.Strike.Value }; case "累计期权": if (trade.trade_accumulator_option.AccumulatorStructureType == AccumulatorStructureTypeEnum.Segmented) { return new double[] { trade.IsMoneynessOptionData ? trade.Strike.Value * trade.SpotPrice.Value : trade.Strike.Value, trade.IsMoneynessOptionData ? trade.trade_accumulator_option.Strike2.Value * trade.SpotPrice.Value : trade.trade_accumulator_option.Strike2.Value, trade.IsMoneynessOptionData ? trade.trade_accumulator_option.Strike3.Value * trade.SpotPrice.Value : trade.trade_accumulator_option.Strike3.Value, }; } else { return new double[] { trade.IsMoneynessOptionData ? trade.Strike.Value * trade.SpotPrice.Value : trade.Strike.Value }; } case "障碍期权": if (trade.trade_barrier_option.BarrierTypeEn.StartsWith("Double")) { return new double[] { trade.IsMoneynessOptionData ? trade.Strike.Value * trade.SpotPrice.Value : trade.Strike.Value, trade.IsMoneynessOptionData ? trade.trade_barrier_option.BarrierPrice.Value* trade.SpotPrice.Value : trade.trade_barrier_option.BarrierPrice.Value, trade.IsMoneynessOptionData ? trade.trade_barrier_option.BarrierPrice.Value* trade.SpotPrice.Value + 0.01 : trade.trade_barrier_option.BarrierPrice.Value + 0.01, trade.IsMoneynessOptionData ? trade.trade_barrier_option.UpperBarrierPrice.Value * trade.SpotPrice.Value : trade.trade_barrier_option.UpperBarrierPrice.Value, trade.IsMoneynessOptionData ? trade.trade_barrier_option.UpperBarrierPrice.Value * trade.SpotPrice.Value - 0.01 : trade.trade_barrier_option.UpperBarrierPrice.Value - 0.01 }; } else { if (trade.trade_barrier_option.BarrierTypeEn.StartsWith("Up")) { return new double[] { trade.IsMoneynessOptionData ? trade.Strike.Value * trade.SpotPrice.Value : trade.Strike.Value, trade.IsMoneynessOptionData ? trade.trade_barrier_option.BarrierPrice.Value * trade.SpotPrice.Value : trade.trade_barrier_option.BarrierPrice.Value, trade.IsMoneynessOptionData ? trade.trade_barrier_option.BarrierPrice.Value * trade.SpotPrice.Value - 0.01 : trade.trade_barrier_option.BarrierPrice.Value - 0.01 }; } else { return new double[] { trade.IsMoneynessOptionData ? trade.Strike.Value * trade.SpotPrice.Value : trade.Strike.Value, trade.IsMoneynessOptionData ? trade.trade_barrier_option.BarrierPrice.Value * trade.SpotPrice.Value : trade.trade_barrier_option.BarrierPrice.Value, trade.IsMoneynessOptionData ? trade.trade_barrier_option.BarrierPrice.Value * trade.SpotPrice.Value + 0.01 : trade.trade_barrier_option.BarrierPrice.Value + 0.01 }; } } case "二元期权": if (trade.ExerciseMode == "American" && trade.trade_binary_option.PayoffType.StartsWith("Double")) { var low = trade.IsMoneynessOptionData ? trade.Strike.Value * trade.SpotPrice.Value : trade.Strike.Value; var high = trade.IsMoneynessOptionData ? trade.trade_binary_option.UpperBarrier.Value * trade.SpotPrice.Value : trade.trade_binary_option.UpperBarrier.Value; return new double[] { low - 0.01, low, low + 0.01, high - 0.01, high, high + 0.01 }; } else { var low = trade.IsMoneynessOptionData ? trade.Strike.Value * trade.SpotPrice.Value : trade.Strike.Value; return new double[] { low - 0.01, low, low + 0.01 }; } case "双鲨期权": return new double[] { trade.IsMoneynessOptionData ? trade.Strike.Value * trade.SpotPrice.Value : trade.Strike.Value, trade.IsMoneynessOptionData ? trade.trade_double_sharkfin_option.StrikeHigh.Value * trade.SpotPrice.Value : trade.trade_double_sharkfin_option.StrikeHigh.Value, trade.IsMoneynessOptionData ? trade.trade_double_sharkfin_option.BarrierHigh * trade.SpotPrice.Value : trade.trade_double_sharkfin_option.BarrierHigh, trade.IsMoneynessOptionData ? trade.trade_double_sharkfin_option.BarrierHigh * trade.SpotPrice.Value - 0.01 : trade.trade_double_sharkfin_option.BarrierHigh - 0.01, trade.IsMoneynessOptionData ? trade.trade_double_sharkfin_option.BarrierLow * trade.SpotPrice.Value : trade.trade_double_sharkfin_option.BarrierLow, trade.IsMoneynessOptionData ? trade.trade_double_sharkfin_option.BarrierLow * trade.SpotPrice.Value + 0.01 : trade.trade_double_sharkfin_option.BarrierLow + 0.01, }; case "Risky期权": return new double[] { trade.IsMoneynessOptionData ? trade.trade_risky_option.Strike1.Value * trade.SpotPrice.Value : trade.trade_risky_option.Strike1.Value, trade.IsMoneynessOptionData ? trade.trade_risky_option.Strike2.Value * trade.SpotPrice.Value : trade.trade_risky_option.Strike2.Value, trade.IsMoneynessOptionData ? trade.trade_risky_option.Strike3.Value * trade.SpotPrice.Value : trade.trade_risky_option.Strike3.Value, }; default: return null; } } /// /// 将trade对象转换为Qdp对应的期权类型 /// private OptionBase ToQdpOption(trade trade, underlying_manager underlying) { underlying = underlying.Clone(); underlying.UnderlyingInstrumentType = ConsGlobal.InstrumentType.ConvertCalcType(underlying.UnderlyingInstrumentType,true); switch (trade.TradeType) { case "香草期权": return QdpTradeBuilder.GetVanillaOptionTrade(trade, null, false)?.Instrument as VanillaOption; case "场内期权": return QdpTradeBuilder.GetVanillaOptionTrade(trade, null, true)?.Instrument as VanillaOption; case "障碍期权": return QdpTradeBuilder.GetBarrierOptionTrade(trade, trade.trade_barrier_option, null)?.Instrument as BarrierOption; case "亚式期权": return QdpTradeBuilder.GetAsianOptionTrade(trade, trade.trade_asian_option, null)?.Instrument as AsianOption; case "二元期权": return QdpTradeBuilder.GetBinaryOptionTrade(trade, trade.trade_binary_option, null)?.Instrument as BinaryOption; case "彩虹期权": return QdpTradeBuilder.GetRainbowOptionTrade(trade, trade.trade_rainbow_option, null)?.Instrument as RainbowOption; case "价差期权": return QdpTradeBuilder.GetSpreadOptionTrade(trade, trade.trade_spread_option, null, null)?.Instrument as SpreadOption; case "合成价差期权": return QdpTradeBuilder.GetSSpreadOptionTrade(trade)?.Instrument as SyntheticNormalSpreadOption; case "亚式合成价差期权": return QdpTradeBuilder.GetAsianSSpreadOptionTrade(trade, trade.trade_asian_option, null)?.Instrument as AsianSyntheticNormalSpreadOption; case "双鲨期权": return QdpTradeBuilder.GetDoubleSharkFinOptionTrade(trade, trade.trade_double_sharkfin_option, null)?.Instrument as DoubleSharkFinOption; case "凤凰期权": return QdpTradeBuilder.GetAutocallOptionTrade(trade, trade.trade_autocall)?.Instrument as AutoCall; case "雪球期权": return QdpTradeBuilder.GetSnowballOptionTrade(trade, trade.trade_snowball)?.Instrument as SimpleSnowball; case "区间累积期权": return QdpTradeBuilder.GetRangeAccrualTrade(trade, trade.trade_rangeaccrual, null)?.Instrument as RangeAccrual; case "累积期权": case "累计期权": return QdpTradeBuilder.GetAccumulatorOptionTrade(trade, trade.trade_accumulator_option, null)?.Instrument as AccumulatorOption; case "气囊结构": return QdpTradeBuilder.GetAirbagOptionTrade(trade, trade.trade_airbag, null)?.Instrument as Airbag; case "收益增强结构": return QdpTradeBuilder.GetUnderlyingEnhanceTrade(trade, trade.trade_underlying_enhance, null)?.Instrument as UnderlyingPayoffEnhance; case "Risky期权": return QdpTradeBuilder.GetVanillaOptionTrade(trade, null, false)?.Instrument as VanillaOption; default: return null; } } public List GetToQdpOptionRisk(trade trade, underlying_manager underlying) { var options = new List(); var tradeclone = trade.Clone(); tradeclone.TradeAmount = tradeclone.TradeAmount = TradeCalcHelper.GetTradeAmountV(trade, trade.TradeAmount, 1); tradeclone.Notional = tradeclone.Notional = TradeCalcHelper.GetTradeAmountV(trade, trade.Notional, underlying.CountRatio); var td1 = tradeclone.Clone(); if (trade.trade_risky_option.ParticipationRate1 != 0) { td1.Strike = trade.trade_risky_option.Strike1; td1.ParticipationRate = trade.trade_risky_option.ParticipationRate1; td1.TradeAmount = TradeCalcHelper.GetTradeAmount(td1, td1.TradeAmount, 1); td1.Notional = TradeCalcHelper.GetTradeAmount(td1, td1.Notional, underlying.CountRatio); td1.OptionType = "看跌"; td1.BuySell = trade.BuySell == "买入" ? "卖出" : "买入"; var option1 = ToQdpOption(td1, underlying); if (option1 != null) { options.Add(option1); } } var td2 = tradeclone.Clone(); if (trade.trade_risky_option.ParticipationRate2 != 0) { td2.Strike = trade.trade_risky_option.Strike2; td2.ParticipationRate = trade.trade_risky_option.ParticipationRate2; td2.TradeAmount = TradeCalcHelper.GetTradeAmount(td2, td2.TradeAmount, 1); td2.Notional = TradeCalcHelper.GetTradeAmount(td2, td2.Notional, underlying.CountRatio); var option2 = ToQdpOption(td2, underlying); if (option2 != null) { options.Add(option2); } } var td3 = tradeclone.Clone(); //decimal 为了解决精度问题: 0.2-0.3=0.0999999999 var participationRate3 = (decimal)trade.trade_risky_option.ParticipationRate3 - (decimal)trade.trade_risky_option.ParticipationRate2; if (participationRate3 != 0) { td3.Strike = trade.trade_risky_option.Strike3; td3.ParticipationRate = (double?)Math.Abs(participationRate3); td3.TradeAmount = TradeCalcHelper.GetTradeAmount(td3, td3.TradeAmount, 1); td3.Notional = TradeCalcHelper.GetTradeAmount(td3, td3.Notional, underlying.CountRatio); if (participationRate3 < 0) { td3.BuySell = trade.BuySell == "买入" ? "卖出" : "买入"; } var option3 = ToQdpOption(td3, underlying); if (option3 != null) { options.Add(option3); } } return options; } } public class CurvePoint { public double X { get; set; } public double Y { get; set; } } }