using Qdp.Pricing.Base.Utilities; using YLErp.DBModels; using YLErp.QdpModule; namespace YLErp.Modules.TradeModule.ExoticOptionModule { /// /// 专业版雪球观察帮助类 /// public class SpecialSnowballObservationHelper { private readonly OtcTradeBase _otcTrade; private readonly trade_snowball _snowball; public SpecialSnowballObservationHelper(OtcTradeBase otcTrade, trade_snowball snowball) { _otcTrade = otcTrade ?? throw new ArgumentNullException(nameof(otcTrade)); _snowball = snowball ?? throw new ArgumentNullException(nameof(snowball)); } /// /// 获取雪球观察结果(买方) /// public SnowballObservationResult GetObservationResultForBuySide(DateTime valueDate, double closePrice, double tradeNotional) { System.Diagnostics.Debug.Assert(_snowball.KnockInOutStatus != ConsTrade.KnockState.KnockedOut); //敲出检查 var koResult = GetKoPayoff(valueDate, closePrice, tradeNotional, out var isKoObservationDay, out var koBarrier); if (koResult != null) { return koResult; } var resultType = _snowball.IsInitialKnockedIn || _snowball.KnockInOutStatus == ConsTrade.KnockState.KnockedIn ? SnowballObservationResultType.KnockedIn : (isKoObservationDay ? SnowballObservationResultType.Monitoring : SnowballObservationResultType.NonObservationDay); //敲入检查 if (resultType != SnowballObservationResultType.KnockedIn && IsNeedCheckKnockIn(valueDate)) { var kiBarrier = _otcTrade.IsMoneynessOptionData ? _snowball.KIBarrier * (_otcTrade.SpotPrice ?? 0) : _snowball.KIBarrier; // 发生敲入事件(看涨 - 向下敲入,看跌 - 向上敲入) resultType = (ConsGlobal.CallPut.IsCall(_otcTrade.CallPut) ? closePrice <= kiBarrier : closePrice >= kiBarrier) ? SnowballObservationResultType.KnockedIn : SnowballObservationResultType.Monitoring; } SnowballObservationResult result; //到期检查 if (valueDate == _otcTrade.ExerciseDate.Value) { result = resultType == SnowballObservationResultType.KnockedIn ? GetKiPayoffAtEndDate(closePrice, tradeNotional) : GetNKiPayoffAtEndDate(tradeNotional); } else { result = new SnowballObservationResult { ResultType = resultType }; } result.KoBarrier = koBarrier ?? (_otcTrade.IsMoneynessOptionData ? _snowball.KOBarrier * (_otcTrade.SpotPrice ?? 0) : _snowball.KOBarrier); return result; } /// /// 获取雪球观察结果(交易员角度) /// public SnowballObservationResult GetObservationResultForTraderSide(DateTime valueDate, double closePrice, double tradeNotional) { var result = GetObservationResultForBuySide(valueDate, closePrice, tradeNotional); if (_otcTrade.BuySell == "卖出") { result.PaymentAmount = -result.PaymentAmount; result.Payoff?.InvertSign(); } return result; } /// /// 获取累积票息(tradeNotional为负值时是卖方角度) /// public PrepaymentSnowballKoPayoff GetEffectiveObservation(DateTime valueDate, double tradeNotional) { var koObservationParseResult = ParseKoObservation(valueDate); //非敲出观察日 if (koObservationParseResult == null) { return null; } var payoff = new PrepaymentSnowballKoPayoff { CouponStartDate = _otcTrade.StartDate.Value, CouponEndDate = valueDate, CouponRate = koObservationParseResult.KoRebate, //票息支付金额 CouponPaymentAmount = koObservationParseResult.KoRebate * tradeNotional * (_otcTrade.SpotPrice ?? 0) }; //票息计息年化方式处理 if (!string.IsNullOrWhiteSpace(_snowball.CouponDayCount)) { //首日是否计息 var startDate = _snowball.CouponIncludeStartDate == true ? _otcTrade.StartDate.Value.AddDays(-1) : _otcTrade.StartDate.Value; //是否支付日计息 var endDate = valueDate; if (_snowball.CouponUsePaymentDate == true) { endDate = GetKoPaymentDate(_snowball.KOObservationSettleDates, koObservationParseResult.KoDateIndex, valueDate); } //终日是否计息 if (_snowball.CouponIncludeEndDate == false) { endDate = endDate.AddDays(-1); } //票息年化计息 var couponDayCount = _snowball.CouponDayCount.ToDayCountImpl(); var dayFraction = couponDayCount.CalcDayCountFraction(startDate, endDate); payoff.CouponPaymentAmount *= dayFraction; } return payoff; } #region----到期日敲入收益---- /// /// 获取到期日敲入收益 /// private SnowballObservationResult GetKiPayoffAtEndDate(double closePrice, double tradeNotional) { var strike = _otcTrade.IsMoneynessOption == "是" ? (_snowball.SpreadStrikeAtMaturity1 ?? 0) * (_otcTrade.SpotPrice ?? 0) : _snowball.SpreadStrikeAtMaturity1 ?? 0; //敲入转香草期权,买卖方向需要反一下,通过notional体现 var notional = -tradeNotional * (_snowball.KIParticipationRate ?? 1); var isCall = ConsGlobal.CallPut.IsCall(_otcTrade.OptionType); var payoff = new PrepaymentSnowballKiPayoff(); //期权支付 if (_snowball.PrincipalProtectionRate.HasValue && _snowball.PrincipalProtectionRate.Value != 0) { var spread = (1 - _snowball.PrincipalProtectionRate.Value) * (_otcTrade.SpotPrice ?? 0); if (isCall) { //熊市价差 payoff.OptionPaymentAmount = Math.Min(Math.Max(strike - closePrice, 0), spread) * notional; } else { //牛市价差 payoff.OptionPaymentAmount = Math.Min(Math.Max(closePrice - strike, 0), spread) * notional; } } else if (isCall) { //看跌期权 payoff.OptionPaymentAmount = Math.Max(strike - closePrice, 0) * notional; } else { //看涨期权 payoff.OptionPaymentAmount = Math.Max(closePrice - strike, 0) * notional; } //预付金返还 if (_snowball.PrepaymentRatio.HasValue) { payoff.PrepaymentAmount = tradeNotional * (_otcTrade.SpotPrice ?? 0) * _snowball.PrepaymentRatio.Value; //利息金额 if (_snowball.PrepaymentInterestRate.HasValue) { payoff.InterestAmount = payoff.PrepaymentAmount * _snowball.PrepaymentInterestRate.Value; //年化计息 if (!string.IsNullOrWhiteSpace(_snowball.CouponDayCount)) { //首日是否计息 var startDate = _snowball.CouponIncludeStartDate == true ? _otcTrade.StartDate.Value.AddDays(-1) : _otcTrade.StartDate.Value; //终日是否计息 var endDate = _snowball.CouponIncludeEndDate == true ? _otcTrade.ExerciseDate.Value : _otcTrade.ExerciseDate.Value.AddDays(-1); var couponDayCount = _snowball.CouponDayCount.ToDayCountImpl(); var dayFraction = couponDayCount.CalcDayCountFraction(startDate, endDate); payoff.InterestAmount *= dayFraction; } } } return new SnowballObservationResult { Payoff = payoff, PaymentAmount = payoff.TotalAmount(), PaymentDate = _otcTrade.ExerciseDate.Value, ResultType = SnowballObservationResultType.KiPayoffAtEndDate }; } #endregion #region----获取到期日未敲入收益---- /// /// 获取到期日未敲入收益 /// private SnowballObservationResult GetNKiPayoffAtEndDate(double tradeNotional) { var notionalValue = tradeNotional * (_otcTrade.SpotPrice ?? 0); var payoff = new PrepaymentSnowballNkiPayoff(); //红利票息支付金额 payoff.CouponPaymentAmount = _snowball.Coupon * notionalValue; //预付金返还 if (_snowball.PrepaymentRatio.HasValue) { payoff.PrepaymentAmount = notionalValue * _snowball.PrepaymentRatio.Value; //利息金额 if (_snowball.PrepaymentInterestRate.HasValue) { payoff.InterestAmount = payoff.PrepaymentAmount * _snowball.PrepaymentInterestRate.Value; } } //年化计息 if (!string.IsNullOrWhiteSpace(_snowball.CouponDayCount)) { //首日是否计息 var startDate = _snowball.CouponIncludeStartDate == true ? _otcTrade.StartDate.Value.AddDays(-1) : _otcTrade.StartDate.Value; //终日是否计息 var endDate = _snowball.CouponIncludeEndDate == true ? _otcTrade.ExerciseDate.Value : _otcTrade.ExerciseDate.Value.AddDays(-1); var couponDayCount = _snowball.CouponDayCount.ToDayCountImpl(); var dayFraction = couponDayCount.CalcDayCountFraction(startDate, endDate); payoff.InterestAmount *= dayFraction; payoff.CouponPaymentAmount *= dayFraction; } return new SnowballObservationResult { Payoff = payoff, PaymentAmount = payoff.TotalAmount(), PaymentDate = _otcTrade.ExerciseDate.Value, ResultType = SnowballObservationResultType.NkiPayoffAtEndDate }; } #endregion #region----获取敲出收益---- /// /// 获取敲出收益 /// private SnowballObservationResult GetKoPayoff(DateTime valueDate, double closePrice, double tradeNotional , out bool isObservationDay, out double? koBarrier) { var koObservationParseResult = ParseKoObservation(valueDate); koBarrier = koObservationParseResult?.KoBarrier; isObservationDay = koObservationParseResult != null; //非敲出观察日 if (!isObservationDay) { return null; } var isCall = ConsGlobal.CallPut.IsCall(_otcTrade.CallPut); // 发生敲出事件(看涨 - 向上敲出支付票息,看跌 - 向下敲出支付票息) if (isCall ? closePrice < koObservationParseResult.KoBarrier : closePrice > koObservationParseResult.KoBarrier) { return null; } var payoff = new PrepaymentSnowballKoPayoff { CouponStartDate = _otcTrade.StartDate.Value, CouponEndDate = valueDate, CouponRate = koObservationParseResult.KoRebate }; var notionalValue = tradeNotional * (_otcTrade.SpotPrice ?? 0); //增强收益支付金额 if (_snowball.EnhancedParticipationRate.HasValue) { payoff.EnhancedPaymentAmount = (closePrice - koObservationParseResult.KoBarrier) * tradeNotional * _snowball.EnhancedParticipationRate.Value; if (!isCall) { payoff.EnhancedPaymentAmount = -payoff.EnhancedPaymentAmount; } } //票息支付金额 payoff.CouponPaymentAmount = koObservationParseResult.KoRebate * notionalValue; //预付金返还 if (_snowball.PrepaymentRatio.HasValue) { payoff.PrepaymentAmount = notionalValue * _snowball.PrepaymentRatio.Value; //利息金额 if (_snowball.PrepaymentInterestRate.HasValue) { payoff.InterestAmount = payoff.PrepaymentAmount * _snowball.PrepaymentInterestRate.Value; } } //票息支付日 var paymentDate = GetKoPaymentDate(_snowball.KOObservationSettleDates, koObservationParseResult.KoDateIndex, valueDate); //票息计息年化方式处理 if (!string.IsNullOrWhiteSpace(_snowball.CouponDayCount)) { //首日是否计息 var startDate = _snowball.CouponIncludeStartDate == true ? _otcTrade.StartDate.Value.AddDays(-1) : _otcTrade.StartDate.Value; //是否支付日计息 var endDate = valueDate; if (_snowball.CouponUsePaymentDate == true) { endDate = paymentDate; } //终日是否计息 if (_snowball.CouponIncludeEndDate == false) { endDate = endDate.AddDays(-1); } payoff.CouponEndDate = endDate; //票息年化计息 var couponDayCount = _snowball.CouponDayCount.ToDayCountImpl(); var dayFraction = couponDayCount.CalcDayCountFraction(startDate, endDate); payoff.CouponPaymentAmount *= dayFraction; //年化利息金额 payoff.InterestAmount *= dayFraction; } //返回敲出观察结果 var result = new SnowballObservationResult { Payoff = payoff, PaymentDate = paymentDate, ResultType = SnowballObservationResultType.KoPayoff, PaymentAmount = payoff.TotalAmount(), KoBarrier = koObservationParseResult.KoBarrier }; if (_snowball.KORebateType == RebateTypeEnum.AtEnd) { result.PaymentDate = _otcTrade.ExerciseDate.Value; } return result; } /// /// 解析敲出观察频率 /// private KoObservationParseResult ParseKoObservation(DateTime valueDate) { (var koDates, var koBarries, var koCoupons) = QdpHelper.ParseAutocallCustomizedInfo(_snowball.KOObservationDates); koDates ??= QdpObservationHelper.GetDefaultKoObservationDatesForSnowbalV2(_otcTrade.StartDate.Value, _otcTrade.ExerciseDate.Value); var koDateIndex = koDates.Select(x => x.DateTime).ToList().IndexOf(valueDate); if (koDateIndex < 0) { return null; } var result = new KoObservationParseResult { KoDateIndex = koDateIndex, KoRebate = koCoupons != null && koCoupons.Length > koDateIndex ? koCoupons[koDateIndex] : _snowball.KORebate, KoBarrier = koBarries != null && koBarries.Length > koDateIndex ? koBarries[koDateIndex] : _snowball.KOBarrier }; if (_otcTrade.IsMoneynessOptionData) { result.KoBarrier *= _otcTrade.SpotPrice ?? 1.0; } return result; } /// /// 获取票息支付日 /// private static DateTime GetKoPaymentDate(string paymentDatesStr, int koDateIndex, DateTime koObservationDate) { var paymentDates = string.IsNullOrWhiteSpace(paymentDatesStr) ? null : paymentDatesStr.Split(new char[] { ',', ';', ',', ';' }).Select(x => DateTime.Parse(x)).ToArray(); if (paymentDates != null && paymentDates.Length > koDateIndex) { var paymentDate = paymentDates[koDateIndex]; //预防错误的支付日数据 if (paymentDate < koObservationDate) { paymentDate = koObservationDate; } return paymentDate; } return koObservationDate; } /// /// 是否需要观察敲入 /// private bool IsNeedCheckKnockIn(DateTime observationDate) { //已在观察日之前敲入则不需要观察是否敲入 if (_snowball.KnockInOutStatus == ConsTrade.KnockState.KnockedIn && _snowball.KnockInOutDate < observationDate) { return false; } //仅在到期日观察 if (_snowball.KIObservationType == KIObservationType.OnlyEndDate) { return observationDate == _otcTrade.ExerciseDate; } //每日观察 return true; } class KoObservationParseResult { /// /// 观察日在敲出观察频率中的索引 /// public int KoDateIndex { get; set; } /// /// 敲出障碍价格 /// public double KoBarrier { get; set; } /// /// 敲出支付票息率 /// public double KoRebate { get; set; } } #endregion } }