diff --git a/Framework/YLErp.Core/DBModels/ClientPosition.cs b/Framework/YLErp.Core/DBModels/ClientPosition.cs index 115521c7..675358d5 100644 --- a/Framework/YLErp.Core/DBModels/ClientPosition.cs +++ b/Framework/YLErp.Core/DBModels/ClientPosition.cs @@ -114,5 +114,7 @@ namespace YLErp.DBModels /// 收支方向 1:收取:2支付 /// public int direction { get; set; } + [NotMapped] + public bool Current { get; set; } } } diff --git a/Framework/YLErp.Core/DBModels/Enums/SwapInterestRule.cs b/Framework/YLErp.Core/DBModels/Enums/SwapInterestRule.cs new file mode 100644 index 00000000..5125202f --- /dev/null +++ b/Framework/YLErp.Core/DBModels/Enums/SwapInterestRule.cs @@ -0,0 +1,14 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Threading.Tasks; + +namespace YLErp.DBModels +{ + public enum SwapInterestRule + { + 前一营业日=-1, + 当前营业日=0, + } +} diff --git a/Framework/YLErp.Core/DBModels/EodSwapPosition.cs b/Framework/YLErp.Core/DBModels/EodSwapPosition.cs index 492f3bf9..97439fe9 100644 --- a/Framework/YLErp.Core/DBModels/EodSwapPosition.cs +++ b/Framework/YLErp.Core/DBModels/EodSwapPosition.cs @@ -421,7 +421,14 @@ namespace YLErp.DBModels /// 虚拟交易费用 /// public decimal VTradingFee { get; set; } - + /// + /// 重置频率(天) + /// + public int? interest_rest_days { get; set; } + /// + /// 利率准则(-1前一营业日,0当前营业日) + /// + public int? interest_rule { get; set; } /// /// 持仓编码 /// diff --git a/Framework/YLErp.Core/DBModels/SwapFloatRate.cs b/Framework/YLErp.Core/DBModels/SwapFloatRate.cs index 9e993053..020cdcfc 100644 --- a/Framework/YLErp.Core/DBModels/SwapFloatRate.cs +++ b/Framework/YLErp.Core/DBModels/SwapFloatRate.cs @@ -96,5 +96,28 @@ namespace YLErp.DBModels [Column("base_underlying_code")] [DataChange] public string BaseUnderlyingCode { get; set; } + + /// + /// 计息方式 0:单利,1:复利 + /// + public int? interest_type { get; set; } + [NotMapped] + public string interest_type_str { get { return interest_type.HasValue ? ((InterestTypeEnum)interest_type.Value).ToString() : ""; } } + /// + /// 是否年化 + /// + public bool? is_annualized { get; set; } + [NotMapped] + public string is_annualized_str { get { return is_annualized == true ? "是" : "否"; } } + /// + /// 重置频率(天) + /// + public int? interest_rest_days { get; set; } + /// + /// 利率准则(-1前一营业日,0当前营业日) + /// + public int? interest_rule { get; set; } + [NotMapped] + public string interest_rule_str { get { return interest_rule.HasValue ? ((SwapInterestRule)interest_rule.Value).ToString() : ""; } } } } diff --git a/Framework/YLErp.Core/DBModels/SwapPosition.cs b/Framework/YLErp.Core/DBModels/SwapPosition.cs index 9eb5ae43..5ba144d3 100644 --- a/Framework/YLErp.Core/DBModels/SwapPosition.cs +++ b/Framework/YLErp.Core/DBModels/SwapPosition.cs @@ -234,6 +234,14 @@ namespace YLErp.DBModels /// public bool Invalid { get; set; } /// + /// 重置频率(天) + /// + public int? interest_rest_days { get; set; } + /// + /// 利率准则(-1前一营业日,0当前营业日) + /// + public int? interest_rule { get; set; } + /// /// 互换观察日集合 /// [NotMapped] diff --git a/Framework/YLErp.Core/DBModels/client_marginrate.cs b/Framework/YLErp.Core/DBModels/client_marginrate.cs index 8bf733b5..4a8ecdba 100644 --- a/Framework/YLErp.Core/DBModels/client_marginrate.cs +++ b/Framework/YLErp.Core/DBModels/client_marginrate.cs @@ -56,6 +56,11 @@ namespace YLErp.DBModels public decimal? LongInitMarginRate { get; set; } [DisplayName("空头初始预付金率")] public decimal? ShortInitMarginRate { get; set; } + + /// + /// 互换默认期限 + /// + public int? SwapEndDays { get; set; } public string GetDataTraceKeyInfo() { return "客户品种预付金率:" + ClientId + Type + VarietyId; diff --git a/YLErpDAL/Model/HengTaiModel/ClientRiskCheckReq.cs b/YLErpDAL/Model/HengTaiModel/ClientRiskCheckReq.cs index 2b2efe42..d6777238 100644 --- a/YLErpDAL/Model/HengTaiModel/ClientRiskCheckReq.cs +++ b/YLErpDAL/Model/HengTaiModel/ClientRiskCheckReq.cs @@ -62,5 +62,9 @@ namespace YLErp.Model.HengTaiModel /// 是否客户端 /// public bool isClient { get; set; } = false; + /// + /// 委托订单id + /// + public long orderId { get; set; } } } diff --git a/YLErpDAL/Model/QuotaMonitor_UnderlyingModel.cs b/YLErpDAL/Model/QuotaMonitor_UnderlyingModel.cs index 5ad717a0..ef399345 100644 --- a/YLErpDAL/Model/QuotaMonitor_UnderlyingModel.cs +++ b/YLErpDAL/Model/QuotaMonitor_UnderlyingModel.cs @@ -12,5 +12,6 @@ namespace YLErp.Model public double StockEqvNotional { get; set; } public int ClientId { get; set; } public int PositionType { get; set; } + public decimal Qty { get; set; } } } diff --git a/YLErpDAL/Modules/MarginModule/MarginRateSwapService.cs b/YLErpDAL/Modules/MarginModule/MarginRateSwapService.cs index 7a41ec13..f00f87cc 100644 --- a/YLErpDAL/Modules/MarginModule/MarginRateSwapService.cs +++ b/YLErpDAL/Modules/MarginModule/MarginRateSwapService.cs @@ -85,8 +85,7 @@ namespace YLErp.Modules.MarginModule InitMarginRate = reader.GetPercent("期初预付金", false) ?? 0, InitMarginRebateRate = reader.GetDecimalPercent("期初预付金利率", false) ?? 0, AddToMarginRebateRate= reader.GetDecimalPercent("追加预付金利率", false) ?? 0, - LongInitMarginRate = reader.GetDecimalPercent("多头期初预付金率", false), - ShortInitMarginRate = reader.GetDecimalPercent("空头期初预付金率", false) + SwapEndDays=reader.GetInt("互换默认期限",true) }; var client = new Client(); marginRate.Type = "普通"; @@ -133,6 +132,7 @@ namespace YLErp.Modules.MarginModule dbMarginRate.AddToMarginRebateRate= marginrate.AddToMarginRebateRate; dbMarginRate.LongInitMarginRate = marginrate.LongInitMarginRate; dbMarginRate.ShortInitMarginRate = marginrate.ShortInitMarginRate; + dbMarginRate.SwapEndDays = marginrate.SwapEndDays; dbMarginRate.OptId = UserId; dbMarginRate.OptName = UserName; dbMarginRate.OptDate = DateTime.Now; @@ -166,6 +166,7 @@ namespace YLErp.Modules.MarginModule dbMarginRate.AddToMarginRebateRate = marginrate.AddToMarginRebateRate; dbMarginRate.LongInitMarginRate = marginrate.LongInitMarginRate; dbMarginRate.ShortInitMarginRate = marginrate.ShortInitMarginRate; + dbMarginRate.SwapEndDays = marginrate.SwapEndDays; dbMarginRate.OptId = UserId; dbMarginRate.OptName = UserName; dbMarginRate.OptDate = DateTime.Now; diff --git a/YLErpDAL/Modules/RiskModule/QuotaMonitorService.cs b/YLErpDAL/Modules/RiskModule/QuotaMonitorService.cs index c0cb89a1..dbbcf203 100644 --- a/YLErpDAL/Modules/RiskModule/QuotaMonitorService.cs +++ b/YLErpDAL/Modules/RiskModule/QuotaMonitorService.cs @@ -1030,15 +1030,7 @@ namespace YLErp.Modules.RiskModule } positionList.AddRange(swapPositionList); } - var hedgeList = CalculateExchangeTrade(); - GuangFaOnExchangeHandleGammaAndTheta(hedgeList); - GetDeltaExposure(positionList, hedgeList, out var optionExposure, out var swapExposure, out var forwardeExposure, out var offExchangeExposure, out var optionNetExposure, out var offExchangeNetExposure); - swap.DeltaExposure = swapExposure; swap.DeltaNetExposure = double.NaN; - GuangFaOnExchangeHandleGammaAndTheta(hedgeList); - var riskDict = GetRiskScale(positionTradeQuery.ToList(), out var offExchangeEquity, out var offExchangeNonEquity, out var swapEquity, out var swapNonEquity, out var forwardEquity, out var forwardNonEquity); - swap.EquityRiskScale = swapEquity; - swap.NonEquityRiskScale = swapNonEquity; var checkPosiList = GetCheckPosiList(); var posiStockEqvNotional = checkPosiList.Sum(s => s.Pv); var underly = new QuotaMonitor_Global() @@ -1123,42 +1115,15 @@ namespace YLErp.Modules.RiskModule /// public List QueryClientFromCalc(QuotaMonitorReq req) { - var query = from risk in DbContext.realtime_trade_risk - join t in DbContext.trade - on risk.TradeId equals t.id - where - risk.VolType == "持仓" && - t.TradeType == "收益互换" && - t.ExerciseDate >= SystemValueDate && - ConsTrade.PositionTradeStatusList.Contains(t.TradeStatus) && - t.ValidState != "InValid" - select new - { - t.id, - t.ClientId, - StockEqvNotional = t.ParentTradeId > 0 ? 0 : t.StockEqvNotional, - t.BuySell, - TradePrice = t.TradeType == "结构化交易" ? 0 : t.TradePrice, - InitialMargin = t.TradeType == "结构化交易" ? 0 : t.InitialMargin - }; - if (req.ClientId != 0) - { - query = query.Where(O => req.ClientId == O.ClientId); - } - if (req.ClientNumber != 0) - { - query = query.Where(O => req.ClientNumber == O.ClientId); - } - if (req.TagIds != null && req.TagIds.Count > 0) - { - var tradeTagIdQuery = from tt in DbContext.trade_tag - where req.TagIds.Contains(tt.TagId) - select tt.TradeId; - query = query.Where(p => tradeTagIdQuery.Contains(p.id)); - } - var clientPositionList = query.ToList(); - var positionDict = clientPositionList.GroupBy(O => O.ClientId).ToDictionary(K => K.Key, V => V.ToList()); + using var bondDb = new BondOmsDBContext(); + var clientPositions = bondDb.client_position.Where(x => x.position_qty > 0).AsNoTracking().AsEnumerable(); + var positionDict = clientPositions.GroupBy(O => O.client_id).ToDictionary(K => K.Key, V => V.ToList()); var clientIdList = positionDict.Keys.ToList(); + List clientIds = new List(); + clientIdList.ForEach(item => + { + clientIds.Add(Convert.ToInt32(item.Value)); + }); var setValue = new Action>((obj, settings) => { var temp = settings.Where(O => O.IsValid && O.Status == QuotaSettingApprovalStatus.Valid && (O.QuotaRange == obj.ClientId || O.QuotaRange == 0)); @@ -1173,13 +1138,6 @@ namespace YLErp.Modules.RiskModule obj.Quota_StockEqvNotional_Lower = stockEqvNotionalSettings?.QuotaLowerLimit ?? double.NaN; obj.Quota_StockEqvNotional_wUpper = stockEqvNotionalSettings?.WarningUpperLimit ?? double.NaN; obj.Quota_StockEqvNotional_wLower = stockEqvNotionalSettings?.WarningLowerLimit ?? double.NaN; - - var holdingFundSettings = temp.Where(O => O.QuotaIndex == "产品占用资金限额").FirstOrDefault()?.Clone(); - holdingFundSettings?.ConvertToAbs(obj.ProductScale.GetValueOrDefault()); - obj.Quota_HoldingFund_Upper = holdingFundSettings?.QuotaUpperLimit ?? double.NaN; - obj.Quota_HoldingFund_Lower = holdingFundSettings?.QuotaLowerLimit ?? double.NaN; - obj.Quota_HoldingFund_wUpper = holdingFundSettings?.WarningUpperLimit ?? double.NaN; - obj.Quota_HoldingFund_wLower = holdingFundSettings?.WarningLowerLimit ?? double.NaN; }); var list = new List(); @@ -1194,45 +1152,8 @@ namespace YLErp.Modules.RiskModule .ToList(); var creditExposureDict = new Dictionary(); - if (req.UseCalcCreditExposure) - { - var config = valuedateBLL.SystemDate; - var tradeIds = clientPositionList.Select(O => O.id); - //去除不需要计算信用风险敞口的交易; - tradeIds = tradeIds.Except( - DbContext.TradeMeta.Where(O => - tradeIds.Contains(O.TradeId) && - O.MetaKey == ConsTradeMetaKey.ignoreRiskExposure && - O.MetaValue == "1") - .Select(O => O.TradeId).ToList()); - var tradeList = DbContext.trade.Where(O => tradeIds.Contains(O.id)).ToList(); - new TradeExtendService(UserInfo).SetTradeExtend(tradeList, true); - var ccrService = new CCRService(UserInfo); - var dict = ccrService.CalculationCCR(tradeList, config.RiskExposureJ, config.RiskExposureN); - var cashTypes = new[] { "出金", "入金" }; - var clientCash = - DbContext.ClientCashInCashOut - .Where(O => - clientIdList.Contains(O.ClientId ?? 0) && - cashTypes.Contains(O.Direction)) - .Select(O => new - { - O.ClientId, - cash = O.Money * (O.Direction == "出金" ? 1 : -1) - }) - .ToList(); - foreach (var item in positionDict) - { - var pv = (from ccr in dict - join info in item.Value - on ccr.Key equals info.id - select ccr.Value).Sum(); - // max(0,ccr + 客户总出金-客户总入金) - creditExposureDict[item.Key] = Math.Max(0, pv + (clientCash.Where(O => O.ClientId == item.Key).Sum(O => O.cash) ?? 0)); - } - } var todayClientBalance = new RealTimeClientBanlanceService(OptUserInfo.SystemUser) - .GetBanlances(clientIdList, SystemValueDate); + .GetBanlances(clientIds, SystemValueDate); foreach (var item in todayClientBalance) { var c = new QuotaMonitor_Client(); @@ -1249,15 +1170,6 @@ namespace YLErp.Modules.RiskModule var credits = creditList.Where(O => O.ClientId == item.ClientId && O.StockEqvNotional.IsNormalize()).Select(O => O.StockEqvNotional).ToArray(); c.StockEqvNotionalScale = credits.Any() ? (credits.Sum() ?? double.NaN) : double.NaN; c.AvailableStockEqvNotional = c.StockEqvNotionalScale - c.StockEqvNotional; - if (client.ClientType == ClientTypeEnum.产品.ToString()) - { - c.HoldingFund = positionDict.ContainsKey(client.id) ? positionDict[client.id].Sum(O => O.BuySell == "卖出" ? (O.TradePrice ?? 0) : (O.InitialMargin ?? 0)) : double.NaN; - if (double.TryParse(client.RegisteredCapital, out var productScale)) - { - c.ProductScale = productScale * 10000; - c.HoldingRate = c.HoldingFund / c.ProductScale; - } - } c.CreditExposure = creditExposureDict.ContainsKey(item.ClientId) ? creditExposureDict[item.ClientId] : null; list.Add(c); } @@ -1298,7 +1210,7 @@ namespace YLErp.Modules.RiskModule var result = new List(); req.sidx = "TradeNumber"; req.sord = "asc"; - var predicate = PredicateBuilder.True(); + var predicate = PredicateBuilder.True().And(t => ConsTrade.PositionTradeStatusList.Contains(t.TradeStatus) && t.ValidState != "InValid" && t.ExerciseDate >= SystemValueDate); if (!req.TradeNumber.IsNullOrWhiteSpace()) { predicate = predicate.And(O => O.TradeNumber.Contains(req.TradeNumber)); @@ -1344,28 +1256,16 @@ namespace YLErp.Modules.RiskModule var posiQuery = from t in DbContext.trade.Where(predicate) join p in DbContext.swap_position on t.id equals p.SwapTradeId where !p.IsInitial && p.PosiDirection > 0 && !p.Invalid - select p; - var query = from risk in DbContext.realtime_trade_risk - join t in DbContext.trade.Where(predicate) - on risk.TradeId equals t.id - where risk.VolType == "持仓" && t.TradeType == "收益互换" && - ConsTrade.PositionTradeStatusList.Contains(t.TradeStatus) && t.ValidState != "InValid" && - t.ExerciseDate >= SystemValueDate - select new QuotaMonitor_TradeDto() - { - trade = t, - ClientId = t.ClientId, - TradeNumber = t.TradeNumber, - ClientName = t.ClientName, - StockEqvNotional = t.StockEqvNotional, - DeltaCash = risk.DeltaCash ?? 0, - GammaCash = risk.GammaCash ?? 0, - Vega = risk.Vega ?? 0, - VegaCash = risk.VegaCash ?? 0, - PnL = (PS.Config.IsPVRounded ? risk.RoundedPositionPnl : risk.PositionPnl) ?? 0 + risk.RealizedPnl - }; + select new QuotaMonitor_TradeDto() + { + trade = t, + ClientId = t.ClientId, + TradeNumber = t.TradeNumber, + ClientName = t.ClientName, + StockEqvNotional = t.StockEqvNotional, + }; - var list = query.ToList(); + var list = posiQuery.ToList(); var setValue = new Action>((obj, settings) => { @@ -1376,41 +1276,6 @@ namespace YLErp.Modules.RiskModule obj.Quota_StockEqvNotional_Lower = stockEqvNotionalSetting?.QuotaLowerLimit ?? double.NaN; obj.Quota_StockEqvNotional_wUpper = stockEqvNotionalSetting?.WarningUpperLimit ?? double.NaN; obj.Quota_StockEqvNotional_wLower = stockEqvNotionalSetting?.WarningLowerLimit ?? double.NaN; - - var deltaCashSetting = temp.Where(O => O.QuotaIndex == "Delta金额").FirstOrDefault()?.Clone(); - deltaCashSetting?.ConvertToAbs(valuedateBLL.SystemDate.BusinessAvailableFunds ?? 0); - obj.Quota_DeltaCash_Upper = deltaCashSetting?.QuotaUpperLimit ?? double.NaN; - obj.Quota_DeltaCash_Lower = deltaCashSetting?.QuotaLowerLimit ?? double.NaN; - obj.Quota_DeltaCash_wUpper = deltaCashSetting?.WarningUpperLimit ?? double.NaN; - obj.Quota_DeltaCash_wLower = deltaCashSetting?.WarningLowerLimit ?? double.NaN; - - var gammaCashSetting = temp.Where(O => O.QuotaIndex == "Gamma金额").FirstOrDefault()?.Clone(); - gammaCashSetting?.ConvertToAbs(valuedateBLL.SystemDate.BusinessAvailableFunds ?? 0); - obj.Quota_GammaCash_Upper = gammaCashSetting?.QuotaUpperLimit ?? double.NaN; - obj.Quota_GammaCash_Lower = gammaCashSetting?.QuotaLowerLimit ?? double.NaN; - obj.Quota_GammaCash_wUpper = gammaCashSetting?.WarningUpperLimit ?? double.NaN; - obj.Quota_GammaCash_wLower = gammaCashSetting?.WarningLowerLimit ?? double.NaN; - - var vegaSetting = temp.Where(O => O.QuotaIndex == "Vega").FirstOrDefault()?.Clone(); - vegaSetting?.ConvertToAbs(valuedateBLL.SystemDate.BusinessAvailableFunds ?? 0); - obj.Quota_Vega_Upper = vegaSetting?.QuotaUpperLimit ?? double.NaN; - obj.Quota_Vega_Lower = vegaSetting?.QuotaLowerLimit ?? double.NaN; - obj.Quota_Vega_wUpper = vegaSetting?.WarningUpperLimit ?? double.NaN; - obj.Quota_Vega_wLower = vegaSetting?.WarningLowerLimit ?? double.NaN; - - var vegaCashSetting = temp.Where(O => O.QuotaIndex == "Vega金额").FirstOrDefault()?.Clone(); - vegaCashSetting?.ConvertToAbs(valuedateBLL.SystemDate.BusinessAvailableFunds ?? 0); - obj.Quota_VegaCash_Upper = vegaCashSetting?.QuotaUpperLimit ?? double.NaN; - obj.Quota_VegaCash_Lower = vegaCashSetting?.QuotaLowerLimit ?? double.NaN; - obj.Quota_VegaCash_wUpper = vegaCashSetting?.WarningUpperLimit ?? double.NaN; - obj.Quota_VegaCash_wLower = vegaCashSetting?.WarningLowerLimit ?? double.NaN; - - var lossSetting = temp.Where(O => O.QuotaIndex == "止损金额").FirstOrDefault()?.Clone(); - obj.Quota_Loss_Upper = lossSetting?.QuotaUpperLimit ?? double.NaN; - obj.Quota_Loss_Lower = lossSetting?.QuotaLowerLimit ?? double.NaN; - obj.Quota_Loss_wUpper = lossSetting?.WarningUpperLimit ?? double.NaN; - obj.Quota_Loss_wLower = lossSetting?.WarningLowerLimit ?? double.NaN; - var swapPercentSetting = temp.Where(O => O.QuotaIndex == "互换价格偏离比例").FirstOrDefault()?.Clone(); obj.Quota_SwapPercent_Upper = swapPercentSetting?.QuotaUpperLimit ?? double.NaN; obj.Quota_SwapPercent_Lower = swapPercentSetting?.QuotaLowerLimit ?? double.NaN; @@ -1424,17 +1289,12 @@ namespace YLErp.Modules.RiskModule foreach (var item in list) { setValue(item, setting); - if (item.trade.TradeType == "收益互换") - { - var sportPrice = item.trade.SpotPrice ?? 0; - var bondPrice = EodPriceQueryService.GetBondPrice(valuedateBLL.ValueDate, item.trade.UnderlyingCode); - var basePrice = bondPrice == null ? 0 : bondPrice.ClosePrice; - var vobp = bondPrice == null ? 0 : Convert.ToDouble(bondPrice.Vobp); - var pricePercent = basePrice == 0 ? 0 : Math.Abs((sportPrice / basePrice) - 1); - item.Quota_SwapPercent = pricePercent; - var stockEqvNotional = posiQuery.Where(s => s.SwapTradeId == item.trade.id).Sum(s => s.PosiNotionalValue); - item.StockEqvNotional = Convert.ToDouble(stockEqvNotional); - } + var sportPrice = item.trade.SpotPrice ?? 0; + var bondPrice = EodPriceQueryService.GetBondPrice(valuedateBLL.ValueDate, item.trade.UnderlyingCode); + var basePrice = bondPrice == null ? 0 : bondPrice.ClosePrice; + var vobp = bondPrice == null ? 0 : Convert.ToDouble(bondPrice.Vobp); + var pricePercent = basePrice == 0 ? 0 : Math.Abs((sportPrice / basePrice) - 1); + item.Quota_SwapPercent = pricePercent; var obj = new QuotaMonitor_Trade(); ObjectHelper.MapValues(obj, item); result.Add(obj); @@ -1448,7 +1308,7 @@ namespace YLErp.Modules.RiskModule Vega = result.Sum(O => O.Vega), VegaCash = result.Sum(O => O.VegaCash), Quota_CCR = result.Sum(O => O.Quota_CCR), - PnL = result.Sum(O => O.PnL) + PnL = result.Sum(O => O.PnL), }; result.Add(total); @@ -2032,23 +1892,19 @@ namespace YLErp.Modules.RiskModule var result = new List(); using var bondDb = new BondOmsDBContext(); var clientPositions = bondDb.client_position.AsNoTracking().AsEnumerable(); - var query = from p in DbContext.swap_position - join t in DbContext.trade - on p.SwapTradeId equals t.id - where - t.TradeType == "收益互换" && - t.ExerciseDate >= SystemValueDate && - ConsTrade.PositionTradeStatusList.Contains(t.TradeStatus) && - t.ValidState != "InValid" && - !p.IsInitial && p.PositionType > 0 - select new QuotaMonitor_UnderlyingModel - { - UnderlyingCode = p.UnderlyingCode, - StockEqvNotional = t.StockEqvNotional, - ClientId = t.ClientId, - PositionType = p.PositionType - }; - var queryGroup = query.ToList().GroupBy(g => g.UnderlyingCode).ToList(); + List underlyingModels = new List(); + foreach (var item in clientPositions) + { + var model = new QuotaMonitor_UnderlyingModel() + { + UnderlyingCode = item.security_id, + StockEqvNotional = Convert.ToDouble(item.position_notional_principal ?? 0), + PositionType = item.side + 1, + Qty = item.position_qty ?? 0, + }; + underlyingModels.Add(model); + } + var queryGroup = underlyingModels.GroupBy(g => g.UnderlyingCode).ToList(); var qutoList = new List(); foreach (var item in queryGroup) { @@ -3565,6 +3421,9 @@ namespace YLErp.Modules.RiskModule if (O.QuotaIndex.Contains("名义本金")) { O.QuotaLowerLimit *= ConsGlobal.quotaMonitorMultiple; + O.QuotaUpperLimit *= ConsGlobal.quotaMonitorMultiple; + O.WarningLowerLimit *= ConsGlobal.quotaMonitorMultiple; + O.WarningUpperLimit *= ConsGlobal.quotaMonitorMultiple; } O.QuotaLowerLimit = O.QuotaLowerLimit.FormatValue(count); O.QuotaUpperLimit = O.QuotaUpperLimit.FormatValue(count); @@ -4275,98 +4134,27 @@ namespace YLErp.Modules.RiskModule { return clientRiskCheckResps; } - var clientPrecheckQuotaSettingList = precheckQuotaSettingList.Where(x => x.QuotaRange == clientRiskCheckReq.clientId && x.QuotaType == QuotaTypeEnum.CLIENT && x.QuotaIndex == "名义本金").ToList(); - var allClientPrecheckQuotaSettingList = precheckQuotaSettingList.Where(x => x.QuotaRange == 0 && x.QuotaType == QuotaTypeEnum.CLIENT && x.QuotaIndex == "名义本金").ToList(); - if (clientPrecheckQuotaSettingList.Count > 0) - { - foreach (var item in allClientPrecheckQuotaSettingList) - { - precheckQuotaSettingList.Remove(item); - } - } var client = DataCacheProvider.GetClientDataSource().GetData(clientRiskCheckReq.clientId); if (client == null) { throw new ServiceException("未找到客户信息"); } using var bondDb = new BondOmsDBContext(); - var clientPositions = bondDb.client_position.AsNoTracking().ToList();//所有持仓 - ClientPosition posi = new ClientPosition(); - posi.commission = clientRiskCheckReq.commission; - posi.side = clientRiskCheckReq.side; - posi.security_id = clientRiskCheckReq.securityId; - posi.full_price_now = clientRiskCheckReq.price * ConsGlobal.bondShowPriceMultiple; - posi.deal_full_price_avg = clientRiskCheckReq.price * ConsGlobal.bondShowPriceMultiple; - posi.client_id = clientRiskCheckReq.clientId; - posi.position_qty = clientRiskCheckReq.qty / 10000; - posi.position_notional_principal = clientRiskCheckReq.qty; - posi.direction = (int)SwapDirectionEnum.支付; - clientPositions.Add(posi); - var umCodes = clientPositions.Select(s => s.security_id).Distinct().ToList(); - umCodes.Add(clientRiskCheckReq.securityId); - umCodes = umCodes.Distinct().ToList(); - var ums = DataCacheProvider.GetUnderlyingDataSource().AsQueryable().Where(x => umCodes.Contains(x.UnderlyingCode)); - List checkPoisiList = new List(); - foreach (var item in clientPositions) - { - var dealDate = DateTime.Parse(clientRiskCheckReq.dealDate); - double lastPrice = 0; - var um = ums.FirstOrDefault(x => x.UnderlyingCode == item.security_id); - CheckQuotaMoitorModel checkQuotaMoitorModel = new CheckQuotaMoitorModel(); - checkQuotaMoitorModel.Qty = (item.position_qty ?? 0) * 10000; ; - checkQuotaMoitorModel.id = item.id; - checkQuotaMoitorModel.Price = (item.deal_full_price_avg ?? 0); - checkQuotaMoitorModel.NowPrice = (item.full_price_now ?? 0); - checkQuotaMoitorModel.ContractSize = um == null ? 1 : Convert.ToDecimal(um.ContractSize); - checkQuotaMoitorModel.Pv = item.position_notional_principal ?? 0; - checkQuotaMoitorModel.Direction = item.direction; - if (item.id == 0) - { - checkQuotaMoitorModel.Pv = checkQuotaMoitorModel.Qty * checkQuotaMoitorModel.ContractSize; - if (client.SwapTradeType == 1) - { - checkQuotaMoitorModel.Pv *= checkQuotaMoitorModel.Price; - } - } - checkQuotaMoitorModel.UnderlyingCode = item.security_id; - checkQuotaMoitorModel.Side = item.side; - checkQuotaMoitorModel.Commision = item.commission ?? 0; - checkQuotaMoitorModel.ClientId = Convert.ToInt32(item.client_id ?? 0); - checkQuotaMoitorModel.Delta = (checkQuotaMoitorModel.Side == 0 ? checkQuotaMoitorModel.Price : -checkQuotaMoitorModel.Price) * checkQuotaMoitorModel.Qty; - if (um != null) - { - checkQuotaMoitorModel.UnderlyingId = um.id; - if (!um.IsBond()) - { - checkQuotaMoitorModel.Pv = checkQuotaMoitorModel.Pv * checkQuotaMoitorModel.Price; - lastPrice = DataCacheProvider.GetUnderlyingDataSource().GetPrice(item.security_id); - } - else - { - checkQuotaMoitorModel.Price *= ConsGlobal.bondPriceMultiple; - checkQuotaMoitorModel.NowPrice *= ConsGlobal.bondPriceMultiple; - var bondPrice = EodPriceQueryService.GetBondPrice(dealDate, item.security_id); - lastPrice = bondPrice != null ? bondPrice.ClosePrice : (um.Price ?? 0) * Convert.ToDouble(ConsGlobal.bondPriceMultiple); - var bond = JsonHelper.Deserialize(um.ExJson); - checkQuotaMoitorModel.Circulation = (bond.IssueSize * 100000000m) ?? 0; - } - } - var ratio = checkQuotaMoitorModel.Side == 0 ? 1 : -1; - var directionRatio = checkQuotaMoitorModel.Direction == (int)SwapDirectionEnum.收取 ? 1 : -1; - checkQuotaMoitorModel.LastPrice = Convert.ToDecimal(lastPrice); - checkQuotaMoitorModel.Pnl = (checkQuotaMoitorModel.NowPrice - checkQuotaMoitorModel.Price) * checkQuotaMoitorModel.Qty; - checkQuotaMoitorModel.Current = checkQuotaMoitorModel.id == 0; - checkPoisiList.Add(checkQuotaMoitorModel); - } + var (clientPositions, clientOrderPositions) = GetClientPositionsAndOrders(bondDb, clientRiskCheckReq.orderId); + var position = clientPositions + .FirstOrDefault(x => x.security_id == clientRiskCheckReq.securityId && x.client_id == clientRiskCheckReq.clientId); + precheckQuotaSettingList = FilterPrecheckQuotaSettings(precheckQuotaSettingList, clientRiskCheckReq); + + AddClientOrderPositions(clientOrderPositions, clientPositions); + AddClientRiskCheckPosition(clientRiskCheckReq, clientPositions); + var umCodes = GetUnderlyingCodes(clientPositions, clientRiskCheckReq.securityId); + var ums = GetUnderlyings(umCodes); + var dealDate = DateTime.Parse(clientRiskCheckReq.dealDate); + var checkPoisiList = BuildCheckQuotaMoitorModels(clientPositions, ums, dealDate); foreach (var settingItem in precheckQuotaSettingList) { - ClientRiskCheckItem clientRiskCheckItem = new ClientRiskCheckItem(); - clientRiskCheckItem.quotaType = settingItem.QuotaIndex; - clientRiskCheckItem.quotaLowerLimit = settingItem.QuotaLowerLimit; - clientRiskCheckItem.quotaUpperLimit = settingItem.QuotaUpperLimit; - clientRiskCheckItem.warningUpperLimit = settingItem.WarningUpperLimit; - clientRiskCheckItem.warningLowerLimit = settingItem.WarningLowerLimit; - clientRiskCheckItem.isPercent = settingItem.Percent; + var clientRiskCheckItem = CreateClientRiskCheckItem(settingItem); + if (clientRiskCheckItem == null) continue; if (!settingItem.QuotaLowerLimit.HasValue && !settingItem.QuotaUpperLimit.HasValue && !settingItem.WarningUpperLimit.HasValue && !settingItem.WarningLowerLimit.HasValue) { continue; @@ -4380,7 +4168,186 @@ namespace YLErp.Modules.RiskModule } return clientRiskCheckResps; } + /// + /// 获取持仓标的 + /// + /// + /// + /// + private List GetUnderlyingCodes(List clientPositions, string securityId) + { + var umCodes = clientPositions.Select(s => s.security_id).Distinct().ToList(); + umCodes.Add(securityId); + return umCodes.Distinct().ToList(); + } + /// + /// 获取标的信息 + /// + /// + /// + private List GetUnderlyings(List umCodes) + { + return DataCacheProvider.GetUnderlyingDataSource().AsQueryable() + .Where(x => umCodes.Contains(x.UnderlyingCode)) + .ToList(); + } + private (List, List) GetClientPositionsAndOrders(BondOmsDBContext bondDb, long orderId) + { + var clientLongIds = DataCacheProvider.GetClientDataSource() + .AsQueryable() + .Where(s => s.BusinessUseType == "否") + .Select(s => (long)s.id) + .ToList(); + var clientPositions = bondDb.client_position.Where(x => clientLongIds.Contains(x.client_id ?? 0)).AsNoTracking().ToList(); + var startDate = DateTime.Now.Date; + var nextDate = startDate.AddDays(1); + var clientOrderPositions = bondDb.client_order.Where(x => x.status == 0 && x.create_time > startDate && x.create_time < nextDate && x.id != orderId).AsNoTracking().ToList(); + return (clientPositions, clientOrderPositions); + } + /// + /// 过滤风控检查项 + /// + /// + /// + /// + /// + private List FilterPrecheckQuotaSettings(List precheckQuotaSettingList, ClientRiskCheckItemParam clientRiskCheckReq) + { + // 过滤出与当前客户端相关的配额设置 + var clientPrecheckQuotaSettingList = precheckQuotaSettingList + .Where(x => x.QuotaRange == clientRiskCheckReq.clientId && x.QuotaType == QuotaTypeEnum.CLIENT) + .ToList(); + // 过滤出所有客户端通用的配额设置 + var allClientPrecheckQuotaSettingList = precheckQuotaSettingList + .Where(x => x.QuotaRange == 0 && x.QuotaType == QuotaTypeEnum.CLIENT) + .ToList(); + + // 获取底层数据源 + var um = DataCacheProvider.GetUnderlyingDataSource().GetData(clientRiskCheckReq.securityId); + var noneClientAllUnderlyingPrecheckQuotaSettingList = precheckQuotaSettingList + .Where(x => x.QuotaType == QuotaTypeEnum.UNDERLYING) + .ToList(); + // 处理客户端相关的配额设置 + if (clientPrecheckQuotaSettingList.Count > 0) + { + precheckQuotaSettingList = precheckQuotaSettingList + .Except(allClientPrecheckQuotaSettingList) + .ToList(); + } + return precheckQuotaSettingList; + } + /// + /// 构建风控指标类 + /// + /// + /// + private ClientRiskCheckItem CreateClientRiskCheckItem(QuotaSetting settingItem) + { + if (!settingItem.QuotaLowerLimit.HasValue && !settingItem.QuotaUpperLimit.HasValue && + !settingItem.WarningUpperLimit.HasValue && !settingItem.WarningLowerLimit.HasValue) + { + return null; + } + + return new ClientRiskCheckItem + { + quotaType = settingItem.QuotaIndex, + quotaLowerLimit = settingItem.QuotaLowerLimit, + quotaUpperLimit = settingItem.QuotaUpperLimit, + warningUpperLimit = settingItem.WarningUpperLimit, + warningLowerLimit = settingItem.WarningLowerLimit, + isPercent = settingItem.Percent + }; + } + /// + /// 将client_order构建 clientPosition + /// + /// + /// + private void AddClientOrderPositions(List clientOrderPositions, List clientPositions) + { + foreach (var item in clientOrderPositions) + { + var posi = new ClientPosition + { + commission = item.commission, + side = item.side ?? 0, + security_id = item.security_id, + full_price_now = item.full_price, + deal_full_price_avg = item.full_price ?? 0, + client_id = item.client_id, + position_qty = item.order_qty, + position_notional_principal = item.full_price * item.order_qty* ConsGlobal.bondPriceMultiple, + direction = (int)SwapDirectionEnum.支付, + id = item.id, + Current = false, + }; + clientPositions.Add(posi); + } + } + /// + /// 将下单风控校构建clientPosition + /// + /// + /// + private void AddClientRiskCheckPosition(ClientRiskCheckItemParam clientRiskCheckReq, List clientPositions) + { + var posi = new ClientPosition + { + commission = clientRiskCheckReq.commission, + side = clientRiskCheckReq.side, + security_id = clientRiskCheckReq.securityId, + full_price_now = clientRiskCheckReq.price* ConsGlobal.bondShowPriceMultiple, + deal_full_price_avg = clientRiskCheckReq.price * ConsGlobal.bondShowPriceMultiple, + client_id = clientRiskCheckReq.clientId, + position_qty = clientRiskCheckReq.qty, + position_notional_principal = clientRiskCheckReq.amount, + direction = (int)SwapDirectionEnum.支付, + id = clientRiskCheckReq.orderId, + Current = true, + }; + clientPositions.Add(posi); + } + /// + /// 给客户持仓标的信息赋值现价,构建风控校验类 + /// + /// + /// + /// + private List BuildCheckQuotaMoitorModels(List clientPositions, List ums,DateTime dealDate) + { + return clientPositions.Select(item => + { + var um = ums.FirstOrDefault(x => x.UnderlyingCode == item.security_id); + var bondPrice = EodPriceQueryService.GetBondPrice(dealDate, item.security_id); + double lastPrice = bondPrice != null ? bondPrice.ClosePrice : (um.Price ?? 0) * Convert.ToDouble(ConsGlobal.bondPriceMultiple); + var bond = new UnderlyingBond(); + if (um.IsBond()&&string.IsNullOrEmpty(um.ExJson)) { + bond = JsonHelper.Deserialize(um.ExJson); + } + return new CheckQuotaMoitorModel + { + Qty = item.position_qty ?? 0, + id = item.id, + Price = (item.deal_full_price_avg ?? 0) * ConsGlobal.bondPriceMultiple, + NowPrice = (item.full_price_now ?? 0) * ConsGlobal.bondPriceMultiple, + ContractSize = (decimal)(um?.ContractSize ?? 1), + Pv = item.position_notional_principal ?? 0, + Direction = item.direction, + UnderlyingCode = item.security_id, + Side = item.side, + Commision = item.commission ?? 0, + ClientId = Convert.ToInt32(item.client_id ?? 0), + Delta = (decimal)((item.side == 0 ? item.deal_full_price_avg : -item.deal_full_price_avg) * (item.position_qty ?? 0) * ConsGlobal.bondPriceMultiple), + UnderlyingId = um?.id ?? 0, + LastPrice = (decimal)lastPrice, + Circulation = (bond.IssueSize * 100000000m) ?? 0, + Pnl = (decimal)((item.full_price_now - item.deal_full_price_avg) * (item.position_qty ?? 0) * ConsGlobal.bondPriceMultiple), + Current = item.Current, + }; + }).ToList(); + } /// /// 获取实时持仓数据构建限额查询数据 /// @@ -5125,9 +5092,9 @@ namespace YLErp.Modules.RiskModule switch (checkItem.quotaType) { case "名义本金": - var clientPv = positionList.Where(s => s.ClientId == current.ClientId && s.UnderlyingCode == current.UnderlyingCode).Sum(s => s.Pv * (s.Side == 0 ? 1 : -1)); - var otherPv = positionList.Where(s => s.ClientId != current.ClientId || s.UnderlyingCode != current.UnderlyingCode).Sum(s => s.Pv); - currentValue = Convert.ToDouble(otherPv + Math.Abs(clientPv)); + var _posiList = DataHelper.DeepCopyObject(positionList); + var clientPv = CalcPv(_posiList, false, false); + currentValue = Convert.ToDouble(clientPv); var posiPv = positionList.Where(s => !s.Current).Sum(s => s.Pv); posiVal = Convert.ToDouble(posiPv); if (!ValidateQuoteResult(checkItem, currentValue, posiVal)) @@ -5135,9 +5102,6 @@ namespace YLErp.Modules.RiskModule return checkItem; } return null; - case "Delta金额": - currentValue = Convert.ToDouble(positionList.Sum(O => O.Delta)); - break; case "浮动盈亏": currentValue = Convert.ToDouble(positionList.Sum(O => O.Pnl)); break; @@ -5242,22 +5206,6 @@ namespace YLErp.Modules.RiskModule return checkItem; } return null; - case "止损金额": - currentValue = Convert.ToDouble(current.Pnl); - checkItem.currentValue = Math.Round(currentValue ?? 0, 2); - if (!ValidateQuoteResult(checkItem)) - { - return checkItem; - } - return null; - case "Delta金额": - currentValue = Convert.ToDouble(current.Delta); - - if (!ValidateQuoteResult(checkItem)) - { - return checkItem; - } - return null; case "互换价格偏离比例": var sportPrice = Convert.ToDouble(current.Price); var basePrice = Convert.ToDouble(current.LastPrice); @@ -5381,16 +5329,16 @@ namespace YLErp.Modules.RiskModule { return null; } - currentPv = positionList.Where(s => s.UnderlyingCode == current.UnderlyingCode).Sum(s => s.Pv * (s.Side == 0 ? 1 : -1)); - currentValue = Convert.ToDouble(currentPv); - currentValue = Math.Abs(currentValue ?? 0); - posiPv = positionList.Where(s => s.UnderlyingCode == current.UnderlyingCode && !s.Current).Sum(s => s.Pv * (s.Side == 0 ? 1 : -1)); - posiVal = Convert.ToDouble(posiPv); + positionList = positionList.Where(s => s.UnderlyingCode == current.UnderlyingCode).ToList(); + var _posiList = DataHelper.DeepCopyObject(positionList); + var posiResult = CalcPv(_posiList, false, true); + currentPv = _posiList.Where(s => s.UnderlyingCode == current.UnderlyingCode).Sum(s => s.Pv * (s.Side == 0 ? 1 : -1)); + posiPv = _posiList.Where(s => s.UnderlyingCode == current.UnderlyingCode && !s.Current).Sum(s => s.Pv * (s.Side == 0 ? 1 : -1)); + posiVal = Math.Abs(Convert.ToDouble(posiPv)); var tag = $"{tag_prefix}({checkItem.quotaType})"; switch (checkItem.quotaType) { case "轧差名义本金": - posiVal = Convert.ToDouble(posiPv); if (!ValidateQuoteResult(checkItem, currentValue, posiVal)) { return checkItem; @@ -5398,7 +5346,7 @@ namespace YLErp.Modules.RiskModule return null; case "轧差集中度": currentValue = current.Circulation == 0 ? 0 : currentValue / Convert.ToDouble(current.Circulation); - posiPv = positionList.Where(s => s.UnderlyingCode == current.UnderlyingCode && !s.Current).Sum(s => s.Pv); + posiPv = _posiList.Where(s => s.UnderlyingCode == current.UnderlyingCode && !s.Current).Sum(s => s.Pv); posiVal = current.Circulation == 0 ? 0 : Convert.ToDouble(posiPv / current.Circulation); if (!ValidateQuoteResult(checkItem, currentValue, posiVal)) { @@ -5514,6 +5462,63 @@ namespace YLErp.Modules.RiskModule return null; } } + + /// + /// 计算client名义本金,最新持仓市值,返回(名义本金,持仓市值) + /// + /// + /// + /// 是否单客户户 + /// 是否轧差 + /// + private decimal CalcPv(List positionList, bool isclient, bool roll) + { + decimal clientPv = 0; + CheckQuotaMoitorModel current = positionList.FirstOrDefault(s => s.Current); + if (current == null) + { + return 0; + } + var clientUnderlyList = positionList.Where(s => s.ClientId == current.ClientId && s.UnderlyingCode == current.UnderlyingCode && s.Side != current.Side).ToList(); + foreach (var item in clientUnderlyList) + { + if (item.Qty == 0) + { + continue; + } + if (item.Qty >= current.Qty) + { + item.Qty -= current.Qty; + item.Pv = item.Qty * item.Price; + current.Qty = 0; + current.Pv = 0; + } + else if (item.Qty < current.Qty) + { + current.Qty -= item.Qty; + current.Pv = current.Qty * current.Price; + item.Qty = 0; + item.Pv = 0; + CalcPv(positionList, isclient, roll); + } + } + if (isclient) + { + clientPv = positionList.Where(s => s.ClientId == current.ClientId).Sum(s => s.Pv); + } + else + { + if (roll) + { + clientPv = positionList.Sum(s => s.Pv * (s.Side == 0 ? 1 : -1)); + } + else + { + clientPv = positionList.Sum(s => s.Pv); + } + } + return clientPv; + } private List checkStockPercent(IEnumerable> positionList, Dictionary stockEqvNotionalDict, string tag_prefix, QuotaSetting[] settings, bool warning) { if (string.IsNullOrEmpty(tag_prefix)) diff --git a/YLErpDAL/Modules/SwapModule/SwapDealService.cs b/YLErpDAL/Modules/SwapModule/SwapDealService.cs index 6055432a..f0d9010b 100644 --- a/YLErpDAL/Modules/SwapModule/SwapDealService.cs +++ b/YLErpDAL/Modules/SwapModule/SwapDealService.cs @@ -400,8 +400,8 @@ namespace YLErp.Modules.SwapModule } else if (position.InterestMode == (int)InterestModeEnum.标的期初全价) { - _closePosiNotionalValue = _posiNotionalValue * grossPrice * closePrecent; - _posiNotionalValue = _posiNotionalValue * grossPrice; + _closePosiNotionalValue = _posiNotionalValue * closePrecent; + _posiNotionalValue = _posiNotionalValue; } else if (position.InterestMode == (int)InterestModeEnum.追加预付金 || position.InterestMode == (int)InterestModeEnum.初始预付金) { @@ -409,24 +409,36 @@ namespace YLErp.Modules.SwapModule _posiNotionalValue = position.InterestPrincipalFix * closePrecent; positionClone.InterestDirection = position.InterestDirection == (int)SwapDirectionEnum.收取 ? (int)SwapDirectionEnum.支付 : (int)SwapDirectionEnum.收取; } + var calendar = "chn"; if (!string.IsNullOrEmpty(position.FloatRateUnderlyingCode)) { - var rateDate = td.StartDate.Value.AddDays(-1); - if (EodPriceQueryService.TryGetPrice(rateDate, position.FloatRateUnderlyingCode, out double floatRate)) + // 获取重置频率,如果为空则默认为1 + int interestPeriod = position.interest_rest_days ?? 1; + + // 计算从 td.StartDate 到 endDate 的天数 + var days = (endDate - td.StartDate.Value).Days; + // 获取合适的 rateDate + DateTime rateDate = GetRateDate(position.interest_rule, td.StartDate.Value, endDate, days, interestPeriod, calendar); + + // 如果不需要重置,并且上一日已有 FloatRate,则不再查找 + if (preEodPosition.id != 0 && days % interestPeriod != 0) { - position.FloatRate = Convert.ToDecimal(floatRate); - positionClone.FloatRate = position.FloatRate; - if (preEodPosition.id == 0) + position.FloatRate = preEodPosition.FloatRate; + positionClone.FloatRate = preEodPosition.FloatRate; + } + else + { + // 如果没有 preEodPosition 数据或需要查找新 Rate,则去查询最新的浮动利率 + if (EodPriceQueryService.TryGetPrice(rateDate, position.FloatRateUnderlyingCode, out double floatRate)) { - preEodPosition.FloatRate = positionClone.FloatRate; - preEodPosition.TdInterestPrincipal = _posiNotionalValue; + position.FloatRate = Convert.ToDecimal(floatRate); + positionClone.FloatRate = position.FloatRate; + } + else if (needPrice) + { + throw new Exception($"获取不到{position.FloatRateUnderlyingCode}在{rateDate:yyyy年MM月dd日}的价格"); } } - else if (needPrice) - { - throw new Exception($"获取不到{position.FloatRateUnderlyingCode}在{rateDate:yyyy年MM月dd日}的价格"); - } - } decimal rate = position.InterestRateDefault; if (swapIntervalToday == null)//当日无适用观察日 @@ -453,6 +465,20 @@ namespace YLErp.Modules.SwapModule return interests; } /// + /// 根据给定条件获取 rateDate + /// + private DateTime GetRateDate(int? interest_rule, DateTime startDate, DateTime endDate, int days, int interestPeriod, string calendar) + { + // 判断是否达到重置周期 + if (days % interestPeriod == 0) + { + return QdpCalendarHelper.GetNonHolidayDefore(endDate.AddDays(interest_rule ?? 0), calendar); + } + + // 如果不在重置周期内,使用 td.StartDate 来获取 rateDate + return QdpCalendarHelper.GetNonHolidayDefore(startDate.AddDays(interest_rule ?? 0), calendar); + } + /// /// 初始化利息腿信息 /// /// 交易编码 @@ -591,7 +617,7 @@ namespace YLErp.Modules.SwapModule var TdInterestPrincipal = preEodPosition.TdInterestPrincipal; decimal interest = 0; decimal tdinterest = 0; - int interestPeriod = 7; + int interestPeriod = position.interest_rest_days ?? 1; decimal dynomicPrincipal = principal; decimal tdDynomicPrincipal = posiPrincipal; var calcDays = (endDate - tradeDate).Days; @@ -605,14 +631,9 @@ namespace YLErp.Modules.SwapModule { dynomicPrincipal = dynomicPrincipal + interest; tdDynomicPrincipal = tdDynomicPrincipal + interest; - //if (rateDate > tradeDate) - //{ - // dynomicPrincipal += interestProfitSum; - // tdDynomicPrincipal += interestProfitSum; - //} if (!string.IsNullOrEmpty(position.FloatRateUnderlyingCode)) { - var fr007RateDate = rateDate.AddDays(-1); + var fr007RateDate = QdpCalendarHelper.GetNonHolidayDefore(endDate.AddDays(position.interest_rule ?? 0)); // 获取前一工作日; if (EodPriceQueryService.TryGetPrice(fr007RateDate, position.FloatRateUnderlyingCode, out double floatRate1)) { if (floatRate1 != 0) @@ -675,7 +696,7 @@ namespace YLErp.Modules.SwapModule decimal interestProfitSum = preEodPosition.InterestProfitSum; decimal interest = preEodPosition.TdInterestIncome; decimal tdinterest = preEodPosition.TdInterestIncome; - int interestPeriod = 7; + int interestPeriod = position.interest_rest_days ?? 1; decimal tdDynomicPrincipal = posiPrincipal; double floatRate = Convert.ToDouble(floateRate); var days = (endDate - tradeDate).Days; @@ -684,7 +705,8 @@ namespace YLErp.Modules.SwapModule tdDynomicPrincipal = tdDynomicPrincipal + interestProfitSum; if (!string.IsNullOrEmpty(position.FloatRateUnderlyingCode)) { - var fr007RateDate = endDate.AddDays(-1); + // 获取合适的 rateDate + var fr007RateDate = QdpCalendarHelper.GetNonHolidayDefore(endDate.AddDays(position.interest_rule ?? 0)); // 获取前一工作日; if (EodPriceQueryService.TryGetPrice(fr007RateDate, position.FloatRateUnderlyingCode, out double floatRate1)) { if (floatRate1 != 0) @@ -696,32 +718,29 @@ namespace YLErp.Modules.SwapModule { throw new Exception($"获取不到{position.FloatRateUnderlyingCode}在{fr007RateDate:yyyy年MM月dd日}的价格"); } - flowEvent.InterestPrincipal = tdDynomicPrincipal * closePercent; - interest = flowEvent.InterestPrincipal * (flowEvent.InterestRate + Convert.ToDecimal(floatRate)); - tdinterest = tdDynomicPrincipal * (flowEvent.InterestRate + Convert.ToDecimal(floatRate)); - if (position.IsAnnualized) - { - interest /= annualDays; - tdinterest /= annualDays; - } + } - else + flowEvent.InterestPrincipal = tdDynomicPrincipal * closePercent; + interest = flowEvent.InterestPrincipal * (flowEvent.InterestRate + Convert.ToDecimal(floatRate)); + tdinterest = tdDynomicPrincipal * (flowEvent.InterestRate + Convert.ToDecimal(floatRate)); + if (position.IsAnnualized) { - flowEvent.InterestPrincipal = (preEodPosition.TdInterestPrincipal + tdDynomicPrincipal - orginPv) * closePercent; - var interest1 = flowEvent.InterestPrincipal * (flowEvent.InterestRate + Convert.ToDecimal(floatRate)); - var tdinterest1 = (preEodPosition.TdInterestPrincipal + tdDynomicPrincipal - orginPv) * (flowEvent.InterestRate + Convert.ToDecimal(floatRate)); - if (position.IsAnnualized) - { - interest1 /= annualDays; - tdinterest1 /= annualDays; - } - interest = interest1; - tdinterest = tdinterest1; + interest /= annualDays; + tdinterest /= annualDays; } } else { - flowEvent.InterestPrincipal = preEodPosition.TdInterestPrincipal * closePercent; + flowEvent.InterestPrincipal = (preEodPosition.TdInterestPrincipal + tdDynomicPrincipal - orginPv) * closePercent; + var interest1 = flowEvent.InterestPrincipal * (flowEvent.InterestRate + Convert.ToDecimal(floatRate)); + var tdinterest1 = (preEodPosition.TdInterestPrincipal + tdDynomicPrincipal - orginPv) * (flowEvent.InterestRate + Convert.ToDecimal(floatRate)); + if (position.IsAnnualized) + { + interest1 /= annualDays; + tdinterest1 /= annualDays; + } + interest = interest1; + tdinterest = tdinterest1; } flowEvent.FloatRate = Convert.ToDecimal(floatRate); InterestAmount = Math.Round(interest, ConsGlobal.PriceRound, MidpointRounding.AwayFromZero); diff --git a/YLErpDAL/Modules/SwapModule/SwapEodPositionService.cs b/YLErpDAL/Modules/SwapModule/SwapEodPositionService.cs index 0a6d5906..4068e2fc 100644 --- a/YLErpDAL/Modules/SwapModule/SwapEodPositionService.cs +++ b/YLErpDAL/Modules/SwapModule/SwapEodPositionService.cs @@ -635,6 +635,8 @@ namespace YLErp.Modules.SwapModule newEodPayPosition.HappenDate = position.HappenDate; newEodPayPosition.Currency = position.Currency; newEodPayPosition.InterestType = position.InterestType; + newEodPayPosition.interest_rest_days = position.interest_rest_days; + newEodPayPosition.interest_rule = position.interest_rule; newEodPayPosition.FloatRate = flowEvents.FirstOrDefault()?.FloatRate ?? 0; newEodPayPosition.FloatRateUnderlyingCode = position.FloatRateUnderlyingCode; newEodPayPosition.InterestFeePending = 0; @@ -733,6 +735,8 @@ namespace YLErp.Modules.SwapModule newEodPayPosition.HappenDate = position.HappenDate; newEodPayPosition.Currency = position.Currency; newEodPayPosition.InterestType = position.InterestType; + newEodPayPosition.interest_rest_days = position.interest_rest_days; + newEodPayPosition.interest_rule = position.interest_rule; newEodPayPosition.FloatRate = interests.Count > 0 ? interests.First().FloatRate ?? 0 : 0; newEodPayPosition.FloatRateUnderlyingCode = position.FloatRateUnderlyingCode; newEodPayPosition.InterestFeePending = 0; @@ -838,6 +842,8 @@ namespace YLErp.Modules.SwapModule newEodPayPosition.HappenDate = position.HappenDate; newEodPayPosition.Currency = position.Currency; newEodPayPosition.InterestType = position.InterestType; + newEodPayPosition.interest_rest_days = position.interest_rest_days; + newEodPayPosition.interest_rule = position.interest_rule; newEodPayPosition.FloatRate = interests.Count > 0 ? interests.First().FloatRate ?? 0 : 0; newEodPayPosition.FloatRateUnderlyingCode = position.FloatRateUnderlyingCode; newEodPayPosition.InterestFeePending = 0; @@ -923,6 +929,8 @@ namespace YLErp.Modules.SwapModule eodPayPosition.InterestType = position.InterestType; eodPayPosition.FloatRate = position.FloatRate; eodPayPosition.FloatRateUnderlyingCode = position.FloatRateUnderlyingCode; + eodPayPosition.interest_rest_days = position.interest_rest_days; + eodPayPosition.interest_rule = position.interest_rule; } if (newEodPayPosition == null) { diff --git a/YLErpDAL/Modules/SwapModule/SwapFloatRateService.cs b/YLErpDAL/Modules/SwapModule/SwapFloatRateService.cs index fdf7586d..b796cd35 100644 --- a/YLErpDAL/Modules/SwapModule/SwapFloatRateService.cs +++ b/YLErpDAL/Modules/SwapModule/SwapFloatRateService.cs @@ -110,6 +110,10 @@ namespace YLErp.Modules.SwapModule swapFloatRate.ClientId = req.ClientId; swapFloatRate.ClientNumber = req.ClientNumber; swapFloatRate.ClientName = req.ClientName; + swapFloatRate.interest_type = req.interest_type; + swapFloatRate.interest_rest_days = req.interest_rest_days; + swapFloatRate.interest_rule = req.interest_rule; + swapFloatRate.is_annualized = req.is_annualized; swapFloatRate.SetOpt(UserId, UserName); } else @@ -262,6 +266,29 @@ namespace YLErp.Modules.SwapModule swapFloatRate.BaseUnderlyingCode = reader.GetString("基准利率"); swapFloatRate.StartDate = reader.GetDate("生效起始日",true).Value; swapFloatRate.EndDate = reader.GetDate("生效终止日", true).Value; + swapFloatRate.interest_rest_days = reader.GetInt32("重置频率", true); + var interestType = reader.GetString("计息方式", true); + if (Enum.TryParse(interestType, out InterestTypeEnum parsedInterestType)) + { + swapFloatRate.interest_type = (int)parsedInterestType; + } + else + { + // 如果转换失败,可以根据需要处理错误 + throw new Exception($"无法将计息方式 '{interestType}' 转换为有效的枚举值"); + } + var rule = reader.GetString("利率准则", true); + if (Enum.TryParse(rule, out SwapInterestRule parsedRule)) + { + swapFloatRate.interest_rule = (int)parsedRule; + } + else + { + // 如果转换失败,可以根据需要处理错误 + throw new Exception($"无法将利率准则 '{rule}' 转换为有效的枚举值"); + } + var is_annualized = reader.GetString("是否年化", true); + swapFloatRate.is_annualized = is_annualized == "是"; swapFloatRate.LongPricePoint = reader.GetDecimal("利差(多头加点)"); swapFloatRate.ShortPricePoint = reader.GetDecimal("利差(空头减点)"); swapFloatRate.ClientName = reader.GetString("客户名称"); diff --git a/YLErpDAL/Modules/SwapModule/SwapTradeService.cs b/YLErpDAL/Modules/SwapModule/SwapTradeService.cs index b9a21eda..ee8c8200 100644 --- a/YLErpDAL/Modules/SwapModule/SwapTradeService.cs +++ b/YLErpDAL/Modules/SwapModule/SwapTradeService.cs @@ -252,7 +252,7 @@ namespace YLErp.Modules.SwapModule string structureType = "普通债券类收益互换", bool cashNeedAfter = false) { - var td = PrepareTrade(flowMerge, client, asset, underlying, structureType); + var td = PrepareTrade(flowMerge, client, asset, underlying, clientMarginTemplate?.SwapEndDays??14, structureType); PrepareTradeExtend(flowMerge, td, underlying, swapFloatRate, clientMarginTemplate); td.TradeNumber = BizLogicSingleton.Instance.GenerateTradeNumberBeforeConfirm(td, DbContext); flowMerge.SwapTradeNo = td.TradeNumber; @@ -311,7 +311,7 @@ namespace YLErp.Modules.SwapModule /// /// /// - private trade PrepareTrade(swap_flow_merge flowMerge, Client client, AssetUnit asset, underlying_manager underlying, string structureType = "普通债券类收益互换") + private trade PrepareTrade(swap_flow_merge flowMerge, Client client, AssetUnit asset, underlying_manager underlying,int swapEndDays, string structureType = "普通债券类收益互换") { trade td = new trade() { @@ -339,7 +339,7 @@ namespace YLErp.Modules.SwapModule { td.StartDate = flowMerge.SettleDate.Value; } - td.ExerciseDate = QdpCalendarHelper.GetNonHolidayDefore(td.StartDate.Value.AddDays(14)); + td.ExerciseDate = QdpCalendarHelper.GetNonHolidayDefore(td.StartDate.Value.AddDays(swapEndDays)); PrepareUnderlying(td, underlying); PrepareSwapTradeClient(td, underlying); td.TradeOldStatus = ConsTrade.新增待确认; @@ -446,6 +446,7 @@ namespace YLErp.Modules.SwapModule PosiMatuirityDate = td.ExerciseDate.Value, PosiStartDate = td.StartDate.Value, IsInitial = true, + interest_rest_days = 7, OptTime = DateTime.Now, OptId = UserInfo.UserId, OptName = UserInfo.UserName, @@ -455,6 +456,9 @@ namespace YLErp.Modules.SwapModule interestPosition.InterestRateDefault = flowMerge.BsType == (int)PositionTypeFlag.Long ? (swapFloatRate.LongPricePoint ?? 0) : (swapFloatRate.ShortPricePoint ?? 0) * -1; interestPosition.InterestRateDefault = interestPosition.InterestRateDefault * 0.0001m; interestPosition.FloatRateUnderlyingCode = swapFloatRate.BaseUnderlyingCode; + interestPosition.interest_rule = swapFloatRate.interest_rule; + interestPosition.interest_rest_days = swapFloatRate.interest_rest_days; + interestPosition.IsAnnualized = swapFloatRate.is_annualized ?? false; } var interval = new IntervalModel() { @@ -1411,6 +1415,8 @@ namespace YLErp.Modules.SwapModule position.InterestType = swap.InterestType; position.FloatRate = swap.FloatRate; position.FloatRateUnderlyingCode = swap.FloatRateUnderlyingCode; + position.interest_rest_days = swap.interest_rest_days; + position.interest_rule = swap.interest_rule; if (position.id == 0) { DbContext.swap_position.Add(position); diff --git a/YLErpWeb/App_Docs/导入模板/互换预付金率导入模板.xlsx b/YLErpWeb/App_Docs/导入模板/互换预付金率导入模板.xlsx index ffe8cdb6..ca6173bf 100644 Binary files a/YLErpWeb/App_Docs/导入模板/互换预付金率导入模板.xlsx and b/YLErpWeb/App_Docs/导入模板/互换预付金率导入模板.xlsx differ diff --git a/YLErpWeb/App_Docs/导入模板/浮动利率特殊设置导入模板.xlsx b/YLErpWeb/App_Docs/导入模板/浮动利率特殊设置导入模板.xlsx index bfccc084..284185c9 100644 Binary files a/YLErpWeb/App_Docs/导入模板/浮动利率特殊设置导入模板.xlsx and b/YLErpWeb/App_Docs/导入模板/浮动利率特殊设置导入模板.xlsx differ diff --git a/YLErpWeb/App_Docs/导入模板/浮动利率默认设置导入模板.xlsx b/YLErpWeb/App_Docs/导入模板/浮动利率默认设置导入模板.xlsx index b782b3d5..b0870dfa 100644 Binary files a/YLErpWeb/App_Docs/导入模板/浮动利率默认设置导入模板.xlsx and b/YLErpWeb/App_Docs/导入模板/浮动利率默认设置导入模板.xlsx differ diff --git a/YLErpWeb/App_Docs/导出模板/浮动利率特殊设置导出模板.xlsx b/YLErpWeb/App_Docs/导出模板/浮动利率特殊设置导出模板.xlsx index e3bbf2ff..4fa37610 100644 Binary files a/YLErpWeb/App_Docs/导出模板/浮动利率特殊设置导出模板.xlsx and b/YLErpWeb/App_Docs/导出模板/浮动利率特殊设置导出模板.xlsx differ diff --git a/YLErpWeb/App_Docs/导出模板/浮动利率默认设置导出模板.xlsx b/YLErpWeb/App_Docs/导出模板/浮动利率默认设置导出模板.xlsx index e76dc351..91c9e335 100644 Binary files a/YLErpWeb/App_Docs/导出模板/浮动利率默认设置导出模板.xlsx and b/YLErpWeb/App_Docs/导出模板/浮动利率默认设置导出模板.xlsx differ diff --git a/YLErpWeb/Controllers/riskController.cs b/YLErpWeb/Controllers/riskController.cs index 5fbc8cc0..19b6faa8 100644 --- a/YLErpWeb/Controllers/riskController.cs +++ b/YLErpWeb/Controllers/riskController.cs @@ -526,18 +526,6 @@ namespace YLErp.Web.Controllers else { result = service.QueryGlobalFromCalc(req); } break; - case "资产汇总": - if (req.ValueDate > DateTime.MinValue && req.ValueDate != valuedateBLL.ValueDate) - { result = service.QueryAssetFromDb(req); } - else - { result = service.QueryAssetFromCalc(req); } - break; - case "品种汇总": - if (req.ValueDate > DateTime.MinValue && req.ValueDate != valuedateBLL.ValueDate) - { result = service.QueryVarietyFromDb(req); } - else - { result = service.QueryVarietyFromCalc(req); } - break; case "标的汇总": if (req.ValueDate > DateTime.MinValue && req.ValueDate != valuedateBLL.ValueDate) { result = service.QueryUnderlyingFromDb(req); } diff --git a/YLErpWeb/Views/MarginRateSwap/MarginRateSwapEdit.cshtml b/YLErpWeb/Views/MarginRateSwap/MarginRateSwapEdit.cshtml index da6050ad..01825430 100644 --- a/YLErpWeb/Views/MarginRateSwap/MarginRateSwapEdit.cshtml +++ b/YLErpWeb/Views/MarginRateSwap/MarginRateSwapEdit.cshtml @@ -14,14 +14,6 @@ @section JS{ } -
@Html.HiddenFor(model => model.id) @@ -63,6 +71,12 @@ *
+
+ + + + * +
diff --git a/YLErpWeb/Views/SwapFloatRate/Detail.cshtml b/YLErpWeb/Views/SwapFloatRate/Detail.cshtml index d2e1529a..298e39fc 100644 --- a/YLErpWeb/Views/SwapFloatRate/Detail.cshtml +++ b/YLErpWeb/Views/SwapFloatRate/Detail.cshtml @@ -47,6 +47,22 @@ 生效终止日 @Model.EndDate.OtcFormatDate() + + 是否年化 + @(Model.is_annualized == true ? "是" : "否") + + + 计息方式 + @Model.interest_type_str + + + 重置频率(天) + @Model.interest_rest_days + + + 利率准则 + @Model.interest_rule_str + 基准利率 @baseUnderLyingCode diff --git a/YLErpWeb/Views/SwapFloatRate/Edit.cshtml b/YLErpWeb/Views/SwapFloatRate/Edit.cshtml index 754835c5..5d8d7a14 100644 --- a/YLErpWeb/Views/SwapFloatRate/Edit.cshtml +++ b/YLErpWeb/Views/SwapFloatRate/Edit.cshtml @@ -51,6 +51,31 @@
+
+ + +
+
+ + +
+
+ + +
+
+ + +
diff --git a/YLErpWeb/Views/SwapTrade2/TradeEdit.cshtml b/YLErpWeb/Views/SwapTrade2/TradeEdit.cshtml index 3321e235..66b03957 100644 --- a/YLErpWeb/Views/SwapTrade2/TradeEdit.cshtml +++ b/YLErpWeb/Views/SwapTrade2/TradeEdit.cshtml @@ -319,6 +319,8 @@ 利率 利率是否年化 计息方式 + 重置频率(天) + 利率准则 结算规则 @@ -340,12 +342,9 @@ -