从山证v2.3.0拷贝

This commit is contained in:
吴方海
2024-05-09 14:06:26 +08:00
parent 566ff33259
commit f9d8a256a6
4471 changed files with 1203456 additions and 9 deletions
@@ -0,0 +1,112 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using YLErp.Abstract;
using YLErp.DBModels;
using YLErp.Modules.TradeModule.AccumulatorOptionModule;
using YLErp.Modules.TradeModule.KnockOutModule.Dto;
namespace YLErp.Modules.TradeModule.KnockOutModule
{
/// <summary>
/// 累计期权敲出计算服务
/// </summary>
public class AccumulatorTradeKnockOutService : ITradeKnockOutService
{
/// <summary>
/// 获取敲出payoff
/// </summary>
/// <param name="td"></param>
/// <param name="underlyingPrice"></param>
/// <param name="_valueDate"></param>
/// <param name="_tradeExtendDataProvider"></param>
/// <returns></returns>
public GetKnockOutPayoffResult GetKnockOutPayoff(trade td, double underlyingPrice, DateTime _valueDate, ITradeExtendDataProvider _tradeExtendDataProvider)
{
var accumulatorOption = _tradeExtendDataProvider.GetTrade_Accumulator_Option(td.id);
if (accumulatorOption == null)
{
return new GetKnockOutPayoffResult { IsKnockOut = false };
}
td.trade_accumulator_option = accumulatorOption;
return GetKnockOutPayoff(td, underlyingPrice, _valueDate);
}
/// <summary>
/// 获取敲出payoff
/// </summary>
/// <param name="td"></param>
/// <param name="underlyingPrice"></param>
/// <param name="_valueDate"></param>
/// <param name="accumulatorOption"></param>
/// <returns></returns>
public GetKnockOutPayoffResult GetKnockOutPayoff(trade td, double underlyingPrice, DateTime _valueDate)
{
GetKnockOutPayoffResult result = new GetKnockOutPayoffResult { IsKnockOut = false };
var accumulatorOption = td.trade_accumulator_option;
if (accumulatorOption == null)
{
return result;
}
var underlying = DataCacheProvider.GetUnderlyingDataSource().GetData(td.UnderlyingCode);
var notional = accumulatorOption.AccumuTradeAmount * underlying.CountRatio;
var changeData = GetChangeDataOfSepecialDay(td.id, _valueDate);
CheckResult checkResult = null;
if (changeData != null)
{
//关键点:还原换月设置到交易时不要更新当前传入的trade和tradeAcc,因为这两个数据涉及到数据库更新
var tdClone = new trade();
YLAutoMapper.Map<trade, trade>(td, tdClone);
var tdAccClone = accumulatorOption.Clone();
tdClone.trade_accumulator_option = accumulatorOption;
TradeAccumulatorHelper.RestoreAccumulatorChangeData(tdClone, tdAccClone, changeData);
checkResult = TradeAccumulatorService.CheckAccumulatorPayoff(tdClone, tdAccClone, _valueDate, underlyingPrice, notional);
}
else
{
checkResult = TradeAccumulatorService.CheckAccumulatorPayoff(td, accumulatorOption, _valueDate, underlyingPrice, notional);
}
if (checkResult != null && "敲出".Equals(checkResult.SettlementMode) && accumulatorOption.EarlyTerminate)
{
result.IsKnockOut = true;
result.Payoff = 0;
}
return result;
}
/// <summary>
/// 获取累计期权换月设置
/// </summary>
/// <param name="tradeId"></param>
/// <param name="valueDate"></param>
/// <returns></returns>
private TradeAccumulatorChangeData GetChangeDataOfSepecialDay(int tradeId, DateTime valueDate)
{
using(var db = DbContextFactory.GetYLDbContext())
{
var query = from a in db.TradeAction.AsNoTracking()
where a.IsValid && a.TradeId == tradeId && a.ValueDate <= valueDate && a.ActionType == TradeActionType.AccumulatorChange
orderby a.ValueDate descending
select a.ActionData;
var actionData = query.FirstOrDefault();
if (string.IsNullOrEmpty(actionData))
{
return null;
}
return JsonHelper.Deserialize<TradeAccumulatorChangeData>(actionData);
}
}
}
}
@@ -0,0 +1,164 @@
using Qdp.Foundation.Implementations;
using Qdp.Pricing.Base.Implementations;
using Qdp.Pricing.Base.Utilities;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using YLErp.Abstract;
using YLErp.BLL.Eod;
using YLErp.DBModels;
using YLErp.Modules.TradeModule.KnockOutModule.Dto;
using YLErp.QdpModule;
namespace YLErp.Modules.TradeModule.KnockOutModule
{
/// <summary>
/// 美式二元期权敲出服务
/// </summary>
public class AmericanBinaryOptionTradeKnockOutService : ITradeKnockOutService
{
public GetKnockOutPayoffResult GetKnockOutPayoff(trade td, double underlyingPrice, DateTime _valueDate, ITradeExtendDataProvider _tradeExtendDataProvider)
{
var binaryOption = _tradeExtendDataProvider.GetTrade_Binary_Option(td.id);
if (binaryOption == null)
{
return new GetKnockOutPayoffResult { IsKnockOut = false };
}
td.trade_binary_option = binaryOption;
return GetKnockOutPayoff(td, underlyingPrice, _valueDate);
}
/// <summary>
/// 获取敲出赔付
/// </summary>
/// <param name="td"></param>
/// <param name="underlyingPrice"></param>
/// <param name="_valueDate"></param>
/// <param name="binaryOption"></param>
/// <returns></returns>
public GetKnockOutPayoffResult GetKnockOutPayoff(trade td, double underlyingPrice, DateTime _valueDate)
{
var binaryOption = td.trade_binary_option;
GetKnockOutPayoffResult result = new GetKnockOutPayoffResult { IsKnockOut = false };
if (binaryOption == null)
{
return result;
}
if (binaryOption.IsDiscreteMonitored)
{
var observationDates = QdpHelper.GetObservationDatesFromString(binaryOption.ObservationDates);
if (observationDates != null && observationDates.Length > 0 && !observationDates.Contains(_valueDate))
{
return result; //非观察日 不做敲出计算
}
}
var strike = td.IsMoneynessOptionData ? td.Strike * td.SpotPrice : td.Strike;
var UpperBarrier = new Lazy<double?>(() =>
td.IsMoneynessOptionData ? binaryOption.UpperBarrier * td.SpotPrice : binaryOption.UpperBarrier);
switch (binaryOption.PayoffType)
{
case "UpOneTouch":
if (underlyingPrice >= strike)
{
//触碰,买方获得盈利,交易结束
result.IsKnockOut = true;
result.Payoff = CalcAmericanBinaryOptionTradeKnockOutPayoff(td, binaryOption, useHighAmount: false, _valueDate);
}
else if (td.ExerciseDate <= _valueDate)
{
result.IsKnockOut = true;
result.Payoff = 0;
}
break;
case "DownOneTouch":
if (underlyingPrice <= strike)
{
//触碰,买方获得盈利,交易结束
result.IsKnockOut = true;
result.Payoff = CalcAmericanBinaryOptionTradeKnockOutPayoff(td, binaryOption, useHighAmount: false, _valueDate);
}
else if (td.ExerciseDate <= _valueDate)
{
result.IsKnockOut = true;
result.Payoff = 0;
}
break;
case "UpNoTouch":
if (underlyingPrice >= strike)
{
//触碰,买方无盈利,交易结束
result.IsKnockOut = true;
result.Payoff = 0;
}
break;
case "DownNoTouch":
if (underlyingPrice <= strike)
{
//触碰,买方无盈利,交易结束
result.IsKnockOut = true;
result.Payoff = 0;
}
break;
case "DoubleOneTouch":
if (underlyingPrice >= UpperBarrier.Value || underlyingPrice <= strike)
{
var breachHighBarrier = underlyingPrice >= UpperBarrier.Value;
result.IsKnockOut = true;
result.Payoff = CalcAmericanBinaryOptionTradeKnockOutPayoff(td, binaryOption, useHighAmount: breachHighBarrier, _valueDate);
}
else if (td.ExerciseDate <= _valueDate)
{
result.IsKnockOut = true;
result.Payoff = 0;
}
break;
case "DoubleNoTouch":
if (underlyingPrice >= UpperBarrier.Value || underlyingPrice <= strike)
{
//触碰上限或下限,买方无盈利,交易结束
result.IsKnockOut = true;
result.Payoff = 0;
}
break;
}
return result;
}
/// <summary>
/// 计算美式二元敲出 收益
/// </summary>
/// <param name="td"></param>
/// <param name="binaryOption"></param>
/// <param name="useHighAmount"></param>
/// <returns></returns>
private double CalcAmericanBinaryOptionTradeKnockOutPayoff(trade td, trade_binary_option binaryOption, bool useHighAmount,DateTime _valueDate)
{
var result = (td.Notional / td.OriginalNotional * (td.OriginalPrincipalSum ?? 0)) ?? 0;
double refund;
if (td.IsUsePremiumRate == true)
{
var rate = useHighAmount ? binaryOption.CashOrNothingAmountHighRate : binaryOption.CashOrNothingAmountRate;
refund = Math.Abs((rate ?? 0) * (td.SpotPrice ?? 0) * td.Notional);
}
else
{
var amount = useHighAmount ? binaryOption.CashOrNothingAmountHigh : binaryOption.CashOrNothingAmount;
refund = Math.Abs((amount ?? 0) * td.Notional);
}
if (binaryOption.RebateAnnualizedAtKO)
{
var rebateDayCountImpl = string.IsNullOrWhiteSpace(binaryOption.RebateDayCount) ? new Act365() : binaryOption.RebateDayCount.ToDayCountImpl();
var fraction = rebateDayCountImpl.CalcDayCountFraction(new Date(td.StartDate.Value), new Date(_valueDate));
refund *= fraction;
refund += Math.Abs((td.TradePrice - td.TradePrice * fraction) ?? 0);
}
return (result + refund) * EodOperationBase.GetSign(td.BuySell);
}
}
}
@@ -0,0 +1,94 @@
using Qdp.Pricing.Library.Options.Products.Autocall.Phoenix;
using YLErp.Abstract;
using YLErp.BLL;
using YLErp.Modules.CalculationModule;
using YLErp.Modules.TradeModule.KnockOutModule.Dto;
namespace YLErp.Modules.TradeModule.KnockOutModule
{
/// <summary>
/// 凤凰期权敲出计算服务
/// </summary>
public class AutocallTradeKnockOutService : ITradeKnockOutService
{
public GetKnockOutPayoffResult GetKnockOutPayoff(trade td, double underlyingPrice, DateTime _valueDate, ITradeExtendDataProvider _tradeExtendDataProvider)
{
var autocallOption = _tradeExtendDataProvider.GetTrade_Autocall_Option(td.id);
if (autocallOption == null)
{
return new GetKnockOutPayoffResult { IsKnockOut = false };
}
td.trade_autocall = autocallOption;
return GetKnockOutPayoff(td, underlyingPrice, _valueDate);
}
public GetKnockOutPayoffResult GetKnockOutPayoff(trade td, double underlyingPrice, DateTime _valueDate)
{
var result = new GetKnockOutPayoffResult { IsKnockOut = false };
var autocallOption = td.trade_autocall;
if (autocallOption == null)
{
return result;
}
//1、获取敲出观察日期
var optionTrade = QdpTradeBuilder.GetAutocallOptionTrade(td, autocallOption,
new OptionTradeParamRequest(valuedateBLL.SysRiskFreeRate()) { ParamOverride = x => { x.notional = td.Notional; } });
var autocall = (AutoCall)optionTrade.Instrument;
var kiBarrier = td.IsMoneynessOptionData ? autocallOption.KIBarrier * td.SpotPrice : autocallOption.KIBarrier;
var isCall = ConsGlobal.CallPut.IsCall(td.CallPut);
if (autocall.KOObsDates.Select(x => x.DateTime).Contains(_valueDate)) //当前日为观察日
{
//2、敲出判断
double koBarrier;
if (autocall.CustomizedKOBarriers != null && autocall.CustomizedKOBarriers.Length > 0)
{
var index = autocall.KOObsDates.Select(x => x.DateTime).ToList().IndexOf(_valueDate);
koBarrier = autocall.CustomizedKOBarriers[index];
}
else
{
koBarrier = autocallOption.KOBarrier;
}
if (td.IsMoneynessOptionData)
{
koBarrier *= td.SpotPrice ?? 1.0;
}
//看涨 - 向上敲出,看跌 - 向下敲出
var isKnockedOut = isCall ? underlyingPrice >= koBarrier : underlyingPrice <= koBarrier;
if (isKnockedOut)
{
result.IsKnockOut = true;
var couponBarrier = td.IsMoneynessOptionData ? autocallOption.CouponBarrier * td.SpotPrice : autocallOption.CouponBarrier;
if (isCall ? underlyingPrice >= couponBarrier : underlyingPrice <= couponBarrier) // 与派息障碍价格比较
{
var observation = autocall.GetEffectiveObservation(_valueDate, includeTradeStartDate: autocallOption.CouponIncludeStartDate == true && autocallOption.CouponDayCount != "Monthly");
if (observation != null)
{
result.Payoff = observation.PaymentAmount;
}
}
if (autocallOption.CouponPayType == CouponPayTypeEnum.AtCreated) //如果是产生时支付,持仓payoff为当日票息
{
return result;
}
//如果到期支付 或者 敲出支付 持仓payoff加上之前的票息
using (var db = DbContextFactory.GetYLDbContext())
{
var obsAmount = db.autocall_observation.Where(o => o.TradeId == td.id && o.EndDate != _valueDate).Sum(p => p.PaymentAmount);
result.Payoff += obsAmount;
}
return result;
}
}
return result;
}
}
}
@@ -0,0 +1,147 @@
using Qdp.Foundation.Implementations;
using Qdp.Pricing.Base.Implementations;
using Qdp.Pricing.Base.Utilities;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using YLErp.Abstract;
using YLErp.BLL.Eod;
using YLErp.BLL;
using YLErp.DBModels;
using YLErp.Modules.TradeModule.KnockOutModule.Dto;
using YLErp.QdpModule;
namespace YLErp.Modules.TradeModule.KnockOutModule
{
/// <summary>
/// 障碍期权敲出服务
/// </summary>
public class BarrierOptionTradeKnockOutService : ITradeKnockOutService
{
public GetKnockOutPayoffResult GetKnockOutPayoff(trade td, double underlyingPrice, DateTime _valueDate, ITradeExtendDataProvider _tradeExtendDataProvider)
{
GetKnockOutPayoffResult result = new GetKnockOutPayoffResult { IsKnockOut = false };
var barrierOption = _tradeExtendDataProvider.GetTrade_Barrier_Option(td.id);
if (barrierOption == null)
{
return new GetKnockOutPayoffResult { IsKnockOut = false };
}
td.trade_barrier_option = barrierOption;
return GetKnockOutPayoff(td, underlyingPrice, _valueDate);
}
public GetKnockOutPayoffResult GetKnockOutPayoff(trade td, double underlyingPrice, DateTime _valueDate)
{
GetKnockOutPayoffResult result = new GetKnockOutPayoffResult { IsKnockOut = false };
var barrierOption = td.trade_barrier_option;
if (barrierOption == null)
{
return result;
}
//连续每天观察 离散根据观察日判断 如果非观察日 不检测敲入敲出
if ("离散".Equals(barrierOption.Discrete))
{
var observationDates = QdpHelper.GetObservationDatesFromString(barrierOption.ObservationDates);
//每日观察或者当前结算日是观察日的时候,才检查是否会敲入敲出
if (observationDates != null && observationDates.Length > 0 && !observationDates.Contains(_valueDate))
{
return result;
}
}
var BarrierPrice = td.IsMoneynessOptionData ? barrierOption.BarrierPrice * td.SpotPrice : barrierOption.BarrierPrice;
var UpperBarrierPrice = td.IsMoneynessOptionData ? barrierOption.UpperBarrierPrice * td.SpotPrice : barrierOption.UpperBarrierPrice;
switch (barrierOption.BarrierType)
{
case "上升敲出":
if (underlyingPrice >= BarrierPrice)
{
result.IsKnockOut = true;
result.Payoff = CalcBarrierOptionTradeKnockOutPayoff(td, barrierOption, underlyingPrice, _valueDate);
}
break;
case "下降敲出":
if (underlyingPrice <= BarrierPrice)
{
result.IsKnockOut = true;
result.Payoff = CalcBarrierOptionTradeKnockOutPayoff(td, barrierOption, underlyingPrice, _valueDate);
}
break;
case "双障碍敲出":
if (underlyingPrice >= UpperBarrierPrice || underlyingPrice <= BarrierPrice)
{
result.IsKnockOut = true;
var upDown = underlyingPrice >= UpperBarrierPrice ? true : false;
result.Payoff = CalcBarrierOptionTradeKnockOutPayoff(td, barrierOption, underlyingPrice, _valueDate, upDown);
}
break;
}
return result;
}
/// <summary>
/// 计算具体收益
/// </summary>
/// <param name="td"></param>
/// <param name="barrierOption"></param>
/// <param name="underlyingPrice"></param>
/// <param name="upDown">双障碍敲出 才有用</param>
/// <returns></returns>
private double CalcBarrierOptionTradeKnockOutPayoff(trade td, trade_barrier_option barrierOption, double underlyingPrice,DateTime _valueDate, bool upDown = false)
{
var spotPrice = td.SpotPrice ?? 0;
double rebate, rebateRate; //计算补偿金额
if (upDown)
{
if (td.IsUsePremiumRate == true)
{
rebateRate = barrierOption.RebateHighRate ?? 0;
rebate = rebateRate * spotPrice;
}
else
{
rebate = barrierOption.RebateHigh ?? 0;
rebateRate = spotPrice > 0 ? rebate / spotPrice : 0;
}
}
else
{
if (td.IsUsePremiumRate == true)
{
rebateRate = barrierOption.RebateRate ?? 0;
rebate = rebateRate * spotPrice;
}
else
{
rebate = barrierOption.Rebate ?? 0; //补偿金额
rebateRate = spotPrice > 0 ? rebate / spotPrice : 0;
}
}
if (barrierOption.RebateAnnualizedAtKO) //补偿按敲出日年化
{
var rebateDayCountImpl = string.IsNullOrWhiteSpace(barrierOption.RebateDayCount) ? new Act365() : barrierOption.RebateDayCount.ToDayCountImpl();
var fraction = rebateDayCountImpl.CalcDayCountFraction(new Date(td.StartDate.Value), new Date(_valueDate));
rebate *= fraction;
rebate += Math.Abs((td.TradeSinglePrice - td.TradeSinglePrice * fraction) ?? 0);
}
var payoff = td.Notional * rebate + ((td.Notional / td.OriginalNotional * td.OriginalPrincipalSum) ?? 0); // 补偿金额 * 持仓份额 + 保底收益总额*尺长比例
//交易员视角
if (valuedateBLL.SystemDate.UnwindAmountAngle == 1)
{
payoff = payoff * (td.BuySell == "卖出" ? -1 : 1);
}
else
{
payoff = payoff * EodOperationBase.GetSign(td.BuySell);
}
return payoff;
}
}
}
@@ -0,0 +1,100 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using YLErp.Abstract;
using YLErp.BLL.Eod;
using YLErp.BLL;
using YLErp.Modules.TradeModule.KnockOutModule.Dto;
using YLErp.QdpModule;
namespace YLErp.Modules.TradeModule.KnockOutModule
{
/// <summary>
/// 双鲨期权敲出计算服务
/// </summary>
public class DoubleSharkTradeKnockOutService : ITradeKnockOutService
{
public GetKnockOutPayoffResult GetKnockOutPayoff(trade td, double underlyingPrice, DateTime _valueDate, ITradeExtendDataProvider _tradeExtendDataProvider)
{
var sharkOption = _tradeExtendDataProvider.GetTrade_Double_SharkFin_Option(td.id);
if (sharkOption == null)
{
return new GetKnockOutPayoffResult { IsKnockOut = false };
}
td.trade_double_sharkfin_option= sharkOption;
return GetKnockOutPayoff(td, underlyingPrice, _valueDate);
}
public GetKnockOutPayoffResult GetKnockOutPayoff(trade td, double underlyingPrice, DateTime _valueDate)
{
GetKnockOutPayoffResult result = new GetKnockOutPayoffResult { IsKnockOut = false };
var sharkOption = td.trade_double_sharkfin_option;
if (sharkOption == null)
{
return result;
}
if (sharkOption.IsDiscrete)
{
var observationDates = QdpHelper.GetObservationDatesFromString(sharkOption.ObservationDates);
if (observationDates != null && observationDates.Length > 0 && !observationDates.Contains(_valueDate))
{
return result; //无需观察时直接返回
}
}
var barrierHigh = td.IsMoneynessOptionData ? sharkOption.BarrierHigh * td.SpotPrice : sharkOption.BarrierHigh;
var barrierLow = td.IsMoneynessOptionData ? sharkOption.BarrierLow * td.SpotPrice : sharkOption.BarrierLow;
if (underlyingPrice >= barrierHigh || underlyingPrice <= barrierLow) //敲出判断条件
{
var useRebate = underlyingPrice <= barrierLow;
result.IsKnockOut = true;
result.Payoff = CalcDoubleSharkTradeKnockOutPayoff(td, sharkOption, underlyingPrice, useRebate);
}
return result;
}
/// <summary>
/// 计算双鲨期权 敲出 收益
/// </summary>
/// <param name="td"></param>
/// <param name="sharkOption"></param>
/// <param name="underlyingPrice"></param>
/// <param name="useRebate"></param>
/// <returns></returns>
private double CalcDoubleSharkTradeKnockOutPayoff(trade td, trade_double_sharkfin_option sharkOption, double underlyingPrice, bool useRebate)
{
double rebate, rebateRate;
var spotPrice = td.SpotPrice ?? 0;
if (td.IsUsePremiumRate == true)
{
rebateRate = (useRebate ? sharkOption.RebateRate : sharkOption.RebateHighRate) ?? 0;
rebate = rebateRate * spotPrice;
}
else
{
rebate = (useRebate ? sharkOption.Rebate : sharkOption.RebateHigh) ?? 0;
rebateRate = spotPrice > 0 ? rebate / spotPrice : 0;
}
var payoff = td.Notional * rebate + ((td.Notional / td.OriginalNotional * td.OriginalPrincipalSum) ?? 0);
if (valuedateBLL.SystemDate.UnwindAmountAngle == 1)
{
payoff = payoff * (td.BuySell == "卖出" ? -1 : 1);
}
else
{
payoff = payoff * EodOperationBase.GetSign(td.BuySell);
}
return payoff;
}
}
}
@@ -0,0 +1,23 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
namespace YLErp.Modules.TradeModule.KnockOutModule.Dto
{
/// <summary>
/// 获取交易敲出 收益结果
/// </summary>
public class GetKnockOutPayoffResult
{
public bool IsKnockOut { get; set; }
/// <summary>
/// 敲出收益
/// </summary>
public double Payoff { get; set; }
}
}
@@ -0,0 +1,36 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using YLErp.Abstract;
using YLErp.Modules.TradeModule.KnockOutModule.Dto;
namespace YLErp.Modules.TradeModule.KnockOutModule
{
/// <summary>
/// 期权交易敲出计算服务
/// </summary>
public interface ITradeKnockOutService
{
/// <summary>
/// 获取交易敲出收益
/// </summary>
/// <param name="td"></param>
/// <param name="underlyingPrice"></param>
/// <param name="_valueDate"></param>
/// <param name="_tradeExtendDataProvider"></param>
/// <returns></returns>
GetKnockOutPayoffResult GetKnockOutPayoff(trade td, double underlyingPrice,DateTime _valueDate, ITradeExtendDataProvider _tradeExtendDataProvider);
/// <summary>
/// 获取交易敲出收益 交易扩展信息不需要从数据库读取,在trade对象的属性中,此方法主要用于定价页面,交易还未生成
/// </summary>
/// <param name="td"></param>
/// <param name="underlyingPrice"></param>
/// <param name="_valueDate"></param>
/// <returns></returns>
GetKnockOutPayoffResult GetKnockOutPayoff(trade td, double underlyingPrice, DateTime _valueDate);
}
}
@@ -0,0 +1,141 @@
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);
}
}
}