394 lines
18 KiB
C#
394 lines
18 KiB
C#
using Qdp.Foundation.Implementations;
|
||
using Qdp.Pricing.Library.Options.Products.Autocall.Phoenix;
|
||
using Qdp.Pricing.Library.Options.Products.Autocall.Snowball;
|
||
using YLErp.BLL;
|
||
using YLErp.Modules.CalculationModule;
|
||
using YLErp.Modules.TradeModule;
|
||
|
||
namespace YLErp.Modules.EodModule
|
||
{
|
||
/// <summary>
|
||
/// 检查交易观察日状态和票息是否产生
|
||
/// </summary>
|
||
public class CheckTradeObservateStatusService : YLBaseService
|
||
{
|
||
public CheckTradeObservateStatusService(YLBaseService baseService) : base(baseService)
|
||
{
|
||
}
|
||
|
||
public CheckTradeObservateStatusService(OptUserInfo userInfo) : base(userInfo)
|
||
{
|
||
}
|
||
|
||
public KeyValuePair<string, bool> CheckTradeObservateStatus(trade td, DateTime valuedate, double[] price, double? SettlementAmount)
|
||
{
|
||
new TradeExtendService(OptUser, DbContext).SetTradeExtend(new[] { td }, tracking: true);
|
||
switch (td.TradeType)
|
||
{
|
||
case "障碍期权":
|
||
if (!string.IsNullOrEmpty(td.trade_barrier_option.KnockInOutStatus))
|
||
{
|
||
return new KeyValuePair<string, bool>();
|
||
}
|
||
if (td.TradeStatus != ConsTrade.确认成交)
|
||
{
|
||
return new KeyValuePair<string, bool>();
|
||
}
|
||
return checkBarrierObservateStatus(td, valuedate, price);
|
||
case "双鲨期权":
|
||
if (!string.IsNullOrEmpty(td.trade_double_sharkfin_option.KnockInOutStatus))
|
||
{
|
||
return new KeyValuePair<string, bool>();
|
||
}
|
||
if (td.TradeStatus != ConsTrade.确认成交)
|
||
{
|
||
return new KeyValuePair<string, bool>();
|
||
}
|
||
return checkDoubleSharkObservateStatus(td, valuedate, price);
|
||
case "区间累积期权":
|
||
return checkRangeAccrualObservateStatus(td, valuedate, price);
|
||
case "凤凰期权":
|
||
return checkAutocallObservateStatus(td, valuedate, price, SettlementAmount);
|
||
case "雪球期权":
|
||
return checkSnowballObservateStatus(td, valuedate, price, SettlementAmount);
|
||
case "气囊结构":
|
||
return checkAirbagObservateStatus(td, valuedate, price);
|
||
default:
|
||
return new KeyValuePair<string, bool>();
|
||
}
|
||
}
|
||
|
||
public KeyValuePair<string, bool> checkBarrierObservateStatus(trade td, DateTime valuedate, double[] price)
|
||
{
|
||
var tradeBarrier = td.trade_barrier_option;
|
||
var BarrierPrice = td.IsMoneynessOptionData ? tradeBarrier.BarrierPrice * td.SpotPrice : tradeBarrier.BarrierPrice;
|
||
var UpperBarrierPrice = td.IsMoneynessOptionData ? tradeBarrier.UpperBarrierPrice * td.SpotPrice : tradeBarrier.UpperBarrierPrice;
|
||
|
||
var highPrice = tradeBarrier.Discrete == "离散" ? price[0] : price[1];
|
||
var lowPrice = tradeBarrier.Discrete == "离散" ? price[0] : price[2];
|
||
|
||
switch (tradeBarrier.BarrierType)
|
||
{
|
||
case "上升敲入":
|
||
if (highPrice >= BarrierPrice)
|
||
{
|
||
return new KeyValuePair<string, bool>(ConsTrade.KnockState.KnockedIn, false);
|
||
}
|
||
break;
|
||
case "上升敲出":
|
||
if (highPrice >= BarrierPrice)
|
||
{
|
||
return new KeyValuePair<string, bool>(ConsTrade.KnockState.KnockedOut, true);
|
||
}
|
||
break;
|
||
case "下降敲入":
|
||
if (lowPrice <= BarrierPrice)
|
||
{
|
||
return new KeyValuePair<string, bool>(ConsTrade.KnockState.KnockedIn, false);
|
||
}
|
||
break;
|
||
case "下降敲出":
|
||
if (lowPrice <= BarrierPrice)
|
||
{
|
||
return new KeyValuePair<string, bool>(ConsTrade.KnockState.KnockedOut, true);
|
||
}
|
||
break;
|
||
case "双障碍敲出":
|
||
if (highPrice >= UpperBarrierPrice || lowPrice <= BarrierPrice)
|
||
{
|
||
return new KeyValuePair<string, bool>(ConsTrade.KnockState.KnockedOut, true);
|
||
}
|
||
break;
|
||
case "双障碍敲入":
|
||
if (highPrice >= UpperBarrierPrice || lowPrice <= BarrierPrice)
|
||
{
|
||
return new KeyValuePair<string, bool>(ConsTrade.KnockState.KnockedIn, false);
|
||
}
|
||
break;
|
||
}
|
||
return new KeyValuePair<string, bool>();
|
||
|
||
}
|
||
|
||
public KeyValuePair<string, bool> checkDoubleSharkObservateStatus(trade td, DateTime valuedate, double[] price)
|
||
{
|
||
var tradeDbShark = td.trade_double_sharkfin_option;
|
||
var barrierHigh = td.IsMoneynessOptionData ? tradeDbShark.BarrierHigh * td.SpotPrice : tradeDbShark.BarrierHigh;
|
||
var barrierLow = td.IsMoneynessOptionData ? tradeDbShark.BarrierLow * td.SpotPrice : tradeDbShark.BarrierLow;
|
||
var highPrice = price[1];
|
||
var lowPrice = price[2];
|
||
if (highPrice >= barrierHigh || lowPrice <= barrierLow)
|
||
{
|
||
return new KeyValuePair<string, bool>(ConsTrade.KnockState.KnockedOut, true);
|
||
}
|
||
return new KeyValuePair<string, bool>();
|
||
}
|
||
|
||
public KeyValuePair<string, bool> checkRangeAccrualObservateStatus(trade td, DateTime valuedate, double[] price)
|
||
{
|
||
var tradeRange = td.trade_rangeaccrual;
|
||
var upperRange = td.IsMoneynessOptionData ? tradeRange.UpperRange * td.SpotPrice.Value : tradeRange.UpperRange;
|
||
var lowerRange = td.IsMoneynessOptionData ? tradeRange.LowerRange * td.SpotPrice.Value : tradeRange.LowerRange;
|
||
|
||
//有区间收益
|
||
if (price[0] < upperRange && price[0] > lowerRange)
|
||
{
|
||
return new KeyValuePair<string, bool>("", true);
|
||
}
|
||
return new KeyValuePair<string, bool>("", false);
|
||
}
|
||
|
||
public KeyValuePair<string, bool> checkAutocallObservateStatus(trade td, DateTime valuedate, double[] price, double? SettlementAmount)
|
||
{
|
||
var tradeCashs = DbContext.trade_cash.Where(x => x.ValidState != ConsGlobal.InValid && !x.IsDeleted && x.TradeId == td.id && x.Action == "系统操作-平仓费" && (x.ValueDate > valuedate && (x.ConfirmDate > valuedate || x.ConfirmDate == DateTime.MinValue)) && x.UnwindNotional < x.Notional).ToList();
|
||
var notional = (ConsTrade.TradeCompleteStatus.Contains(td.TradeStatus) && td.UnWindDate <= valuedate ? 0 : td.Notional) + tradeCashs.Sum(x => x.UnwindNotional).Value;
|
||
var optionTrade = QdpTradeBuilder.GetAutocallOptionTrade(td, td.trade_autocall,
|
||
new OptionTradeParamRequest(valuedateBLL.SysRiskFreeRate()) { ParamOverride = x => { x.notional = notional; } });
|
||
var autocall = (AutoCall)optionTrade.Instrument;
|
||
var kiBarrier = td.IsMoneynessOptionData ? td.trade_autocall.KIBarrier * td.SpotPrice : td.trade_autocall.KIBarrier;
|
||
|
||
if (autocall.KOObsDates.Select(x => x.DateTime).Contains(valuedate)
|
||
&& td.trade_autocall.KnockInOutStatus != ConsTrade.KnockState.KnockedOut)
|
||
{
|
||
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 = td.trade_autocall.KOBarrier;
|
||
}
|
||
|
||
if (td.IsMoneynessOptionData)
|
||
{
|
||
koBarrier *= td.SpotPrice ?? 1.0;
|
||
}
|
||
|
||
#region 票息检查
|
||
var couponBarrier =
|
||
td.IsMoneynessOptionData ?
|
||
td.trade_autocall.CouponBarrier * td.SpotPrice :
|
||
td.trade_autocall.CouponBarrier;
|
||
|
||
var isKnockedOut = price[0] >= koBarrier;
|
||
|
||
//有票息
|
||
if (price[0] >= couponBarrier)
|
||
{
|
||
//利息计算时,当autocall的Notional包含了符号,则GetEffectiveObservation考虑了买卖方向了
|
||
var observation = autocall.GetEffectiveObservation(new Date(valuedate));
|
||
|
||
if (observation != null)
|
||
{
|
||
//观察日页面设置的结算金额
|
||
if (SettlementAmount != null)
|
||
{
|
||
//到期敲入且未敲出情况
|
||
if (valuedate == autocall.ExerciseDates.Last().DateTime && !isKnockedOut)
|
||
{
|
||
//当前满足敲入或者已经敲入了
|
||
if (autocall.KIObsDates.Select(x => x.DateTime).Contains(valuedate) && price[0] <= kiBarrier || td.trade_autocall.KnockInOutStatus == ConsTrade.KnockState.KnockedIn)
|
||
{
|
||
return new KeyValuePair<string, bool>(ConsTrade.KnockState.KnockedIn, true);
|
||
}
|
||
}
|
||
}
|
||
//SaveCouponObservation(trade.trade, observation, valuedate, closePrice, isKnockedOut);
|
||
}
|
||
return new KeyValuePair<string, bool>(ConsTrade.KnockState.Monitoring, true);
|
||
}
|
||
#endregion
|
||
|
||
#region 敲出检查
|
||
|
||
// 发生敲出事件
|
||
if (isKnockedOut)
|
||
{
|
||
return new KeyValuePair<string, bool>(ConsTrade.KnockState.KnockedOut, false);
|
||
}
|
||
#endregion
|
||
}
|
||
|
||
#region 敲入检查
|
||
//在当前结算日之前未敲出且未敲入:
|
||
if (!((td.trade_autocall.KnockInOutStatus == ConsTrade.KnockState.KnockedOut
|
||
|| td.trade_autocall.KnockInOutStatus == ConsTrade.KnockState.KnockedIn)
|
||
&& td.trade_autocall.KnockInOutDate < valuedate)
|
||
&& autocall.KIObsDates.Select(x => x.DateTime).Contains(valuedate))
|
||
{
|
||
// 发生敲入事件
|
||
if (price[0] <= kiBarrier)
|
||
{
|
||
return new KeyValuePair<string, bool>(ConsTrade.KnockState.KnockedIn, false);
|
||
}
|
||
}
|
||
#endregion
|
||
|
||
#region 到期检查
|
||
if (valuedate == autocall.ExerciseDates.Last().DateTime)
|
||
{
|
||
td.UnWindDate = valuedate;
|
||
if (td.trade_autocall.KnockInOutStatus == ConsTrade.KnockState.KnockedIn)
|
||
{
|
||
return new KeyValuePair<string, bool>(ConsTrade.KnockState.KnockedIn, false);
|
||
}
|
||
else if (td.trade_autocall.KnockInOutStatus != ConsTrade.KnockState.KnockedOut)
|
||
{
|
||
return new KeyValuePair<string, bool>(ConsTrade.KnockState.Monitoring, false);
|
||
}
|
||
else if (td.trade_autocall.KnockInOutStatus == ConsTrade.KnockState.KnockedOut)
|
||
{
|
||
return new KeyValuePair<string, bool>(ConsTrade.KnockState.KnockedOut, false);
|
||
}
|
||
}
|
||
#endregion
|
||
|
||
return new KeyValuePair<string, bool>();
|
||
}
|
||
|
||
public KeyValuePair<string, bool> checkSnowballObservateStatus(trade td, DateTime valueDate, double[] price, double? SettlementAmount)
|
||
{
|
||
var tradeCashs = DbContext.trade_cash.Where(x => x.ValidState != ConsGlobal.InValid && !x.IsDeleted && x.TradeId == td.id && x.Action == "系统操作-平仓费" && (x.ValueDate > valueDate && (x.ConfirmDate > valueDate || x.ConfirmDate == DateTime.MinValue)) && x.UnwindNotional < x.Notional).ToList();
|
||
|
||
var request = new OptionTradeParamRequest(valuedateBLL.SysRiskFreeRate())
|
||
{
|
||
ParamOverride = p => p.notional = (ConsTrade.TradeCompleteStatus.Contains(td.TradeStatus) && td.UnWindDate <= valueDate ? 0 : td.Notional) + tradeCashs.Sum(x => x.UnwindNotional).Value
|
||
};
|
||
|
||
var optionTrade = QdpTradeBuilder.GetSnowballOptionTrade(td, td.trade_snowball, request);
|
||
var datesStr = td.trade_snowball.KOObservationSettleDates;
|
||
var KOObsSettleDates = string.IsNullOrWhiteSpace(datesStr) ? null : datesStr.Split(new char[] { ',', ';', ',', ';' }, StringSplitOptions.RemoveEmptyEntries).Select(x => DateTime.Parse(x)).ToArray();
|
||
|
||
var snowball = (SimpleSnowball)optionTrade.Instrument;
|
||
|
||
//只在敲出观察日检查敲出和票息情况
|
||
//如果交易已经是敲出状态了,不用再做票息和敲出检查
|
||
if (snowball.KOObsDates.Select(x => x.DateTime).Contains(valueDate)
|
||
&& td.trade_snowball.KnockInOutStatus != ConsTrade.KnockState.KnockedOut)
|
||
{
|
||
#region 敲出检查
|
||
double koBarrier;
|
||
var koSettleDate = valueDate;
|
||
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 = td.trade_snowball.KOBarrier;
|
||
if (KOObsSettleDates != null && KOObsSettleDates.Any())
|
||
{
|
||
koSettleDate = KOObsSettleDates[0];
|
||
}
|
||
}
|
||
|
||
if (td.trade_snowball.KORebateType == RebateTypeEnum.AtEnd)
|
||
{
|
||
koSettleDate = td.ExerciseDate.Value;
|
||
}
|
||
else if (koSettleDate < valueDate)
|
||
{
|
||
koSettleDate = valueDate;
|
||
}
|
||
|
||
if (td.IsMoneynessOptionData)
|
||
{
|
||
koBarrier *= td.SpotPrice ?? 1.0;
|
||
}
|
||
|
||
// 发生敲出事件
|
||
if (price[0] >= koBarrier)
|
||
{
|
||
|
||
if (snowball.UseOptionPayoffAtKO)
|
||
{
|
||
return new KeyValuePair<string, bool>(ConsTrade.KnockState.KnockedOut, false);
|
||
|
||
}
|
||
else
|
||
{
|
||
if (SettlementAmount != null)
|
||
{
|
||
return new KeyValuePair<string, bool>(ConsTrade.KnockState.KnockedOut, false);
|
||
}
|
||
else
|
||
{
|
||
return new KeyValuePair<string, bool>(ConsTrade.KnockState.KnockedOut, true);
|
||
}
|
||
}
|
||
}
|
||
return new KeyValuePair<string, bool>(td.trade_snowball.KnockInOutStatus, true);
|
||
#endregion
|
||
}
|
||
|
||
#region 敲入检查
|
||
//在当前结算日之前未敲出且未敲入:
|
||
// !((KO || KI) && KOKIDate < valueDate)
|
||
if (!((td.trade_snowball.KnockInOutStatus == ConsTrade.KnockState.KnockedOut
|
||
|| td.trade_snowball.KnockInOutStatus == ConsTrade.KnockState.KnockedIn)
|
||
&& td.trade_snowball.KnockInOutDate < valueDate)
|
||
&& snowball.KIObsDates.Select(x => x.DateTime).Contains(valueDate)
|
||
&& td.trade_snowball.KIPayoffType != KIPayoffTypeEnum.None)
|
||
{
|
||
var kiBarrier =
|
||
td.IsMoneynessOptionData ?
|
||
td.trade_snowball.KIBarrier * td.SpotPrice :
|
||
td.trade_snowball.KIBarrier;
|
||
|
||
// 发生敲入事件
|
||
if (price[0] <= kiBarrier)
|
||
{
|
||
return new KeyValuePair<string, bool>(ConsTrade.KnockState.KnockedIn, false);
|
||
}
|
||
}
|
||
#endregion
|
||
|
||
#region 到期检查
|
||
if (valueDate == snowball.ExerciseDates.Last().DateTime)
|
||
{
|
||
if (snowball.UseOptionPayoffAtMaturity &&
|
||
(td.trade_snowball.KnockInOutStatus == ConsTrade.KnockState.KnockedIn || td.trade_snowball.KIBarrier <= 0))
|
||
{
|
||
return new KeyValuePair<string, bool>(ConsTrade.KnockState.KnockedIn, false);
|
||
}
|
||
else if (td.trade_snowball.KnockInOutStatus != ConsTrade.KnockState.KnockedOut)
|
||
{
|
||
return new KeyValuePair<string, bool>(ConsTrade.KnockState.Monitoring, false);
|
||
}
|
||
}
|
||
#endregion
|
||
return new KeyValuePair<string, bool>();
|
||
}
|
||
|
||
public KeyValuePair<string, bool> checkAirbagObservateStatus(trade td, DateTime valuedate, double[] price)
|
||
{
|
||
//气囊结构暂时都是每日连续观察,不用考虑观察周期问题
|
||
//气囊结构暂时都是向下敲入
|
||
var tradeAirbag = td.trade_airbag;
|
||
if (valuedate == td.ExerciseDate)
|
||
{
|
||
if (price[0] >= td.SpotPrice)
|
||
{
|
||
return new KeyValuePair<string, bool>(tradeAirbag.KnockInOutStatus, true);
|
||
}
|
||
else if (tradeAirbag.KnockInOutStatus == ConsTrade.KnockState.KnockedIn)
|
||
{
|
||
return new KeyValuePair<string, bool>(tradeAirbag.KnockInOutStatus, true);
|
||
}
|
||
else
|
||
{
|
||
return new KeyValuePair<string, bool>(tradeAirbag.KnockInOutStatus, false);
|
||
}
|
||
}
|
||
return new KeyValuePair<string, bool>(tradeAirbag.KnockInOutStatus, false);
|
||
}
|
||
|
||
}
|
||
} |