Files
zszq-trs/YLErpDAL/Modules/TradeModule/ExoticOptionModule/SpecialSnowballObservationHelper.cs
T
2024-05-09 14:06:26 +08:00

496 lines
18 KiB
C#
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
using Qdp.Pricing.Base.Utilities;
using YLErp.DBModels;
using YLErp.QdpModule;
namespace YLErp.Modules.TradeModule.ExoticOptionModule
{
/// <summary>
/// 专业版雪球观察帮助类
/// </summary>
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));
}
/// <summary>
/// 获取雪球观察结果(买方)
/// </summary>
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;
}
/// <summary>
/// 获取雪球观察结果(交易员角度)
/// </summary>
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;
}
/// <summary>
/// 获取累积票息(tradeNotional为负值时是卖方角度)
/// </summary>
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----到期日敲入收益----
/// <summary>
/// 获取到期日敲入收益
/// </summary>
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----获取到期日未敲入收益----
/// <summary>
/// 获取到期日未敲入收益
/// </summary>
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----获取敲出收益----
/// <summary>
/// 获取敲出收益
/// </summary>
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;
}
/// <summary>
/// 解析敲出观察频率
/// </summary>
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;
}
/// <summary>
/// 获取票息支付日
/// </summary>
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;
}
/// <summary>
/// 是否需要观察敲入
/// </summary>
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
{
/// <summary>
/// 观察日在敲出观察频率中的索引
/// </summary>
public int KoDateIndex { get; set; }
/// <summary>
/// 敲出障碍价格
/// </summary>
public double KoBarrier { get; set; }
/// <summary>
/// 敲出支付票息率
/// </summary>
public double KoRebate { get; set; }
}
#endregion
}
}