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
}
}