从山证v2.3.0拷贝
This commit is contained in:
@@ -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);
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
}
|
||||
+164
@@ -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);
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user