using Qdp.ComputeService.Data.CommonModels.ValuationParams.Equity; using Qdp.ComputeService.Data.CommonModels.ValuationParams.FixedIncome; using Qdp.ComputeServiceV2.Data.CommonModels.TradeInfos.Options; using Qdp.ComputeServiceV2.Data.CommonModels.TradeInfos.Rates; using Qdp.Foundation.Implementations; using Qdp.Pricing.Base.Enums; using Qdp.Pricing.Base.Implementations; using Qdp.Pricing.Base.Utilities; using Qdp.Pricing.Library.Common.Products.Rates; 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 Qdp.Pricing.Library.Options.Utilities; using System.Runtime.InteropServices; using YLErp.BLL; using YLErp.BLL.Calculation; using YLErp.Modules.CalculationModule.Abstract; using YLErp.Modules.TradeModule.Structure_dz; using YLErp.Modules.VolatilityModule; using YLErp.QdpModule; using YLErp.ThirdParty.CaculatePrice.DongZheng.Dto; namespace YLErp.Modules.CalculationModule { /// /// 构建QDP交易 /// public class QdpTradeBuilder { private static IYcLogger log = LogFactory.GetLogger(nameof(QdpTradeBuilder)); #region ----香草期权---- /// /// 香草期权 /// public static VanillaOptionTrade GetVanillaOptionTrade(VanillaOptionTradeParam param) { CheckOptionTradeParam(param); var exercise = QdpConverter.ConvertExerciseType(param.exerciseType); var optionStartDate = param.startDate; var underlyingMaturityDate = param.endDate; var temp_exerciseDate = param.exerciseDate; if (temp_exerciseDate < optionStartDate) { return null; } Date[] exerciseDates, observationDates; if (exercise == OptionExercise.American) { exerciseDates = CalendarImpl.Get("chn").BizDaysBetweenDatesInclEndDay(optionStartDate, temp_exerciseDate).ToArray(); observationDates = CalendarImpl.Get("chn").BizDaysBetweenDatesExcluStartDay(optionStartDate, temp_exerciseDate).ToArray(); } else { exerciseDates = new Date[] { temp_exerciseDate }; observationDates = new Date[] { temp_exerciseDate }; } var optionDayCount = CalculatorHelper.GetTradeDayCount(); var pricingTOverride = QdpCalendarHelper.CalculateTFromDays(param.timeToMaturityDays, optionDayCount, optionStartDate.DateTime); var vanillaOption = new VanillaOption( startDate: optionStartDate, maturityDate: underlyingMaturityDate, exercise: exercise, optionType: param.optionType, strike: param.strike, underlyingInstrumentType: (InstrumentType)Enum.Parse(typeof(InstrumentType), param.underlyingInstrumentType), calendar: CalendarImpl.Get("chn"), dayCount: optionDayCount.ToDayCountImpl(), payoffCcy: CurrencyCode.CNY, settlementCcy: CurrencyCode.CNY, exerciseDates: exerciseDates, observationDates: observationDates, notional: param.notional, settlementGap: null, optionPremiumPaymentDate: null, optionPremium: 0.0, isMoneynessOption: param.isMoneynessOption, initialSpotPrice: param.initialSpotPrice, dividends: param.dividends, hasNightMarket: param.hasNightMarket, commodityFuturesPreciseTimeMode: param.preciseTimeMode, pricingToverride: pricingTOverride, riskFreeRateOverride: param.riskFreeRate, dividendRateOverride: param.dividendRate, participationRate: param.participationRate, isAnnualized: param.isAnnualized, annualizedFactor: param.annualizedFactor) { UnderlyingTickers = param.underlyingTickers }; if (string.IsNullOrWhiteSpace(param.tradeId)) { param.tradeId = Guid.NewGuid().ToString(); } return new VanillaOptionTrade( tradeId: param.tradeId, tradeDate: param.tradeDate, startDate: vanillaOption.StartDate, maturityDate: vanillaOption.ExerciseDates.Last(), tradeType: QdpConverter.ConvertTradeType(param.buysell), notional: param.notional, price: 0.0, option: vanillaOption) { ValuationParameters = new OptionValuationParameters(MarketProxy.RiskFreeDiscountCurve, MarketProxy.ConstantZeroCurve, param.volSurfaceNames?.FirstOrDefault(), vanillaOption.UnderlyingTickers[0]), }; } /// /// 香草期权 /// public static VanillaOptionTrade GetVanillaOptionTrade(OtcTradeBase td, OptionTradeParamRequest request, bool isExchangeOption) { var param = GetVanillaOptionTradeParam(td, request, isExchangeOption); return GetVanillaOptionTrade(param); } /// /// 香草期权 /// /// 是否场内期权 public static VanillaOptionTradeParam GetVanillaOptionTradeParam(OtcTradeBase td, OptionTradeParamRequest request, bool isExchangeOption) { var result = BuildTradeParamBase(td, request); if (isExchangeOption) { result.buysell = "Buy"; result.isMoneynessOption = false; } else if (td.Comments?.Trim() == "YQ") { result.riskFreeRate = 0; result.isForwardTrade = true; } return result; } #endregion #region ----合成价差期权---- /// /// 合成价差期权 /// public static SyntheticNormalSpreadOptionTrade GetSSpreadOptionTrade(SSpreadOptionTradeParam param) { CheckOptionTradeParam(param); if (param.underlyingInstrumentType == null) { throw new Exception("标的资产类型不能为空"); } var exercise = QdpConverter.ConvertExerciseType(param.exerciseType); var optionStartDate = param.startDate; var temp_exerciseDate = param.exerciseDate; if (temp_exerciseDate < optionStartDate) { return null; } Date[] exerciseDates, observationDates; if (exercise == OptionExercise.American) { exerciseDates = CalendarImpl.Get("chn").BizDaysBetweenDatesInclEndDay(optionStartDate, temp_exerciseDate).ToArray(); observationDates = CalendarImpl.Get("chn").BizDaysBetweenDatesExcluStartDay(optionStartDate, temp_exerciseDate).ToArray(); } else { exerciseDates = new Date[] { temp_exerciseDate }; observationDates = new Date[] { temp_exerciseDate }; } var optionDayCount = CalculatorHelper.GetTradeDayCount(); var pricingTOverride = QdpCalendarHelper.CalculateTFromDays(param.timeToMaturityDays, optionDayCount, optionStartDate.DateTime); var syntheticNormalSpreadOption = new SyntheticNormalSpreadOption( startDate: optionStartDate, exercise: exercise, optionType: param.optionType, strike: param.strike, underlyingInstrumentType: (InstrumentType)Enum.Parse(typeof(InstrumentType), param.underlyingInstrumentType), calendar: CalendarImpl.Get("chn"), dayCount: optionDayCount.ToDayCountImpl(), payoffCcy: CurrencyCode.CNY, settlementCcy: CurrencyCode.CNY, exerciseDates: exerciseDates, observationDates: observationDates, syntheticCoefficients: param.coefficients, notional: param.notional, settlementGap: null, optionPremiumPaymentDate: null, optionPremium: 0.0, isMoneynessOption: param.isMoneynessOption, initialSpotPrice: param.initialSpotPrice, dividends: param.dividends, hasNightMarket: param.hasNightMarket, commodityFuturesPreciseTimeMode: param.preciseTimeMode, pricingToverride: pricingTOverride, riskFreeRateOverride: param.riskFreeRate, dividendRateOverride: param.dividendRate, participationRate: param.participationRate, isAnnualized: param.isAnnualized, annualizedFactor: param.annualizedFactor) { UnderlyingTickers = param.underlyingTickers }; if (string.IsNullOrWhiteSpace(param.tradeId)) { param.tradeId = Guid.NewGuid().ToString(); } return new SyntheticNormalSpreadOptionTrade( tradeId: param.tradeId, tradeDate: param.tradeDate, startDate: syntheticNormalSpreadOption.StartDate, maturityDate: syntheticNormalSpreadOption.ExerciseDates.Last(), tradeType: QdpConverter.ConvertTradeType(param.buysell), notional: param.notional, price: 0.0, option: syntheticNormalSpreadOption) { ValuationParameters = new OptionValuationParameters(MarketProxy.RiskFreeDiscountCurve, MarketProxy.ConstantZeroCurve, param.volSurfaceNames?.FirstOrDefault(), syntheticNormalSpreadOption.UnderlyingTickers[0]), }; } /// /// 合成价差期权 /// public static SyntheticNormalSpreadOptionTrade GetSSpreadOptionTrade(OtcTradeBase td, OptionTradeParamRequest request = null) { var param = GetSSpreadOptionTradeParam(td, request); return GetSSpreadOptionTrade(param); } /// /// 合成价差期权 /// public static SSpreadOptionTradeParam GetSSpreadOptionTradeParam(OtcTradeBase td, OptionTradeParamRequest request, double[] coefficients = null) { var result = BuildTradeParamBase(td, request); if (td.Comments?.Trim() == "YQ") { result.riskFreeRate = 0; result.isForwardTrade = true; } result.coefficients = coefficients; return result; } #endregion #region ----亚式期权---- /// /// 亚式期权 /// public static AsianOptionTrade GetAsianOptionTrade(AsianOptionTradeParam param) { CheckOptionTradeParam(param); var exercise = QdpConverter.ConvertExerciseType(param.exerciseType); var optionStartDate = param.startDate; var underlyingMaturityDate = param.endDate; var temp_exerciseDate = param.exerciseDate; Date[] exerciseDates, observationDates; var calendar = CalendarImpl.Get("chn"); if (exercise == OptionExercise.American) { exerciseDates = calendar.BizDaysBetweenDatesInclEndDay(optionStartDate, temp_exerciseDate).ToArray(); } else { exerciseDates = new Date[] { temp_exerciseDate }; } if (!string.IsNullOrWhiteSpace(param.observationDateStr)) { observationDates = QdpHelper.ParseObservationDate(param.observationDateStr); } else { var observationStartDate = param.averagingPeriodStartDate ?? optionStartDate; var observationEndDate = QdpCalendarHelper.GetNonHolidayDefore(param.originalExerciseDate); if (PS.Config.ErpElement.AsianOptionObDatesIncludeStartDate) { observationDates = calendar.BizDaysBetweenDatesInclEndDay(observationStartDate, observationEndDate).ToArray(); } else { observationDates = calendar.BizDaysBetweenDatesExcluStartDay(observationStartDate, observationEndDate).ToArray(); } } var asianType = (AsianType)Enum.Parse(typeof(AsianType), param.payoffType); if (observationDates.Length <= 1 && asianType != AsianType.EnhancedArithmeticAverage) { throw new ServiceException("均价起算日不合理"); } var fixingValues = QdpHelper.ParseFixingsFromString(param.fixings); var optionDayCount = CalculatorHelper.GetTradeDayCount(); var pricingTOverride = QdpCalendarHelper.CalculateTFromDays(param.timeToMaturityDays, optionDayCount, optionStartDate.DateTime); var asianOption = new AsianOption( startDate: optionStartDate, maturityDate: underlyingMaturityDate, exercise: (OptionExercise)Enum.Parse(typeof(OptionExercise), param.exerciseType), optionType: param.optionType, asianType: asianType, strikeStyle: (StrikeStyle)Enum.Parse(typeof(StrikeStyle), param.strikeStyle), strike: param.strike, underlyingInstrumentType: (InstrumentType)Enum.Parse(typeof(InstrumentType), param.underlyingInstrumentType), calendar: CalendarImpl.Get("chn"), dayCount: optionDayCount.ToDayCountImpl(), payoffCcy: CurrencyCode.CNY, settlementCcy: CurrencyCode.CNY, exerciseDates: exerciseDates, observationDates: observationDates, fixings: fixingValues, strikeGearingFator: param.strikeGearingFactor, participationRate: param.participationRate, notional: param.notional, settlementGap: null, optionPremiumPaymentDate: null, optionPremium: 0, isMoneynessOption: param.isMoneynessOption, initialSpotPrice: param.initialSpotPrice, dividends: param.dividends, hasNightMarket: param.hasNightMarket, commodityFuturesPreciseTimeMode: param.preciseTimeMode, pricingToverride: pricingTOverride, riskFreeRateOverride: param.riskFreeRate, dividendRateOverride: param.dividendRate, isAnnualized: param.isAnnualized, annualizedFactor: param.annualizedFactor, enhancedPrice: param.enhancedPrice) { SettleMode = param.settleMode == "AtEnd" ? SettleMode.AtEnd : SettleMode.AtHit, UnderlyingTickers = param.underlyingTickers }; if (string.IsNullOrWhiteSpace(param.tradeId)) { param.tradeId = Guid.NewGuid().ToString(); } return new AsianOptionTrade( tradeId: param.tradeId, tradeDate: param.tradeDate, startDate: asianOption.StartDate, maturityDate: asianOption.ExerciseDates.Last(), tradeType: QdpConverter.ConvertTradeType(param.buysell), notional: param.notional, price: 0.0, option: asianOption) { ValuationParameters = new OptionValuationParameters(MarketProxy.RiskFreeDiscountCurve, MarketProxy.ConstantZeroCurve, param.volSurfaceNames?.FirstOrDefault(), asianOption.UnderlyingTickers[0]), }; } /// /// 亚式期权 /// public static AsianOptionTrade GetAsianOptionTrade(OtcTradeBase td, trade_asian_option asianOption, OptionTradeParamRequest request = null) { var param = GetAsianOptionTradeParam(td, asianOption, request); return GetAsianOptionTrade(param); } /// /// 亚式合成价差期权 /// public static AsianSyntheticNormalSpreadOptionTrade GetAsianSSpreadOptionTrade(AsianOptionTradeParam param) { CheckOptionTradeParam(param); var exercise = QdpConverter.ConvertExerciseType(param.exerciseType); var optionStartDate = param.startDate; var temp_exerciseDate = param.exerciseDate; if (temp_exerciseDate < optionStartDate) { return null; } Date[] exerciseDates, observationDates; var calendar = CalendarImpl.Get("chn"); if (exercise == OptionExercise.American) { exerciseDates = calendar.BizDaysBetweenDatesInclEndDay(optionStartDate, temp_exerciseDate).ToArray(); } else { exerciseDates = new Date[] { temp_exerciseDate }; } if (!string.IsNullOrWhiteSpace(param.observationDateStr)) { observationDates = QdpHelper.ParseObservationDate(param.observationDateStr); } else { var observationStartDate = param.averagingPeriodStartDate ?? optionStartDate; observationDates = calendar.BizDaysBetweenDatesExcluStartDay(observationStartDate, temp_exerciseDate).Union(new[] { temp_exerciseDate }).ToArray(); } var fixingValues = QdpHelper.ParseFixingsFromString(param.fixings); var optionDayCount = CalculatorHelper.GetTradeDayCount(); var pricingTOverride = QdpCalendarHelper.CalculateTFromDays(param.timeToMaturityDays, optionDayCount, optionStartDate.DateTime); var asianOption = new AsianSyntheticNormalSpreadOption( startDate: optionStartDate, exercise: (OptionExercise)Enum.Parse(typeof(OptionExercise), param.exerciseType), optionType: param.optionType, asianType: (AsianType)Enum.Parse(typeof(AsianType), param.payoffType), strikeStyle: (StrikeStyle)Enum.Parse(typeof(StrikeStyle), param.strikeStyle), strike: param.strike, underlyingInstrumentType: (InstrumentType)Enum.Parse(typeof(InstrumentType), param.underlyingInstrumentType), calendar: CalendarImpl.Get("chn"), dayCount: optionDayCount.ToDayCountImpl(), payoffCcy: CurrencyCode.CNY, settlementCcy: CurrencyCode.CNY, exerciseDates: exerciseDates, observationDates: observationDates, fixings: fixingValues, strikeGearingFactor: 1.0, participationRate: param.participationRate, notional: param.notional, settlementGap: null, optionPremiumPaymentDate: null, optionPremium: 0, isMoneynessOption: param.isMoneynessOption, initialSpotPrice: param.initialSpotPrice, dividends: param.dividends, hasNightMarket: param.hasNightMarket, commodityFuturesPreciseTimeMode: param.preciseTimeMode, pricingToverride: pricingTOverride, riskFreeRateOverride: param.riskFreeRate, dividendRateOverride: param.dividendRate, isAnnualized: param.isAnnualized, annualizedFactor: param.annualizedFactor) { SettleMode = param.settleMode == "AtEnd" ? SettleMode.AtEnd : SettleMode.AtHit, UnderlyingTickers = param.underlyingTickers }; if (string.IsNullOrWhiteSpace(param.tradeId)) { param.tradeId = Guid.NewGuid().ToString(); } return new AsianSyntheticNormalSpreadOptionTrade( tradeId: param.tradeId, tradeDate: param.tradeDate, startDate: asianOption.StartDate, maturityDate: asianOption.ExerciseDates.Last(), tradeType: QdpConverter.ConvertTradeType(param.buysell), notional: param.notional, price: 0.0, option: asianOption) { ValuationParameters = new OptionValuationParameters(MarketProxy.RiskFreeDiscountCurve, MarketProxy.ConstantZeroCurve, param.volSurfaceNames?.FirstOrDefault(), asianOption.UnderlyingTickers[0]) }; } /// /// 亚式合成价差期权 /// public static AsianSyntheticNormalSpreadOptionTrade GetAsianSSpreadOptionTrade(OtcTradeBase td, trade_asian_option asianOption, OptionTradeParamRequest request = null) { var param = GetAsianOptionTradeParam(td, asianOption, request); return GetAsianSSpreadOptionTrade(param); } /// /// 亚式期权 /// /// 标的现价 public static AsianOptionTradeParam GetAsianOptionTradeParam(OtcTradeBase td, trade_asian_option asianOption, OptionTradeParamRequest request) { if (asianOption == null) { throw new TradeCalcExpception(td.id, "未获取到对应的亚式期权交易数据"); } var result = BuildTradeParamBase(td, request); result.payoffType = asianOption.PayoffType; result.settleMode = asianOption.SettleMode; result.strikeStyle = asianOption.StrikeType; result.strikeGearingFactor = asianOption.StrikeGearingFactor ?? 1; result.fixings = request?.fixings; //在计算的时候判断 //result.fixings = TradeCalcHelper.CheckAsiaFixings(td,asianOption,request.fixings,spotPrice); result.averagingPeriodStartDate = asianOption.AveragingPeriodStartDate ?? td.TradeDate ?? DateTime.MaxValue; result.observationDateStr = asianOption.ObservationDates; result.enhancedPrice = asianOption.EnhancedPrice; return result; } #endregion #region ----二元期权---- /// /// 二元期权 /// public static BinaryOptionTrade GetBinaryOptionTrade(BinaryOptionTradeParam param) { CheckOptionTradeParam(param); var exercise = QdpConverter.ConvertExerciseType(param.exerciseType); var optionStartDate = param.startDate; var underlyingMaturityDate = param.endDate; var rebateType = (string.IsNullOrEmpty(param.binaryRebateType) ? "AtEnd" : param.binaryRebateType).ToBinaryRebateType(); var temp_exerciseDate = param.exerciseDate; if (temp_exerciseDate < optionStartDate) { return null; } Date[] exerciseDates, observationDates; exerciseDates = new Date[] { temp_exerciseDate }; observationDates = new Date[] { temp_exerciseDate }; var barrierObservationDates = QdpHelper.ParseObservationDate(param.observationDateStr); if (barrierObservationDates != null) { observationDates = barrierObservationDates; } var optionDayCount = CalculatorHelper.GetTradeDayCount(); var pricingTOverride = QdpCalendarHelper.CalculateTFromDays(param.timeToMaturityDays, optionDayCount, optionStartDate.DateTime); var binaryOption = new BinaryOption( startDate: optionStartDate, maturityDate: underlyingMaturityDate, exercise: exercise, optionType: param.optionType, strike: param.strike, underlyingProductType: (InstrumentType)Enum.Parse(typeof(InstrumentType), param.underlyingInstrumentType), binaryOptionPayoffType: (BinaryOptionPayoffType)Enum.Parse(typeof(BinaryOptionPayoffType), param.payoffType), cashOrNothingAmount: param.cashOrNothingAmount, calendar: CalendarImpl.Get("chn"), dayCount: optionDayCount.ToDayCountImpl(), payoffCcy: CurrencyCode.CNY, settlementCcy: CurrencyCode.CNY, exerciseDates: exerciseDates, observationDates: observationDates, notional: param.notional, settlementGap: null, optionPremiumPaymentDate: null, optionPremium: 0.0, isMoneynessOption: param.isMoneynessOption, initialSpotPrice: param.initialSpotPrice, upperBarrier: param.upperBarrier, cashOrNothingAmountHigh: param.cashOrNothingAmountHigh, isDiscreteMonitored: param.isDiscreteMonitored, rebateAnnualizedAtKO: param.rebateAnnualizedAtKO, rebateDayCount: string.IsNullOrWhiteSpace(param.rebateDayCount) ? new Act365() : param.rebateDayCount.ToDayCountImpl(), dividends: param.dividends, binaryRebateType: rebateType, pricingToverride: pricingTOverride, hasNightMarket: param.hasNightMarket, commodityFuturesPreciseTimeMode: param.preciseTimeMode, riskFreeRateOverride: param.riskFreeRate, dividendRateOverride: param.dividendRate, participationRate: param.participationRate, isAnnualized: param.isAnnualized, annualizedFactor: param.annualizedFactor) { UnderlyingTickers = param.underlyingTickers }; if (string.IsNullOrWhiteSpace(param.tradeId)) { param.tradeId = Guid.NewGuid().ToString(); } return new BinaryOptionTrade( tradeId: param.tradeId, tradeDate: param.tradeDate, startDate: binaryOption.StartDate, maturityDate: binaryOption.ExerciseDates.Last(), tradeType: QdpConverter.ConvertTradeType(param.buysell), notional: param.notional, price: 0.0, option: binaryOption) { ValuationParameters = new OptionValuationParameters(MarketProxy.RiskFreeDiscountCurve, MarketProxy.ConstantZeroCurve, param.volSurfaceNames?.FirstOrDefault(), binaryOption.UnderlyingTickers[0]), }; } /// /// 二元期权 /// public static BinaryOptionTrade GetBinaryOptionTrade(OtcTradeBase td, trade_binary_option binaryOption, OptionTradeParamRequest request = null) { var param = GetBinaryOptionTradeParam(td, binaryOption, request); return GetBinaryOptionTrade(param); } /// /// 二元期权 /// public static BinaryOptionTradeParam GetBinaryOptionTradeParam(OtcTradeBase td, trade_binary_option binaryOption, OptionTradeParamRequest request) { if (binaryOption == null) { throw new TradeCalcExpception(td.id, "未获取到对应的二元期权交易数据"); } var result = BuildTradeParamBase(td, request); result.payoffType = binaryOption.PayoffType; result.upperBarrier = binaryOption.UpperBarrier ?? double.NaN; if (td.IsUsePremiumRate == true) { result.cashOrNothingAmount = (binaryOption.CashOrNothingAmountRate ?? 0) * (td.SpotPrice ?? 0); result.cashOrNothingAmountHigh = (binaryOption.CashOrNothingAmountHighRate ?? 0) * (td.SpotPrice ?? 0); } else { result.cashOrNothingAmount = binaryOption.CashOrNothingAmount ?? 0; result.cashOrNothingAmountHigh = binaryOption.CashOrNothingAmountHigh ?? 0; } result.binaryRebateType = binaryOption.RebateType; result.isDiscreteMonitored = binaryOption.IsDiscreteMonitored; result.binaryOptionReplicationStrategy = valuedateBLL.BinaryReplicationStrategy; result.replicationShiftSize = binaryOption.Offset ?? (td.Strike ?? 0) * 0.01; result.observationDateStr = binaryOption.ObservationDates; result.rebateDayCount = binaryOption.RebateDayCount; result.rebateAnnualizedAtKO = binaryOption.RebateAnnualizedAtKO; return result; } #endregion #region ----障碍期权---- /// /// 障碍期权 /// public static BarrierOptionTrade GetBarrierOptionTrade(BarrierOptionTradeParam param, string valueDateStr = null) { CheckOptionTradeParam(param); var exercise = QdpConverter.ConvertExerciseType(param.exerciseType); //这里不容易理解:为了实现在定价界面的“交易日为过去日期和未来日期的交易,应把交易日当天完整计算在内”的逻辑, //交易日期(通常被往前推了一天)有可能小于开始日,这种情况下应该把开始日设置成和估值日一样,否则从交易日到第一个观察日的时间长度就不准确了 var optionStartDate = param.startDate; if (valueDateStr != null) { var tradeDate = new Date(DateTime.Parse(valueDateStr)); if (tradeDate < param.startDate) { optionStartDate = tradeDate; } } var underlyingMaturityDate = param.endDate; var temp_exerciseDate = param.exerciseDate; if (temp_exerciseDate < optionStartDate) { return null; } Date[] exerciseDates, observationDates; if (exercise == OptionExercise.American) { exerciseDates = CalendarImpl.Get("chn").BizDaysBetweenDatesInclEndDay(optionStartDate, temp_exerciseDate).ToArray(); observationDates = CalendarImpl.Get("chn").BizDaysBetweenDatesExcluStartDay(optionStartDate, temp_exerciseDate).ToArray(); } else { exerciseDates = new Date[] { temp_exerciseDate }; observationDates = CalendarImpl.Get("chn").BizDaysBetweenDatesExcluStartDay(optionStartDate, temp_exerciseDate).Union(new[] { temp_exerciseDate }).ToArray(); } var barrierObservationDates = QdpHelper.ParseObservationDate(param.observationDateStr); if (barrierObservationDates != null) { observationDates = barrierObservationDates; } var optionDayCount = CalculatorHelper.GetTradeDayCount(); var pricingTOverride = QdpCalendarHelper.CalculateTFromDays(param.timeToMaturityDays, optionDayCount, optionStartDate.DateTime); var barrierOption = new BarrierOption( startDate: optionStartDate, maturityDate: underlyingMaturityDate, exercise: exercise, optionType: param.optionType, strike: param.strike, rebate: param.rebate, coupon: 0.0, participationRate: param.participationRate, barrierType: (BarrierType)Enum.Parse(typeof(BarrierType), param.barrierType), lowerBarrier: param.barrierPrice, upperBarrier: param.upperBarrierPrice, isDiscreteMonitored: param.isDiscrete, underlyingType: (InstrumentType)Enum.Parse(typeof(InstrumentType), param.underlyingInstrumentType), calendar: CalendarImpl.Get("chn"), dayCount: optionDayCount.ToDayCountImpl(), payoffCcy: CurrencyCode.CNY, settlementCcy: CurrencyCode.CNY, exerciseDates: exerciseDates, observationDates: observationDates, barrierObservationDates: barrierObservationDates, highRebate: param.rebateHigh, notional: param.notional, settlementGap: null, optionPremiumPaymentDate: null, optionPremium: 0.0, isMoneynessOption: param.isMoneynessOption, initialSpotPrice: param.initialSpotPrice, dividends: param.dividends, position: QdpConverter.ConvertPosition(param.buysell), barrierShift: param.barrierShift, rebateAnnualizedAtKO: param.rebateAnnualizedAtKO, rebateDayCount: string.IsNullOrWhiteSpace(param.rebateDayCount) ? new Act365() : param.rebateDayCount.ToDayCountImpl(), pricingToverride: pricingTOverride, rebateType: (string.IsNullOrEmpty(param.rebateType) ? "AtEnd" : param.rebateType).ToBinaryRebateType(), barrierStatus: QdpConverter.ConvertBarrierStatus(param.barrierStatus), hasNightMarket: param.hasNightMarket, commodityFuturesPreciseTimeMode: param.preciseTimeMode, riskFreeRateOverride: param.riskFreeRate, dividendRateOverride: param.dividendRate, isAnnualized: param.isAnnualized, annualizedFactor: param.annualizedFactor) { UnderlyingTickers = param.underlyingTickers }; if (string.IsNullOrWhiteSpace(param.tradeId)) { param.tradeId = Guid.NewGuid().ToString(); } return new BarrierOptionTrade( tradeId: param.tradeId, tradeDate: param.tradeDate, startDate: barrierOption.StartDate, maturityDate: barrierOption.ExerciseDates.Last(), tradeType: QdpConverter.ConvertTradeType(param.buysell), notional: param.notional, price: 0.0, option: barrierOption) { ValuationParameters = new OptionValuationParameters(MarketProxy.RiskFreeDiscountCurve, MarketProxy.ConstantZeroCurve, param.volSurfaceNames?.FirstOrDefault(), barrierOption.UnderlyingTickers[0]), }; } /// /// 障碍期权 /// public static BarrierOptionTrade GetBarrierOptionTrade(OtcTradeBase td, trade_barrier_option barrierOption, OptionTradeParamRequest request = null) { var param = GetBarrierOptionTradeParam(td, barrierOption, request); return GetBarrierOptionTrade(param); } /// /// 障碍期权 /// public static BarrierOptionTradeParam GetBarrierOptionTradeParam(OtcTradeBase td, trade_barrier_option barrierOption, OptionTradeParamRequest request) { if (barrierOption == null) { throw new TradeCalcExpception(td.id, "未获取到对应的障碍期权交易数据"); } var result = BuildTradeParamBase(td, request); result.barrierType = barrierOption.BarrierTypeEn; result.barrierPrice = barrierOption.BarrierPrice ?? 0; result.upperBarrierPrice = barrierOption.UpperBarrierPrice ?? 0; result.isDiscrete = barrierOption.IsDiscrete; result.observationDateStr = barrierOption.ObservationDates; if (td.IsUsePremiumRate == true) { result.rebate = (barrierOption.RebateRate ?? 0) * (td.SpotPrice ?? 0); } else { result.rebate = barrierOption.Rebate ?? 0; } if (td.IsUsePremiumRate == true) { result.rebateHigh = (barrierOption.RebateHighRate ?? double.NaN) * (td.SpotPrice ?? 0); } else { result.rebateHigh = barrierOption.RebateHigh ?? double.NaN; } result.rebateType = barrierOption.RebateType; result.barrierShift = barrierOption.BarrierShift ?? 0.0; result.barrierStatus = barrierOption.KnockInOutStatus; result.rebateAnnualizedAtKO = barrierOption.RebateAnnualizedAtKO; result.rebateDayCount = barrierOption.RebateDayCount; //if (result.rebateAnnualizedAtKO) //{ // var dayCount = string.IsNullOrWhiteSpace(result.rebateDayCount) ? new Act365() : result.rebateDayCount.ToDayCountImpl(); // result.rebate /= dayCount.CalcDayCountFraction(result.startDate, result.endDate); //} return result; } #endregion #region ----双鲨期权---- /// /// 双鲨期权 /// public static DoubleSharkFinOptionTrade GetDoubleSharkFinOptionTrade(DoubleSharkFinOptionTradeParam param) { CheckOptionTradeParam(param); var exercise = QdpConverter.ConvertExerciseType(param.exerciseType); var optionStartDate = param.startDate; var underlyingMaturityDate = param.endDate; var temp_exerciseDate = param.exerciseDate; if (temp_exerciseDate < optionStartDate) { return null; } Date[] exerciseDates; var observationDates = QdpHelper.ParseObservationDate(param.observationDateStr); if (exercise == OptionExercise.American) { exerciseDates = CalendarImpl.Get("chn").BizDaysBetweenDatesInclEndDay(optionStartDate, temp_exerciseDate).ToArray(); if (observationDates == null) { observationDates = CalendarImpl.Get("chn").BizDaysBetweenDatesExcluStartDay(optionStartDate, temp_exerciseDate).ToArray(); } } else { exerciseDates = new Date[] { temp_exerciseDate }; if (observationDates == null) { observationDates = CalendarImpl.Get("chn").BizDaysBetweenDatesExcluStartDay(optionStartDate, temp_exerciseDate).Union(new[] { temp_exerciseDate }).ToArray(); } } var optionDayCount = CalculatorHelper.GetTradeDayCount(); var pricingTOverride = QdpCalendarHelper.CalculateTFromDays(param.timeToMaturityDays, optionDayCount, optionStartDate.DateTime); var lowRebateValue = double.IsNaN(param.rebate) ? 0.0 : param.rebate; var highReateValue = double.IsNaN(param.rebateHigh) ? 0.0 : param.rebateHigh; var doubleSharkFinOption = new DoubleSharkFinOption( strikeHigh: param.strikeHigh, strikeLow: param.strikeLow, barrierHigh: Enumerable.Repeat(param.barrierHigh, observationDates.Length).ToArray(), barrierLow: Enumerable.Repeat(param.barrierLow, observationDates.Length).ToArray(), rebateLow: Enumerable.Repeat(lowRebateValue, observationDates.Length).ToArray(), rebateHigh: Enumerable.Repeat(highReateValue, observationDates.Length).ToArray(), rebateType: (string.IsNullOrEmpty(param.rebateType) ? "AtEnd" : param.rebateType).ToBinaryRebateType(), observationDates: observationDates, startDate: optionStartDate, maturityDate: underlyingMaturityDate, participationRate: param.participationRate, isDiscreteMonitored: param.isDiscrete, underlyingInstrumentType: (InstrumentType)Enum.Parse(typeof(InstrumentType), param.underlyingInstrumentType), calendar: CalendarImpl.Get("chn"), dayCount: optionDayCount.ToDayCountImpl(), payoffCcy: CurrencyCode.CNY, settlementCcy: CurrencyCode.CNY, exerciseDates: exerciseDates, callParticipationRate: param.callParticipationRate, putParticipationRate: param.putParticipationRate, isMoneynessOption: param.isMoneynessOption, initialSpotPrice: param.initialSpotPrice, notional: param.notional, settlementGap: null, optionPremiumPaymentDate: null, optionPremium: 0, pricingToverride: pricingTOverride, barrierStatus: QdpConverter.ConvertBarrierStatus(param.barrierStatus), hasNightMarket: param.hasNightMarket, commodityFuturesPreciseTimeMode: param.preciseTimeMode, riskFreeRateOverride: param.riskFreeRate, dividendRateOverride: param.dividendRate, isAnnualized: param.isAnnualized, annualizedFactor: param.annualizedFactor) { UnderlyingTickers = param.underlyingTickers }; if (string.IsNullOrWhiteSpace(param.tradeId)) { param.tradeId = Guid.NewGuid().ToString(); } return new DoubleSharkFinOptionTrade( tradeId: param.tradeId, tradeDate: param.tradeDate, startDate: doubleSharkFinOption.StartDate, maturityDate: doubleSharkFinOption.ExerciseDates.Last(), tradeType: QdpConverter.ConvertTradeType(param.buysell), notional: param.notional, price: 0.0, option: doubleSharkFinOption) { ValuationParameters = new OptionValuationParameters(MarketProxy.RiskFreeDiscountCurve, MarketProxy.ConstantZeroCurve, param.volSurfaceNames?.FirstOrDefault(), doubleSharkFinOption.UnderlyingTickers[0]), }; } /// /// 双鲨期权 /// public static DoubleSharkFinOptionTrade GetDoubleSharkFinOptionTrade(OtcTradeBase td, trade_double_sharkfin_option dbsharkOption, OptionTradeParamRequest request = null) { var param = GetDoubleSharkFinOptionTradeParam(td, dbsharkOption, request); return GetDoubleSharkFinOptionTrade(param); } /// /// 双鲨期权 /// public static DoubleSharkFinOptionTradeParam GetDoubleSharkFinOptionTradeParam(OtcTradeBase td, trade_double_sharkfin_option dbsharkOption, OptionTradeParamRequest request) { if (dbsharkOption == null) { throw new TradeCalcExpception(td.id, "未获取到对应的双鲨期权交易数据"); } var result = BuildTradeParamBase(td, request); result.strikeLow = td.Strike ?? 0.0; result.strikeHigh = dbsharkOption.StrikeHigh ?? td.Strike ?? 0.0; result.barrierHigh = dbsharkOption.BarrierHigh; result.barrierLow = dbsharkOption.BarrierLow; if (td.IsUsePremiumRate == true) { result.rebate = (dbsharkOption.RebateRate ?? 0) * (td.SpotPrice ?? 0); result.rebateHigh = (dbsharkOption.RebateHighRate ?? 0) * (td.SpotPrice ?? 0); } else { result.rebate = dbsharkOption.Rebate ?? 0; result.rebateHigh = dbsharkOption.RebateHigh ?? 0; } result.rebateType = dbsharkOption.RebateType; result.isDiscrete = dbsharkOption.IsDiscrete; result.observationDateStr = dbsharkOption.ObservationDates; result.callParticipationRate = dbsharkOption.CallParticipationRate ?? 1.0; result.putParticipationRate = dbsharkOption.PutParticipationRate ?? 1.0; result.barrierStatus = dbsharkOption.KnockInOutStatus; return result; } #endregion #region ----雪球期权---- /// /// 雪球期权 /// /// 构建参数 /// 雪球期权类型 /// public static SnowballTrade GetSnowballOptionTrade(SnowballOptionTradeParam param , SnowBallOptionMode snowballMold = SnowBallOptionMode.Auto) { CheckOptionTradeParam(param); var exercise = QdpConverter.ConvertExerciseType(param.exerciseType); var optionStartDate = param.startDate; var underlyingMaturityDate = param.endDate; var temp_exerciseDate = param.exerciseDate; if (temp_exerciseDate < optionStartDate) { return null; } var observationDates = QdpHelper.ParseObservationDate(param.observationDateStr); //敲出观察 var customizedResults = QdpHelper.ParseAutocallCustomizedInfo(param.koObservationDateStr); var koObservationDates = customizedResults.Item1; var customizedKOBarriers = customizedResults.Item2; var customizedCoupons = customizedResults.Item3; Date[] exerciseDates; if (exercise == OptionExercise.American) { exerciseDates = CalendarImpl.Get("chn").BizDaysBetweenDatesInclEndDay(optionStartDate, temp_exerciseDate).ToArray(); if (observationDates == null) { observationDates = CalendarImpl.Get("chn").BizDaysBetweenDatesExcluStartDay(optionStartDate, temp_exerciseDate).ToArray(); } } else { exerciseDates = new Date[] { temp_exerciseDate }; if (observationDates == null) { observationDates = CalendarImpl.Get("chn").BizDaysBetweenDatesExcluStartDay(optionStartDate, temp_exerciseDate).Union(new[] { temp_exerciseDate }).ToArray(); } } koObservationDates ??= QdpObservationHelper.GetDefaultKoObservationDatesForSnowbalV2(optionStartDate, temp_exerciseDate); //票息日历规则 if (string.IsNullOrWhiteSpace(param.couponDayCount)) { param.couponDayCount = "Act365"; } //票息支付日期 var couponPaymentDates = QdpHelper.ParseObservationDate(param.couponPaymentDateStr); var optionDayCount = CalculatorHelper.GetTradeDayCount(); var pricingToverride = QdpCalendarHelper.CalculateTFromDays(param.timeToMaturityDays, optionDayCount, optionStartDate.DateTime); SimpleSnowball snowball; if (snowballMold == SnowBallOptionMode.AnnualizedPremiumSnowball || snowballMold == SnowBallOptionMode.Auto && param.annualizedPremiumRate > 0) { snowball = new AnnualizedPremiumSnowball( startDate: optionStartDate, maturityDate: underlyingMaturityDate, koBarrier: param.koBarrier, kiBarrier: double.NaN, coupon: param.coupon, optionParticipationRate: 1.0, // 在OTC系统增加“期权参与率”字段之前,请保留一直传1.0 koObsDates: koObservationDates, kiObsDates: observationDates, observationDates: null, notional: param.notional, initialSpotPrice: param.initialSpotPrice, underlyingType: (InstrumentType)Enum.Parse(typeof(InstrumentType), param.underlyingInstrumentType), calendar: CalendarImpl.Get("chn"), dayCount: optionDayCount.ToDayCountImpl(), payoffCcy: CurrencyCode.CNY, settlementCcy: CurrencyCode.CNY, exerciseDates: exerciseDates, exercise: OptionExercise.European, isMoneynessOption: param.isMoneynessOption, optionType: param.optionType, barrierType: param.optionType == OptionType.Put ? BarrierType.DownAndOut : BarrierType.UpAndOut, strike: 0, spreadStrikeAtKO: param.spreadStrikeAtKO, spreadStrikeAtMaturity: param.spreadStrikeAtMaturity, fixedCoupon: param.isFixedCoupon, kORebate: param.koRebate, kOBarrierAdjustStep: param.koBarrierAdjustStep, useOptionPayoffAtKO: param.useOptionPayoffAtKO, useOptionPayoffAtMaturity: false, annualizedOptionPayoff: param.annualizedOptionPayoff, customizedKOBarriers: customizedKOBarriers, customizedCoupons: customizedCoupons, pricingToverride: pricingToverride, barrierStatus: QdpConverter.ConvertBarrierStatus(param.barrierStatus), hasNightMarket: param.hasNightMarket, commodityFuturesPreciseTimeMode: param.preciseTimeMode, riskFreeRateOverride: param.riskFreeRate, dividendRateOverride: param.dividendRate, annualizedFactor: param.annualizedFactor, koStrike: param.koStrike, couponDayCount: param.couponDayCount.ToDayCountImpl(), koOptionType: param.koOptionType, kiOptionType: param.kiOptionType, couponPaymentDates: couponPaymentDates); ((AnnualizedPremiumSnowball)snowball).AnnualizedPremium = param.annualizedPremiumRate ?? 0; } else { snowball = new SimpleSnowball( startDate: optionStartDate, maturityDate: underlyingMaturityDate, koBarrier: param.koBarrier, kiBarrier: param.kiBarrier, coupon: param.coupon, optionParticipationRate: 1.0, // 在OTC系统增加“期权参与率”字段之前,请保留一直传1.0 koObsDates: koObservationDates, kiObsDates: observationDates, observationDates: null, notional: param.notional, initialSpotPrice: param.initialSpotPrice, underlyingType: (InstrumentType)Enum.Parse(typeof(InstrumentType), param.underlyingInstrumentType), calendar: CalendarImpl.Get("chn"), dayCount: optionDayCount.ToDayCountImpl(), payoffCcy: CurrencyCode.CNY, settlementCcy: CurrencyCode.CNY, exerciseDates: exerciseDates, exercise: OptionExercise.European, isMoneynessOption: param.isMoneynessOption, optionType: param.optionType, barrierType: param.optionType == OptionType.Put ? BarrierType.DownAndOut : BarrierType.UpAndOut, strike: param.strike, spreadStrikeAtKO: param.spreadStrikeAtKO, spreadStrikeAtMaturity: param.spreadStrikeAtMaturity, fixedCoupon: param.isFixedCoupon, kORebate: param.koRebate, kOBarrierAdjustStep: param.koBarrierAdjustStep, useOptionPayoffAtKO: param.useOptionPayoffAtKO, useOptionPayoffAtMaturity: param.useOptionPayoffAtMaturity, annualizedOptionPayoff: param.annualizedOptionPayoff, customizedKOBarriers: customizedKOBarriers, customizedCoupons: customizedCoupons, pricingToverride: pricingToverride, barrierStatus: QdpConverter.ConvertBarrierStatus(param.barrierStatus), hasNightMarket: param.hasNightMarket, commodityFuturesPreciseTimeMode: param.preciseTimeMode, riskFreeRateOverride: param.riskFreeRate, dividendRateOverride: param.dividendRate, annualizedFactor: param.annualizedFactor, koStrike: param.koStrike, couponDayCount: param.couponDayCount.ToDayCountImpl(), koOptionType: param.koOptionType, kiOptionType: param.kiOptionType, couponPaymentDates: couponPaymentDates); } snowball.UnderlyingTickers = param.underlyingTickers; if (string.IsNullOrWhiteSpace(param.tradeId)) { param.tradeId = Guid.NewGuid().ToString(); } return new SnowballTrade( tradeId: param.tradeId, tradeDate: param.tradeDate, startDate: snowball.StartDate, maturityDate: snowball.ExerciseDates.Last(), tradeType: QdpConverter.ConvertTradeType(param.buysell), notional: param.notional, price: 0.0, option: snowball) { ValuationParameters = new OptionValuationParameters(MarketProxy.RiskFreeDiscountCurve, MarketProxy.ConstantZeroCurve, param.volSurfaceNames?.FirstOrDefault(), snowball.UnderlyingTickers[0]), }; } /// /// 雪球期权 /// public static SnowballTrade GetSnowballOptionTrade(OtcTradeBase td, trade_snowball snowball, OptionTradeParamRequest request = null, SnowBallOptionMode snowballMold = SnowBallOptionMode.Auto) { var tradeParam = GetSnowballTradeParam(td, snowball, request); return GetSnowballOptionTrade(tradeParam, snowballMold); } /// /// 雪球期权 /// public static SnowballOptionTradeParam GetSnowballTradeParam(OtcTradeBase td, trade_snowball snowball, OptionTradeParamRequest request) { if (snowball is null) { throw new ArgumentNullException(nameof(snowball)); } var result = BuildTradeParamBase(td, request); //敲出设置 result.koBarrier = snowball.KOBarrier; result.koObservationDateStr = snowball.KOObservationDates; result.couponPaymentDateStr = snowball.CouponUsePaymentDate == true ? snowball.KOObservationSettleDates : null; result.koRebate = snowball.KORebate; result.isFixedCoupon = snowball.IsFixedCoupon; result.couponDayCount = snowball.CouponDayCount; result.useOptionPayoffAtKO = snowball.KOPayoffType != KOPayoffTypeEnum.Rebate;//敲出转期权 result.koOptionType = QdpConverter.ConvertAutocallKOOptionType(snowball.KOPayoffType, QdpConverter.ConvertCallPut(td.CallPut)); result.kiOptionType = QdpConverter.ConvertOptionType(snowball.KIPayoffType); result.koStrike = snowball.KOPayoffType != KOPayoffTypeEnum.Rebate ? (snowball.SpreadStrikeAtKO1 ?? -1) : double.NaN; result.spreadStrikeAtKO = snowball.SpreadStrikeAtKO ?? -1; result.koBarrierAdjustStep = snowball.KOBarrierAdjustStep; //敲入设置 result.kiBarrier = snowball.KIBarrier; result.observationDateStr = snowball.ObservationDates; result.useOptionPayoffAtMaturity = snowball.KIPayoffType != KIPayoffTypeEnum.None;//未敲出转期权 result.strike = snowball.SpreadStrikeAtMaturity1 ?? -1; result.spreadStrikeAtMaturity = snowball.SpreadStrikeAtMaturity ?? -1; //红利票息 result.coupon = snowball.Coupon > 0 ? snowball.Coupon : 0; //保本雪球 result.annualizedPremiumRate = snowball.AnnualizedPremiumRate; //其他 result.barrierStatus = snowball.KnockInOutStatus; //使用自身的年化系数设置 result.annualizedOptionPayoff = snowball.IsAnnualized2; result.annualizedFactor = snowball.AnnualizeFactor2 ?? 1.0; return result; } /// /// 雪球期权 /// public static SnowballSpecialistOptionTradeParam GetSnowballSpecialistTradeParam(OtcTradeBase td, trade_snowball snowball, OptionTradeParamRequest request) { if (snowball is null) { throw new ArgumentNullException(nameof(snowball)); } var result = BuildTradeParamBase(td, request); //敲出设置 result.koBarrier = snowball.KOBarrier; result.koObservationDateStr = snowball.KOObservationDates; result.couponPaymentDateStr = snowball.CouponUsePaymentDate == true ? snowball.KOObservationSettleDates : null; result.koRebate = snowball.KORebate; result.isFixedCoupon = snowball.IsFixedCoupon; result.couponDayCount = snowball.CouponDayCount; result.useOptionPayoffAtKO = snowball.KOPayoffType != KOPayoffTypeEnum.Rebate;//敲出转期权 result.koOptionType = QdpConverter.ConvertAutocallKOOptionType(snowball.KOPayoffType, QdpConverter.ConvertCallPut(td.CallPut)); result.kiOptionType = QdpConverter.ConvertOptionType(snowball.KIPayoffType); result.koStrike = snowball.KOPayoffType != KOPayoffTypeEnum.Rebate ? (snowball.SpreadStrikeAtKO1 ?? -1) : double.NaN; result.spreadStrikeAtKO = snowball.SpreadStrikeAtKO ?? -1; result.koBarrierAdjustStep = snowball.KOBarrierAdjustStep; //敲入设置 result.kiBarrier = snowball.KIBarrier; result.observationDateStr = snowball.ObservationDates; result.useOptionPayoffAtMaturity = snowball.KIPayoffType != KIPayoffTypeEnum.None;//未敲出转期权 result.strike = snowball.SpreadStrikeAtMaturity1 ?? -1; result.spreadStrikeAtMaturity = snowball.SpreadStrikeAtMaturity ?? -1; //红利票息 result.coupon = snowball.Coupon > 0 ? snowball.Coupon : 0; //保本雪球 result.annualizedPremiumRate = snowball.AnnualizedPremiumRate; //其他 result.barrierStatus = snowball.KnockInOutStatus; //使用自身的年化系数设置 result.annualizedOptionPayoff = snowball.IsAnnualized2; result.annualizedFactor = snowball.AnnualizeFactor2 ?? 1.0; //预付金是否参与定价 result.PrepaymentUsed = snowball.PrepaymentUsed; //预付金比例 result.PrepaymentRatio = snowball.PrepaymentRatio ?? 0; //预付金返息率 result.PrepaymentInterestRate = snowball.PrepaymentInterestRate ?? 0; //预付金折现率 result.PrepaymentConvertCashRate = snowball?.PrepaymentConvertCashRate ?? 0; //终日是否计息 result.CouponIncludeEndDate = snowball.CouponIncludeEndDate ?? false; //增强参与率 result.EnhancedParticipationRate = snowball.EnhancedParticipationRate ?? 0; // 敲入参与率 result.KIParticipationRate = snowball.KIParticipationRate ?? 0; // 保本比率 result.PrincipalProtectionRate = snowball.PrincipalProtectionRate ?? 0; return result; } #endregion #region ----凤凰期权---- /// /// 凤凰期权 /// public static AutocallTrade GetAutocallOptionTrade(AutocallOptionTradeParam param) { CheckOptionTradeParam(param); var exercise = QdpConverter.ConvertExerciseType(param.exerciseType); var optionStartDate = param.startDate; var underlyingMaturityDate = param.endDate; var temp_exerciseDate = param.exerciseDate; if (temp_exerciseDate < optionStartDate) { return null; } Date[] exerciseDates = null; var observationDates = QdpHelper.ParseObservationDate(param.observationDateStr); var customizedResults = QdpHelper.ParseAutocallCustomizedInfoV2(param.koObservationDateStr, param.callput, param.initialSpotPrice, param.isMoneynessOption); var koObservationDates = customizedResults.Item1; var customizedKOBarriers = customizedResults.Item2; var customizedCoupons = customizedResults.Item3; //Date[] koObservationDates = QDPHelper.ParseObservationDate(koObservationDateStr); if (exercise == OptionExercise.American) { exerciseDates = CalendarImpl.Get("chn").BizDaysBetweenDatesInclEndDay(optionStartDate, temp_exerciseDate).ToArray(); if (observationDates == null) { observationDates = CalendarImpl.Get("chn").BizDaysBetweenDatesExcluStartDay(optionStartDate, temp_exerciseDate).ToArray(); } } else { exerciseDates = new Date[] { temp_exerciseDate }; if (observationDates == null) { observationDates = CalendarImpl.Get("chn").BizDaysBetweenDatesExcluStartDay(optionStartDate, temp_exerciseDate).Union(new[] { temp_exerciseDate }).ToArray(); } } if (koObservationDates == null) { koObservationDates = QdpObservationHelper.GetDatesWithFixedTerm(optionStartDate, temp_exerciseDate, "1M", bdc: BusinessDayConvention.Following, alignEnd: true) .Select(n => (Date)n).ToArray(); } var optionDayCount = CalculatorHelper.GetTradeDayCount(); var pricingTOverride = QdpCalendarHelper.CalculateTFromDays(param.timeToMaturityDays, optionDayCount, optionStartDate.DateTime); var autocall = new AutoCall( startDate: optionStartDate, maturityDate: underlyingMaturityDate, koBarrier: param.koBarrier, kiBarrier: param.kiBarrier, couponBarrier: param.couponBarrier, coupon: param.coupon, couponPayAtMaturity: param.couponPayAtMaturity, includeCouponAfterKI: param.includeCouponAfterKI, optionParticipationRate: 1.0, // 在OTC系统增加“期权参与率”字段之前,请保留一直传1.0 koObsDates: koObservationDates, kiObsDates: observationDates, observationDates: null, notional: param.notional, initialSpotPrice: param.initialSpotPrice, underlyingType: (InstrumentType)Enum.Parse(typeof(InstrumentType), param.underlyingInstrumentType), calendar: CalendarImpl.Get("chn"), dayCount: optionDayCount.ToDayCountImpl(), couponDayCount: (param.couponDayCount.TrimToNull() ?? "Act365").ToDayCountImpl(), payoffCcy: CurrencyCode.CNY, settlementCcy: CurrencyCode.CNY, exerciseDates: exerciseDates, barrierType: param.optionType == OptionType.Put ? BarrierType.DownAndOut : BarrierType.UpAndOut,//20220415 strike: param.strike, spreadStrike: param.spreadStrike, fixedCoupon: param.isFixedCoupon, annualizedOptionPayoff: param.annualizedOptionPayoff, kiOptionType: param.kiOptionType.ToOptionType(), customizedKOBarriers: customizedKOBarriers, customizedCoupons: customizedCoupons, isMoneynessOption: param.isMoneynessOption, happenedObservations: param.happenedObservations?.Where(o => o != null).Select(o => new ObservationPayment() { StartDate = new Date(o.StartDate), EndDate = new Date(o.EndDate), CouponRate = o.CouponRate, Notional = o.StockEqvNotional, PaymentAmount = o.PaymentAmount, PaymentDate = new Date(o.PaymentDate) }).ToList(), pricingToverride: pricingTOverride, barrierStatus: QdpConverter.ConvertBarrierStatus(param.barrierStatus), hasNightMarket: param.hasNightMarket, commodityFuturesPreciseTimeMode: param.preciseTimeMode, riskFreeRateOverride: param.riskFreeRate, dividendRateOverride: param.dividendRate, annualizedFactor: param.annualizedFactor) { UnderlyingTickers = param.underlyingTickers }; if (string.IsNullOrWhiteSpace(param.tradeId)) { param.tradeId = Guid.NewGuid().ToString(); } return new AutocallTrade( tradeId: param.tradeId, tradeDate: param.tradeDate, startDate: autocall.StartDate, maturityDate: autocall.ExerciseDates.Last(), tradeType: QdpConverter.ConvertTradeType(param.buysell), notional: param.notional,//autocall.Notional price: 0.0, option: autocall) { ValuationParameters = new OptionValuationParameters(MarketProxy.RiskFreeDiscountCurve, MarketProxy.ConstantZeroCurve, param.volSurfaceNames?.FirstOrDefault(), autocall.UnderlyingTickers[0]), }; } /// /// 凤凰期权 /// public static AutocallTrade GetAutocallOptionTrade(OtcTradeBase td, trade_autocall autocall, OptionTradeParamRequest request = null) { var tradeParam = GetAutocallOptionTradeParam(td, autocall, request); return GetAutocallOptionTrade(tradeParam); } /// /// 凤凰期权 /// public static AutocallOptionTradeParam GetAutocallOptionTradeParam(OtcTradeBase td, trade_autocall autocall, OptionTradeParamRequest request) { if (autocall == null) { throw new TradeCalcExpception(td.id, "未获取到对应的凤凰期权交易数据"); } var result = BuildTradeParamBase(td, request); result.coupon = autocall.Coupon; result.isFixedCoupon = autocall.IsFixedCoupon; result.couponDayCount = autocall.CouponDayCount; result.kiOptionType = QdpConverter.ConvertOptionType(autocall.KIPayoffType).ToString(); result.couponBarrier = autocall.CouponBarrier; result.couponPayAtMaturity = autocall.CouponPayType != CouponPayTypeEnum.AtCreated; result.koBarrier = autocall.KOBarrier; result.koObservationDateStr = autocall.KOObservationDates; result.kiBarrier = autocall.KIBarrier; result.observationDateStr = autocall.ObservationDates; result.includeCouponAfterKI = autocall.IncludeCouponAfterKI; //对于凤凰期权,td.Strike应该等于敲入行权价,特别情况下会出现不符合的情况 result.strike = autocall.SpreadStrike1 ?? -1; result.spreadStrike = autocall.SpreadStrike ?? -1; result.happenedObservations = autocall.HappenedObservations; result.barrierStatus = autocall.KnockInOutStatus; //使用自己的年化系数配置 result.annualizedOptionPayoff = autocall.IsAnnualized2; result.annualizedFactor = autocall.AnnualizeFactor2 ?? 1.0; return result; } #endregion #region ----收益增强---- /// /// 收益增强 /// public static UnderlyingPayoffEnhanceTrade GetUnderlyingEnhanceTrade(UnderlyingEnhanceTradeParam param) { CheckOptionTradeParam(param); var exercise = QdpConverter.ConvertExerciseType(param.exerciseType); var optionStartDate = param.startDate; var underlyingMaturityDate = param.endDate; var temp_exerciseDate = param.exerciseDate; if (temp_exerciseDate < optionStartDate) { throw new Exception("到期日期不能小于起始日"); } Date[] exerciseDates, observationDates; if (exercise == OptionExercise.American) { exerciseDates = CalendarImpl.Get("chn").BizDaysBetweenDatesInclEndDay(optionStartDate, temp_exerciseDate).ToArray(); observationDates = CalendarImpl.Get("chn").BizDaysBetweenDatesExcluStartDay(optionStartDate, temp_exerciseDate).ToArray(); } else { exerciseDates = new Date[] { temp_exerciseDate }; observationDates = new Date[] { temp_exerciseDate }; } var optionDayCount = CalculatorHelper.GetTradeDayCount(); var pricingTOverride = QdpCalendarHelper.CalculateTFromDays(param.timeToMaturityDays, optionDayCount, optionStartDate.DateTime); var underlyingEnhance = new UnderlyingPayoffEnhance( startDate: optionStartDate, maturityDate: underlyingMaturityDate, exercise: exercise, optionType: param.optionType, strike: param.strike, underlyingInstrumentType: (InstrumentType)Enum.Parse(typeof(InstrumentType), param.underlyingInstrumentType), calendar: CalendarImpl.Get("chn"), dayCount: optionDayCount.ToDayCountImpl(), enhancePayoffDayCount: new Act365(), payoffCcy: CurrencyCode.CNY, settlementCcy: CurrencyCode.CNY, exerciseDates: exerciseDates, observationDates: observationDates, annualizedEnhanceRate: param.annualizedEnhanceRate, notional: param.notional, settlementGap: null, optionPremiumPaymentDate: null, optionPremium: 0.0, isMoneynessOption: param.isMoneynessOption, initialSpotPrice: param.initialSpotPrice, dividends: param.dividends, hasNightMarket: param.hasNightMarket, commodityFuturesPreciseTimeMode: param.preciseTimeMode, pricingToverride: pricingTOverride, riskFreeRateOverride: param.riskFreeRate, dividendRateOverride: param.dividendRate, participationRate: param.participationRate, isAnnualized: param.isAnnualized, annualizedFactor: param.annualizedFactor) { UnderlyingTickers = param.underlyingTickers }; if (string.IsNullOrWhiteSpace(param.tradeId)) { param.tradeId = Guid.NewGuid().ToString(); } return new UnderlyingPayoffEnhanceTrade( tradeId: param.tradeId, tradeDate: param.tradeDate, startDate: underlyingEnhance.StartDate, maturityDate: underlyingEnhance.ExerciseDates.Last(), tradeType: QdpConverter.ConvertTradeType(param.buysell), notional: param.notional, price: 0.0, option: underlyingEnhance) { ValuationParameters = new OptionValuationParameters(MarketProxy.RiskFreeDiscountCurve, MarketProxy.ConstantZeroCurve, param.volSurfaceNames?.FirstOrDefault(), underlyingEnhance.UnderlyingTickers[0]) }; } /// /// 收益增强 /// public static UnderlyingPayoffEnhanceTrade GetUnderlyingEnhanceTrade(OtcTradeBase td, trade_underlying_enhance underlyingEnhance, OptionTradeParamRequest request = null) { var tradeParam = GetUnderlyingEnhanceTradeParam(td, underlyingEnhance, request); return GetUnderlyingEnhanceTrade(tradeParam); } /// /// 收益增强 /// public static UnderlyingEnhanceTradeParam GetUnderlyingEnhanceTradeParam(OtcTradeBase td, trade_underlying_enhance underlyingEnhance, OptionTradeParamRequest request) { if (underlyingEnhance == null) { throw new TradeCalcExpception(td.id, "未获取到对应的收益增强结构交易数据"); } var result = BuildTradeParamBase(td, request); result.annualizedEnhanceRate = underlyingEnhance.AnnualizedEnhanceRate ?? 0; return result; } #endregion #region ----气囊结构---- /// /// 气囊结构 /// public static AirbagTrade GetAirbagOptionTrade(AirBagOptionTradeParam param) { CheckOptionTradeParam(param); var exercise = QdpConverter.ConvertExerciseType(param.exerciseType); var optionStartDate = param.startDate; var underlyingMaturityDate = param.endDate; var temp_exerciseDate = param.exerciseDate; if (temp_exerciseDate < optionStartDate) { return null; } Date[] exerciseDates, observationDates; if (exercise == OptionExercise.American) { exerciseDates = CalendarImpl.Get("chn").BizDaysBetweenDatesInclEndDay(optionStartDate, temp_exerciseDate).ToArray(); observationDates = CalendarImpl.Get("chn").BizDaysBetweenDatesExcluStartDay(optionStartDate, temp_exerciseDate).ToArray(); } else { exerciseDates = new Date[] { temp_exerciseDate }; observationDates = CalendarImpl.Get("chn").BizDaysBetweenDatesExcluStartDay(optionStartDate, temp_exerciseDate).Union(new[] { temp_exerciseDate }).ToArray(); } var optionDayCount = CalculatorHelper.GetTradeDayCount(); var pricingTOverride = QdpCalendarHelper.CalculateTFromDays(param.timeToMaturityDays, optionDayCount, optionStartDate.DateTime); var airbag = new Airbag( startDate: optionStartDate, maturityDate: underlyingMaturityDate, exercise: exercise, optionType: param.optionType, lowStrike: param.strike, highStrike: param.highStrike, barrier: param.barrier, participationRate: param.participationRate, kiParticipationRate: param.kiParticipationRate, isDiscreteMonitored: param.isDiscrete, hasPayoffLimit: param.hasPayoffLimit, underlyingInstrumentType: (InstrumentType)Enum.Parse(typeof(InstrumentType), param.underlyingInstrumentType), calendar: CalendarImpl.Get("chn"), dayCount: optionDayCount.ToDayCountImpl(), payoffCcy: CurrencyCode.CNY, settlementCcy: CurrencyCode.CNY, exerciseDates: exerciseDates, observationDates: observationDates, notional: param.notional, settlementGap: null, optionPremiumPaymentDate: null, optionPremium: 0, isMoneynessOption: param.isMoneynessOption, initialSpotPrice: param.initialSpotPrice, dividends: param.dividends, barrierStatus: QdpConverter.ConvertBarrierStatus(param.barrierStatus), hasNightMarket: param.hasNightMarket, commodityFuturesPreciseTimeMode: param.preciseTimeMode, pricingToverride: pricingTOverride, riskFreeRateOverride: param.riskFreeRate, dividendRateOverride: param.dividendRate, isAnnualized: param.isAnnualized, annualizedFactor: param.annualizedFactor) { UnderlyingTickers = param.underlyingTickers }; if (string.IsNullOrWhiteSpace(param.tradeId)) { param.tradeId = Guid.NewGuid().ToString(); } return new AirbagTrade( tradeId: param.tradeId, tradeDate: param.tradeDate, startDate: airbag.StartDate, maturityDate: airbag.ExerciseDates.Last(), tradeType: QdpConverter.ConvertTradeType(param.buysell), notional: param.notional, price: 0.0, option: airbag) { ValuationParameters = new OptionValuationParameters(MarketProxy.RiskFreeDiscountCurve, MarketProxy.ConstantZeroCurve, param.volSurfaceNames?.FirstOrDefault(), airbag.UnderlyingTickers[0]), }; } /// /// 气囊结构 /// public static AirbagTrade GetAirbagOptionTrade(OtcTradeBase td, trade_airbag airbag, OptionTradeParamRequest request = null) { var param = GetAirbagOptionTradeParam(td, airbag, request); return GetAirbagOptionTrade(param); } /// /// 气囊结构 /// public static AirBagOptionTradeParam GetAirbagOptionTradeParam(OtcTradeBase td, trade_airbag airbag, OptionTradeParamRequest request) { if (airbag == null) { throw new TradeCalcExpception(td.id, "未获取到对应的气囊结构交易数据"); } var result = BuildTradeParamBase(td, request); result.barrier = airbag.Barrier; result.highStrike = airbag.HighStrike; result.kiParticipationRate = airbag.KIParticipationRate; result.hasPayoffLimit = airbag.HasPayoffLimit; result.isDiscrete = airbag.IsDiscreteMonitored; result.observationDateStr = null; result.barrierStatus = airbag.KnockInOutStatus; return result; } #endregion #region ----区间累积---- /// /// 区间累积 /// public static RangeAccrualTrade GetRangeAccrualTrade(RangeAccrualTradeParam param) { CheckOptionTradeParam(param); var exercise = QdpConverter.ConvertExerciseType(param.exerciseType); var optionStartDate = param.startDate; var underlyingMaturityDate = param.endDate; var temp_exerciseDate = param.exerciseDate; if (temp_exerciseDate < optionStartDate) { throw new Exception("到期日期不能小于起始日"); } Date[] exerciseDates = null; var observationDates = QdpHelper.ParseObservationDate(param.observationDateStr); if (exercise == OptionExercise.American) { exerciseDates = CalendarImpl.Get("chn").BizDaysBetweenDatesInclEndDay(optionStartDate, temp_exerciseDate).ToArray(); observationDates = CalendarImpl.Get("chn").BizDaysBetweenDatesInclEndDay(optionStartDate, temp_exerciseDate).ToArray(); } else { exerciseDates = new Date[] { temp_exerciseDate }; if (observationDates == null) { observationDates = CalendarImpl.Get("chn"). BizDaysBetweenDatesInclEndDay(optionStartDate, temp_exerciseDate).ToArray(); } } var optionDayCount = CalculatorHelper.GetTradeDayCount(); var pricingTOverride = double.IsNaN(param.timeToMaturityDays) ? double.NaN : QdpCalendarHelper.CalculateTFromDays(param.timeToMaturityDays, optionDayCount, optionStartDate.DateTime); var fixingValues = QdpHelper.ParseFixingsFromString(param.fixings); var bonusRateSettlementDate = param.settlementDate ?? temp_exerciseDate; if (bonusRateSettlementDate < optionStartDate) { bonusRateSettlementDate = temp_exerciseDate; } var rangeAccrual = new RangeAccrual( startDate: optionStartDate, maturityDate: underlyingMaturityDate, exercise: exercise, optionType: param.optionType, strike: param.strike, ranges: new RangeDefinition[] { new RangeDefinition(param.lowerRange,param.upperRange,param.bonusRate, bonusRateSettlementDate, observationDates) }, underlyingInstrumentType: (InstrumentType)Enum.Parse(typeof(InstrumentType), param.underlyingInstrumentType), calendar: CalendarImpl.Get("chn"), dayCount: optionDayCount.ToDayCountImpl(), payoffCcy: CurrencyCode.CNY, settlementCcy: CurrencyCode.CNY, exerciseDates: exerciseDates, observationDates: observationDates, fixings: fixingValues, happenedObservations: param.happenedObservations?.Where(o => o != null).Select(o => new ObservationPayment() { StartDate = new Date(o.StartDate), EndDate = new Date(o.EndDate), CouponRate = o.CouponRate, Notional = o.StockEqvNotional, PaymentAmount = o.PaymentAmount, PaymentDate = new Date(o.PaymentDate) }).ToList(), notional: param.notional, settlementGap: null, optionPremiumPaymentDate: null, optionPremium: 0.0, isMoneynessOption: param.isMoneynessOption, initialSpotPrice: param.initialSpotPrice, hasNightMarket: param.hasNightMarket, commodityFuturesPreciseTimeMode: param.preciseTimeMode, pricingToverride: pricingTOverride, riskFreeRateOverride: param.riskFreeRate, dividendRateOverride: param.dividendRate, participationRate: param.participationRate, isAnnualized: param.isAnnualized, annualizedFactor: param.annualizedFactor) { UnderlyingTickers = param.underlyingTickers }; if (string.IsNullOrWhiteSpace(param.tradeId)) { param.tradeId = Guid.NewGuid().ToString(); } return new RangeAccrualTrade( tradeId: param.tradeId, tradeDate: param.startDate, startDate: rangeAccrual.StartDate, maturityDate: rangeAccrual.ExerciseDates.Last(), tradeType: QdpConverter.ConvertTradeType(param.buysell), notional: rangeAccrual.Notional, price: 0.0, option: rangeAccrual) { ValuationParameters = new OptionValuationParameters(MarketProxy.RiskFreeDiscountCurve, MarketProxy.ConstantZeroCurve, param.volSurfaceNames?.FirstOrDefault(), rangeAccrual.UnderlyingTickers[0]), }; } /// /// 区间累积 /// public static RangeAccrualTrade GetRangeAccrualTrade(OtcTradeBase td, trade_rangeaccrual rangeaccrual, OptionTradeParamRequest request = null) { var tradeParam = GetRangeAccrualTradeParam(td, rangeaccrual, request); return GetRangeAccrualTrade(tradeParam); } /// /// 区间累积 /// public static RangeAccrualTradeParam GetRangeAccrualTradeParam(OtcTradeBase td, trade_rangeaccrual rangeaccrual, OptionTradeParamRequest request) { if (rangeaccrual == null) { throw new TradeCalcExpception(td.id, "未获取到对应的区间累积交易数据"); } var result = BuildTradeParamBase(td, request); result.lowerRange = rangeaccrual.LowerRange; result.upperRange = rangeaccrual.UpperRange; //注意:在QDP中未处理百分比形式的上下限价格(2021-10-21) if (result.isMoneynessOption) { result.isMoneynessOption = false; result.strike *= result.initialSpotPrice; result.lowerRange *= result.initialSpotPrice; result.upperRange *= result.initialSpotPrice; } result.bonusRate = rangeaccrual.BonusRate; result.observationDateStr = rangeaccrual.ObservationDates; result.happenedObservations = rangeaccrual.HappenedObservations; result.fixings = request != null ? request.fixings : ""; return result; } #endregion #region ----彩虹期权---- /// /// 彩虹期权 /// public static RainbowOptionTrade GetRainbowOptionTrade(RainbowOptionTradeParam param) { CheckOptionTradeParam(param); var optionStartDate = param.startDate; var underlyingMaturityDate = param.endDate; var temp_exerciseDate = param.exerciseDate; if (temp_exerciseDate < optionStartDate) { return null; } var exerciseDates = new[] { temp_exerciseDate }; var calendar = CalendarImpl.Get("chn"); var observationDates = calendar.BizDaysBetweenDatesExcluStartDay(optionStartDate, temp_exerciseDate).ToArray(); var optionDayCount = CalculatorHelper.GetTradeDayCount(); var rainbowOption = new RainbowOption( startDate: optionStartDate, maturityDate: underlyingMaturityDate, exercise: (OptionExercise)Enum.Parse(typeof(OptionExercise), param.exerciseType), optionType: param.optionType, rainbowType: (RainbowType)Enum.Parse(typeof(RainbowType), param.rainbowType), strikes: param.strikes, cashAmount: param.cashAmount, underlyingInstrumentType: (InstrumentType)Enum.Parse(typeof(InstrumentType), param.underlyingInstrumentType), calendar: CalendarImpl.Get("chn"), dayCount: optionDayCount.ToDayCountImpl(), payoffCcy: CurrencyCode.CNY, settlementCcy: CurrencyCode.CNY, exerciseDates: exerciseDates, observationDates: observationDates, underlyingTickers: param.underlyingTickers, notional: param.notional, settlementGap: null, optionPremiumPaymentDate: null, optionPremium: 0, hasNightMarket: param.hasNightMarket, commodityFuturesPreciseTimeMode: param.preciseTimeMode, participationRate: param.participationRate, isAnnualized: param.isAnnualized, annualizedFactor: param.annualizedFactor, riskFreeRateOverride: param.riskFreeRate, dividendRateOverride: param.dividendRate) { UnderlyingTickers = param.underlyingTickers }; if (string.IsNullOrWhiteSpace(param.tradeId)) { param.tradeId = Guid.NewGuid().ToString(); } return new RainbowOptionTrade( tradeId: param.tradeId, tradeDate: param.tradeDate, startDate: rainbowOption.StartDate, maturityDate: rainbowOption.ExerciseDates.Last(), tradeType: QdpConverter.ConvertTradeType(param.buysell), notional: param.notional, price: 0.0, option: rainbowOption) { ValuationParameters = new OptionValuationParameters( discountCurveName: MarketProxy.RiskFreeDiscountCurve, dividendCurveNames: new string[] { MarketProxy.ConstantZeroCurve, MarketProxy.ConstantZeroCurve }, volSurfNames: param.volSurfaceNames, corrSurfNames: new string[] { param.underlyingTickers[0] + "_" + param.underlyingTickers[1] }, underlyingId: null), }; } /// /// 彩虹期权 /// public static RainbowOptionTrade GetRainbowOptionTrade(OtcTradeBase td, trade_rainbow_option rainbowOption, OptionTradeParamRequest request = null) { var param = GetRainbowOptionTradeParam(td, rainbowOption, request); return GetRainbowOptionTrade(param); } /// /// 彩虹期权 /// public static RainbowOptionTradeParam GetRainbowOptionTradeParam(OtcTradeBase td, trade_rainbow_option rainbowOption, OptionTradeParamRequest request) { if (rainbowOption == null) { throw new TradeCalcExpception(td.id, "未获取到对应的彩虹期权交易数据"); } var result = BuildTradeParamBase(td, request); result.underlyingTickers = rainbowOption.UnderlyingCodes; result.strikes = rainbowOption.Strikes; result.rainbowType = rainbowOption.RainbowType; result.cashAmount = rainbowOption.CashAmount ?? 0; result.correlation = rainbowOption.CorRelation ?? 0; return result; } #endregion #region ----价差期权---- /// /// 价差期权 /// public static SpreadOptionTrade GetSpreadOptionTrade(SpreadOptionTradeParam param) { CheckOptionTradeParam(param); var exercise = QdpConverter.ConvertExerciseType(param.exerciseType); var optionStartDate = param.startDate; var underlyingMaturityDate = param.endDate; var temp_exerciseDate = param.exerciseDate; if (temp_exerciseDate < optionStartDate) { return null; } var exerciseDates = new[] { temp_exerciseDate }; var calendar = CalendarImpl.Get("chn"); var observationDates = calendar.BizDaysBetweenDatesExcluStartDay(optionStartDate, temp_exerciseDate).ToArray(); var normalizedWeight = new double[] { 1.0, 1.0, 1.0, 1.0 }; for (var i = 0; i < param.weights.Length; i++) { normalizedWeight[i] = param.weights[i]; } var optionDayCount = CalculatorHelper.GetTradeDayCount(); var spreadOption = new SpreadOption( startDate: optionStartDate, maturityDate: underlyingMaturityDate, exercise: exercise, optionType: param.optionType, spreadType: param.spreadType, weights: normalizedWeight, strike: param.strike, underlyingInstrumentType: (InstrumentType)Enum.Parse(typeof(InstrumentType), param.underlyingInstrumentType), calendar: CalendarImpl.Get("chn"), dayCount: optionDayCount.ToDayCountImpl(), payoffCcy: CurrencyCode.CNY, settlementCcy: CurrencyCode.CNY, exerciseDates: exerciseDates, observationDates: observationDates, underlyingTickers: param.underlyingTickers, notional: param.notional, settlementGap: null, optionPremiumPaymentDate: null, optionPremium: 0, hasNightMarket: param.hasNightMarket, commodityFuturesPreciseTimeMode: param.preciseTimeMode, participationRate: param.participationRate, isAnnualized: param.isAnnualized, annualizedFactor: param.annualizedFactor, riskFreeRateOverride: param.riskFreeRate); if (string.IsNullOrWhiteSpace(param.tradeId)) { param.tradeId = Guid.NewGuid().ToString(); } return new SpreadOptionTrade( tradeId: param.tradeId, tradeDate: param.tradeDate, startDate: spreadOption.StartDate, maturityDate: spreadOption.ExerciseDates.Last(), tradeType: QdpConverter.ConvertTradeType(param.buysell), notional: param.notional, price: 0.0, option: spreadOption) { ValuationParameters = GetParametersForSpreadOption(param.underlyingTickers, MarketProxy.RiskFreeDiscountCurve, param.volSurfaceNames) }; } /// /// 价差期权 /// public static SpreadOptionTrade GetSpreadOptionTrade(OtcTradeBase td, trade_spread_option spreadOption, OptionTradeParamRequest request, double[] correlations) { var param = GetSpreadOptionTradeParam(td, spreadOption, request, correlations); return GetSpreadOptionTrade(param); } public static OptionValuationParameters GetParametersForSpreadOption(string[] underlyingTickers, string discountCurveName, string[] volSurfaceNames) { var dividendCurveNames = new string[] { MarketProxy.ConstantZeroCurve, MarketProxy.ConstantZeroCurve }; var corrSurfNames = new string[] { underlyingTickers[0] + "_" + underlyingTickers[1] }; // construct correlation surface names // see SpreadOptionTrade.GenerateMarketConditionFromPrebuilt for the order if (underlyingTickers.Length == 3) { dividendCurveNames = new string[] { MarketProxy.ConstantZeroCurve, MarketProxy.ConstantZeroCurve, MarketProxy.ConstantZeroCurve }; corrSurfNames = new string[] { underlyingTickers[0] + "_" + underlyingTickers[1], underlyingTickers[0] + "_" + underlyingTickers[2], underlyingTickers[1] + "_" + underlyingTickers[2] }; } else if (underlyingTickers.Length == 4) { dividendCurveNames = new string[] { MarketProxy.ConstantZeroCurve, MarketProxy.ConstantZeroCurve, MarketProxy.ConstantZeroCurve, MarketProxy.ConstantZeroCurve }; corrSurfNames = new string[] { underlyingTickers[0] + "_" + underlyingTickers[1], underlyingTickers[0] + "_" + underlyingTickers[2], underlyingTickers[1] + "_" + underlyingTickers[2], underlyingTickers[0] + "_" + underlyingTickers[3], underlyingTickers[1] + "_" + underlyingTickers[3], underlyingTickers[2] + "_" + underlyingTickers[3], }; } return new OptionValuationParameters(discountCurveName, dividendCurveNames, volSurfaceNames, corrSurfNames, null); } /// /// 价差期权 /// public static SpreadOptionTradeParam GetSpreadOptionTradeParam(OtcTradeBase td, trade_spread_option spreadOption, OptionTradeParamRequest request, double[] correlations) { if (spreadOption == null) { throw new TradeCalcExpception(td.id, "未获取到对应的价差期权交易数据"); } var result = BuildTradeParamBase(td, request); result.underlyingTickers = spreadOption.UnderlyingAssetCodes(); var payoff = new Model.SpreadOptionPayoff(spreadOption.Payoff); result.weights = payoff.Weights; result.spreadType = payoff.SpreadType; result.correlations = correlations; return result; } #endregion #region ----累计期权---- public static AccumulatorOptionTrade GetAccumulatorOptionTrade(AccumulatorOptionTradeParam param) { if (param is SegmentedAccumulatorOptionTradeParam tradeParam) { return GetSegmentedAccumulatorOptionTrade(tradeParam); } else { return GetStandardAccumulatorOptionTrade(param); } } private static AccumulatorOptionTrade GetStandardAccumulatorOptionTrade(AccumulatorOptionTradeParam param) { CheckOptionTradeParam(param); var exercise = QdpConverter.ConvertExerciseType(param.exerciseType); var optionStartDate = param.startDate; var underlyingMaturityDate = param.endDate; var temp_exerciseDate = param.exerciseDate; if (temp_exerciseDate < optionStartDate) { return null; } Date[] exerciseDates, observationDates; if (exercise == OptionExercise.American) { exerciseDates = CalendarImpl.Get("chn").BizDaysBetweenDatesInclEndDay(optionStartDate, temp_exerciseDate).ToArray(); observationDates = CalendarImpl.Get("chn").BizDaysBetweenDatesExcluStartDay(optionStartDate, temp_exerciseDate).ToArray(); } else { exerciseDates = new Date[] { temp_exerciseDate }; observationDates = CalendarImpl.Get("chn").BizDaysBetweenDatesExcluStartDay(optionStartDate, temp_exerciseDate).Union(new[] { temp_exerciseDate }).ToArray(); } var customizedResults = QdpHelper.ParseAutocallCustomizedInfo(param.KOObservationDates); var koObservationDates = customizedResults.Item1; var customizedKOBarriers = customizedResults.Item2; var customizedCoupons = customizedResults.Item3; if (koObservationDates == null || koObservationDates.Length == 0) { koObservationDates = observationDates; } if (customizedKOBarriers == null || customizedKOBarriers.Length == 0) { customizedKOBarriers = Enumerable.Repeat(param.Barrier, koObservationDates.Length).ToArray(); } else if (param.isMoneynessOptionOrigianl) { customizedKOBarriers = customizedKOBarriers.Select(n => n * param.initialSpotPrice).ToArray(); } //if (customizedCoupons == null || customizedCoupons.Length == 0) //{ // customizedCoupons = Enumerable.Repeat(param.Coupon, koObservationDates.Length).ToArray(); //} //票息支付日期 var couponPaymentDates = QdpHelper.ParseObservationDate(param.couponPaymentDateStr); var optionDayCount = CalculatorHelper.GetTradeDayCount(); var pricingTOverride = QdpCalendarHelper.CalculateTFromDays(param.timeToMaturityDays, optionDayCount, optionStartDate.DateTime); var settleMode = AccumuSettleMode.CashAtHit; switch (param.SettlementMode) { case "现金当日": case "现金实物合并结算": settleMode = AccumuSettleMode.CashAtHit; break; case "现金期末": settleMode = AccumuSettleMode.CashAtEnd; break; case "实物交割": settleMode = AccumuSettleMode.ToFoward; break; } var happenedObservations = settleMode == AccumuSettleMode.CashAtEnd ? param.happenedObservations?.Where(o => o != null).Select(o => new AccumuObservationSettleData { SettleDate = new Date(o.EndDate), SettleNotional = o.PaymentAmount }).ToList() : null; var accumulatorOption = new AccumulatorOption( startDate: optionStartDate, maturityDate: underlyingMaturityDate, optionType: param.optionType, strike: param.strike, barriers: customizedKOBarriers, callMultiplier: param.CallMultiplier, putMultiplier: param.PutMultiplier, earlyTerminate: param.EarlyTerminate, underlyingInstrumentType: (InstrumentType)Enum.Parse(typeof(InstrumentType), param.underlyingInstrumentType), calendar: CalendarImpl.Get("chn"), dayCount: optionDayCount.ToDayCountImpl(), payoffCcy: CurrencyCode.CNY, settlementCcy: CurrencyCode.CNY, exerciseDates: exerciseDates, observationDates: koObservationDates, coupon: param.Coupon, payCoupon: param.PayCoupon, couponPaymentDates: couponPaymentDates, couponDayCount: param.couponDayCount.ToDayCountImpl(), fixedCoupon: param.isFixedCoupon, includeStartDateCoupon: param.includeStartDateCoupon, notional: param.notional, settlementGap: null, optionPremiumPaymentDate: null, optionPremium: 0.0, isMoneynessOption: param.isMoneynessOption, initialSpotPrice: param.initialSpotPrice, dividends: param.dividends, pricingToverride: pricingTOverride, hasNightMarket: param.hasNightMarket, commodityFuturesPreciseTimeMode: param.preciseTimeMode, riskFreeRateOverride: param.riskFreeRate, dividendRateOverride: param.dividendRate, participationRate: param.participationRate, isAnnualized: param.isAnnualized, annualizedFactor: param.annualizedFactor, accumuType: param.AccumuType == "子弹" ? AccumuType.bullet : AccumuType.none, settleMode: settleMode, happenedObservations: happenedObservations) { UnderlyingTickers = param.underlyingTickers }; if (string.IsNullOrWhiteSpace(param.tradeId)) { param.tradeId = Guid.NewGuid().ToString(); } return new AccumulatorOptionTrade( tradeId: param.tradeId, tradeDate: param.tradeDate, startDate: accumulatorOption.StartDate, maturityDate: accumulatorOption.ExerciseDates.Last(), tradeType: QdpConverter.ConvertTradeType(param.buysell), notional: param.notional, price: 0.0, option: accumulatorOption) { ValuationParameters = new OptionValuationParameters(MarketProxy.RiskFreeDiscountCurve, MarketProxy.ConstantZeroCurve, param.volSurfaceNames?.FirstOrDefault(), accumulatorOption.UnderlyingTickers[0]), }; } private static SegmentedAccumulatorOptionTrade GetSegmentedAccumulatorOptionTrade(SegmentedAccumulatorOptionTradeParam param) { CheckOptionTradeParam(param); var exercise = QdpConverter.ConvertExerciseType(param.exerciseType); var optionStartDate = param.startDate; var underlyingMaturityDate = param.endDate; var temp_exerciseDate = param.exerciseDate; if (temp_exerciseDate < optionStartDate) { return null; } Date[] exerciseDates, observationDates; if (exercise == OptionExercise.American) { exerciseDates = CalendarImpl.Get("chn").BizDaysBetweenDatesInclEndDay(optionStartDate, temp_exerciseDate).ToArray(); observationDates = CalendarImpl.Get("chn").BizDaysBetweenDatesExcluStartDay(optionStartDate, temp_exerciseDate).ToArray(); } else { exerciseDates = new Date[] { temp_exerciseDate }; observationDates = CalendarImpl.Get("chn").BizDaysBetweenDatesExcluStartDay(optionStartDate, temp_exerciseDate).Union(new[] { temp_exerciseDate }).ToArray(); } var customizedResults = QdpHelper.ParseAutocallCustomizedInfo(param.KOObservationDates); var koObservationDates = customizedResults.Item1; var customizedKOBarriers = customizedResults.Item2; var customizedCoupons = customizedResults.Item3; if (koObservationDates == null || koObservationDates.Length == 0) { koObservationDates = observationDates; } if (customizedKOBarriers == null || customizedKOBarriers.Length == 0) { customizedKOBarriers = Enumerable.Repeat(param.Barrier, koObservationDates.Length).ToArray(); } else if (param.isMoneynessOptionOrigianl) { customizedKOBarriers = customizedKOBarriers.Select(n => n * param.initialSpotPrice).ToArray(); } //if (customizedCoupons == null || customizedCoupons.Length == 0) //{ // customizedCoupons = Enumerable.Repeat(param.Coupon, koObservationDates.Length).ToArray(); //} //票息支付日期 var couponPaymentDates = QdpHelper.ParseObservationDate(param.couponPaymentDateStr); var optionDayCount = CalculatorHelper.GetTradeDayCount(); var pricingTOverride = QdpCalendarHelper.CalculateTFromDays(param.timeToMaturityDays, optionDayCount, optionStartDate.DateTime); var settleMode = AccumuSettleMode.CashAtHit; switch (param.SettlementMode) { case "现金当日": settleMode = AccumuSettleMode.CashAtHit; break; case "现金期末": settleMode = AccumuSettleMode.CashAtEnd; break; case "实物交割": settleMode = AccumuSettleMode.ToFoward; break; } var settleMode2 = AccumuSettleMode.CashAtHit; switch (param.SettleMode2) { case "现金当日": settleMode2 = AccumuSettleMode.CashAtHit; break; case "实物交割": settleMode2 = AccumuSettleMode.ToFoward; break; } var settleMode3 = AccumuSettleMode.CashAtHit; switch (param.SettleMode3) { case "现金当日": settleMode3 = AccumuSettleMode.CashAtHit; break; case "实物交割": settleMode3 = AccumuSettleMode.ToFoward; break; } var happenedObservations = settleMode == AccumuSettleMode.CashAtEnd ? param.happenedObservations?.Where(o => o != null).Select(o => new AccumuObservationSettleData { SettleDate = new Date(o.EndDate), SettleNotional = o.PaymentAmount }).ToList() : null; var accumulatorOption = new SegmentedAccumulatorOption( startDate: optionStartDate, maturityDate: underlyingMaturityDate, optionType: param.optionType, strike: param.strike, barriers: customizedKOBarriers, multiplier2: param.Multiplier2, multiplier1: param.Multiplier1, earlyTerminate: param.EarlyTerminate, underlyingInstrumentType: (InstrumentType)Enum.Parse(typeof(InstrumentType), param.underlyingInstrumentType), calendar: CalendarImpl.Get("chn"), dayCount: optionDayCount.ToDayCountImpl(), payoffCcy: CurrencyCode.CNY, settlementCcy: CurrencyCode.CNY, exerciseDates: exerciseDates, observationDates: koObservationDates, coupon: param.Coupon, payCoupon: param.PayCoupon, couponPaymentDates: couponPaymentDates, couponDayCount: param.couponDayCount.ToDayCountImpl(), fixedCoupon: param.isFixedCoupon, includeStartDateCoupon: param.includeStartDateCoupon, notional: param.notional, settlementGap: null, optionPremiumPaymentDate: null, optionPremium: 0.0, isMoneynessOption: param.isMoneynessOption, initialSpotPrice: param.initialSpotPrice, dividends: param.dividends, pricingToverride: pricingTOverride, hasNightMarket: param.hasNightMarket, commodityFuturesPreciseTimeMode: param.preciseTimeMode, riskFreeRateOverride: param.riskFreeRate, dividendRateOverride: param.dividendRate, participationRate: param.participationRate, isAnnualized: param.isAnnualized, annualizedFactor: param.annualizedFactor, accumuType: param.AccumuType == "子弹" ? AccumuType.bullet : AccumuType.none, settleMode: settleMode, settleMode2: settleMode2, settleMode3: settleMode3, happenedObservations: happenedObservations, strike2: param.Strike2, strike3: param.Strike3, coupon2: param.Coupon2, multiplier3: param.Multiplier3) { UnderlyingTickers = param.underlyingTickers }; if (string.IsNullOrWhiteSpace(param.tradeId)) { param.tradeId = Guid.NewGuid().ToString(); } return new SegmentedAccumulatorOptionTrade( tradeId: param.tradeId, tradeDate: param.tradeDate, startDate: accumulatorOption.StartDate, maturityDate: accumulatorOption.ExerciseDates.Last(), tradeType: QdpConverter.ConvertTradeType(param.buysell), notional: param.notional, price: 0.0, option: accumulatorOption) { ValuationParameters = new OptionValuationParameters(MarketProxy.RiskFreeDiscountCurve, MarketProxy.ConstantZeroCurve, param.volSurfaceNames?.FirstOrDefault(), accumulatorOption.UnderlyingTickers[0]), }; } public static AccumulatorOptionTrade GetAccumulatorOptionTrade(OtcTradeBase td, trade_accumulator_option accumulatorOption , OptionTradeParamRequest request = null) { var param = GetAccumulatorOptionTradeParam(td, accumulatorOption, request); return GetAccumulatorOptionTrade(param); } public static AccumulatorOptionTradeParam GetAccumulatorOptionTradeParam(OtcTradeBase td, trade_accumulator_option accumulatorOption , OptionTradeParamRequest request) { if (accumulatorOption == null) { throw new TradeCalcExpception(td.id, "未获取到对应的累计期权交易数据"); } AccumulatorOptionTradeParam result = null; if (accumulatorOption.AccumulatorStructureType == AccumulatorStructureTypeEnum.Segmented) { var segmentedResult = BuildTradeParamBase(td, request); segmentedResult.Multiplier1 = (accumulatorOption.Multiplier ?? 0); segmentedResult.Multiplier2 = (accumulatorOption.Multiplier2 ?? 0); segmentedResult.Multiplier3 = (accumulatorOption.Multiplier3 ?? 0); segmentedResult.Coupon2 = accumulatorOption.CouponPercent ? (accumulatorOption.Coupon2 ?? 0) * (td.SpotPrice ?? 0) : (accumulatorOption.Coupon2 ?? 0); segmentedResult.SettleMode2 = accumulatorOption.SettlementMode2; segmentedResult.SettleMode3 = accumulatorOption.SettlementMode3; if (segmentedResult.isMoneynessOption) { segmentedResult.Strike2 = (accumulatorOption.Strike2 ?? 0) * segmentedResult.initialSpotPrice; segmentedResult.Strike3 = (accumulatorOption.Strike3 ?? 0) * segmentedResult.initialSpotPrice; } else { segmentedResult.Strike2 = (accumulatorOption.Strike2 ?? 0); segmentedResult.Strike3 = (accumulatorOption.Strike3 ?? 0); } result = segmentedResult; result.initialSpotPrice = segmentedResult.initialSpotPrice; } else { result = BuildTradeParamBase(td, request); result.PutMultiplier = accumulatorOption.PutMultiplier; result.CallMultiplier = accumulatorOption.CallMultiplier; } result.EarlyTerminate = accumulatorOption.EarlyTerminate; result.PayCoupon = accumulatorOption.PayoffType == "固定"; result.couponDayCount = accumulatorOption.CouponDayCount.TrimToNull() ?? "Act365"; ; result.couponPaymentDateStr = accumulatorOption.KOObservationSettleDates; result.isFixedCoupon = accumulatorOption.IsFixedCoupon; result.includeStartDateCoupon = false; result.AccumuType = accumulatorOption.AccumuType; result.SettlementMode = accumulatorOption.SettlementMode; //barrier result.Barrier = accumulatorOption.KOBarrier.HasValue ? (result.isMoneynessOption ? accumulatorOption.KOBarrier.Value * result.initialSpotPrice : accumulatorOption.KOBarrier.Value) : (result.optionType == OptionType.Call ? 1e10 : -1e10); if (result.isMoneynessOption) { result.strike *= result.initialSpotPrice; } result.isMoneynessOption = false; result.Coupon = accumulatorOption.CouponPercent ? accumulatorOption.Coupon * (td.SpotPrice ?? 0) : accumulatorOption.Coupon; result.KOObservationDates = accumulatorOption.KOObservationDates; // Removed updownPriceMode as it's not supported in QDP AccumulatorOption constructor // result.updownPriceMode = GlobalConfig.AccumulatorCriticalMode == "updown"; result.happenedObservations = request?.happenedObservations; //将累计期权引擎需要传入的notional从原来的观察日总数量更正为沽购数量 result.notional = accumulatorOption.AccumuTradeAmount * (td.CountRatio ?? 1); return result; } #endregion #region ----现金流交易---- public static DepositTrade GetCashFlowTrade(CashFlowTradeParam param, double stockEqvNotional) { var optionStartDate = param.startDate; var underlyingMaturityDate = param.endDate; var deposit = new Deposit( startDate: optionStartDate, maturityDate: underlyingMaturityDate, depositRate: param.ProfitRate, dayCount: param.ProfitDayCount.ToDayCountImpl(), calendar: CalendarImpl.Get("chn"), bda: BusinessDayConvention.None, currency: CurrencyCode.CNY, notional: stockEqvNotional, annualizedRate: param.RateType == CashFlowRateTypeEnum.年化利率, depositType: param.DepositType == CashflowDepositTypeEnum.成本摊还 ? DepositType.CostRedemption : DepositType.Regular, payCouponOnly: true ); return new DepositTrade( tradeId: param.tradeId, tradeDate: param.tradeDate, startDate: param.startDate, maturityDate: underlyingMaturityDate, tradeType: QdpConverter.ConvertTradeType(param.buysell), notional: stockEqvNotional, price: 0.0, instrument: deposit) { ValuationParameters = new SimpleCfValuationParameters("RiskFreeDiscountCurve", "RiskFreeDiscountCurve", "RiskFreeDiscountCurve") }; } public static DepositTrade GetCashFlowTrade(OtcTradeBase td, trade_cashflow cashflowTrade, OptionTradeParamRequest request = null) { var param = GetCashFlowTradeParam(td, cashflowTrade, request); return GetCashFlowTrade(param, td.StockEqvNotional); } public static CashFlowTradeParam GetCashFlowTradeParam(OtcTradeBase td, trade_cashflow cashflow, OptionTradeParamRequest request) { if (cashflow == null) { throw new TradeCalcExpception(td.id, "未获取到对应的现金流交易数据"); } var result = BuildTradeParamBase(td, request); result.ProfitRate = cashflow.ProfitRate; result.RateType = cashflow.RateType; result.ProfitDayCount = cashflow.ProfitDayCount; result.DepositType = cashflow.DepositType; result.PrepayRatio = cashflow.PrepayRatio; return result; } #endregion #region ----结构化产品---- public static StructProductTradeParam GetStructProductTradeParam(OptionValueCalcRequest req, MarketProxy marketProxy, trade trade, underlying_manager underlying = null) { if (underlying == null) { underlying = DataCacheProvider.GetUnderlyingDataSource().GetData(trade.UnderlyingCode); } var dzStructureService = new DZStructureService(OptUserInfo.SystemUser); var components = new StructureComponent[4]; var componentsLength = 0; if (trade.MetaDic.ContainsKey("structures")) { var structures = dzStructureService.getStructureList(trade.MetaDic["structures"]); if (structures != null && structures.Count > 0) { structures.ForEach(p => { if (!"None".Equals(p.OptionType)) { components[componentsLength] = new StructureComponent { componentType = p.OptionType, strike = p.Strike != null ? (double)p.Strike : 0, barrier = p.BarrierPrice != null ? (double)p.BarrierPrice : 0, multiplier = p.Multiplier != null ? (double)p.Multiplier : 0, ramp = string.IsNullOrEmpty(p.RAMP) ? 0 : double.Parse(p.RAMP), isAccAtStrike = p.IsAccAlSrike == null ? false : (bool)p.IsAccAlSrike }; componentsLength++; } }); } } else { throw new Exception("未设置结构要素"); } var scheduleList = new List(); if (trade.MetaDic.ContainsKey("observationDate")) { var observationDateStr = trade.MetaDic["observationDate"]; if (!string.IsNullOrEmpty(observationDateStr)) { var arr = observationDateStr.Split(new char[] { ';' }); var dateArr = arr[0].Split(new char[] { ',' }); var fixedPriceArr = arr[1].Split(new char[] { ',' }); var isClosedArr = arr[2].Split(new char[] { ',' }); var closePriceArr = arr[3].Split(new char[] { ',' }); var arrLength = dateArr.Length; for (int i = 0; i < arrLength; i++) { StructureObsSchedule sos = new StructureObsSchedule { valueDate = GetTotalSecends(DateTime.Parse(dateArr[i])), fixedPrice = string.IsNullOrEmpty(fixedPriceArr[i]) ? 0 : double.Parse(fixedPriceArr[i]), isClosed = string.IsNullOrEmpty(isClosedArr[i]) ? false : bool.Parse(isClosedArr[i]), closePrice = string.IsNullOrEmpty(closePriceArr[i]) ? 0 : double.Parse(closePriceArr[i]), }; scheduleList.Add(sos); } } } else { throw new Exception("未设置观察日"); } var scheduleArr = scheduleList.ToArray(); var volsurface = new VolSurface() { }; var vols = VolatilityHelper.GetVol((DateTime)trade.TradeDate, "交易", underlying.UnderlyingCode, ""); if (vols != null && vols.VolTable != null && vols.VolTable.Any()) { var strikeArr = vols.VolTable.Select(p => p.Strike).Distinct().OrderBy(p => p).ToArray(); var expireArr = vols.VolTable.Select(p => p.Expire).Distinct().OrderBy(p => p).ToList(); var verticalAxisList = new List(); expireArr.ForEach(p => { if (p.EndsWith("D")) { verticalAxisList.Add(new ExpireDto { Days = double.Parse(p.Substring(0, p.Length - 1)), DayStr = p }); } else if (p.EndsWith("M")) { verticalAxisList.Add(new ExpireDto { Days = double.Parse(p.Substring(0, p.Length - 1)) * 365 / 12, DayStr = p }); } else if (p.EndsWith("Y")) { verticalAxisList.Add(new ExpireDto { Days = double.Parse(p.Substring(0, p.Length - 1)) * 365, DayStr = p }); } else if (p.EndsWith("W")) { verticalAxisList.Add(new ExpireDto { Days = double.Parse(p.Substring(0, p.Length - 1)) * 7, DayStr = p }); } }); verticalAxisList = verticalAxisList.OrderBy(p => p.Days).ToList(); List flattenedVolList = new List(); foreach (var hor in strikeArr) { foreach (var ver in verticalAxisList) { flattenedVolList.Add(vols.VolTable.First(p => p.Strike == hor && p.Expire == ver.DayStr).Vol); } } volsurface.horizontalAxis = Marshal.UnsafeAddrOfPinnedArrayElement(strikeArr, 0); volsurface.horizontalAxisLength = strikeArr.Length; var verticalAxisArr = verticalAxisList.Select(p => p.Days).ToArray(); volsurface.verticalAxis = Marshal.UnsafeAddrOfPinnedArrayElement(verticalAxisArr, 0); volsurface.verticalAxisLength = verticalAxisArr.Length; var flattenedVolArr = flattenedVolList.ToArray(); volsurface.flattenedVol = Marshal.UnsafeAddrOfPinnedArrayElement(flattenedVolArr, 0); volsurface.flattenedVolLength = flattenedVolArr.Length; log.Info("期权定价波动率请求1:" + JsonHelper.Serialize(strikeArr)); log.Info("期权定价波动率请求2:" + JsonHelper.Serialize(verticalAxisArr)); log.Info("期权定价波动率请求3:" + JsonHelper.Serialize(flattenedVolArr)); } int isCashSettle = 1; if (trade.MetaDic.ContainsKey("cashOrPhysical") && !"Cash".Equals(trade.MetaDic["cashOrPhysical"])) { isCashSettle = 0; } TradeValueResult result = new TradeValueResult(); var request = new StructureRequest { buySell = "卖出".Equals(trade.BuySell) ? -1 : 1, r = req.sysRiskFreeRate, constantVol = req.vols[0], underlierPrice = req.spotPrices[0], valueTime = GetTotalSecends(marketProxy.ValueDate), isCashSettle = isCashSettle, scenarioPrice = 0, //情景价格,计算预付金使用 标的价格*涨跌幅 = 情景价格,调用dll component = components, componentLength = componentsLength, //volSurface = new IntPtr(&volsurface), schedule = Marshal.UnsafeAddrOfPinnedArrayElement(scheduleArr, 0), scheduleLength = scheduleArr.Length, valueType = "a" }; log.Info("期权定价观察日请求:" + JsonHelper.Serialize(scheduleArr)); StructureResult sResult = new StructureResult { message = new string(' ', 100), pv = 0, delta = 0, gamma = 0, thetaPerDay = 0, rhoPercentage = 0, vegaPercentage = 0, }; return new StructProductTradeParam { Request = request, VolSurface = volsurface }; } private static long GetTotalSecends(DateTime date) { return (long)(date - DateTime.Parse("1970-1-1")).TotalSeconds; } #endregion #region----内部方法---- private static void CheckOptionTradeParam(OptionTradeParamBase param) { if (param is null) { throw new ArgumentNullException(nameof(param)); } if (param.underlyingTickers == null || !param.underlyingTickers.Any()) { throw new Exception("缺少标的代码"); } } /// /// QDP计算参数基础设置 /// private static T BuildTradeParamBase(OtcTradeBase td, OptionTradeParamRequest request) where T : OptionTradeParamBase, new() { if (td is null) { throw new ArgumentNullException(nameof(td)); } request ??= new OptionTradeParamRequest(valuedateBLL.RiskFreeRate / 100); //---------------------------------- // 补足交易数据缺失 //---------------------------------- if (!td.StartDate.HasValue) { td.StartDate = td.TradeDate; } if (td.ExerciseDate == null) { throw new ServiceException("缺少到期日"); } if (td.HasUnderlying()) { if (string.IsNullOrWhiteSpace(td.UnderlyingCode)) { throw new ServiceException("交易参数缺少标的代码"); } if (string.IsNullOrWhiteSpace(td.UnderlyingInstrumentType)) { var un = DataCacheProvider.GetUnderlyingDataSource().GetData(td.UnderlyingCode); if (un == null) { throw new ServiceException("交易参数找不到标的数据:" + td.UnderlyingCode); } td.UnderlyingInstrumentType = un.UnderlyingInstrumentType; } } //---------------------------------- // 生成计算参数对象 //---------------------------------- var param = new T { underlyingTickers = new[] { td.UnderlyingCode }, underlyingInstrumentType = td.UnderlyingInstrumentType, hasNightMarket = request.hasNightMarket, startDate = new Date(td.StartDate.Value), originalExerciseDate = td.ExerciseDate.Value, exerciseDate = GetExerciseDate(td.ExerciseDate, request.maturityShift), endDate = GetEndDate(td.MaturityDate ?? td.ExerciseDate, request.maturityShift), settlementDate = td.SettlementDate, buysell = td.BuySell, optionType = QdpConverter.ConvertOptionType(td.CallPut), exerciseType = td.ExerciseMode, strike = td.Strike ?? 0, initialSpotPrice = td.SpotPrice ?? 0, isMoneynessOption = td.IsMoneynessOptionData, isMoneynessOptionOrigianl = td.IsMoneynessOptionData, participationRate = td.ParticipationRate ?? 1.0, principalRate = td.PrincipalRate ?? 0, isAnnualized = td.IsAnnualized, annualizedFactor = td.AnnualizeFactor ?? 1.0, notional = td.Notional, dividendRate = td.DividendRate ?? td.NoRiskRate ?? request.sysRiskFreeRate, riskFreeRate = td.NoRiskRate ?? request.sysRiskFreeRate, preciseTimeMode = request.preciseTimeMode, tradeId = request.tradeId, tradeNumber = td.TradeNumber, timeToMaturityDays = request.timeToMaturityDays, dividends = request.dividends, volSurfaceNames = request.volSurfaceNames }; //if (td.TradeType != param.tradeType && (td.TradeType != "场内期权" || param.tradeType != "香草期权")) //{ // throw new ArgumentNullException($"结构类型不符,期望的结构类型为'{param.tradeType}',但传入结构类型为'{td.TradeType}'"); //} request.ParamOverride?.Invoke(param); return param; } /// /// 获取结束日(endDate必须有值否则QDP会报错) /// private static Date GetEndDate(DateTime? maturityDate, int maturityShift) { if (maturityDate == null || DateTime.Now.AddYears(3) < maturityDate.Value) { maturityDate = DateTime.Today.AddYears(3); } var endDate = QdpCalendarHelper.BizDayShift2(maturityDate.Value); if (maturityShift != 0) { var dayCount = CalculatorHelper.GetTradeDayCount(); endDate = QdpCalendarHelper.ShiftDate(endDate, dayCount, maturityShift); } return endDate; } /// /// 获取行权日 /// private static Date GetExerciseDate(DateTime? exerciseDate, int maturityShift) { if (exerciseDate != null) { var date = exerciseDate.Value; if (maturityShift != 0) { var dayCount = CalculatorHelper.GetTradeDayCount(); date = QdpCalendarHelper.ShiftDate(date, dayCount, maturityShift); } return date; } return null; } #endregion } /// /// 雪球期权类型 /// public enum SnowBallOptionMode { /// /// 根据传入参数自动判别 /// Auto, /// /// 普通雪球 /// SimpleSnowball, /// /// 保本雪球 /// AnnualizedPremiumSnowball } }