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_double_sharkfin_optionBLL /// public class DoubleSharkOptionKnockoutService : TradeCashServiceEx { public DoubleSharkOptionKnockoutService(YLBaseService baseService) : base(baseService) { } public DoubleSharkOptionKnockoutService(OptUserInfo userInfo) : base(userInfo) { } /// /// 检出敲入敲出 /// public void CheckKnockoutStatus(DateTime valueDate, IEodPriceProviderV2 priceProvider, DateTime? startDate = null , Action afterKnowInOut = null, System.Collections.Generic.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_double_sharkfin_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, tradeDbShark = 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;//已到期交易不再观察; } var tradeStatus = tr.trade.TradeStatus; var observationDates = QdpHelper.GetObservationDatesFromString(tr.tradeDbShark.ObservationDates); //每日观察或者当前结算日是观察日的时候,才检查是否会敲敲出 if (observationDates == null || observationDates.Contains(valueDate) || !tr.tradeDbShark.IsDiscrete) { //根据是否为离散观察来确定用来比较的价格 //如果是离散观察,只用收盘价比较 //如果是连续观察,使用最高价和最低价 double? upPrice, lowPrice; if (manuallyTradeObservationPrices.TryGetValue(tr.trade.id, out var manuallyTradeObservationPrice)) { upPrice = 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 if (tr.tradeDbShark.IsDiscrete) { upPrice = eodprice.ClosePrice; lowPrice = eodprice.ClosePrice; } else { upPrice = eodprice.HighPrice; lowPrice = eodprice.LowPrice; } var oldKnockInOutStatus = tr.tradeDbShark.KnockInOutStatus; CheckDoubleSharkFinKnockOutStatus(tr.trade, tr.tradeDbShark, valueDate, upPrice, lowPrice); if (oldKnockInOutStatus != tr.tradeDbShark.KnockInOutStatus) { var KnockInOutStatus = tr.tradeDbShark.KnockInOutStatus == ConsTrade.KnockState.KnockedIn ? "敲入" : "敲出"; AddTradeOperationHistoryAndSetParentTradeInfo(false, tr.trade, KnockInOutStatus, KnockInOutStatus); } if (tradeStatus != tr.trade.TradeStatus|| oldKnockInOutStatus != tr.tradeDbShark.KnockInOutStatus) { //删除E/Bod_Trade记录 RemoveEodTradeAndFutureInfo(false, tr.trade.id, valueDate); } if (afterKnowInOut != null && DbContext.Entry(tr.tradeDbShark).State == EntityState.Modified) { afterKnowInOut(tr.trade, tr.tradeDbShark); } } //更新,不能放到循环外,黑箱交易的子交易相互有依赖关系 DbContext.SaveChanges(); } } public void CheckDoubleSharkFinKnockOutStatus(OtcTradeBase td, trade_double_sharkfin_option tradeDbShark, DateTime valuedate, double? upPrice, double? lowPrice) { var barrierHigh = td.IsMoneynessOptionData ? tradeDbShark.BarrierHigh * td.SpotPrice : tradeDbShark.BarrierHigh; var barrierLow = td.IsMoneynessOptionData ? tradeDbShark.BarrierLow * td.SpotPrice : tradeDbShark.BarrierLow; if (upPrice >= barrierHigh || lowPrice <= barrierLow) { var useRebate = lowPrice <= barrierLow; var price = upPrice >= barrierHigh ? upPrice : lowPrice; tradeDbShark.KnockInOutStatus = ConsTrade.KnockState.KnockedOut; tradeDbShark.KnockInOutDate = valuedate; td.TradeStatus = ConsTrade.已平仓; td.UnWindDate = valuedate; SaveDoubleSharkFinRebateCash(td, tradeDbShark, price, valuedate, useRebate); } } /// /// 双鲨期权敲出,应获得rebate,保存相应的资金信息 /// public trade_cash SaveDoubleSharkFinRebateCash(OtcTradeBase td, trade_double_sharkfin_option tradeDbShark, double? closePrice, DateTime valueDate, bool useRebate, bool saveChanges = true) { double rebate, rebateRate; var spotPrice = td.SpotPrice ?? 0; if (td.IsUsePremiumRate == true) { rebateRate = (useRebate ? tradeDbShark.RebateRate : tradeDbShark.RebateHighRate) ?? 0; rebate = rebateRate * spotPrice; } else { rebate = (useRebate ? tradeDbShark.Rebate : tradeDbShark.RebateHigh) ?? 0; rebateRate = spotPrice > 0 ? rebate / spotPrice : 0; } var req = new TradeCashReq { UnwindNotional = td.Notional, UnwindPercentRate = td.OriginalNotional > 0 ? td.Notional / td.OriginalNotional.Value : 0, FinalPrice = closePrice, UnwindPrice = rebate, UnwindPricePercentRate = rebateRate, Notional = td.Notional, TradeAmount = td.TradeAmount, ValueDate = valueDate, HappenedDate = valueDate, BarrierPrice = useRebate ? tradeDbShark.BarrierLow : tradeDbShark.BarrierHigh }; 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: false); tc.ValidState = ConsGlobal.Valid; tc.ExerciseWay = TradeCashExerciseWayEnum.提前终止行权; if (tradeDbShark.RebateType == "AtEnd") { tc.ValueDate = td.ExerciseDate.Value; tc.HappenedDate = valueDate; } //加入平仓份额和平仓日期 td.UnWindDate = tc.ValueDate; 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; } } if (saveChanges) { SaveTradeCashDetail(tc); //增加出入金记录 new ClientCashinCashoutBLL(this).CloseTrade_ClientCashInCashOutSave(td, tc, tc.ValueDate); } return tc; } } }