using Qdp.Foundation.Implementations; using Qdp.Pricing.Base.Implementations; using Qdp.Pricing.Base.Utilities; using YLErp.Abstract.DataProviders; using YLErp.BLL; using YLErp.DBModels.Consts; using YLErp.Model; using YLErp.Modules.DataProviderModule; using YLErp.QdpModule; namespace YLErp.Modules.TradeModule.ExoticOptionModule { /// /// 障碍期权敲入敲出操作 /// 迁移自:trade_barrier_optionBLL /// public class BarrierOptionKnockioService : TradeCashServiceEx { public BarrierOptionKnockioService(YLBaseService baseService) : base(baseService) { } public BarrierOptionKnockioService(OptUserInfo userInfo) : base(userInfo) { } /// /// 设置障碍期权敲入敲出 ,返回error /// public void SetKnockInOut(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 td in DbContext.trade join tb in DbContext.trade_barrier_option on td.id equals tb.TradeId where td.TradeDate > startDate.Value && td.TradeDate <= valueDate && string.IsNullOrEmpty(tb.KnockInOutStatus) && td.TradeType == "障碍期权" && ConsTrade.确认成交 == td.TradeStatus && td.ValidState != ConsGlobal.InValid && td.DividendDate < valueDate select new { trade = td, tradeBarrier = tb }; #region 增加客户筛选 tw if (clienIds != null) { query = query.Where(l => clienIds.Contains(l.trade.ClientId)); } #endregion var trades = query.ToList(); if (trades == null || !trades.Any()) { return; } 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;//已到期交易不再观察; } double? closePrice, highPrice, lowPrice; if (manuallyTradeObservationPrices.TryGetValue(tr.trade.id, out var manuallyTradeObservationPrice)) { closePrice = manuallyTradeObservationPrice.Price; highPrice = manuallyTradeObservationPrice.Price; lowPrice = manuallyTradeObservationPrice.Price; } 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; highPrice = eodprice.HighPrice; lowPrice = eodprice.LowPrice; } var tradeStatus = tr.trade.TradeStatus; var oldKnockInOutStatus = tr.tradeBarrier.KnockInOutStatus; CheckBarrierKnockInOutStatus(tr.trade, tr.tradeBarrier, valueDate, closePrice, highPrice, lowPrice); if (oldKnockInOutStatus != tr.tradeBarrier.KnockInOutStatus) { var KnockInOutStatus = tr.tradeBarrier.KnockInOutStatus == ConsTrade.KnockState.KnockedIn ? "敲入" : "敲出"; AddTradeOperationHistoryAndSetParentTradeInfo(false, tr.trade, KnockInOutStatus, KnockInOutStatus); } //到期仍未敲入的情况 if (tr.tradeBarrier.BarrierType.Contains("敲入") && (string.IsNullOrWhiteSpace(tr.tradeBarrier.KnockInOutStatus) || ConsTrade.KnockState.IsMonitoring(tr.tradeBarrier.KnockInOutStatus)) && tr.trade.ExerciseDate <= valueDate) { tr.trade.TradeStatus = ConsTrade.已到期; tr.trade.UnWindDate = valueDate; SaveBarrierRebateCash(tr.trade, tr.tradeBarrier, valueDate, closePrice, knockOutBarrierPrice: null); } if (tradeStatus != tr.trade.TradeStatus || oldKnockInOutStatus != tr.tradeBarrier.KnockInOutStatus) { //删除E/Bod_Trade记录 RemoveEodTradeAndFutureInfo(false, tr.trade.id, valueDate); } if (afterKnowInOut != null && DbContext.Entry(tr.tradeBarrier).State == EntityState.Modified) { afterKnowInOut(tr.trade, tr.tradeBarrier); } //更新,不能放到循环外,黑箱交易的子交易相互有依赖关系 DbContext.SaveChanges(); } } /// /// /// public void CheckBarrierKnockInOutStatus(OtcTradeBase td, trade_barrier_option tradeBarrier , DateTime valuedate, double? closePrice, double? highPrice, double? lowPrice) { var BarrierPrice = td.IsMoneynessOptionData ? tradeBarrier.BarrierPrice * td.SpotPrice : tradeBarrier.BarrierPrice; var UpperBarrierPrice = td.IsMoneynessOptionData ? tradeBarrier.UpperBarrierPrice * td.SpotPrice : tradeBarrier.UpperBarrierPrice; switch (tradeBarrier.Discrete) { case "离散": var observationDates = QdpHelper.GetObservationDatesFromString(tradeBarrier.ObservationDates); //每日观察或者当前结算日是观察日的时候,才检查是否会敲入敲出 if (observationDates == null || observationDates.Contains(valuedate)) { switch (tradeBarrier.BarrierType) { case "上升敲入": if (closePrice >= BarrierPrice) { tradeBarrier.KnockInOutStatus = ConsTrade.KnockState.KnockedIn; tradeBarrier.KnockInOutDate = valuedate; tradeBarrier.KnockInOutNotional = td.Notional; } break; case "上升敲出": if (closePrice >= BarrierPrice) { tradeBarrier.KnockInOutStatus = ConsTrade.KnockState.KnockedOut; tradeBarrier.KnockInOutDate = valuedate; tradeBarrier.KnockInOutNotional = td.Notional; td.TradeStatus = ConsTrade.已平仓; td.UnWindDate = valuedate; SaveBarrierRebateCash(td, tradeBarrier, valuedate, closePrice, tradeBarrier.BarrierPrice); } break; case "下降敲入": if (closePrice <= BarrierPrice) { tradeBarrier.KnockInOutStatus = ConsTrade.KnockState.KnockedIn; tradeBarrier.KnockInOutDate = valuedate; tradeBarrier.KnockInOutNotional = td.Notional; } break; case "下降敲出": if (closePrice <= BarrierPrice) { tradeBarrier.KnockInOutStatus = ConsTrade.KnockState.KnockedOut; tradeBarrier.KnockInOutDate = valuedate; tradeBarrier.KnockInOutNotional = td.Notional; td.TradeStatus = ConsTrade.已平仓; td.UnWindDate = valuedate; SaveBarrierRebateCash(td, tradeBarrier, valuedate, closePrice, tradeBarrier.BarrierPrice); } break; case "双障碍敲出": if (closePrice >= UpperBarrierPrice || closePrice <= BarrierPrice) { var barrierPrice = closePrice >= UpperBarrierPrice ? tradeBarrier.UpperBarrierPrice : tradeBarrier.BarrierPrice; tradeBarrier.KnockInOutStatus = ConsTrade.KnockState.KnockedOut; tradeBarrier.KnockInOutDate = valuedate; tradeBarrier.KnockInOutNotional = td.Notional; td.TradeStatus = ConsTrade.已平仓; td.UnWindDate = valuedate; var upDown = closePrice >= UpperBarrierPrice ? true : false; SaveBarrierRebateCash(td, tradeBarrier, valuedate, closePrice, barrierPrice, upDown); } break; case "双障碍敲入": if (closePrice >= UpperBarrierPrice || closePrice <= BarrierPrice) { tradeBarrier.KnockInOutStatus = ConsTrade.KnockState.KnockedIn; tradeBarrier.KnockInOutDate = valuedate; tradeBarrier.KnockInOutNotional = td.Notional; } break; } } break; case "连续": switch (tradeBarrier.BarrierType) { case "上升敲入": if (highPrice >= BarrierPrice) { tradeBarrier.KnockInOutStatus = ConsTrade.KnockState.KnockedIn; tradeBarrier.KnockInOutDate = valuedate;//UpdateDate tradeBarrier.KnockInOutNotional = td.Notional; } break; case "上升敲出": if (highPrice >= BarrierPrice) { tradeBarrier.KnockInOutStatus = ConsTrade.KnockState.KnockedOut; tradeBarrier.KnockInOutDate = valuedate; tradeBarrier.KnockInOutNotional = td.Notional; td.TradeStatus = ConsTrade.已平仓; td.UnWindDate = valuedate; SaveBarrierRebateCash(td, tradeBarrier, valuedate, highPrice, tradeBarrier.BarrierPrice); } break; case "下降敲入": if (lowPrice <= BarrierPrice) { tradeBarrier.KnockInOutStatus = ConsTrade.KnockState.KnockedIn; tradeBarrier.KnockInOutDate = valuedate; tradeBarrier.KnockInOutNotional = td.Notional; } break; case "下降敲出": if (lowPrice <= BarrierPrice) { tradeBarrier.KnockInOutStatus = ConsTrade.KnockState.KnockedOut; tradeBarrier.KnockInOutDate = valuedate; tradeBarrier.KnockInOutNotional = td.Notional; td.TradeStatus = ConsTrade.已平仓; td.UnWindDate = valuedate; SaveBarrierRebateCash(td, tradeBarrier, valuedate, lowPrice, tradeBarrier.BarrierPrice); } break; case "双障碍敲出": if (highPrice >= UpperBarrierPrice || lowPrice <= BarrierPrice) { var price = highPrice >= UpperBarrierPrice ? highPrice : lowPrice; var barrierPrice = highPrice >= UpperBarrierPrice ? tradeBarrier.UpperBarrierPrice : tradeBarrier.BarrierPrice; tradeBarrier.KnockInOutStatus = ConsTrade.KnockState.KnockedOut; tradeBarrier.KnockInOutDate = valuedate; tradeBarrier.KnockInOutNotional = td.Notional; td.TradeStatus = ConsTrade.已平仓; td.UnWindDate = valuedate; var upDown = highPrice >= UpperBarrierPrice ? true : false; SaveBarrierRebateCash(td, tradeBarrier, valuedate, price, barrierPrice, upDown); } break; case "双障碍敲入": if (highPrice >= UpperBarrierPrice || lowPrice <= BarrierPrice) { tradeBarrier.KnockInOutStatus = ConsTrade.KnockState.KnockedIn; tradeBarrier.KnockInOutDate = valuedate; tradeBarrier.KnockInOutNotional = td.Notional; } break; } break; } } /// /// 障碍期权到期时仍未敲入,或者已经敲出,应获得rebate,保存相应的资金信息 /// public void SaveBarrierRebateCash(OtcTradeBase td, trade_barrier_option tradeBarrier, DateTime settleDate, double? closePrice, double? knockOutBarrierPrice, bool upDown = false) { var tc = SetTradeCash(td, tradeBarrier, settleDate, closePrice, knockOutBarrierPrice, upDown); SaveTradeCashDetail(tc); //增加出入金记录 new ClientCashinCashoutBLL(this).CloseTrade_ClientCashInCashOutSave(td, tc, tc.ValueDate); } /// /// 生成TradeCash /// /// 交易 /// 子对象 /// 敲出/了结日期 /// 收盘价 /// 障碍价 /// 是否保存 /// public trade_cash SetTradeCash(OtcTradeBase td, trade_barrier_option tradeBarrier, DateTime settleDate, double? closePrice, double? knockOutBarrierPrice, bool upDown = false, bool saveChanges = true) { var spotPrice = td.SpotPrice ?? 0; double rebate, rebateRate; if (upDown) { if (td.IsUsePremiumRate == true) { rebateRate = tradeBarrier.RebateHighRate ?? 0; rebate = rebateRate * spotPrice; } else { rebate = tradeBarrier.RebateHigh ?? 0; rebateRate = spotPrice > 0 ? rebate / spotPrice : 0; } } else { if (td.IsUsePremiumRate == true) { rebateRate = tradeBarrier.RebateRate ?? 0; rebate = rebateRate * spotPrice; } else { rebate = tradeBarrier.Rebate ?? 0; rebateRate = spotPrice > 0 ? rebate / spotPrice : 0; } } if (tradeBarrier.RebateAnnualizedAtKO) { var rebateDayCountImpl = string.IsNullOrWhiteSpace(tradeBarrier.RebateDayCount) ? new Act365() : tradeBarrier.RebateDayCount.ToDayCountImpl(); var fraction = rebateDayCountImpl.CalcDayCountFraction(new Date(td.StartDate.Value), new Date(settleDate)); rebate *= fraction; rebate += Math.Abs((td.TradeSinglePrice - td.TradeSinglePrice * fraction) ?? 0); } var req = new TradeCashReq { UnwindNotional = td.Notional, UnwindPrice = rebate, UnwindPricePercentRate = rebateRate, FinalPrice = closePrice, UnwindPercentRate = td.OriginalNotional > 0 ? td.Notional / td.OriginalNotional.Value : 0, Notional = td.Notional, TradeAmount = td.TradeAmount, ValueDate = settleDate, HappenedDate = settleDate,//记录流水记录的结算日 BarrierPrice = knockOutBarrierPrice }; req.UnwindFee = req.UnwindNotional * (req.UnwindPrice ?? 0) + ((req.UnwindNotional / td.OriginalNotional * td.OriginalPrincipalSum) ?? 0); if (valuedateBLL.SystemDate.UnwindAmountAngle == 1) { req.UnwindFee = req.UnwindFee * (td.BuySell == "卖出" ? -1 : 1); } if (valuedateBLL.SystemDate.UnwindSinglePriceAngle == 1) { req.UnwindPrice = req.UnwindPrice * (td.BuySell == "卖出" ? -1 : 1); } var tc = CloseTrade_TradeCashSave(td, req, isFromRecheckOrKO: true, isLastAction: true, saveChanges: saveChanges); if (tradeBarrier.RebateType == "AtEnd") { tc.ValueDate = td.ExerciseDate.Value; tc.HappenedDate = settleDate; } if (td.IsGroup == 2 && td.ParentTradeId > 0) { var groupAction = DbContext.trade_cash_group_action.FirstOrDefault(x => x.TradeId == td.id && x.Status != "已完成"); if (groupAction != null) { groupAction.Status = "已完成"; tc.ParentTradeCashId = groupAction.ParentTradeCashId; tc.ParentTradeId = groupAction.ParentTradeId; } else { tc.ParentTradeId = td.ParentTradeId; tc.ParentTradeCashId = SaveGroupUnwindCash(td, tc.ValueDate, tc.Amount, closePrice ?? 0, out var continueTradeCashHandle).id; } } tc.ValidState = "Valid"; //敲出价格为null,代表该交易为敲入类型的交易,但未敲入,到期后需要返还补偿金额的一条tradecash记录,归为到期行为 if (knockOutBarrierPrice == null) { tc.ExerciseWay = TradeCashExerciseWayEnum.到期行权; } else { tc.ExerciseWay = TradeCashExerciseWayEnum.提前终止行权; } return tc; } } }