142 lines
6.3 KiB
C#
142 lines
6.3 KiB
C#
using CsvHelper;
|
||
using Qdp.Foundation.Implementations;
|
||
using Qdp.Pricing.Library.Options.Products.Autocall.Snowball;
|
||
using System;
|
||
using System.Collections.Generic;
|
||
using System.Linq;
|
||
using System.Text;
|
||
using System.Threading.Tasks;
|
||
using YLErp.Abstract;
|
||
using YLErp.BLL;
|
||
using YLErp.DBModels;
|
||
using YLErp.DBModels.Helpers;
|
||
using YLErp.Modules.CalculationModule;
|
||
using YLErp.Modules.TradeModule.ExoticOptionModule;
|
||
using YLErp.Modules.TradeModule.KnockOutModule.Dto;
|
||
|
||
namespace YLErp.Modules.TradeModule.KnockOutModule
|
||
{
|
||
/// <summary>
|
||
/// 雪球期权敲出计算服务
|
||
/// </summary>
|
||
public class SnowBallTradeKnockOutService : ITradeKnockOutService
|
||
{
|
||
public GetKnockOutPayoffResult GetKnockOutPayoff(trade td, double underlyingPrice, DateTime _valueDate, ITradeExtendDataProvider _tradeExtendDataProvider)
|
||
{
|
||
var snowBallOption = _tradeExtendDataProvider.GetTrade_Snowball_Option(td.id);
|
||
if (snowBallOption == null)
|
||
{
|
||
return new GetKnockOutPayoffResult { IsKnockOut = false };
|
||
}
|
||
td.trade_snowball = snowBallOption;
|
||
return GetKnockOutPayoff(td, underlyingPrice, _valueDate);
|
||
}
|
||
|
||
public GetKnockOutPayoffResult GetKnockOutPayoff(trade td, double underlyingPrice, DateTime _valueDate)
|
||
{
|
||
GetKnockOutPayoffResult result = new GetKnockOutPayoffResult { IsKnockOut = false };
|
||
var snowBallOption = td.trade_snowball;
|
||
if (snowBallOption == null)
|
||
{
|
||
return result;
|
||
}
|
||
if (snowBallOption.PrepaymentUsed)
|
||
{
|
||
var specialSnowBallResult = new SpecialSnowballObservationHelper(td, snowBallOption).GetObservationResultForTraderSide(_valueDate, underlyingPrice, td.Notional);
|
||
if (specialSnowBallResult != null && specialSnowBallResult.ResultType == SnowballObservationResultType.KoPayoff)
|
||
{
|
||
result.IsKnockOut = true;
|
||
result.Payoff = specialSnowBallResult.PaymentAmount;
|
||
}
|
||
return result;
|
||
}
|
||
|
||
var request = new OptionTradeParamRequest(valuedateBLL.SysRiskFreeRate())
|
||
{
|
||
ParamOverride = p => p.notional = td.Notional
|
||
};
|
||
|
||
var optionTrade = QdpTradeBuilder.GetSnowballOptionTrade(td, snowBallOption, request);
|
||
var snowball = (SimpleSnowball)optionTrade.Instrument;
|
||
var isCall = ConsGlobal.CallPut.IsCall(td.CallPut);
|
||
|
||
if (snowball.KOObsDates.Select(x => x.DateTime).Contains(_valueDate))
|
||
{
|
||
(var koSettleDate, var koBarrier) = GetSnowBallKoSettleInfo(_valueDate, td, snowBallOption, snowball);
|
||
// 发生敲出事件(看涨 - 向上敲出支付票息,看跌 - 向下敲出支付票息)
|
||
if (isCall ? underlyingPrice >= koBarrier : underlyingPrice <= koBarrier)
|
||
{
|
||
result.IsKnockOut = true;
|
||
if (snowball.UseOptionPayoffAtKO)
|
||
{
|
||
double paymentAmount = 0;
|
||
var koOptionCashflows = snowball.GetKOPayoff(new Date(_valueDate), underlyingPrice);
|
||
paymentAmount = TradeHelper.GetAmountByPaymentAmount(koOptionCashflows[0].PaymentAmount, td.PrincipalSum(), td.BuySell);
|
||
result.Payoff = paymentAmount;
|
||
|
||
}
|
||
else
|
||
{
|
||
var CouponPayment = snowball.CouponPayment(_valueDate, includeStartDate: snowBallOption.CouponIncludeStartDate == true && snowBallOption.CouponDayCount != "Monthly");
|
||
var couponPayment = TradeHelper.GetAmountByPaymentAmount(CouponPayment, td.PrincipalSum(), td.BuySell);
|
||
if (snowBallOption.AnnualizedPremiumRate.HasValue && snowBallOption.AnnualizedPremiumRate != 0)
|
||
{
|
||
var tradePrice = (td.StockEqvNotional * td.ParticipationRate * snowBallOption.AnnualizedPremiumRate * snowball.CouponDayCount.CalcDayCountFraction(snowball.StartDate, new Date(_valueDate))) ?? 0;
|
||
if (tradePrice != 0)
|
||
{
|
||
couponPayment += (td.BuySell == "买入" ? -1 : 1) * tradePrice;
|
||
}
|
||
}
|
||
result.Payoff = couponPayment;
|
||
}
|
||
}
|
||
}
|
||
|
||
return result;
|
||
}
|
||
|
||
//获取雪球期权敲出时要准备的信息
|
||
public (DateTime koSettleDate, double koBarrier) GetSnowBallKoSettleInfo(DateTime valueDate, OtcTradeBase otcTrade, trade_snowball tradeSnowball, SimpleSnowball snowball)
|
||
{
|
||
double koBarrier;
|
||
var koSettleDate = valueDate;
|
||
var datesStr = tradeSnowball.KOObservationSettleDates;
|
||
var KOObsSettleDates = string.IsNullOrWhiteSpace(datesStr) ? null : datesStr.Split(new char[] { ',', ';', ',', ';' }, StringSplitOptions.RemoveEmptyEntries).Select(x => DateTime.Parse(x)).ToArray();
|
||
if (snowball.CustomizedKOBarriers != null && snowball.CustomizedKOBarriers.Length > 0)
|
||
{
|
||
var index = snowball.KOObsDates.Select(x => x.DateTime).ToList().IndexOf(valueDate);
|
||
koBarrier = snowball.CustomizedKOBarriers[index];
|
||
if (KOObsSettleDates != null && KOObsSettleDates.Length > index)
|
||
{
|
||
koSettleDate = KOObsSettleDates[index];
|
||
}
|
||
}
|
||
else
|
||
{
|
||
koBarrier = tradeSnowball.KOBarrier;
|
||
if (KOObsSettleDates != null && KOObsSettleDates.Any())
|
||
{
|
||
koSettleDate = KOObsSettleDates[0];
|
||
}
|
||
}
|
||
|
||
if (tradeSnowball.KORebateType == RebateTypeEnum.AtEnd)
|
||
{
|
||
koSettleDate = otcTrade.ExerciseDate.Value;
|
||
}
|
||
else if (koSettleDate < valueDate)
|
||
{
|
||
koSettleDate = valueDate;
|
||
}
|
||
|
||
if (otcTrade.IsMoneynessOptionData)
|
||
{
|
||
koBarrier *= otcTrade.SpotPrice ?? 1.0;
|
||
}
|
||
|
||
return (koSettleDate, koBarrier);
|
||
}
|
||
|
||
}
|
||
}
|