using Qdp.Foundation.Implementations; using Qdp.Pricing.Base.Implementations; using Qdp.Pricing.Base.Utilities; using YLErp.Abstract.DataProviders; using YLErp.BLL; using YLErp.BLL.Eod; using YLErp.DBModels.Consts; using YLErp.DBModels.Enums; using YLErp.Modules.DataProviderModule; using YLErp.Modules.TradeModule.ExoticOptionModule; using YLErp.QdpModule; namespace YLErp.Modules.TradeModule.DealModule { /// /// 二元期权操作 /// 迁移自:trade_binary_optionBLL /// public class BinaryOptionDealService : TradeCashServiceEx { public BinaryOptionDealService(YLBaseService baseService) : base(baseService) { } public BinaryOptionDealService(OptUserInfo userInfo) : base(userInfo) { } /// /// /// public void CheckTouchStatus(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_binary_option on td.id equals tb.TradeId where td.TradeDate > startDate.Value && td.TradeDate <= valueDate && td.ExerciseDate >= valueDate && ConsTrade.确认成交 == td.TradeStatus && td.ValidState != ConsGlobal.InValid && td.TradeType == "二元期权" && td.ExerciseMode == "American" && td.DividendDate < valueDate select new { trade = td, tradeBinary = 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; } // 美式二元检查 foreach (var tr in trades) { if (tr.trade.ExerciseDate < valueDate) { continue;//已到期交易不再观察; } var tradeStatus = tr.trade.TradeStatus; if (!priceProvider.TryGetEodPrice(tr.trade.UnderlyingCode, out var eodprice)) { throw new Exception($"[{tr.trade.TradeType}:{tr.trade.TradeNumber}]标的:{tr.trade.UnderlyingCode} 未找到结算价"); } double? upPrice = 0, lowPrice = 0; var isObservationDate = false; //根据是否为离散观察来确定用来比较的价格 //如果是离散观察,只用收盘价比较 //如果是连续观察,使用最高价和最低价 if (tr.tradeBinary.IsDiscreteMonitored) { var observationDates = QdpHelper.GetObservationDatesFromString(tr.tradeBinary.ObservationDates); if (observationDates == null || observationDates.Contains(valueDate)) { isObservationDate = true; upPrice = lowPrice = eodprice.ClosePrice; } } else { isObservationDate = true; upPrice = eodprice.HighPrice; lowPrice = eodprice.LowPrice; } var strike = tr.trade.IsMoneynessOptionData ? tr.trade.Strike * tr.trade.SpotPrice : tr.trade.Strike; var UpperBarrier = new Lazy(() => tr.trade.IsMoneynessOptionData ? tr.tradeBinary.UpperBarrier * tr.trade.SpotPrice : tr.tradeBinary.UpperBarrier); if (isObservationDate) { switch (tr.tradeBinary.PayoffType) { case "UpOneTouch": if (upPrice > strike) { //触碰,买方获得盈利,交易结束 var cash = GetCashOrNothingAmount(tr.trade, tr.tradeBinary, valueDate, useHighAmount: false); SaveBinarySettleCash(tr.trade, tr.tradeBinary, valueDate, cash, true, upPrice); tr.trade.TradeStatus = ConsTrade.已执行; tr.trade.UnWindDate = valueDate; } else if (tr.trade.ExerciseDate <= valueDate) { SaveBinarySettleCash(tr.trade, tr.tradeBinary, valueDate, 0, false, upPrice); } break; case "DownOneTouch": if (lowPrice < strike) { //触碰,买方获得盈利,交易结束 var cash = GetCashOrNothingAmount(tr.trade, tr.tradeBinary, valueDate, useHighAmount: false); SaveBinarySettleCash(tr.trade, tr.tradeBinary, valueDate, cash, true, lowPrice); tr.trade.TradeStatus = ConsTrade.已执行; tr.trade.UnWindDate = valueDate; } else if (tr.trade.ExerciseDate <= valueDate) { SaveBinarySettleCash(tr.trade, tr.tradeBinary, valueDate, 0, false, lowPrice); } break; case "UpNoTouch": if (upPrice > strike) { //触碰,买方无盈利,交易结束 SaveBinarySettleCash(tr.trade, tr.tradeBinary, valueDate, 0, false, upPrice); tr.trade.TradeStatus = ConsTrade.已到期; tr.trade.UnWindDate = valueDate; } break; case "DownNoTouch": if (lowPrice < strike) { //触碰,买方无盈利,交易结束 SaveBinarySettleCash(tr.trade, tr.tradeBinary, valueDate, 0, false, lowPrice); tr.trade.TradeStatus = ConsTrade.已到期; tr.trade.UnWindDate = valueDate; } break; case "DoubleOneTouch": if (upPrice >= UpperBarrier.Value || lowPrice <= strike) { var breachHighBarrier = upPrice >= UpperBarrier.Value; var price = breachHighBarrier ? upPrice : lowPrice; var cash = GetCashOrNothingAmount(tr.trade, tr.tradeBinary, valueDate, useHighAmount: breachHighBarrier); //触碰上限或下限,买方获得盈利,交易结束 SaveBinarySettleCash(tr.trade, tr.tradeBinary, valueDate, cash, true, price); tr.trade.TradeStatus = ConsTrade.已执行; tr.trade.UnWindDate = valueDate; } else if (tr.trade.ExerciseDate <= valueDate) { SaveBinarySettleCash(tr.trade, tr.tradeBinary, valueDate, 0, false, eodprice.ClosePrice); } break; case "DoubleNoTouch": if (upPrice > UpperBarrier.Value || lowPrice < strike) { var price = upPrice >= tr.tradeBinary.UpperBarrier ? upPrice : lowPrice; //触碰上限或下限,买方无盈利,交易结束 SaveBinarySettleCash(tr.trade, tr.tradeBinary, valueDate, 0, false, price); tr.trade.TradeStatus = ConsTrade.已到期; tr.trade.UnWindDate = valueDate; } break; } } //OneTouch类型,到期仍未触碰,则交易结束 if (tr.tradeBinary.PayoffType.Contains("OneTouch") && tr.trade.ExerciseDate <= valueDate) { //SaveBinarySettleCash(tr.trade, tr.tradeBinary, valueDate, 0, false, eodprice.ClosePrice); tr.trade.TradeStatus = ConsTrade.已到期; tr.trade.UnWindDate = valueDate; } //NoTouch类型,到期仍未触碰,买方获得盈利,交易结束 if (tr.tradeBinary.PayoffType.Contains("NoTouch") && tr.trade.ExerciseDate <= valueDate && !ConsTrade.TradeCompleteStatus.Contains(tr.trade.TradeStatus)) { var cash = GetCashOrNothingAmount(tr.trade, tr.tradeBinary, valueDate, useHighAmount: false); SaveBinarySettleCash(tr.trade, tr.tradeBinary, valueDate, cash, false, eodprice.ClosePrice); tr.trade.TradeStatus = ConsTrade.已执行; tr.trade.UnWindDate = valueDate; } if (tradeStatus != tr.trade.TradeStatus) { //删除E/Bod_Trade记录 RemoveEodTradeAndFutureInfo(false, tr.trade.id, valueDate); } if (afterKnowInOut != null && DbContext.Entry(tr.tradeBinary).State == EntityState.Modified) { afterKnowInOut(tr.trade, tr.tradeBinary); } //更新,不能放到循环外,黑箱交易的子交易相互有依赖关系 DbContext.SaveChanges(); } } /// /// /// public static double GetCashOrNothingAmount(OtcTradeBase trade, trade_binary_option tradeBinary, DateTime settleDate, bool useHighAmount = false) { var result = (trade.Notional / trade.OriginalNotional * (trade.OriginalPrincipalSum ?? 0)) ?? 0; double refund; if (trade.IsUsePremiumRate == true) { var rate = useHighAmount ? tradeBinary.CashOrNothingAmountHighRate : tradeBinary.CashOrNothingAmountRate; refund = Math.Abs((rate ?? 0) * (trade.SpotPrice ?? 0) * trade.Notional); } else { var amount = useHighAmount ? tradeBinary.CashOrNothingAmountHigh : tradeBinary.CashOrNothingAmount; refund = Math.Abs((amount ?? 0) * trade.Notional); } if (tradeBinary.RebateAnnualizedAtKO) { var rebateDayCountImpl = string.IsNullOrWhiteSpace(tradeBinary.RebateDayCount) ? new Act365() : tradeBinary.RebateDayCount.ToDayCountImpl(); var fraction = rebateDayCountImpl.CalcDayCountFraction(new Date(trade.StartDate.Value), new Date(settleDate)); refund *= fraction; refund += Math.Abs((trade.TradePrice - trade.TradePrice * fraction) ?? 0); } return result + refund; } /// /// 美式二元期权触碰后,买方获得盈利,记录资金信息 /// public trade_cash SaveBinarySettleCash(OtcTradeBase td, trade_binary_option tradeBinary, DateTime valuedate, double cash, bool earlyExecute, double? price, bool saveChanges = true) { var tradeCash = new trade_cash { ValidState = "Valid", OptDate = DateTime.Now, OptId=UserId, OptName=UserName, Action = ClientCashInCashOut.系统操作_行权费, IsLastAction = true, ValueDate = valuedate, Strike = td.Strike, CallPut = td.CallPut, Amount = cash * EodOperationBase.GetSign(td.BuySell), UnwindPrice = cash / td.Notional, UnwindPricePercentRate = cash / td.OriginalStockEqvNotional, Status = TradeCashStatusEnum.已执行, TradeType = td.BuySell, TradeId = td.id, Notional = td.Notional, TradeAmount = td.TradeAmount, UnwindPercentRate = td.OriginalNotional > 0 ? td.Notional / td.OriginalNotional : 0, FinalPrice = price, ExerciseWay = earlyExecute ? TradeCashExerciseWayEnum.提前终止行权 : TradeCashExerciseWayEnum.到期行权 }; if (tradeBinary.RebateType == "AtEnd") { tradeCash.ValueDate = td.ExerciseDate.Value; tradeCash.HappenedDate = valuedate; } if (saveChanges) { 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 = "已完成"; tradeCash.ParentTradeCashId = groupAction.ParentTradeCashId; tradeCash.ParentTradeId = groupAction.ParentTradeId; } else { tradeCash.ParentTradeId = td.ParentTradeId; tradeCash.ParentTradeCashId = SaveGroupUnwindCash(td, tradeCash.ValueDate, tradeCash.Amount, price ?? 0, out bool continueTradeCashHandle).id; } } DbContext.trade_cash.Add(tradeCash); DbContext.SaveChanges(); SaveTradeCashDetail(tradeCash); new ClientCashinCashoutBLL(this).CloseTrade_ClientCashInCashOutSave(td, tradeCash, tradeCash.ValueDate); } return tradeCash; } } }