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 { /// /// 检查交易观察日状态和票息是否产生 /// public class CheckTradeObservateStatusService : YLBaseService { public CheckTradeObservateStatusService(YLBaseService baseService) : base(baseService) { } public CheckTradeObservateStatusService(OptUserInfo userInfo) : base(userInfo) { } public KeyValuePair 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(); } if (td.TradeStatus != ConsTrade.确认成交) { return new KeyValuePair(); } return checkBarrierObservateStatus(td, valuedate, price); case "双鲨期权": if (!string.IsNullOrEmpty(td.trade_double_sharkfin_option.KnockInOutStatus)) { return new KeyValuePair(); } if (td.TradeStatus != ConsTrade.确认成交) { return new KeyValuePair(); } 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(); } } public KeyValuePair 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(ConsTrade.KnockState.KnockedIn, false); } break; case "上升敲出": if (highPrice >= BarrierPrice) { return new KeyValuePair(ConsTrade.KnockState.KnockedOut, true); } break; case "下降敲入": if (lowPrice <= BarrierPrice) { return new KeyValuePair(ConsTrade.KnockState.KnockedIn, false); } break; case "下降敲出": if (lowPrice <= BarrierPrice) { return new KeyValuePair(ConsTrade.KnockState.KnockedOut, true); } break; case "双障碍敲出": if (highPrice >= UpperBarrierPrice || lowPrice <= BarrierPrice) { return new KeyValuePair(ConsTrade.KnockState.KnockedOut, true); } break; case "双障碍敲入": if (highPrice >= UpperBarrierPrice || lowPrice <= BarrierPrice) { return new KeyValuePair(ConsTrade.KnockState.KnockedIn, false); } break; } return new KeyValuePair(); } public KeyValuePair 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(ConsTrade.KnockState.KnockedOut, true); } return new KeyValuePair(); } public KeyValuePair 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("", true); } return new KeyValuePair("", false); } public KeyValuePair 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(ConsTrade.KnockState.KnockedIn, true); } } } //SaveCouponObservation(trade.trade, observation, valuedate, closePrice, isKnockedOut); } return new KeyValuePair(ConsTrade.KnockState.Monitoring, true); } #endregion #region 敲出检查 // 发生敲出事件 if (isKnockedOut) { return new KeyValuePair(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(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(ConsTrade.KnockState.KnockedIn, false); } else if (td.trade_autocall.KnockInOutStatus != ConsTrade.KnockState.KnockedOut) { return new KeyValuePair(ConsTrade.KnockState.Monitoring, false); } else if (td.trade_autocall.KnockInOutStatus == ConsTrade.KnockState.KnockedOut) { return new KeyValuePair(ConsTrade.KnockState.KnockedOut, false); } } #endregion return new KeyValuePair(); } public KeyValuePair 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(ConsTrade.KnockState.KnockedOut, false); } else { if (SettlementAmount != null) { return new KeyValuePair(ConsTrade.KnockState.KnockedOut, false); } else { return new KeyValuePair(ConsTrade.KnockState.KnockedOut, true); } } } return new KeyValuePair(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(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(ConsTrade.KnockState.KnockedIn, false); } else if (td.trade_snowball.KnockInOutStatus != ConsTrade.KnockState.KnockedOut) { return new KeyValuePair(ConsTrade.KnockState.Monitoring, false); } } #endregion return new KeyValuePair(); } public KeyValuePair checkAirbagObservateStatus(trade td, DateTime valuedate, double[] price) { //气囊结构暂时都是每日连续观察,不用考虑观察周期问题 //气囊结构暂时都是向下敲入 var tradeAirbag = td.trade_airbag; if (valuedate == td.ExerciseDate) { if (price[0] >= td.SpotPrice) { return new KeyValuePair(tradeAirbag.KnockInOutStatus, true); } else if (tradeAirbag.KnockInOutStatus == ConsTrade.KnockState.KnockedIn) { return new KeyValuePair(tradeAirbag.KnockInOutStatus, true); } else { return new KeyValuePair(tradeAirbag.KnockInOutStatus, false); } } return new KeyValuePair(tradeAirbag.KnockInOutStatus, false); } } }