148 lines
6.2 KiB
C#
148 lines
6.2 KiB
C#
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;
|
|
}
|
|
}
|
|
}
|