using Qdp.Foundation.Implementations; using YLErp.Abstract.DataProviders; using YLErp.BLL; using YLErp.Commons; using YLErp.DBModels.Enums; using YLErp.DBModels; using YLErp.Modules.DataProviderModule; using YLErp.Modules.EodModule; using YLErp.QdpModule; using NPOI.SS.Formula.Functions; namespace YLErp.Modules.CalculationModule { /// /// 收益互换计算服务 /// public class PayoffSwapCalcService { /// /// 因为互换涉及到多标的所以需要确保所有标的都可以找到价格 /// private static IAggregatePriceProvider GetAutoPriceProvider(DateTime valueDate, IPriceProvider priceProvider, bool isEodSettlement) { if (priceProvider is IAggregatePriceProvider) { return (IAggregatePriceProvider)priceProvider; } if (isEodSettlement) { return new AggregatePriceProvider(priceProvider, new EodPriceProvider(valueDate).GetPriceProvider(priceProvider is IEodPriceProviderWrap wrap ? wrap.SettlementType : SettlementTypeEnum.ClosePrice)); } return new AggregatePriceProvider(priceProvider, DataCacheProvider.GetUnderlyingDataSource()); } /// /// /// public static TradeValueResult CalcValue(int tradeId, DateTime valueDate, IPriceProvider priceProvider, bool isEodSettlement) { using (var db = DbContextFactory.GetYLDbContext()) { var trade = db.trade.Find(tradeId); if (trade == null) { return new TradeValueResult(false) { TradeId = tradeId, FailReason = TradeValueFailReason.missingTrade, ErrorMessage = "[收益互换]没有找到交易数据,tradeId:" + tradeId }; } return CalcValue(trade, valueDate, priceProvider, isEodSettlement); } } public static TradeValueResult CalcValue(OtcTradeBase trade, DateTime valueDate, IPriceProvider priceProvider, bool isEodSettlement) { return CalcValueSingle(trade, valueDate, priceProvider, isEodSettlement); } /// /// 互换收益PV计算 普通 /// public static TradeValueResult CalcValueSingle(OtcTradeBase trade, DateTime valueDate, IPriceProvider priceProvider, bool isEodSettlement, double spotPrice = double.NaN) { if (trade is null) { throw new ArgumentNullException(nameof(trade)); } if(double.IsNaN(spotPrice)) { //因为互换涉及到多标的所以需要确保所有标的都可以找到价格 if (trade.StructureType== "多空组合") { spotPrice = 0; } else { priceProvider = GetAutoPriceProvider(valueDate, priceProvider, isEodSettlement); if (!priceProvider.TryGetPrice(trade.UnderlyingCode, out spotPrice)) { spotPrice = DataCacheProvider.GetUnderlyingDataSource().GetPrice(trade.UnderlyingCode); } } } using (var db = DbContextFactory.GetYLDbContext()) { var client = DataCacheProvider.GetClientDataSource().GetData(trade.ClientId); var rate = new EodCurrencyRateService(OptUserInfo.SystemUser).GetCurrencyRate(trade.QuoteCurrency, trade.SettlementCurrency, valueDate, seekPreday: !isEodSettlement); var rateTradeDate = new EodCurrencyRateService(OptUserInfo.SystemUser).GetCurrencyRate(trade.QuoteCurrency, trade.SettlementCurrency, trade.TradeDate.Value, seekPreday: !isEodSettlement); var lastEodSwap = GetEodSwapData(trade, db,valueDate); var pv = lastEodSwap.PostionValue; var clientCashOut= db.ClientCashInCashOut.FirstOrDefault(x=>x.Action== "系统操作-期权费"&&x.TradeId== trade.id&&x.HappenDate<= valueDate) ; var credit = db.credit.FirstOrDefault(x => x.CreditStartDate <= valueDate && x.CreditDeadLine >= valueDate && x.ClientId == trade.ClientId && x.ProcessStatus == "已审批"); var hasCredit = credit != null && credit.PFECredit > 0; client_variety_marginrate clientVarietyMarginRate = new client_variety_marginrate { ClientId = trade.ClientId, ClientName = trade.ClientName, HighMarginRate = 1, LowMarginRate = 1, ValueDate = valueDate, }; var OptionValue = new TradeValueResult { TradeId = trade.id, Pv =Convert.ToDouble(pv), ExtendInfo = new TradeValueResultExtend() { QuoteFloatingWinLoss =Convert.ToDouble(lastEodSwap.FloatingPnL), FloatingWinLoss = Convert.ToDouble(lastEodSwap.FloatingPnL) * rate, RealPnl = Convert.ToDouble(lastEodSwap.RealizedPnL) * rate, QuoteCommission = clientCashOut?.Money??0, Commission = (clientCashOut?.Money ?? 0) * rate, QuoteAnnualFee = 0, AnnualFee = 0, QuotePv = Convert.ToDouble(pv), QuoteIM = client.BoundSide == BoundSideEnum.南向 ? (trade.Notional * spotPrice * clientVarietyMarginRate.LowMarginRate) : (trade.StockEqvNotional * (hasCredit ? clientVarietyMarginRate.LowMarginRate : clientVarietyMarginRate.HighMarginRate)), IM = (client.BoundSide == BoundSideEnum.南向 ? (trade.Notional * spotPrice * clientVarietyMarginRate.LowMarginRate) : (trade.StockEqvNotional * (hasCredit ? clientVarietyMarginRate.LowMarginRate : clientVarietyMarginRate.HighMarginRate))) * rate, QuotePFE = client.BoundSide == BoundSideEnum.南向 ? trade.Notional * spotPrice * clientVarietyMarginRate.HighMarginRate : 0, PFE = client.BoundSide == BoundSideEnum.南向 ? trade.Notional * spotPrice * clientVarietyMarginRate.HighMarginRate * rate : 0 }, RoundedPv = Convert.ToDouble(pv), Delta = (lastEodSwap.MarketValueLong>0 ? 1 : -1) * trade.Notional, Gamma = 0, Vega = 0, TradingDayTheta = 0, CalendarDayTheta = 0, Rho = 0, DeltaCash = (lastEodSwap.MarketValueLong > 0 ? spotPrice : -spotPrice) * trade.Notional, GammaCash = 0, SpotPrice= spotPrice }; return OptionValue; } } /// /// 构建eodswap /// /// /// /// private static eod_swap GetEodSwapData(OtcTradeBase trade, YLContext db,DateTime valueDate) { var eodSwap = db.eod_swap.Where(x => x.SwapTradeId == trade.id && x.ValueDate == valueDate).FirstOrDefault(); if (eodSwap!=null) { return eodSwap; } eodSwap = new eod_swap(); var positions = db.swap_position.Where(x => x.PosiQuantity > 0 && !x.IsInitial && x.SwapTradeId == trade.id).ToList(); eodSwap.SwapTradeId = trade.id; eodSwap.NotionalValueLong = positions.Where(x => x.PositionType == (int)PositionTypeFlag.Long).Sum(s => s.PosiNotionalValue); eodSwap.NotionalValueShort = positions.Where(x => x.PositionType == (int)PositionTypeFlag.Short).Sum(s => s.PosiNotionalValue); eodSwap.NotionalValue = eodSwap.NotionalValueLong + eodSwap.NotionalValueShort; foreach (var item in positions) { decimal shortRatio = item.PositionType == (int)PositionTypeFlag.Long ? 1 : -1;//多空方向 int directionRatio = item.PosiDirection == (int)SwapDirectionEnum.收取 ? 1 : -1; var pv = item.PosiQuantity* shortRatio; var data = DataCacheProvider.GetUnderlyingDataSource().GetData(item.UnderlyingCode); if (data != null) { if (data.IsBond()) { var bondPrice = EodPriceQueryService.GetBondPrice(valuedateBLL.ValueDate, data.UnderlyingCode); var price = Convert.ToDecimal(bondPrice?.ClosePrice??0); eodSwap.FloatingPnL = (price - item.PosiGrossPrice) * item.PosiQuantity * item.ContractSize * shortRatio * directionRatio; } } if (shortRatio > 0) { eodSwap.MarketValueLong += pv; } else { eodSwap.MarketValueShort += pv; } } eodSwap.PostionValue = eodSwap.NotionalValue; return eodSwap; } /// /// 获取互换固定收益PV /// /// /// /// public static Dictionary GetFixedInterestRatePV(List tradeIds, DateTime valueDate) { var result = new Dictionary(); if (tradeIds == null || tradeIds.Count == 0) { return result; } List tradeSwapList = null; List tradeCashList = null; List tradeCashSwapList = null; List tradeList = null; List eodTradeList = null; using (var db = DbContextFactory.GetYLDbContext()) { //tradeList = db.trade.AsNoTracking().Where(p => tradeIds.Contains(p.id)).ToList(); eodTradeList = db.eod_trade.AsNoTracking().Where(p => p.ValueDate == valueDate && tradeIds.Contains(p.TradeId)).ToList(); tradeSwapList = db.trade_swap.AsNoTracking().Where(p => tradeIds.Contains(p.TradeId)).ToList(); tradeCashList = db.trade_cash.AsNoTracking().Where(y => tradeIds.Contains(y.TradeId) && y.Action == "系统操作-互换" && y.ValidState != "InValid" && !y.IsDeleted && y.ValueDate <= valueDate).ToList(); tradeCashSwapList = db.trade_cash_swap.AsNoTracking().Where(p => tradeIds.Contains(p.TradeId)).ToList(); } if (tradeSwapList == null) { tradeSwapList = new List(); } if (tradeCashList == null) { tradeCashList = new List(); } if (tradeCashSwapList == null) { tradeCashSwapList = new List(); } if (eodTradeList != null && eodTradeList.Count > 0) { tradeList = eodTradeList.Select(p => p.trade).ToList(); } if (tradeList == null) { tradeList = new List(); } if (tradeList.Count > 0) { foreach (var trade in tradeList) { var tradeSwap = tradeSwapList.FirstOrDefault(d => d.TradeId == trade.id); if (tradeSwap == null) { continue; } var tradeCashs = tradeCashList.Where(y => y.TradeId == trade.id && y.Action == "系统操作-互换" && y.ValidState != "InValid" && !y.IsDeleted && y.ValueDate <= valueDate); var tradeCashIds = tradeCashs.Select(x => x.id); var tradeCash = tradeCashs.OrderByDescending(y => y.id).FirstOrDefault(); var cashSwaps = tradeCashSwapList.Where(x => x.TradeId == trade.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 ? tradeCashs.FirstOrDefault(x => x.id == lastManualCashSwap.TradeCashId) : null; DateTime endDate; double fixAmount = 0; if (!tradeSwap.IsGetFloatingProfit) { var preSwapDate = GetSwapRateStartDate(trade, tradeSwap, valueDate, tradeCash, lastManualCash, tradeSwap.IsGetFloatingProfit, out endDate); var extraAmountGet = GetExtraAmountBySwapRate(trade.ClientId, trade.TradeDate, tradeSwap.GetSwapTimeAndRate, preSwapDate, endDate, tradeSwap.AnnualDays ?? 0, trade.StockEqvNotional); fixAmount += extraAmountGet; } if (!tradeSwap.IsPayFloatingProfit) { var preSwapDate = GetSwapRateStartDate(trade, tradeSwap, valueDate, tradeCash, lastManualCash, tradeSwap.IsPayFloatingProfit, out endDate); var extraAmountPay = GetExtraAmountBySwapRate(trade.ClientId, trade.TradeDate, tradeSwap.PaySwapTimeAndRate, preSwapDate, endDate, tradeSwap.AnnualDays ?? 0, trade.StockEqvNotional); fixAmount -= extraAmountPay; } result.Add(trade.id, fixAmount); } } return result; } /// /// 多空组合PV计算 子交易计算模式 /// public static TradeValueResult CalcValue(OtcTradeBase trade, trade_swap trade_swap, List trade_swap_details, DateTime valueDate, IPriceProvider priceProvider, bool isEodSettlement) { if (trade_swap.SwapType == "多空组合") { using (var db = DbContextFactory.GetYLDbContext()) { var tradeIds = trade_swap_details.Select(t => t.ChildTradeId).ToList(); var subTradeList = db.trade.Where(t => tradeIds.Contains(t.id)).ToList(); var resultList = new List(); foreach (var subTrade in subTradeList) { var price = DataCacheProvider.GetUnderlyingDataSource().GetPrice(subTrade.UnderlyingCode); var subResult = CalcValue(subTrade, valueDate, priceProvider, isEodSettlement); resultList.Add(subResult); } var OptionValue = new TradeValueResult { TradeId = trade.id, Pv = 0, ExtendInfo = new TradeValueResultExtend() { QuoteFloatingWinLoss = 0, FloatingWinLoss = 0, QuoteCommission = 0, Commission = 0, QuoteAnnualFee = 0, AnnualFee = 0, QuotePv = 0, QuoteIM = 0, IM = 0, QuotePFE = 0, PFE = 0 }, RoundedPv = 0, Delta = 0, Gamma = 0, Vega = 0, TradingDayTheta = 0, CalendarDayTheta = 0, Rho = 0, DeltaCash = 0, GammaCash = 0 }; foreach (var valueResult in resultList) { OptionValue.Pv += valueResult.Pv; OptionValue.ExtendInfo.QuoteFloatingWinLoss += valueResult.ExtendInfo.QuoteFloatingWinLoss; OptionValue.ExtendInfo.FloatingWinLoss += valueResult.ExtendInfo.FloatingWinLoss; OptionValue.ExtendInfo.QuoteCommission += valueResult.ExtendInfo.QuoteCommission; OptionValue.ExtendInfo.Commission += valueResult.ExtendInfo.Commission; OptionValue.ExtendInfo.QuoteAnnualFee += valueResult.ExtendInfo.QuoteAnnualFee; OptionValue.ExtendInfo.AnnualFee += valueResult.ExtendInfo.AnnualFee; OptionValue.ExtendInfo.QuotePv += valueResult.ExtendInfo.QuotePv; OptionValue.ExtendInfo.QuoteIM += valueResult.ExtendInfo.QuoteIM; OptionValue.ExtendInfo.IM += valueResult.ExtendInfo.IM; OptionValue.ExtendInfo.QuotePFE += valueResult.ExtendInfo.QuotePFE; OptionValue.ExtendInfo.PFE += valueResult.ExtendInfo.PFE; OptionValue.RoundedPv += valueResult.RoundedPv; OptionValue.DeltaCash += valueResult.DeltaCash; } return OptionValue; } } else { return CalcValue(trade,valueDate, priceProvider, isEodSettlement); } } /// /// 计算互换利息 /// public static double GetExtraAmountBySwapRate(int clientId, DateTime? tradeDate, string timeRate, DateTime startDate, DateTime valueDate, int annualDays, double stockEqvNotional) { if (valueDate == tradeDate && PS.Config.Company == Configuration.CompanyEnum.中金) { var client = DataCacheProvider.GetClientDataSource().GetData(clientId); if (client.BoundSide == BoundSideEnum.北向) { startDate = startDate.AddDays(-1); } } double extraAmountGet = 0; var swapDates = GetSwapDatesBetween(timeRate, startDate, valueDate); if (swapDates != null && swapDates.Any()) { swapDates.ForEach(x => { var itemDays = (x.DateTime - startDate).Days; var itemRate = GetSwapRateByDate(timeRate, x.DateTime); extraAmountGet += stockEqvNotional * itemRate * ((double)itemDays / annualDays); startDate = x.DateTime; }); } if (startDate < valueDate) { var latestDays = (valueDate - startDate).Days; var LatestRate = GetSwapRateByDate(timeRate, valueDate); extraAmountGet += stockEqvNotional * LatestRate * ((double)latestDays / annualDays); } return extraAmountGet.Normalize().FormatValue(2); } /// /// 获取计息开始日期,结束日期 /// /// /// /// /// /// /// /// public static DateTime GetSwapRateStartDate(OtcTradeBase td, trade_swap tradeSwap, DateTime valueDate, trade_cash tradeCash, trade_cash lastManualCash, bool floating, out DateTime endDate) { var tradeStartDate = PS.Config.Company == Configuration.CompanyEnum.中金 ? td.TradeDate.Value : td.StartDate.Value; var calcFirst = tradeSwap.RateCalcMode.StartsWith("1");//算头 var calcLast = tradeSwap.RateCalcMode.EndsWith("1");//算尾 DateTime startDate = calcFirst ? tradeStartDate.AddDays(-1) : tradeStartDate; //如果valueDate超过了到期日,利息以到期日来计算 endDate = valueDate > td.ExerciseDate ? td.ExerciseDate.Value : valueDate; //是否算尾 if (td.ExerciseDate == endDate && !calcLast) { endDate = endDate.AddDays(-1); } if (floating) { if(lastManualCash != null) { startDate = lastManualCash.ValueDate; } } else if (tradeCash != null) { startDate = tradeCash.ValueDate; } return startDate; } /// /// 获取计息开始日期,结束日期 /// /// /// /// /// /// /// /// public static DateTime GetSwapRateStartDatePre(OtcTradeBase td, trade_swap tradeSwap, DateTime valueDate, trade_cash tradeCash, trade_cash_pre lastManualCash, bool floating, out DateTime endDate) { var tradeStartDate = PS.Config.Company == Configuration.CompanyEnum.中金 ? td.TradeDate.Value : td.StartDate.Value; var calcFirst = tradeSwap.RateCalcMode.StartsWith("1");//算头 var calcLast = tradeSwap.RateCalcMode.EndsWith("1");//算尾 DateTime startDate = calcFirst ? tradeStartDate.AddDays(-1) : tradeStartDate; endDate = valueDate; if (td.ExerciseDate == endDate && !calcLast) { endDate = endDate.AddDays(-1); } if (floating) { if (lastManualCash != null) { startDate = lastManualCash.ValueDate; } } else if (tradeCash != null) { startDate = tradeCash.ValueDate; } return startDate; } /// /// 计算互换手续费 /// /// /// /// /// /// public static double GetCostFee(trade tradePosition, trade tradeImport, trade_cash tradeCash, bool isForGet, bool isOpenFee) { if (tradeImport.trade_swap == null) { throw new Exception($"该交易[{tradeImport.TradeNumber}]对应的trade_swap未赋值"); } double costFee = 0; if (isForGet) { var underlying = DataCacheProvider.GetUnderlyingDataSource().GetData(tradeImport.UnderlyingCode); costFee += (tradeImport.trade_swap.GetSingleFee ?? 0) * (tradeCash.UnwindNotional ?? 0) / underlying.ContractSize; costFee += (tradeImport.trade_swap.GetUnAnnualRate ?? 0) * (tradeCash.UnwindNotional ?? 0) * (isOpenFee ? (tradePosition.SpotPrice ?? 0) : (tradeCash.FinalPrice ?? 0)); } else { var underlying = DataCacheProvider.GetUnderlyingDataSource().GetData(tradeImport.UnderlyingCode); costFee += (tradeImport.trade_swap.PaySingleFee ?? 0) * (tradeCash.UnwindNotional ?? 0) / underlying.ContractSize; costFee += (tradeImport.trade_swap.PayUnAnnualRate ?? 0) * (tradeCash.UnwindNotional ?? 0) * (isOpenFee ? (tradePosition.SpotPrice ?? 0) : (tradeCash.FinalPrice ?? 0)); } return costFee.FormatValue(2); } /// /// /// public static double GetInitialAmountSwapGet(OtcTradeBase trade, trade_swap trade_swap, double lastFinalPrice , double price, double stockEqvNotional, DateTime valueDate, DateTime? preSwapDate) { double? initialAmount; if (trade_swap.IsGetFloatingProfit) { var annualRate = GetAnnualVarIncomeRate(trade_swap, valueDate, preSwapDate, trade.StartDate.Value, trade.ExerciseDate.Value); initialAmount = GetInitialAmountSwap(lastFinalPrice, price , (trade_swap.GetNotional ?? 0) * stockEqvNotional / trade.OriginalStockEqvNotional.Value , trade_swap.GetLongShort, annualRate); } else { initialAmount = trade_swap.GetFixedProfit * stockEqvNotional / trade.OriginalStockEqvNotional; } return (initialAmount ?? 0).FormatValue(2); } /// /// /// public static double GetInitialAmountSwapPay(OtcTradeBase trade, trade_swap trade_swap, double lastFinalPrice , double price, double stockEqvNotional, DateTime valueDate, DateTime? preSwapDate) { double? initialAmount; if (trade_swap.IsPayFloatingProfit) { var annualRate = GetAnnualVarIncomeRate(trade_swap, valueDate, preSwapDate, trade.StartDate.Value, trade.ExerciseDate.Value); initialAmount = GetInitialAmountSwap(lastFinalPrice, price , (trade_swap.PayNotional ?? 0) * stockEqvNotional / trade.OriginalStockEqvNotional.Value , trade_swap.PayLongShort, annualRate); } else { initialAmount = trade_swap.PayFixedProfit * stockEqvNotional / trade.OriginalStockEqvNotional; } return (initialAmount ?? 0).FormatValue(2); } /// /// /// /// /// /// /// /// 年化浮动收益率(为null表示非年化) /// public static double GetInitialAmountSwap(double lastFinalPrice, double price, double notional, string longShort, double? annualVarIncomeRate) { var amount = (price - lastFinalPrice) * notional.Normalize() * (longShort == "多头" ? 1 : -1); return annualVarIncomeRate.HasValue ? amount * annualVarIncomeRate.Value : amount; } private static List GetSwapDatesBetween(string swapTimeAndRate, DateTime startDate, DateTime endDate) { var customizedResults = QdpHelper.ParseAutocallCustomizedInfo(swapTimeAndRate); var dates = customizedResults.Item1; if (dates == null) { return null; } return dates.Where(x => x.DateTime > startDate && x.DateTime <= endDate).ToList(); } public static double GetSwapRateByDate(string swapTimeAndRate, DateTime valueDate) { double swapRate = 0; var customizedResults = QdpHelper.ParseAutocallCustomizedInfo(swapTimeAndRate); var dates = customizedResults.Item1; if (dates == null) { return swapRate; } var getSwapRates = customizedResults.Item2; var latestDate = dates.Where(x => x.DateTime >= valueDate).OrderBy(x => x.DateTime).FirstOrDefault(); //展期情况互换利率获取最后一个日期的互换利率 if (latestDate == null) { latestDate = dates.Max(); } if (getSwapRates != null && getSwapRates.Any()) { swapRate = getSwapRates[GetDateIndex(dates, latestDate)]; } return swapRate; } private static int GetDateIndex(Date[] source, Date value) { if (source is null) { throw new ArgumentNullException(nameof(source)); } var index = 0; foreach (var item in source) { if (item.DateTime == value.DateTime) { return index; } index++; } return -1; } /// /// 获取互换交易变动收益年化率,返回null表示非年化 /// /// 互换交易 /// 结算日期 /// 上次互换日期 /// 交易开始日期 /// 返回null表示非年化 public static double? GetAnnualVarIncomeRate(trade_swap trade_swap, DateTime valueDate, DateTime? preSwapDate, DateTime tradeStartDate, DateTime exerciseDate) { if (trade_swap is null) { throw new ArgumentNullException(nameof(trade_swap)); } //浮动收益是否年化 if (!trade_swap.AnnualVarIncome) { return null; } if (!trade_swap.AnnualDays.HasValue || trade_swap.AnnualDays < 1) { return null; } DateTime startDate; if (preSwapDate != null) { startDate = preSwapDate.Value.AddDays(1); } else { //2021-05-11:浮动收益年化时,首日计息规则会影响到收益金额的计算 bool calcFirst = trade_swap.RateCalcMode.StartsWith("1");//算头 startDate = calcFirst ? tradeStartDate : tradeStartDate.AddDays(1); } var days = (valueDate - startDate.Date).Days + 1; if (exerciseDate == valueDate && trade_swap.RateCalcMode.EndsWith("0")) { days -= 1; } if (days < 0) { days = 0; } return (double)days / trade_swap.AnnualDays.Value; } } }