using Qdp.Foundation.Implementations; using Qdp.Pricing.Base.Implementations; using Qdp.Pricing.Library.Options.Products.Autocall.Phoenix; using Qdp.Pricing.Library.Options.Products.Autocall.Snowball; using YLErp.BLL; using YLErp.BLL.Calculation.V2; using YLErp.BLL.EodSettlement; using YLErp.DBModels; using YLErp.DBModels.Helpers; using YLErp.Models; using YLErp.Modules.CalculationModule; using YLErp.Modules.TradeModule; using YLErp.Modules.TradeModule.DealModule; using YLErp.Modules.TradeModule.ExoticOptionModule; using YLErp.QdpModule; namespace YLErp.Modules.RiskModule { /// /// 资金流动性预测 /// public class FlowRiskService : YLBaseService { public FlowRiskService(OptUserInfo userInfo) : base(userInfo) { } public FlowRiskService(YLBaseService baseService) : base(baseService) { } /// /// 价格 /// private Dictionary _prices = new Dictionary(); /// /// 障碍期权 /// private BarrierOptionKnockioService barrierService = null; /// /// 二元期权 /// private BinaryOptionDealService binaryService = null; /// /// 双鲨期权 /// private DoubleSharkOptionKnockoutService doubleSharkService = null; /// /// 区间累积期权 /// private TradeRangeAccrualService rangeAccrualService = null; /// /// 凤凰期权 /// private TradeAutocallBLL autocallService = null; /// /// 雪球期权 /// private TradeSnowballBLL snowballService = null; public List QueryFlowRiskList(DateTime startDate, DateTime endDate) { if (endDate < startDate) { return new List(); } var flowRiskList = new List(); var dates = QdpCalendarHelper.AllBizDays(startDate, endDate); var list = (from t in DbContext.trade where ConsTrade.PositionTradeStatusList.Contains(t.TradeStatus) && t.ExerciseDate >= startDate && t.ValidState != "InValid" select t).ToArray(); #if DEBUG //string temp = ""; //list = DbContext.trade.Where(O => O.TradeNumber == temp).ToArray(); #endif tradeBLL.SetFieldsByTradeType(list); var subTradeDict = list.Where(O => O.ParentTradeId > 0).GroupBy(O => O.ParentTradeId).ToDictionary(K => K.Key, V => V.ToList()); list = list.Where(O => O.ParentTradeId == 0).ToArray(); foreach (var item in list) { if (subTradeDict.ContainsKey(item.id)) { item.SubTrades = subTradeDict[item.id]; } if (item.UnderlyingCode == null) { item.UnderlyingCode = ""; } if (!_prices.ContainsKey(item.UnderlyingCode)) { _prices[item.UnderlyingCode] = DataCacheProvider.GetUnderlyingDataSource().GetPrice(item.UnderlyingCode); } if (item.SubTrades != null && item.SubTrades.Any()) { var endAll = true; DateTime? settledate = null; var trades = item.SubTrades.Where(O => O.TradeType != "现金流交易"); foreach (var sItem in trades) { if (!_prices.ContainsKey(sItem.UnderlyingCode)) { _prices[sItem.UnderlyingCode] = DataCacheProvider.GetUnderlyingDataSource().GetPrice(sItem.UnderlyingCode); } var obj = CheckTradeStatus(sItem, startDate, endDate); flowRiskList.Add(obj); settledate = obj?.ValueDate ?? sItem.ExerciseDate ?? endDate; if (!ConsTrade.TradeCompleteStatus.Contains(sItem.TradeStatus)) { endAll = false; } } if (endAll) { //结构化交易中所有非现金流交易全部了结时,现金流交易也应该了结; trades = item.SubTrades.Where(O => O.TradeType == "现金流交易"); foreach (var sItem in trades) { var obj = new FlowRiskModel(); var udm = new underlying_manager(); udm.QuotationDate = sItem.ExerciseDate; var ttm = TradeCalcHelper.CalculateTTMDays(sItem.ExerciseDate.Value, sItem.ExerciseDate.Value, udm.UnderlyingTypeId, false); var OptionValue = ValueCalculator.GetOptionValueResultV2( UserInfo.UserId.ToString(), udm, sItem, new double[] { 0.0 }, new double[] { 0.0 }, null, timeToMaturityDays: ttm, request: QdpPricingRequest.BASIC_PRICING); obj.Execute -= OptionValue.Pv; obj.ValueDate = settledate ?? startDate; obj.TradeType = "现金流交易"; obj.TradeNumber = sItem.TradeNumber; obj.EncryptId = sItem.EncryptId; flowRiskList.Add(obj); } } } else { var obj = CheckTradeStatus(item, startDate, endDate); flowRiskList.Add(obj); } } var dict = flowRiskList.Where(O => O != null && O.TotleCash != 0).GroupBy(O => O.ValueDate).ToDictionary(K => K.Key, V => V.ToList()).OrderBy(O => O.Key); var result = new List(); var total = new FlowRiskModel() { ParentLevel = 2 }; foreach (var item in dict) { var parent = new FlowRiskModel() { ParentLevel = 1, ValueDate = item.Key, KO = item.Value.Sum(O => O.KO), Execute = item.Value.Sum(O => O.Execute) }; total.KO += parent.KO; total.Execute += parent.Execute; result.Add(parent); result.AddRange(item.Value); } result.Add(total); return result; } private FlowRiskModel CheckTradeStatus(trade td, DateTime startDate, DateTime endDate) { var result = new FlowRiskModel(); result.TradeNumber = td.TradeNumber; result.TradeType = td.TradeType; result.EncryptId = td.EncryptId; switch (td.TradeType) { case "香草期权": case "亚式期权": case "合成价差期权": case "收益增强结构": case "自定义交易": if (td.ExerciseDate > endDate) { result = null; break; } trade_cash r = new trade_cash(); SettlementCalcCommons.SetPossibleExec(r, td, _prices[td.UnderlyingCode]); td.TradeStatus = "已到期"; result.Execute = -r.Amount; result.ValueDate = td.ExerciseDate.Value; break; case "收益互换": if (td.ExerciseDate > endDate) { result = null; break; } var tradeCashList = DbContext.trade_cash.Where(y => y.TradeId == td.id && y.Action == "系统操作-互换" && y.ValidState != "InValid" && !y.IsDeleted && y.ValueDate <= td.ExerciseDate); var tradeCashIds = tradeCashList.Select(x => x.id); var tradeCash = tradeCashList.OrderByDescending(y => y.id).FirstOrDefault(); var cashSwaps = DbContext.trade_cash_swap.Where(x => x.TradeId == td.id && tradeCashIds.Contains(x.TradeCashId)).ToArray(); var tradeCashSwap = tradeCash != null ? cashSwaps.FirstOrDefault(x => x.TradeCashId == tradeCash.id) : null; //取最后一次手动收益; var lastManualCashSwap = cashSwaps.OrderByDescending(o => o.StartDate).FirstOrDefault(x => !x.IsAuto); var lastManualCash = lastManualCashSwap != null ? tradeCashList.FirstOrDefault(x => x.id == lastManualCashSwap.TradeCashId) : null; DateTime endDateSwap; var preSwapDate = PayoffSwapCalcService.GetSwapRateStartDate(td, td.trade_swap, td.ExerciseDate.Value, tradeCash, lastManualCash, td.trade_swap.IsGetFloatingProfit, out endDateSwap); var initialAmountPay = PayoffSwapCalcService.GetInitialAmountSwapPay(td, td.trade_swap, tradeCashSwap?.PayFinalPrice ?? td.trade_swap.PaySpotPrice ?? 0 , _prices[td.UnderlyingCode], td.StockEqvNotional, td.ExerciseDate.Value, tradeCash?.ValueDate); var extraAmountGet = PayoffSwapCalcService.GetExtraAmountBySwapRate(td.ClientId, td.TradeDate, td.trade_swap.GetSwapTimeAndRate, preSwapDate, endDateSwap, td.trade_swap.AnnualDays ?? 0, td.StockEqvNotional); preSwapDate = PayoffSwapCalcService.GetSwapRateStartDate(td, td.trade_swap, td.ExerciseDate.Value, tradeCash, lastManualCash, td.trade_swap.IsPayFloatingProfit, out endDateSwap); var initialAmountGet = PayoffSwapCalcService.GetInitialAmountSwapGet(td, td.trade_swap, tradeCashSwap?.GetFinalPrice ?? td.trade_swap.GetSpotPrice ?? 0 , _prices[td.UnderlyingCode], td.StockEqvNotional, td.ExerciseDate.Value, tradeCash?.ValueDate); var extraAmountPay = PayoffSwapCalcService.GetExtraAmountBySwapRate(td.ClientId, td.TradeDate, td.trade_swap.PaySwapTimeAndRate, preSwapDate, endDateSwap, td.trade_swap.AnnualDays ?? 0, td.StockEqvNotional); td.TradeStatus = "已到期"; result.Execute = -((initialAmountGet + extraAmountGet) - (initialAmountPay + extraAmountPay)); result.ValueDate = td.ExerciseDate.Value; break; case "现金流交易": if (td.ExerciseDate > endDate) { result = null; break; } var udm = new underlying_manager(); udm.QuotationDate = td.ExerciseDate; var ttm = TradeCalcHelper.CalculateTTMDays(td.ExerciseDate.Value, td.ExerciseDate.Value, udm.UnderlyingTypeId, false); var OptionValue = ValueCalculator.GetOptionValueResultV2( UserInfo.UserId.ToString(), udm, td, new double[] { 0.0 }, new double[] { 0.0 }, null, timeToMaturityDays: ttm, request: QdpPricingRequest.BASIC_PRICING); result.Execute -= OptionValue.Pv; result.ValueDate = td.ExerciseDate.Value; if ((td.SettlementDate ?? td.ExerciseDate.GetValueOrDefault()) > endDate) { result = null; } break; case "凤凰期权": { autocallService = autocallService ?? new TradeAutocallBLL(UserInfo); var observDates = CalendarImpl.Get("chn").BizDaysBetweenDatesInclEndDay( new Date(td.TradeDate), new Date(td.ExerciseDate.Value)).Where(O => O.DateTime != td.TradeDate).ToArray(); //敲入转期权和到期支付票息同时存在时,若设置观察价格页面设置了结算金额,作为票息处理,敲入了结金额维持系统计算逻辑不变 bool hasUseSettlementAmount = false; var tradeCashs = DbContext.trade_cash.Where(x => x.ValidState != ConsGlobal.InValid && !x.IsDeleted && x.TradeId == td.id && x.Action == "系统操作-平仓费" && (x.ValueDate > startDate && (x.ConfirmDate > startDate || x.ConfirmDate == DateTime.MinValue)) && x.UnwindNotional < x.Notional).ToList(); var notional = (ConsTrade.TradeCompleteStatus.Contains(td.TradeStatus) && td.UnWindDate <= startDate ? 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; var happenedObservations = new List(); foreach (var date in observDates) { if (date.DateTime < startDate || date.DateTime > endDate) { continue; } var isKnockedOut = false; //只在敲出观察日检查敲出和票息情况 //如果交易已经是敲出状态了,不用再做票息和敲出检查 if (autocall.KOObsDates.Select(x => x.DateTime).Contains(date.DateTime) && 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(date.DateTime); 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; isKnockedOut = true;// _prices[td.UnderlyingCode] >= koBarrier; //有票息 //if (_prices[td.UnderlyingCode] >= couponBarrier) { //利息计算时,当autocall的Notional包含了符号,则GetEffectiveObservation考虑了买卖方向了 var observation = autocall.GetEffectiveObservation(date); if (observation != null) { td.trade_autocall = td.trade_autocall; //观察日页面设置的结算金额 if (td.SettlementAmount != null) { td.SettlementAmount = td.SettlementAmount; hasUseSettlementAmount = true; //到期敲入且未敲出情况 if (date.DateTime == autocall.ExerciseDates.Last().DateTime && !isKnockedOut) { //当前满足敲入或者已经敲入了 if (autocall.KIObsDates.Select(x => x.DateTime).Contains(date.DateTime) && _prices[td.UnderlyingCode] <= kiBarrier || td.trade_autocall.KnockInOutStatus == ConsTrade.KnockState.KnockedIn) { var optionPayoffPayment = autocall.GetPayoff(new double[] { _prices[td.UnderlyingCode] }); var paymentAmount = TradeHelper.GetAmountByPaymentAmount(optionPayoffPayment[0].PaymentAmount, td.PrincipalSum(), td.BuySell); td.SettlementAmount -= paymentAmount; } } } happenedObservations.Add(autocallService.SaveCouponObservation(td, td.trade_autocall, observation, date.DateTime, _prices[td.UnderlyingCode], isKnockedOut, td.SettlementAmount, false)); } } #endregion #region 敲出检查 // 发生敲出事件 if (isKnockedOut) { // 更新观察状态 td.trade_autocall.KnockInOutStatus = ConsTrade.KnockState.KnockedOut; td.trade_autocall.KnockInOutDate = date.DateTime; // 更新交易状态 td.TradeStatus = ConsTrade.已平仓; td.UnWindDate = date.DateTime; happenedObservations.ForEach(O => { result.KO -= O.PaymentAmount; }); result.ValueDate = date; if (td.trade_autocall.CouponPayType != CouponPayTypeEnum.AtMaturity) { break; } } #endregion } #region 敲入检查 //在当前结算日之前未敲出且未敲入: if (autocall.KIObsDates.Select(x => x.DateTime).Contains(date.DateTime) && !isKnockedOut) { //看涨 - 向下敲入,看跌 - 向上敲入 var knockedin = ConsGlobal.CallPut.IsCall(td.CallPut) ? _prices[td.UnderlyingCode] <= kiBarrier : _prices[td.UnderlyingCode] >= kiBarrier; // 发生敲入事件 if (knockedin) { // 更新观察状态 td.trade_autocall.KnockInOutStatus = ConsTrade.KnockState.KnockedIn; td.trade_autocall.KnockInOutDate = date.DateTime; } } #endregion #region 到期检查 if (date.DateTime == autocall.ExerciseDates.Last().DateTime) { td.UnWindDate = date.DateTime; if (td.trade_autocall.KnockInOutStatus == ConsTrade.KnockState.KnockedIn) { td.TradeStatus = ConsTrade.已执行; if (!autocall.IncludeCouponAfterKI) { // 敲入不支付票息,则要将之前累积的票息删除掉 happenedObservations.Clear(); hasUseSettlementAmount = false; } double paymentAmount = 0; if (td.SettlementAmount != null && !hasUseSettlementAmount) { paymentAmount = td.SettlementAmount ?? 0; } else { //已敲入,到期时计算期权收益 var optionPayoffPayment = autocall.GetPayoff(new double[] { _prices[td.UnderlyingCode] }); //optionPayoffPayment[0].PaymentAmount包含了买卖方向的处理了 paymentAmount = TradeHelper.GetAmountByPaymentAmount(optionPayoffPayment[0].PaymentAmount, td.PrincipalSum(), td.BuySell); } if (td.trade_autocall.CouponPayAtMaturity && td.trade_autocall.IncludeCouponAfterKI) { happenedObservations.AddRange(DbContext.autocall_observation.Where(o => o.TradeId == td.id).ToList()); } } else if (td.trade_autocall.KnockInOutStatus != ConsTrade.KnockState.KnockedOut) { // 更新交易状态 td.TradeStatus = ConsTrade.已到期; if (td.trade_autocall.CouponPayAtMaturity) { happenedObservations.AddRange(DbContext.autocall_observation.Where(o => o.TradeId == td.id).ToList()); } if ((td.SettlementDate ?? td.ExerciseDate.GetValueOrDefault()) > endDate) { result = null; } } else if (td.trade_autocall.KnockInOutStatus == ConsTrade.KnockState.KnockedOut) { if (td.trade_autocall.CouponPayAtMaturity) { happenedObservations.AddRange(DbContext.autocall_observation.Where(o => o.TradeId == td.id).ToList()); } } if (result != null) { happenedObservations.ForEach(O => { result.Execute -= O.PaymentAmount; }); result.ValueDate = date; } break; } #endregion } } break; case "雪球期权": { snowballService = snowballService ?? new TradeSnowballBLL(UserInfo); var observDates = CalendarImpl.Get("chn").BizDaysBetweenDatesInclEndDay( new Date(td.TradeDate), new Date(td.ExerciseDate.Value)).Where(O => O.DateTime != td.TradeDate).ToArray(); var tradeCashs = DbContext.trade_cash.Where(x => x.ValidState != ConsGlobal.InValid && !x.IsDeleted && x.TradeId == td.id && x.Action == "系统操作-平仓费" && (x.ValueDate > startDate && (x.ConfirmDate > startDate || 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 <= startDate ? 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; var happenedObservations = new List(); foreach (var date in observDates) { if (date.DateTime < startDate || date.DateTime > endDate) { continue; } //只在敲出观察日检查敲出和票息情况 //如果交易已经是敲出状态了,不用再做票息和敲出检查 if (snowball.KOObsDates.Select(x => x.DateTime).Contains(date.DateTime) && td.trade_snowball.KnockInOutStatus != ConsTrade.KnockState.KnockedOut) { #region 敲出检查 double koBarrier; var koSettleDate = date.DateTime; if (snowball.CustomizedKOBarriers != null && snowball.CustomizedKOBarriers.Length > 0) { var index = snowball.KOObsDates.Select(x => x.DateTime).ToList().IndexOf(date.DateTime); 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 < date.DateTime) { koSettleDate = date.DateTime; } if (koSettleDate > endDate) { result = null; break; } if (td.IsMoneynessOptionData) { koBarrier *= td.SpotPrice ?? 1.0; } // 发生敲出事件 //if (_prices[td.UnderlyingCode] >= koBarrier) { // 更新观察状态 td.trade_snowball.KnockInOutStatus = ConsTrade.KnockState.KnockedOut; td.trade_snowball.KnockInOutDate = date.DateTime; // 更新交易状态 td.TradeStatus = ConsTrade.已平仓; td.UnWindDate = date.DateTime; if (snowball.UseOptionPayoffAtKO) { double paymentAmount = 0; var koOptionCashflows = snowball.GetKOPayoff(date, _prices[td.UnderlyingCode]); if (td.SettlementAmount != null) { paymentAmount = td.SettlementAmount ?? 0; } else { paymentAmount = TradeHelper.GetAmountByPaymentAmount(koOptionCashflows[0].PaymentAmount, td.PrincipalSum(), td.BuySell); } result.KO -= paymentAmount; result.ValueDate = koSettleDate; break; //koOptionCashflows[0].PaymentAmount包含了买卖方向的处理了 } else { if (td.SettlementAmount != null) { double tradePrice = 0; var settlementAmount = td.SettlementAmount ?? 0; //观察日价格页面的结算金额包含了年化期权费,该处逻辑需要先按照扣除年化期权费来算,后面逻辑会补上年化期权费,否则会重复运算 if (td.trade_snowball.AnnualizedPremiumRate.HasValue && td.trade_snowball.AnnualizedPremiumRate != 0) { tradePrice = td.StockEqvNotional * td.ParticipationRate * td.trade_snowball.AnnualizedPremiumRate * snowball.CouponDayCount.CalcDayCountFraction(snowball.StartDate, date) ?? 0; if (tradePrice != 0) { settlementAmount -= (td.BuySell == "买入" ? -1 : 1) * tradePrice; } } result.KO -= settlementAmount; result.ValueDate = date; break; } else { var couponPayment = TradeHelper.GetAmountByPaymentAmount(snowball.CouponPayment(date), td.PrincipalSum(), td.BuySell); result.KO -= couponPayment; result.ValueDate = date; break; } } } #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 < date.DateTime) && snowball.KIObsDates.Select(x => x.DateTime).Contains(date.DateTime) && td.trade_snowball.KIPayoffType != KIPayoffTypeEnum.None) { var kiBarrier = td.IsMoneynessOptionData ? td.trade_snowball.KIBarrier * td.SpotPrice : td.trade_snowball.KIBarrier; // 发生敲入事件 if (_prices[td.UnderlyingCode] <= kiBarrier) { // 更新观察状态 td.trade_snowball.KnockInOutStatus = ConsTrade.KnockState.KnockedIn; td.trade_snowball.KnockInOutDate = date.DateTime; } } #endregion #region 到期检查 if (date.DateTime == snowball.ExerciseDates.Last().DateTime) { if (snowball.UseOptionPayoffAtMaturity && (td.trade_snowball.KnockInOutStatus == ConsTrade.KnockState.KnockedIn || td.trade_snowball.KIBarrier <= 0)) { td.TradeStatus = ConsTrade.已执行; td.UnWindDate = date.DateTime; double paymentAmount = 0; //已敲入,到期时计算期权收益 var optionPayoffPayment = snowball.GetPayoff(new double[] { _prices[td.UnderlyingCode] }); if (td.SettlementAmount != null) { paymentAmount = td.SettlementAmount ?? 0; } else { paymentAmount = TradeHelper.GetAmountByPaymentAmount(optionPayoffPayment[0].PaymentAmount, td.PrincipalSum(), td.BuySell); } //optionPayoffPayment[0].PaymentAmount包含了买卖方向的处理了 result.Execute -= paymentAmount; result.ValueDate = date; break; } else if (td.trade_snowball.KnockInOutStatus != ConsTrade.KnockState.KnockedOut) { td.TradeStatus = ConsTrade.已到期; td.UnWindDate = date.DateTime; var maturityCouponRate = snowball.Coupon * snowball.InitialSpotPrice; var maturityCouponPayment = snowball.FixedCoupon ? maturityCouponRate * snowball.Notional : maturityCouponRate * snowball.Notional * snowball.CouponDayCount.CalcDayCountFraction(snowball.StartDate, snowball.ExerciseDates.Last()); maturityCouponPayment = TradeHelper.GetAmountByPaymentAmount(maturityCouponPayment, td.PrincipalSum(), td.BuySell); double tradePrice = 0; if (td.trade_snowball.AnnualizedPremiumRate.HasValue && td.trade_snowball.AnnualizedPremiumRate != 0) { tradePrice = td.StockEqvNotional * td.ParticipationRate * td.trade_snowball.AnnualizedPremiumRate * snowball.CouponDayCount.CalcDayCountFraction(snowball.StartDate, date) ?? 0; } tradeCash = new trade_cash(); if (td.SettlementAmount != null) { //观察日价格页面的结算金额包含了年化期权费,该处逻辑需要先按照扣除年化期权费来算,后面逻辑会补上年化期权费,否则会重复运算 var settlementAmount = td.SettlementAmount ?? 0; if (tradePrice != 0) { settlementAmount -= (td.BuySell == "买入" ? -1 : 1) * tradePrice; } result.Execute -= settlementAmount; result.ValueDate = date; } else { result.Execute -= maturityCouponPayment; result.ValueDate = date; } if ((td.SettlementDate ?? td.ExerciseDate.GetValueOrDefault()) > endDate) { result = null; } } } #endregion } } break; //case "气囊结构": // //气囊结构没有计算赔付; // airbagService = airbagService ?? new TradeAirbagService(UserInfo); // airbagService.CheckAirbagKnockInStatus(td, td.trade_airbag, startDate, _prices[td.UnderlyingCode]); // break; case "区间累积期权": { if (td.ExerciseDate > endDate) { result = null; break; } rangeAccrualService = rangeAccrualService ?? new TradeRangeAccrualService(UserInfo); var observDates = QdpHelper.ParseObservationDate(td.trade_rangeaccrual.ObservationDates)?.ToArray(); if (observDates == null) { observDates = CalendarImpl.Get("chn").BizDaysBetweenDatesInclEndDay( new Date(td.TradeDate.Value), new Date(td.ExerciseDate.Value)).Where(O => O.DateTime != td.TradeDate).ToArray(); } observDates = observDates.Where(O => O.DateTime >= startDate).ToArray(); var happenedObservations = new List(); if (observDates != null) { foreach (var date in observDates) { var upperRange = td.IsMoneynessOptionData ? td.trade_rangeaccrual.UpperRange * td.SpotPrice.Value : td.trade_rangeaccrual.UpperRange; var lowerRange = td.IsMoneynessOptionData ? td.trade_rangeaccrual.LowerRange * td.SpotPrice.Value : td.trade_rangeaccrual.LowerRange; //有区间收益 if (_prices[td.UnderlyingCode] < upperRange && _prices[td.UnderlyingCode] > lowerRange) { var couponCash = td.trade_rangeaccrual.BonusRate * td.Notional * (td.SpotPrice ?? 0) / observDates.Length * (td.BuySell == "卖出" ? -1 : 1); happenedObservations.Add(rangeAccrualService.SaveObservation(td, td.trade_rangeaccrual, date, couponCash, td.Notional, false)); } } } //到期日生成票息资金记录 td.TradeStatus = ConsTrade.已到期; happenedObservations.AddRange(DbContext.autocall_observation.Where(o => o.TradeId == td.id).ToList()); //happenedObservations?.ForEach(o => //{ // var paymentAmount = TradeHelper.GetAmountByPaymentAmount(o.PaymentAmount, td.PrincipalSum(), td.BuySell); // result.Execute -= paymentAmount; //}); result.Execute -= happenedObservations.Sum(n => n.PaymentAmount) + td.PrincipalSum(); result.ValueDate = td.ExerciseDate.Value; if ((td.SettlementDate ?? td.ExerciseDate.GetValueOrDefault()) > endDate) { result = null; } } break; case "双鲨期权": { var observDates = QdpHelper.ParseObservationDate(td.trade_double_sharkfin_option.ObservationDates)?.ToArray(); if (observDates == null) { observDates = CalendarImpl.Get("chn").BizDaysBetweenDatesInclEndDay( new Date(td.TradeDate), new Date(td.ExerciseDate.Value)).Where(O => O.DateTime != td.TradeDate).ToArray(); } var lastDate = observDates.Where(O => O >= new Date(startDate) && O <= new Date(endDate)).OrderBy(O => (O)).FirstOrDefault(); if (lastDate != default) { doubleSharkService = doubleSharkService ?? new DoubleSharkOptionKnockoutService(UserInfo); var barrierHigh = td.IsMoneynessOptionData ? td.trade_double_sharkfin_option.BarrierHigh * td.SpotPrice : td.trade_double_sharkfin_option.BarrierHigh; var barrierLow = td.IsMoneynessOptionData ? td.trade_double_sharkfin_option.BarrierLow * td.SpotPrice : td.trade_double_sharkfin_option.BarrierLow; var useRebate = _prices[td.UnderlyingCode] <= barrierLow; var price = _prices[td.UnderlyingCode]; td.trade_double_sharkfin_option.KnockInOutStatus = ConsTrade.KnockState.KnockedOut; td.trade_double_sharkfin_option.KnockInOutDate = startDate; td.TradeStatus = ConsTrade.已平仓; td.UnWindDate = startDate; r = doubleSharkService.SaveDoubleSharkFinRebateCash(td, td.trade_double_sharkfin_option, price, startDate, useRebate, false); result.KO = -r?.Amount; if (td.trade_double_sharkfin_option.RebateType == "AtHit") { result.ValueDate = lastDate; } else if (td.trade_double_sharkfin_option.RebateType == "AtEnd" && td.ExerciseDate <= endDate) { result.ValueDate = td.ExerciseDate.Value; } else { result = null; } } else if (td.ExerciseDate <= endDate) { td.TradeStatus = ConsTrade.已到期; td.UnWindDate = td.ExerciseDate; r = new trade_cash(); SettlementCalcCommons.SetPossibleExec(r, td, _prices[td.UnderlyingCode]); result.Execute = -r?.Amount; result.ValueDate = td.ExerciseDate.Value; if ((td.SettlementDate ?? td.ExerciseDate.GetValueOrDefault()) > endDate) { result = null; } } else { result = null; } } break; case "二元期权": { if (td.ExerciseMode == "American") { r = null; binaryService = binaryService ?? new BinaryOptionDealService(UserInfo); var isObservationDate = true; double upPrice = _prices[td.UnderlyingCode], lowPrice = _prices[td.UnderlyingCode]; var strike = td.IsMoneynessOptionData ? td.Strike * td.SpotPrice : td.Strike; var UpperBarrier = new Lazy(() => td.IsMoneynessOptionData ? td.trade_binary_option.UpperBarrier * td.SpotPrice : td.trade_binary_option.UpperBarrier); if (isObservationDate) { switch (td.trade_binary_option.PayoffType) { case "UpOneTouch": //触碰,买方获得盈利,交易结束 var cash = BinaryOptionDealService.GetCashOrNothingAmount(td, td.trade_binary_option, startDate, useHighAmount: false); r = binaryService.SaveBinarySettleCash(td, td.trade_binary_option, startDate, cash, true, upPrice, false); td.TradeStatus = ConsTrade.已执行; td.UnWindDate = startDate; break; case "DownOneTouch": //触碰,买方获得盈利,交易结束 cash = BinaryOptionDealService.GetCashOrNothingAmount(td, td.trade_binary_option, startDate, useHighAmount: false); r = binaryService.SaveBinarySettleCash(td, td.trade_binary_option, startDate, cash, true, lowPrice, false); td.TradeStatus = ConsTrade.已执行; td.UnWindDate = startDate; break; case "UpNoTouch": //触碰,买方无盈利,交易结束 r = binaryService.SaveBinarySettleCash(td, td.trade_binary_option, startDate, 0, false, upPrice, false); td.TradeStatus = ConsTrade.已到期; td.UnWindDate = startDate; break; case "DownNoTouch": //触碰,买方无盈利,交易结束 r = binaryService.SaveBinarySettleCash(td, td.trade_binary_option, startDate, 0, false, lowPrice, false); td.TradeStatus = ConsTrade.已到期; td.UnWindDate = startDate; break; case "DoubleOneTouch": var breachHighBarrier = upPrice >= UpperBarrier.Value; var price = breachHighBarrier ? upPrice : lowPrice; cash = BinaryOptionDealService.GetCashOrNothingAmount(td, td.trade_binary_option, startDate, useHighAmount: breachHighBarrier); //触碰上限或下限,买方获得盈利,交易结束 r = binaryService.SaveBinarySettleCash(td, td.trade_binary_option, startDate, cash, true, price, false); td.TradeStatus = ConsTrade.已执行; td.UnWindDate = startDate; break; case "DoubleNoTouch": price = upPrice >= td.trade_binary_option.UpperBarrier ? upPrice : lowPrice; //触碰上限或下限,买方无盈利,交易结束 r = binaryService.SaveBinarySettleCash(td, td.trade_binary_option, startDate, 0, false, price, false); td.TradeStatus = ConsTrade.已到期; td.UnWindDate = startDate; break; } if (td.trade_binary_option.RebateType == "AtHit") { result.ValueDate = startDate; } else if (td.trade_binary_option.RebateType == "AtEnd" && td.ExerciseDate <= endDate) { result.ValueDate = td.ExerciseDate.Value; } else { result = null; } } //OneTouch类型,到期仍未触碰,则交易结束 if (td.trade_binary_option.PayoffType.Contains("OneTouch") && td.ExerciseDate <= startDate) { binaryService.SaveBinarySettleCash(td, td.trade_binary_option, startDate, 0, false, _prices[td.UnderlyingCode], false); td.TradeStatus = ConsTrade.已到期; td.UnWindDate = startDate; } //NoTouch类型,到期仍未触碰,买方获得盈利,交易结束 if (td.trade_binary_option.PayoffType.Contains("NoTouch") && td.ExerciseDate <= endDate) { var cash = BinaryOptionDealService.GetCashOrNothingAmount(td, td.trade_binary_option, startDate, useHighAmount: false); r = binaryService.SaveBinarySettleCash(td, td.trade_binary_option, startDate, cash, false, _prices[td.UnderlyingCode], false); td.TradeStatus = ConsTrade.已执行; td.UnWindDate = endDate; } result.KO = -r?.Amount; } else if (td.ExerciseDate <= endDate) { r = new trade_cash(); SettlementCalcCommons.SetPossibleExec(r, td, _prices[td.UnderlyingCode]); result.Execute = -r.Amount; result.ValueDate = td.ExerciseDate.Value; if ((td.SettlementDate ?? td.ExerciseDate.GetValueOrDefault()) > endDate) { result = null; } } else { result = null; } } break; case "障碍期权": { r = null; barrierService = barrierService ?? new BarrierOptionKnockioService(UserInfo); var observDates = QdpHelper.ParseObservationDate(td.trade_barrier_option.ObservationDates)?.ToArray(); if (observDates == null) { observDates = CalendarImpl.Get("chn").BizDaysBetweenDatesInclEndDay( new Date(td.TradeDate), new Date(td.ExerciseDate.Value)).Where(O => O.DateTime != td.TradeDate).ToArray(); } var lastDate = observDates.Where(O => O >= new Date(startDate) && O <= new Date(endDate)).OrderBy(O => (O)).FirstOrDefault(); if (lastDate != default) { switch (td.trade_barrier_option.Discrete) { case "离散": switch (td.trade_barrier_option.BarrierType) { case "上升敲出": td.trade_barrier_option.KnockInOutStatus = ConsTrade.KnockState.KnockedOut; td.trade_barrier_option.KnockInOutDate = lastDate; td.trade_barrier_option.KnockInOutNotional = td.Notional; td.TradeStatus = ConsTrade.已平仓; td.UnWindDate = lastDate; r = barrierService.SetTradeCash(td, td.trade_barrier_option, lastDate, _prices[td.UnderlyingCode], td.trade_barrier_option.BarrierPrice, false); break; case "下降敲出": td.trade_barrier_option.KnockInOutStatus = ConsTrade.KnockState.KnockedOut; td.trade_barrier_option.KnockInOutDate = lastDate; td.trade_barrier_option.KnockInOutNotional = td.Notional; td.TradeStatus = ConsTrade.已平仓; td.UnWindDate = lastDate; r = barrierService.SetTradeCash(td, td.trade_barrier_option, lastDate, _prices[td.UnderlyingCode], td.trade_barrier_option.BarrierPrice, false); break; case "双障碍敲出": td.trade_barrier_option.KnockInOutStatus = ConsTrade.KnockState.KnockedOut; td.trade_barrier_option.KnockInOutDate = lastDate; td.trade_barrier_option.KnockInOutNotional = td.Notional; td.TradeStatus = ConsTrade.已平仓; td.UnWindDate = lastDate; r = barrierService.SetTradeCash(td, td.trade_barrier_option, lastDate, _prices[td.UnderlyingCode], td.trade_barrier_option.UpperBarrierPrice, false); break; } break; case "连续": switch (td.trade_barrier_option.BarrierType) { case "上升敲出": td.trade_barrier_option.KnockInOutStatus = ConsTrade.KnockState.KnockedOut; td.trade_barrier_option.KnockInOutDate = lastDate; td.trade_barrier_option.KnockInOutNotional = td.Notional; td.TradeStatus = ConsTrade.已平仓; td.UnWindDate = lastDate; r = barrierService.SetTradeCash(td, td.trade_barrier_option, lastDate, _prices[td.UnderlyingCode], td.trade_barrier_option.BarrierPrice, false); break; case "下降敲出": td.trade_barrier_option.KnockInOutStatus = ConsTrade.KnockState.KnockedOut; td.trade_barrier_option.KnockInOutDate = lastDate; td.trade_barrier_option.KnockInOutNotional = td.Notional; td.TradeStatus = ConsTrade.已平仓; td.UnWindDate = lastDate; r = barrierService.SetTradeCash(td, td.trade_barrier_option, lastDate, _prices[td.UnderlyingCode], td.trade_barrier_option.BarrierPrice, false); break; case "双障碍敲出": td.trade_barrier_option.KnockInOutStatus = ConsTrade.KnockState.KnockedOut; td.trade_barrier_option.KnockInOutDate = lastDate; td.trade_barrier_option.KnockInOutNotional = td.Notional; td.TradeStatus = ConsTrade.已平仓; td.UnWindDate = lastDate; r = barrierService.SetTradeCash(td, td.trade_barrier_option, lastDate, _prices[td.UnderlyingCode], td.trade_barrier_option.UpperBarrierPrice, false); break; } break; } result.KO = -r?.Amount; if (td.trade_barrier_option.RebateType == "AtHit") { result.ValueDate = lastDate; } else if (td.trade_barrier_option.RebateType == "AtEnd" && td.ExerciseDate <= endDate) { result.ValueDate = td.ExerciseDate.Value; } else { result = null; } } else if (td.ExerciseDate <= endDate) { td.TradeStatus = ConsTrade.已到期; td.UnWindDate = td.ExerciseDate; r = new trade_cash(); SettlementCalcCommons.SetPossibleExec(r, td, _prices[td.UnderlyingCode]); result.Execute = -r.Amount; result.ValueDate = td.ExerciseDate.Value; if ((td.SettlementDate ?? td.ExerciseDate.GetValueOrDefault()) > endDate) { result = null; } } else { result = null; } } break; case "远期": case "掉期": { if (td.ExerciseDate > endDate) { result = null; break; } var variety = DataCacheProvider.GetVariety(td.UnderlyingCode); result.Execute = ((td.Strike - _prices[td.UnderlyingCode]) * td.Notional * variety.CountRatio) ?? 0; if ((td.BuySell == "卖出" && td.OptionType == "看涨") || (td.BuySell == "买入" && td.OptionType == "看跌")) { result.Execute *= -1; } result.ValueDate = td.ExerciseDate.Value; result.TradeType = "远期"; if ((td.SettlementDate ?? td.ExerciseDate.GetValueOrDefault()) > endDate) { result = null; } } break; case "累计期权": { var payment = 0d; var cashAtEnd = td.trade_accumulator_option.SettlementMode == "现金期末"; _prices.TryGetValue(td.UnderlyingCode, out var price); var underlying = DataCacheProvider.GetUnderlyingDataSource().GetData(td.UnderlyingCode); var notional = td.trade_accumulator_option.AccumuTradeAmount * underlying.CountRatio; if (cashAtEnd || td.trade_accumulator_option.SettlementMode == "实物交割") { td.trade_accumulator_option.SettlementMode = "现金当日"; } result.ValueDate = startDate; for (var dt = startDate; dt <= endDate; dt = dt.AddDays(1)) { var checkResult = TradeModule.AccumulatorOptionModule.TradeAccumulatorService.CheckAccumulatorPayoff(td, td.trade_accumulator_option, dt, price, notional); if (checkResult == null) continue; if (checkResult.SettlementMode == "票息" || checkResult.SettlementMode == "现金当日") { payment += checkResult.PaymentAmount; } else if (checkResult.SettlementMode == "敲出") { if (td.trade_accumulator_option.EarlyTerminate) { result.ValueDate = dt; result.KO = cashAtEnd || payment >= 0 ? 0 : -payment; break; } } if (dt >= td.ExerciseDate.Value) { result.ValueDate = dt; result.Execute = payment >= 0 ? 0 : -payment; break; } } } break; default: result = null; break; } if (result != null && result.TotleCash != 0) { if (td.PremiumPayDate.GetValueOrDefault() > td.TradeDate && td.PremiumPayDate.GetValueOrDefault() >= startDate && td.PremiumPayDate.GetValueOrDefault() <= endDate) { var tradeprice = td.TradePrice * (td.BuySell == "卖出" ? -1 : 1); if (result.KO != 0) { result.KO += tradeprice; } if (result.Execute != 0) { result.Execute += tradeprice; } } } if (result == null) { //避免结构化交易装填判断错误; td.TradeStatus = "确认成交"; } return result; } } /// /// 流动性风险 /// public class FlowRiskModel { public int ParentLevel { get; set; } public string EncryptId { get; set; } public string TradeType { get; set; } /// /// 日期 /// public DateTime? ValueDate { get; set; } /// /// 准备金 /// public double TotleCash { get { return (KO ?? 0) + (Execute ?? 0); } } /// /// 交易编号 /// public string TradeNumber { get; set; } /// /// 敲出 /// public double? KO { get; set; } /// /// 到期 /// public double? Execute { get; set; } public FlowRiskModel() { this.KO = 0; this.Execute = 0; } } }