using YLErp.BLL; using YLErp.Modules.TradeModule.AccumulatorOptionModule; using YLErp.Modules.TradeModule.ExoticOptionModule; using YLErp.QdpModule; namespace YLErp.Modules.TradeModule.ObservationModule { /// /// 期权交易观察操作 /// public partial class OptionObservationService : TradeServiceBase { public OptionObservationService(YLBaseService baseService) : base(baseService) { } public OptionObservationService(OptUserInfo userInfo) : base(userInfo) { } /// /// 检查观察状态(from tradeController.GetIsObservationDay) /// public CheckObservationResult CheckObservation(CheckObservationRequest req) { var td = DbContext.trade.AsNoTracking().FirstOrDefault(n => n.id == req.tradeId); if (td == null) { throw new ServiceException("找不到交易信息"); } return CheckObservation(td, req.valueDate, req.price); } /// /// 检查观察状态(from tradeController.GetIsObservationDay) /// public CheckObservationResult CheckObservation(trade td, DateTime valueDate, double price) { if (td is null) { throw new ArgumentNullException(nameof(td)); } tradeBLL.SetFieldsByTradeType(td); CheckObservationResult result; switch (td.TradeType) { case "障碍期权": result = CheckBarrieKioStatus(td, valueDate, price); break; case "双鲨期权": result = CheckDbSharkKioStatus(td, valueDate, price); break; case "区间累积期权": result = CheckRangeAccKioStatus(td, valueDate, price); break; case "凤凰期权": result = CheckAutoCallKioStatus(td, valueDate, price); break; case "雪球期权": result = CheckSnowballKioStatus(td, valueDate, price); break; case "气囊结构": result = CheckAirbagKioStatus(td, valueDate, price); break; case "累计期权": result = CheckAccumulatorObservation(td, valueDate, price); break; default: throw new ServiceException("不支持此交易类型:" + td.TradeType); } return result; } private static (bool blKI, bool blKO) IsObservationDay(OtcTrade td, DateTime valueDate, string observationDateStr, string koObservationDateStr) { if (valueDate < td.TradeDate.Value || valueDate > td.ExerciseDate.Value) { return (false, false); } var observationDates = QdpHelper.GetObservationDatesFromString(observationDateStr); var observationDatesKO = QdpHelper.GetObservationDatesFromString(koObservationDateStr); var blKI = observationDates == null ? valueDate > td.StartDate.Value : observationDates.Contains(valueDate); if (ConsGlobal.TradeType.RangeAcc.Equals(td.TradeType)) { blKI = observationDates == null ? valueDate >= td.StartDate.Value : observationDates.Contains(valueDate); } var blKO = observationDatesKO == null || observationDatesKO.Contains(valueDate); return (blKI, blKO); } /// /// 与IsObservationDay区别 增加票息日期处理 /// private static (bool blKI, bool blKO, bool blKOCoupon) IsObservationDayV2(OtcTrade td, DateTime valueDate, string observationDateStr, string koObservationDateStr, string CouponBarrierStr) { if (valueDate < td.TradeDate.Value || valueDate > td.ExerciseDate.Value) { return (false, false, false); } var observationDates = QdpHelper.GetObservationDatesFromString(observationDateStr); var observationDatesKO = QdpHelper.GetObservationDatesFromString(koObservationDateStr); var CouponBarrierKO = QdpHelper.GetObservationDatesFromString(CouponBarrierStr); var blKI = observationDates == null ? valueDate > td.StartDate.Value : observationDates.Contains(valueDate); var blKO = observationDatesKO == null || observationDatesKO.Contains(valueDate); var blKOCoupon = CouponBarrierKO == null || CouponBarrierKO.Contains(valueDate); return (blKI, blKO, blKOCoupon); } //障碍期权 private CheckObservationResult CheckBarrieKioStatus(trade td, DateTime valueDate, double price) { var result = new CheckObservationResult { UnderlyingCode = td.UnderlyingCode }; var tdEx = td.trade_barrier_option; result.IsObservationDay = IsObservationDay(td, valueDate, tdEx.ObservationDates, null).blKI; if (result.IsObservationDay) { result.KnockInOutStatusObservation = "观察中"; var barrier = td.IsMoneynessOptionData ? tdEx.BarrierPrice * td.SpotPrice : tdEx.BarrierPrice; var upperBarrier = new Lazy(() => td.IsMoneynessOptionData ? tdEx.UpperBarrierPrice * td.SpotPrice : tdEx.UpperBarrierPrice); if (tdEx.BarrierType == "上升敲出") { if (price >= barrier) { result.KnockInOutStatusObservation = "敲出"; } } else if (tdEx.BarrierType == "上升敲入") { if (price >= barrier) { result.KnockInOutStatusObservation = "敲入"; } } else if (tdEx.BarrierType == "下降敲出") { if (price <= barrier) { result.KnockInOutStatusObservation = "敲出"; } } else if (tdEx.BarrierType == "下降敲入") { if (price <= barrier) { result.KnockInOutStatusObservation = "敲入"; } } else if (tdEx.BarrierType == "双障碍敲出") { if (price <= barrier || price >= upperBarrier.Value) { result.KnockInOutStatusObservation = "敲出"; } } else if (tdEx.BarrierType == "双障碍敲入") { if (price <= barrier || price >= upperBarrier.Value) { result.KnockInOutStatusObservation = "敲入"; } } } return result; } //双鲨期权 private CheckObservationResult CheckDbSharkKioStatus(trade td, DateTime valueDate, double price) { var result = new CheckObservationResult { UnderlyingCode = td.UnderlyingCode }; var tdEx = td.trade_double_sharkfin_option; result.IsObservationDay = IsObservationDay(td, valueDate, tdEx.ObservationDates, null).blKI; if (result.IsObservationDay) { result.KnockInOutStatusObservation = "观察中"; var barrierLow = td.IsMoneynessOptionData ? tdEx.BarrierLow * td.SpotPrice : tdEx.BarrierLow; var barrierHigh = new Lazy(() => td.IsMoneynessOptionData ? tdEx.BarrierHigh * td.SpotPrice : tdEx.BarrierHigh); if (price <= barrierLow || price >= barrierHigh.Value) { result.KnockInOutStatusObservation = "敲出"; } } return result; } //凤凰期权 private CheckObservationResult CheckAutoCallKioStatus(trade td, DateTime valueDate, double price) { var result = new CheckObservationResult { UnderlyingCode = td.UnderlyingCode }; var tdEx = td.trade_autocall; var ko = TradeObservationHelper.GetAutocallKOObservationAndCoupon(tdEx.KOObservationDates, tdEx.CouponBarrier); var (blKI, blKO, blKOCoupon) = IsObservationDayV2(td, valueDate, tdEx.ObservationDates, ko.Item2, ko.Item1); result.IsObservationDay = blKI || blKO || blKOCoupon; //若所有条件都未满足的默认值处理 if (tdEx.KnockInOutStatusCn == "已敲入") { result.KnockInOutStatusObservation = "敲入"; } else if (result.IsObservationDay) { result.KnockInOutStatusObservation = "观察中"; } //当前周期是否计息 result.CheckData["autocall_NeedCoupon"] = "否"; if (result.IsObservationDay) { var isCall = ConsGlobal.CallPut.IsCall(td.CallPut); result.KOBarrier = TradeObservationHelper.GetPriceByDateV2(ko.Item2, valueDate) ?? tdEx.KOBarrier; if (td.IsMoneynessOptionData) { result.KOBarrier *= td.SpotPrice ?? 0; } if (blKO && (isCall ? price >= result.KOBarrier.Value : price <= result.KOBarrier.Value)) { result.KnockInOutStatusObservation = "敲出"; } else if (blKI) { var kiBarrier = td.IsMoneynessOptionData ? tdEx.KIBarrier * td.SpotPrice : tdEx.KIBarrier; if (isCall ? price <= kiBarrier : price >= kiBarrier) { result.KnockInOutStatusObservation = "敲入"; } } if (blKOCoupon) { //满足敲入到期不支付票息的情况时,当前周期是否计息应该为否 if (!tdEx.IncludeCouponAfterKI && result.KnockInOutStatusObservation == "敲入") { result.CheckData["autocall_NeedCoupon"] = "否"; } else if (isCall ? price >= (td.IsMoneynessOptionData ? tdEx.CouponBarrier * td.SpotPrice : tdEx.CouponBarrier) : price <= (td.IsMoneynessOptionData ? tdEx.CouponBarrier * td.SpotPrice : tdEx.CouponBarrier)) { result.CheckData["autocall_NeedCoupon"] = "是"; } } } else { result.KOBarrier = tdEx.KOBarrier; if (td.IsMoneynessOptionData) { result.KOBarrier *= td.SpotPrice ?? 0; } } //结算金额 var defaultAmount = new TradeAutocallBLL(this).GetDefaultAmount(td, tdEx, valueDate, price); result.CheckData["结算金额"] = defaultAmount.OtcFormatMoney(); return result; } //气囊结构 private CheckObservationResult CheckAirbagKioStatus(trade td, DateTime valueDate, double price) { var result = new CheckObservationResult { UnderlyingCode = td.UnderlyingCode, IsObservationDay = true }; result.KnockInOutStatusObservation = "观察中"; //若所有条件都未满足的默认值处理 if (td.trade_airbag.KnockInOutStatusCn == "已敲入") { result.KnockInOutStatusObservation = "敲入"; } else if (price <= (td.IsMoneynessOptionData ? td.trade_airbag.Barrier * td.SpotPrice : td.trade_airbag.Barrier)) { result.KnockInOutStatusObservation = "敲入"; } return result; } //区间累计 private CheckObservationResult CheckRangeAccKioStatus(trade td, DateTime valueDate, double price) { var result = new CheckObservationResult { UnderlyingCode = td.UnderlyingCode }; var tdEx = td.trade_rangeaccrual; result.IsObservationDay = IsObservationDay(td, valueDate, tdEx.ObservationDates, null).blKI; if (result.IsObservationDay) { var rangeCoupon = new TradeRangeAccrualService(this).GetRangeCoupon(td, td.trade_rangeaccrual, valueDate, price); //观察后区间收益 result.CheckData["rangeacc_BonusObservation"] = rangeCoupon.OtcFormatMoney(); } return result; } //累计期权 private CheckObservationResult CheckAccumulatorObservation(trade td, DateTime valueDate, double price) { new OptionTradeActionRestoreService(this).RestoreTradeDataToSpecialDay(td, valueDate); var tdEx = td.trade_accumulator_option; var checkResult = TradeAccumulatorService.CheckAccumulatorPayoff(td, tdEx, valueDate, price, tdEx.AccumuTradeAmount * (td.CountRatio ?? 1)); var result = new CheckObservationResult { UnderlyingCode = td.UnderlyingCode, IsObservationDay = checkResult != null }; if (result.IsObservationDay) { result.KnockInOutStatusObservation = "观察中"; result.KOBarrier = checkResult.Barrier; if (valueDate == td.ExerciseDate.Value.Date && tdEx.SettlementMode == "现金期末" && (checkResult.SettlementMode != "敲出" || !tdEx.EarlyTerminate)) { checkResult.PaymentNotional = checkResult.PaymentAmount += DbContext.autocall_observation.Where(n => n.TradeId == td.id && n.SettlementMode == "现金期末") .Select(n => n.PaymentAmount).AsEnumerable().DefaultIfEmpty(0).Sum(); checkResult.PaymentAmount *= ConsGlobal.CallPut.IsCall(td.OptionType) ? price - checkResult.Strike : checkResult.Strike - price; checkResult.SettlementMode = "现金当日"; if (td.BuySell != "买入") { checkResult.PaymentAmount = -checkResult.PaymentAmount; } } result.CheckData["累计结算数量"] = "0.00"; result.CheckData["累计结算金额"] = "0.00"; if (checkResult.SettlementMode == "票息" || checkResult.SettlementMode == "现金当日" || checkResult.SettlementMode == "现金当日&转远期") { result.CheckData["累计结算金额"] = checkResult.PaymentAmount.OtcFormatMoney(); result.CheckData["累计结算数量"] = (checkResult.PaymentNotional / checkResult.CountRatio).OtcFormatFlex(2); } else { result.CheckData["累计结算数量"] = (checkResult.PaymentNotional / checkResult.CountRatio).OtcFormatFlex(2); if (checkResult.SettlementMode == "敲出") { result.CheckData["累计结算金额"] = checkResult.PaymentAmount.OtcFormatMoney(); } } result.KnockInOutStatusObservation = checkResult.SettlementMode; } else { result.KOBarrier = tdEx.KOBarrier; if (td.IsMoneynessOptionData) { result.KOBarrier *= td.SpotPrice ?? 0; } } return result; } /// /// 结算金额 /// /// /// /// public double GetTradeObservationCoupon(CheckObservationRequest req) { if (req.price < 0) { throw new ServiceException("观察标的价格不合法!"); } var defaultAmount = 0d; var td = DbContext.trade.Find(req.tradeId); switch (td.TradeType) { case "凤凰期权": var trade_autocall = DbContext.trade_autocall.FirstOrDefault(x => x.TradeId == req.tradeId); defaultAmount = new TradeAutocallBLL(this).GetDefaultAmount(td, trade_autocall, req.valueDate, req.price); break; case "雪球期权": var trade_snowball = DbContext.trade_snowball.FirstOrDefault(x => x.TradeId == req.tradeId); defaultAmount = new TradeSnowballBLL(this).GetDefaultAmount(td, trade_snowball, req.valueDate, req.price); break; default: throw new ServiceException("不支持的交易类型:" + td.TradeType); } return defaultAmount; } /// /// 保存自定义观察日 /// public void SaveManuallyTradeObservationPrice(SaveManuallyTradeObservationRequest req) { //if (req.price < 0) //{ // throw new ServiceException("观察标的价格不合法!"); //} trade td = null; if (req.tradeId > 0) { td = DbContext.trade.Find(req.tradeId); } else { td = DbContext.trade.FirstOrDefault(n => n.TradeNumber == req.tradeNumber); req.tradeId = td.id; } if (td == null) { throw new ServiceException("交易信息不存在:" + (req.tradeId > 0 ? req.tradeId.ToString() : req.tradeNumber)); } new TradeExtendService(this).SetTradeExtend(new[] { td }, tracking: true); var tradeStatus = td.TradeStatus; switch (td.TradeType) { case "障碍期权": if (!string.IsNullOrEmpty(td.trade_barrier_option.KnockInOutStatus)) { throw new ServiceException("请确认该交易的敲入敲出状态"); } if (td.TradeStatus != ConsTrade.确认成交) { throw new ServiceException("该交易非确认成交状态,无法设置"); } new BarrierOptionKnockioService(this).CheckBarrierKnockInOutStatus(td, td.trade_barrier_option, req.valueDate, req.price, req.price, req.price); break; case "双鲨期权": if (!string.IsNullOrEmpty(td.trade_double_sharkfin_option.KnockInOutStatus)) { throw new ServiceException("请确认该交易的敲入敲出状态"); } if (td.TradeStatus != ConsTrade.确认成交) { throw new ServiceException("该交易非确认成交状态,无法设置"); } new DoubleSharkOptionKnockoutService(this).CheckDoubleSharkFinKnockOutStatus(td, td.trade_double_sharkfin_option, req.valueDate, req.price, req.price); break; case "区间累积期权": new TradeRangeAccrualService(this).CheckRangeAccrualBonus(td, td.trade_rangeaccrual, req.valueDate, req.price); break; case "凤凰期权": new TradeAutocallBLL(this).CheckAutocallKnockInOutStatus(td, td.trade_autocall, req.valueDate, req.price, req.SettlementAmount); break; case "雪球期权": new TradeSnowballBLL(this).CheckSnowballKnockInOutStatus(td, td.trade_snowball, req.valueDate, req.price, req.SettlementAmount); break; case "气囊结构": new TradeAirbagService(this).CheckAirbagKnockInStatus(td, td.trade_airbag, req.valueDate, req.price); break; case "累计期权": new TradeAccumulatorService(this).CheckAccumulatorKnockInStatus(td, td.trade_accumulator_option, req.valueDate, req.price); break; default: throw new ServiceException("期权类型不支持:" + td.TradeType); } var records = DbContext.manually_trade_observation_price.Where(x => x.TradeId == req.tradeId && x.ValueDate == req.valueDate); DbContext.manually_trade_observation_price.RemoveRange(records); var r = new manually_trade_observation_price { SettlementAmount = req.SettlementAmount, Price = req.price, TradeId = req.tradeId, ValueDate = req.valueDate, OptDate = DateTime.Now, OptId = UserId, OptName = UserName }; DbContext.manually_trade_observation_price.Add(r); AddTradeOperationHistoryAndSetParentTradeInfo(false, td, "设置观察价格"); if (tradeStatus != td.TradeStatus) { //删除E/Bod_Trade记录 RemoveEodTradeAndFutureInfo(false, td.id, req.valueDate); } DbContext.SaveChanges(); } } }