using Qdp.Pricing.Library.Options.Products.Autocall.Phoenix; using YLErp.Abstract.DataProviders; using YLErp.BLL; using YLErp.Commons; using YLErp.DBModels.Consts; using YLErp.DBModels.Enums; using YLErp.DBModels.Helpers; using YLErp.Modules.CalculationModule; using YLErp.Modules.DataProviderModule; namespace YLErp.Modules.TradeModule { public class TradeAutocallBLL : ExoticOptionModule.TradeCashServiceEx { public TradeAutocallBLL(OptUserInfo userInfo) : base(userInfo) { } public TradeAutocallBLL(YLBaseService baseService) : base(baseService) { } public void CheckStatus(DateTime valueDate, IEodPriceProviderV2 priceProvider, DateTime? startDate = null , Action afterKnowInOut = null, IEnumerable clienIds = null) { if (priceProvider is null) { priceProvider = new EodPriceProvider(valueDate); } if (startDate == null) { startDate = valueDate.AddYears(-5); } //未敲出的,以及已敲出但敲出日期大于等于当前收盘日的(为了历史收盘) var query = from trade in DbContext.trade join autocall in DbContext.trade_autocall on trade.id equals autocall.TradeId join underlying in DbContext.underlying_manager on trade.UnderlyingId equals underlying.id where trade.TradeDate > startDate.Value && trade.TradeDate <= valueDate && trade.ExerciseDate >= valueDate && (trade.TradeType == "凤凰期权") && (ConsTrade.确认成交 == trade.TradeStatus) && trade.ValidState != ConsGlobal.InValid && (autocall.KnockInOutStatus != ConsTrade.KnockState.KnockedOut || (autocall.KnockInOutStatus == ConsTrade.KnockState.KnockedOut && autocall.KnockInOutDate >= valueDate)) && trade.DividendDate < valueDate select new { underlying = underlying, trade = trade, trade_autocall = autocall }; #region 增加客户筛选 tw if (clienIds != null) { query = query.Where(l => clienIds.Contains(l.trade.ClientId)); } #endregion var trades = query.ToList(); var tradeIds = trades.Select(x => x.trade.id).ToList(); var manuallyTradeObservationPrices = DbContext.manually_trade_observation_price .Where(x => tradeIds.Contains(x.TradeId) && x.ValueDate == valueDate).ToDictionary(n => n.TradeId); foreach (var tr in trades) { if (tr.trade.ExerciseDate < valueDate) { continue;//已到期交易不再观察; } var tradeStatus = tr.trade.TradeStatus; var knockInOutStatus = tr.trade_autocall.KnockInOutStatus; double closePrice; double? SettlementAmount = null; if (manuallyTradeObservationPrices.TryGetValue(tr.trade.id, out var manuallyTradeObservationPrice)) { closePrice = manuallyTradeObservationPrice.Price ?? 0; SettlementAmount = manuallyTradeObservationPrice.SettlementAmount; } else if (!priceProvider.TryGetEodPrice(tr.trade.UnderlyingCode, out var eodPrice)) { throw new Exception($"[{tr.trade.TradeType}:{tr.trade.TradeNumber},标的:{tr.trade.UnderlyingCode}]未找到结算价"); } else { closePrice = eodPrice.ClosePrice; } CheckAutocallKnockInOutStatus(tr.trade, tr.trade_autocall, valueDate, closePrice, SettlementAmount); if (tradeStatus != tr.trade.TradeStatus || knockInOutStatus != tr.trade_autocall.KnockInOutStatus) { //删除E/Bod_Trade记录 RemoveEodTradeAndFutureInfo(false, tr.trade.id, valueDate); } if (afterKnowInOut != null && DbContext.Entry(tr.trade_autocall).State == EntityState.Modified) { afterKnowInOut(tr.trade, tr.trade_autocall); } } DbContext.SaveChanges(); } public double GetDefaultAmount(OtcTradeBase otcTrade, trade_autocall tradeAutoCall, DateTime valueDate, double closePrice) { var defaultAmount = 0d; var tradeCashs = DbContext.trade_cash.Where(x => x.ValidState != ConsGlobal.InValid && !x.IsDeleted && x.TradeId == otcTrade.id && x.Action == "系统操作-平仓费" && (x.ValueDate > valueDate && (x.ConfirmDate > valueDate || x.ConfirmDate == DateTime.MinValue)) && x.UnwindNotional < x.Notional).ToList(); var notional = (ConsTrade.TradeCompleteStatus.Contains(otcTrade.TradeStatus) && otcTrade.UnWindDate <= valueDate ? 0 : otcTrade.Notional) + tradeCashs.Sum(x => x.UnwindNotional).Value; var optionTrade = QdpTradeBuilder.GetAutocallOptionTrade(otcTrade, tradeAutoCall, new OptionTradeParamRequest(valuedateBLL.SysRiskFreeRate()) { ParamOverride = x => { x.notional = notional; } }); var autocall = (AutoCall)optionTrade.Instrument; var isCall = ConsGlobal.CallPut.IsCall(otcTrade.CallPut); //只在敲出观察日检查敲出和票息情况 //如果交易已经是敲出状态了,不用再做票息和敲出检查 if (autocall.KOObsDates.Select(x => x.DateTime).Contains(valueDate) && tradeAutoCall.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 = tradeAutoCall.KOBarrier; } if (otcTrade.IsMoneynessOptionData) { koBarrier *= otcTrade.SpotPrice ?? 1.0; } #region 票息检查 var couponBarrier = otcTrade.IsMoneynessOptionData ? tradeAutoCall.CouponBarrier * otcTrade.SpotPrice : tradeAutoCall.CouponBarrier; //看涨 - 向上敲出,看跌 - 向下敲出 var isKnockedOut = isCall ? closePrice >= koBarrier : closePrice <= koBarrier; //有票息 if (isCall ? closePrice >= couponBarrier : closePrice <= couponBarrier) { //利息计算时,当autocall的Notional包含了符号,则CouponPayment考虑了买卖方向了 defaultAmount = autocall.CouponPayment(valueDate, includeTradeStartDate: tradeAutoCall.CouponIncludeStartDate == true && tradeAutoCall.CouponDayCount != "Monthly"); if (isKnockedOut) { tradeAutoCall.KnockInOutStatus = ConsTrade.KnockState.KnockedOut; defaultAmount = TradeHelper.GetAmountByPaymentAmount(defaultAmount, otcTrade.PrincipalSum(), otcTrade.BuySell); } } #endregion } #region 敲入检查 //在当前结算日之前未敲出且未敲入: if (!((tradeAutoCall.KnockInOutStatus == ConsTrade.KnockState.KnockedOut || tradeAutoCall.KnockInOutStatus == ConsTrade.KnockState.KnockedIn) && tradeAutoCall.KnockInOutDate < valueDate) && autocall.KIObsDates.Select(x => x.DateTime).Contains(valueDate)) { var kiBarrier = otcTrade.IsMoneynessOptionData ? tradeAutoCall.KIBarrier * otcTrade.SpotPrice : tradeAutoCall.KIBarrier; //看涨 - 向下敲入,看跌 - 向上敲入 var knockedin = isCall ? closePrice <= kiBarrier : closePrice >= kiBarrier; // 发生敲入事件 if (knockedin) { // 更新观察状态 tradeAutoCall.KnockInOutStatus = ConsTrade.KnockState.KnockedIn; } } #endregion #region 到期检查 if (valueDate == autocall.ExerciseDates.Last().DateTime) { if (tradeAutoCall.KnockInOutStatus == ConsTrade.KnockState.KnockedIn) { //已敲入,到期时计算期权收益 var optionPayoffPayment = autocall.GetPayoff(new double[] { closePrice }); //敲入是否支付票息 if (tradeAutoCall.IncludeCouponAfterKI) { //optionPayoffPayment[0].PaymentAmount包含了买卖方向的处理了 defaultAmount += TradeHelper.GetAmountByPaymentAmount(optionPayoffPayment[0].PaymentAmount, otcTrade.PrincipalSum(), otcTrade.BuySell); } else { //optionPayoffPayment[0].PaymentAmount包含了买卖方向的处理了 defaultAmount = TradeHelper.GetAmountByPaymentAmount(optionPayoffPayment[0].PaymentAmount, otcTrade.PrincipalSum(), otcTrade.BuySell); } } else if(tradeAutoCall.KnockInOutStatus != ConsTrade.KnockState.KnockedOut) { defaultAmount = TradeHelper.GetAmountByPaymentAmount(defaultAmount, otcTrade.PrincipalSum(), otcTrade.BuySell); } } #endregion return defaultAmount; } public void CheckAutocallKnockInOutStatus(OtcTradeBase otcTrade, trade_autocall tradeAutoCall, DateTime valueDate, double closePrice, double? SettlementAmount) { try { var oldKnockInOutStatus = tradeAutoCall.KnockInOutStatus; InnerCheckAutocallKnockInOutStatus(otcTrade, tradeAutoCall, valueDate, closePrice, SettlementAmount); if (oldKnockInOutStatus != tradeAutoCall.KnockInOutStatus) { var KnockInOutStatus = tradeAutoCall.KnockInOutStatus == ConsTrade.KnockState.KnockedIn ? "敲入" : "敲出"; AddTradeOperationHistoryAndSetParentTradeInfo(false, otcTrade, KnockInOutStatus, KnockInOutStatus); if (KnockInOutStatus == "敲出" && tradeAutoCall.CouponPayType == CouponPayTypeEnum.AtKnockout) { var observations = DbContext.autocall_observation.Where(n => n.TradeId == otcTrade.id).ToArray(); foreach (var item in observations) { item.PaymentDate = valueDate; } DbContext.SaveChanges(); } } } catch (Exception ex) { throw new ServiceException($"[检查敲入敲出]交易编号:{otcTrade.TradeNumber},{ex.Message}", ex); } } private void InnerCheckAutocallKnockInOutStatus(OtcTradeBase otcTrade, trade_autocall tradeAutoCall, DateTime valueDate, double closePrice, double? SettlementAmount) { var hasUseSettlementAmount = false; double notional = 0; var eodTrade = DbContext.eod_trade.FirstOrDefault(x => x.TradeId == otcTrade.id && x.ValueDate == valueDate); if (eodTrade != null) { notional = eodTrade.trade.Notional; } else { var bodTrade = DbContext.bod_Trade.FirstOrDefault(x => x.TradeId == otcTrade.id && x.ValueDate == valueDate); notional = bodTrade != null ? bodTrade.trade.Notional : otcTrade.Notional; var tradeCashs = DbContext.trade_cash.Where(x => x.ValidState != ConsGlobal.InValid && !x.IsDeleted && x.TradeId == otcTrade.id && x.Action != "系统操作-行权费" && x.Action != "系统操作-票息" && x.ValueDate == valueDate).ToList(); notional -= tradeCashs.Sum(x => x.UnwindNotional ?? 0); } //敲出到期支付,或者敲入到期支付票息时,交易可能已经敲出了或者到起执行了,这个时候到期日收盘,需要补上到期票息,这里处理该票息对应的持仓份额 if (tradeAutoCall.CouponPayType == CouponPayTypeEnum.AtMaturity && valueDate == otcTrade.ExerciseDate && ConsTrade.TradeCompleteStatus.Contains(otcTrade.TradeStatus)) { var tradeCash = DbContext.trade_cash.Where(x => x.ValidState != ConsGlobal.InValid && !x.IsDeleted && x.TradeId == otcTrade.id && x.IsLastAction).FirstOrDefault(); if (tradeCash != null) { notional = tradeCash.Notional; } } //敲入转期权和到期支付票息同时存在时,若设置观察价格页面设置了结算金额,作为票息处理,敲入了结金额维持系统计算逻辑不变 var optionTrade = QdpTradeBuilder.GetAutocallOptionTrade(otcTrade, tradeAutoCall, new OptionTradeParamRequest(valuedateBLL.SysRiskFreeRate()) { ParamOverride = x => { x.notional = notional; } }); var autocall = (AutoCall)optionTrade.Instrument; var kiBarrier = otcTrade.IsMoneynessOptionData ? tradeAutoCall.KIBarrier * otcTrade.SpotPrice : tradeAutoCall.KIBarrier; var isCall = ConsGlobal.CallPut.IsCall(otcTrade.CallPut); //只在敲出观察日检查敲出和票息情况 //如果交易已经是敲出状态了,不用再做票息和敲出检查 if (autocall.KOObsDates.Select(x => x.DateTime).Contains(valueDate) && tradeAutoCall.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 = tradeAutoCall.KOBarrier; } if (otcTrade.IsMoneynessOptionData) { koBarrier *= otcTrade.SpotPrice ?? 1.0; } #region 票息检查 var couponBarrier = otcTrade.IsMoneynessOptionData ? tradeAutoCall.CouponBarrier * otcTrade.SpotPrice : tradeAutoCall.CouponBarrier; //看涨 - 向上敲出,看跌 - 向下敲出 var isKnockedOut = isCall ? closePrice >= koBarrier : closePrice <= koBarrier; //有票息 if (isCall ? closePrice >= couponBarrier : closePrice <= couponBarrier) { //利息计算时,当autocall的Notional包含了符号,则GetEffectiveObservation考虑了买卖方向了 var observation = autocall.GetEffectiveObservation(valueDate, includeTradeStartDate: tradeAutoCall.CouponIncludeStartDate == true && tradeAutoCall.CouponDayCount != "Monthly"); if (observation != null) { //otcTrade.trade_autocall = tradeAutocall; var _settlementAmount = SettlementAmount; //观察日页面设置的结算金额 if (SettlementAmount != null) { hasUseSettlementAmount = true; //到期敲入且未敲出情况 if (valueDate == autocall.ExerciseDates.Last().DateTime && !isKnockedOut) { //当前满足敲入或者已经敲入了 if (autocall.KIObsDates.Select(x => x.DateTime).Contains(valueDate) && closePrice <= kiBarrier || tradeAutoCall.KnockInOutStatus == ConsTrade.KnockState.KnockedIn) { tradeAutoCall.KnockInOutStatus = ConsTrade.KnockState.KnockedIn; var optionPayoffPayment = autocall.GetPayoff(new double[] { closePrice }); var paymentAmount = TradeHelper.GetAmountByPaymentAmount(optionPayoffPayment[0].PaymentAmount, otcTrade.PrincipalSum(), otcTrade.BuySell); //在记录票息时将敲入部分的payoff先减掉,在后面到期处理时会再添加一笔敲入的资金记录 _settlementAmount -= paymentAmount; } } } SaveCouponObservation(otcTrade, tradeAutoCall, observation, valueDate, closePrice, isKnockedOut, _settlementAmount); } } #endregion #region 敲出检查 // 发生敲出事件 if (isKnockedOut) { // 更新观察状态 tradeAutoCall.KnockInOutStatus = ConsTrade.KnockState.KnockedOut; tradeAutoCall.KnockInOutDate = valueDate; // 更新交易状态 otcTrade.TradeStatus = ConsTrade.已平仓; otcTrade.UnWindDate = valueDate; var parentTradeId = 0; var parentTradeCashId = 0; //敲出时支付的票息在敲出日写入资金记录 if (tradeAutoCall.CouponPayType != CouponPayTypeEnum.AtCreated) { var hasUnfinishedGroupAction = false; var continueTradeCashHandle = false; var tradeCash = new trade_cash(); if (otcTrade.IsGroup == 2 && otcTrade.ParentTradeId > 0) { var groupAction = DbContext.trade_cash_group_action.FirstOrDefault(x => x.TradeId == otcTrade.id && x.Status != "已完成"); if (groupAction != null) { hasUnfinishedGroupAction = true; groupAction.Status = "已完成"; parentTradeCashId = groupAction.ParentTradeCashId; parentTradeId = groupAction.ParentTradeId; } else { parentTradeId = otcTrade.ParentTradeId; tradeCash = SaveGroupUnwindCash(otcTrade, valueDate, 0, closePrice, out continueTradeCashHandle); parentTradeCashId = tradeCash.id; } } var amount = SaveCouponCashOnEnd(otcTrade, tradeAutoCall.CouponPayType == CouponPayTypeEnum.AtMaturity ? otcTrade.ExerciseDate.Value : valueDate, parentTradeId, parentTradeCashId, closePrice); if (otcTrade.IsGroup == 2 && otcTrade.ParentTradeId > 0 && !hasUnfinishedGroupAction && continueTradeCashHandle) { tradeCash.Amount += amount; DbContext.SaveChanges(); } } SaveCash(otcTrade, ClientCashInCashOut.系统操作_票息, null, 0, tradeAutoCall.CouponPayType == CouponPayTypeEnum.AtMaturity ? otcTrade.ExerciseDate.Value : valueDate, closePrice, valueDate, true, isLastAction: true, parentTradeId: parentTradeId, parentTradeCashId: parentTradeCashId); } #endregion } #region 敲入检查 //在当前结算日之前未敲出且未敲入: if (!((tradeAutoCall.KnockInOutStatus == ConsTrade.KnockState.KnockedOut || tradeAutoCall.KnockInOutStatus == ConsTrade.KnockState.KnockedIn) && tradeAutoCall.KnockInOutDate < valueDate) && autocall.KIObsDates.Select(x => x.DateTime).Contains(valueDate)) { //看涨 - 向下敲入,看跌 - 向上敲入 var knockedin = isCall ? closePrice <= kiBarrier : closePrice >= kiBarrier; // 发生敲入事件 if (knockedin) { // 更新观察状态 tradeAutoCall.KnockInOutStatus = ConsTrade.KnockState.KnockedIn; tradeAutoCall.KnockInOutDate = valueDate; } } #endregion #region 到期检查 if (valueDate == autocall.ExerciseDates.Last().DateTime) { if (tradeAutoCall.KnockInOutStatus == ConsTrade.KnockState.KnockedIn) { otcTrade.TradeStatus = ConsTrade.已执行; otcTrade.UnWindDate = valueDate; if (!autocall.IncludeCouponAfterKI) { // 敲入不支付票息,则要将之前累积的票息删除掉 RemoveAccumulatedCoupon(otcTrade.id); hasUseSettlementAmount = false; } double paymentAmount = 0; if (SettlementAmount != null && !hasUseSettlementAmount) { paymentAmount = SettlementAmount ?? 0; } else { //已敲入,到期时计算期权收益 var optionPayoffPayment = autocall.GetPayoff(new double[] { closePrice }); //optionPayoffPayment[0].PaymentAmount包含了买卖方向的处理了 paymentAmount = TradeHelper.GetAmountByPaymentAmount(optionPayoffPayment[0].PaymentAmount, otcTrade.PrincipalSum(), otcTrade.BuySell); } var parentTradeId = 0; var parentTradeCashId = 0; var hasUnfinishedGroupAction = false; var continueTradeCashHandle = false; var tradeCash = new trade_cash(); if (otcTrade.IsGroup == 2 && otcTrade.ParentTradeId > 0) { var groupAction = DbContext.trade_cash_group_action.FirstOrDefault(x => x.TradeId == otcTrade.id && x.Status != "已完成"); if (groupAction != null) { hasUnfinishedGroupAction = true; groupAction.Status = "已完成"; parentTradeCashId = groupAction.ParentTradeCashId; parentTradeId = groupAction.ParentTradeId; } else { parentTradeId = otcTrade.ParentTradeId; tradeCash = SaveGroupUnwindCash(otcTrade, valueDate, paymentAmount, closePrice, out continueTradeCashHandle); parentTradeCashId = tradeCash.id; } } SaveOptionPayoffCash(otcTrade, paymentAmount, valueDate, closePrice, valueDate, parentTradeId, parentTradeCashId); //期末支付的票息在到期日写入资金记录 if (tradeAutoCall.CouponPayType == CouponPayTypeEnum.AtMaturity && tradeAutoCall.IncludeCouponAfterKI) { var amount = SaveCouponCashOnEnd(otcTrade, valueDate, parentTradeId, parentTradeCashId, closePrice); if (otcTrade.IsGroup == 2 && otcTrade.ParentTradeId > 0 && !hasUnfinishedGroupAction && continueTradeCashHandle) { tradeCash.Amount += amount; DbContext.SaveChanges(); } } } else { if (tradeAutoCall.KnockInOutStatus != ConsTrade.KnockState.KnockedOut) { // 更新交易状态 otcTrade.TradeStatus = ConsTrade.已到期; otcTrade.UnWindDate = valueDate; } var parentTradeId = 0; var parentTradeCashId = 0; var hasUnfinishedGroupAction = false; var continueTradeCashHandle = false; var tradeCash = new trade_cash(); if (otcTrade.IsGroup == 2 && otcTrade.ParentTradeId > 0) { var groupAction = DbContext.trade_cash_group_action.FirstOrDefault(x => x.TradeId == otcTrade.id && x.Status != "已完成"); if (groupAction != null) { hasUnfinishedGroupAction = true; groupAction.Status = "已完成"; parentTradeCashId = groupAction.ParentTradeCashId; parentTradeId = groupAction.ParentTradeId; } else { parentTradeId = otcTrade.ParentTradeId; tradeCash = SaveGroupUnwindCash(otcTrade, valueDate, 0, closePrice, out continueTradeCashHandle); parentTradeCashId = tradeCash.id; } } //期末支付的票息在到期日写入资金记录 if (tradeAutoCall.CouponPayType != CouponPayTypeEnum.AtCreated && tradeAutoCall.KnockInOutStatus != ConsTrade.KnockState.KnockedOut) { var amount = SaveCouponCashOnEnd(otcTrade, valueDate, parentTradeId, parentTradeCashId, closePrice); if (otcTrade.IsGroup == 2 && otcTrade.ParentTradeId > 0 && !hasUnfinishedGroupAction && continueTradeCashHandle) { tradeCash.Amount += amount; DbContext.SaveChanges(); } } if (tradeAutoCall.KnockInOutStatus != ConsTrade.KnockState.KnockedOut) { SaveCash(otcTrade, ClientCashInCashOut.系统操作_票息, "到期行权", 0, valueDate, closePrice, valueDate, false, isLastAction: true, parentTradeId: parentTradeId, parentTradeCashId: parentTradeCashId); } } } #endregion } /// /// 当票息在到期支付时,从autocall_observation表记录的票息累积数据生成对应的资金记录 /// private double SaveCouponCashOnEnd(OtcTradeBase trade, DateTime valueDate, int parentTradeId, int parentTradeCashId, double closePrice) { double amount = 0; var tradeCashes = DbContext.trade_cash.Where(t => t.TradeId == trade.id && !t.IsDeleted && t.Action == ClientCashInCashOut.系统操作_票息).ToList(); if (tradeCashes != null) { DbContext.trade_cash.RemoveRange(tradeCashes); var tradeCashIds = tradeCashes.Select(x => x.id); var tradeCashDetials = DbContext.trade_cash_detail.Where(x => tradeCashIds.Contains(x.TradeCashId)); DbContext.trade_cash_detail.RemoveRange(tradeCashDetials); } var clientCashes = DbContext.ClientCashInCashOut.Where(c => c.TradeId == trade.id && c.Action == ClientCashInCashOut.系统操作_票息).ToList(); if (clientCashes != null) { DbContext.ClientCashInCashOut.RemoveRange(clientCashes); } var observations = DbContext.autocall_observation.Where(o => o.TradeId == trade.id).OrderBy(x => x.EndDate).ToList(); var maxEndDate = observations.Max(x => (DateTime?)x.EndDate) ?? DateTime.MinValue; observations.ForEach(o => { amount += o.PaymentAmount; // 保存trade_cash var tc = new trade_cash { OptId = UserId, OptName = UserName, OptDate = DateTime.Now, ExceciseType = "现金", TradeType = trade.BuySell, CallPut = trade.CallPut, Notional = trade.Notional, TradeAmount = trade.TradeAmount, UnwindNotional = maxEndDate == o.EndDate ? trade.Notional : 0, UnwindTradeAmount = maxEndDate == o.EndDate ? trade.TradeAmount : 0, UnwindPercentRate = maxEndDate == o.EndDate ? (trade.OriginalNotional != 0 ? trade.Notional / trade.OriginalNotional : 0) : 0, Amount = o.PaymentAmount, UnwindPrice = Math.Abs(trade.Notional != 0 ? o.PaymentAmount / trade.Notional : 0), UnwindPricePercentRate = Math.Abs(trade.Notional != 0 && trade.SpotPrice != null && trade.SpotPrice != 0 ? o.PaymentAmount / trade.Notional / trade.SpotPrice.Value : 0), FinalPrice = closePrice, TradeId = trade.id, HappenedDate = o.EndDate, Action = ClientCashInCashOut.系统操作_票息, ExerciseWay = "到期行权", Status = TradeCashStatusEnum.已执行, ValueDate = valueDate, ParentTradeId = parentTradeId, ParentTradeCashId = parentTradeCashId }; DbContext.trade_cash.Add(tc); DbContext.SaveChanges(); SaveTradeCashDetail(tc); // 保存ClientCashInCashOut var client = DataCacheProvider.GetClientDataSource().GetData(trade.ClientId); if (client != null) { var cashInOutRecord = new ClientCashInCashOut { Direction = "应收", Number = UniqueTimeId.GetStr(), ClientId = client.id, ClientName = client.Name, ClientNumber = client.Number, Money = -tc.Amount, HappenDate = valueDate, State = ClientCashInCashOut.已确认, OptDate = tc.OptDate, OptId = tc.OptId, CreatorName = tc.OptName, CreateDate = tc.OptDate, CreatorId = tc.OptId, OptName = tc.OptName, TradeId = trade.id, TradeCashId = tc.id, Action = ClientCashInCashOut.系统操作_票息, TradeNumber = trade.TradeNumber, IsGroup = trade.IsGroup }; DbContext.ClientCashInCashOut.Add(cashInOutRecord); } }); return amount; } /// /// 适用于票息到期支付,但敲入后不支付票息的情况下,将已经累计的票息信息删除 /// private void RemoveAccumulatedCoupon(int tradeId) { var observations = DbContext.autocall_observation.Where(o => o.TradeId == tradeId).ToList(); if (observations.Count > 0) { DbContext.autocall_observation.RemoveRange(observations); } } private trade_cash SaveOptionPayoffCash(OtcTradeBase trade, double amount, DateTime valueDate, double underlyingPrice, DateTime happenDate, int parentTradeId, int parentTradeCashId) { return SaveCash(trade, ClientCashInCashOut.系统操作_行权费, TradeCashExerciseWayEnum.到期行权, amount, valueDate, underlyingPrice, happenDate, false, isLastAction: true, parentTradeId: parentTradeId, parentTradeCashId: parentTradeCashId); } public autocall_observation SaveCouponObservation(OtcTradeBase trade, trade_autocall tradeAutoCall , ObservationPayment observation, DateTime happenDate, double underlyingPrice, bool isKnockedOut, double? SettlementAmount, bool saveChanges = true) { //保存autocall_observation var observationRecord = DbContext.autocall_observation.FirstOrDefault(o => o.TradeId == trade.id && o.EndDate == happenDate); if (observationRecord == null) { observationRecord = new autocall_observation() { TradeId = trade.id, StartDate = observation.StartDate.DateTime.Date, EndDate = observation.EndDate.DateTime.Date, CouponRate = observation.CouponRate, StockEqvNotional = observation.Notional, PaymentAmount = SettlementAmount == null ? observation.PaymentAmount : SettlementAmount.Value, PaymentDate = observation.PaymentDate.DateTime.Date }; DbContext.autocall_observation.Add(observationRecord); } else { observationRecord.StartDate = observation.StartDate.DateTime.Date; observationRecord.EndDate = observation.EndDate.DateTime.Date; observationRecord.CouponRate = observation.CouponRate; observationRecord.StockEqvNotional = observation.Notional; if (SettlementAmount != null) { observationRecord.PaymentAmount = SettlementAmount.Value; } else { // 系统操作_票息 修改 功能暂时禁掉 //bool isupdate = true; //var trade_cash = DbContext.trade_cash.Where(x => x.id == observationRecord.CashId && x.ValidState != "InValid" && x.Action == ClientCashInCashOut.系统操作_票息).FirstOrDefault(); //if (trade_cash != null) //{ // var cashInOutRecord = DbContext.ClientCashInCashOut.FirstOrDefault(c => c.TradeCashId == trade_cash.id); // if (cashInOutRecord != null) // { // if (DbContext.clientcashincashout_update.Where(x => x.ClientcashincashoutId == cashInOutRecord.id && x.ValidState != "InValid" && x.State == "已确认").Any()) // { // isupdate = false; // } // } //} //if (isupdate) //{ // observationRecord.PaymentAmount = observation.PaymentAmount; //} observationRecord.PaymentAmount = observation.PaymentAmount; } observationRecord.PaymentDate = observation.PaymentDate.DateTime.Date; } if (saveChanges) { // 票息当期付,立即产生资金记录 if (tradeAutoCall.CouponPayType == CouponPayTypeEnum.AtCreated) { var parentTradeId = 0; var parentTradeCashId = 0; if (trade.IsGroup == 2 && trade.ParentTradeId > 0) { //已经存在票息记录的说明该票息已经和组合主交易有关联了,不需要再做处理 if (DbContext.trade_cash.Any(t => t.TradeId == trade.id && t.Action == ClientCashInCashOut.系统操作_票息 && !t.IsDeleted && t.HappenedDate == happenDate)) { return observationRecord; } var groupAction = DbContext.trade_cash_group_action.FirstOrDefault(x => x.TradeId == trade.id && x.Status != "已完成"); if (groupAction != null) { groupAction.Status = "已完成"; parentTradeCashId = groupAction.ParentTradeCashId; parentTradeId = groupAction.ParentTradeId; } else { parentTradeId = trade.ParentTradeId; var paymentAmount = SettlementAmount != null ? SettlementAmount.Value : observation.PaymentAmount; parentTradeCashId = SaveGroupCouponCash(trade, happenDate, paymentAmount, underlyingPrice); } } bool isLastAction = isKnockedOut || (happenDate == trade.ExerciseDate && tradeAutoCall.KnockInOutStatus != ConsTrade.KnockState.KnockedIn); var tradeCash = new trade_cash(); if (SettlementAmount != null) { tradeCash = SaveCash(trade, ClientCashInCashOut.系统操作_票息, null, SettlementAmount ?? 0, happenDate, underlyingPrice, happenDate, isKnockedOut, isLastAction: isLastAction, parentTradeId: parentTradeId, parentTradeCashId: parentTradeCashId); } else { var paymentAmount = observation.PaymentAmount; if (isKnockedOut) { paymentAmount = TradeHelper.GetAmountByPaymentAmount(paymentAmount, trade.PrincipalSum(), trade.BuySell); } tradeCash = SaveCash(trade, ClientCashInCashOut.系统操作_票息, happenDate == trade.ExerciseDate ? "到期行权" : null, paymentAmount, happenDate, underlyingPrice, happenDate, isKnockedOut, isLastAction: isLastAction, parentTradeId: parentTradeId, parentTradeCashId: parentTradeCashId); } observationRecord.CashId = tradeCash.id; } DbContext.SaveChanges(); } return observationRecord; } public List QueryHappenedObservations(int tradeId, DateTime valueDate) { using (var db = new YLContext()) { return db.autocall_observation.AsNoTracking().Where(o => o.TradeId == tradeId && o.EndDate <= valueDate).ToList(); } } } }