using BaseOUDAL; using Confluent.Kafka; using CsvHelper; using Dapper; using DocumentFormat.OpenXml.Office.CustomUI; using DocumentFormat.OpenXml.Office2010.Excel; using DocumentFormat.OpenXml.Spreadsheet; using Qdp.Foundation.Utilities; using Qdp.Pricing.Ecosystem.Trade.FixedIncome; using Qdp.Pricing.Library.Base.Utilities; using Qdp.Pricing.Library.Common.Products.Rates; using System.Collections.Generic; using System.Data; using System.Linq; using System.Runtime.Intrinsics.Arm; using System.Text.RegularExpressions; using YieldChain.Helpers; using YLErp.Abstract.DataProviders; using YLErp.BLL; using YLErp.BLL.Calculation; using YLErp.BLL.Eod; using YLErp.BLL.Hedge; using YLErp.Commons; using YLErp.DataBase; using YLErp.DBModels; using YLErp.DBModels.Consts; using YLErp.DBModels.Enums; using YLErp.Enums; using YLErp.Helpers; using YLErp.Model; using YLErp.Model.Enum; using YLErp.Model.HengTaiModel; using YLErp.Models; using YLErp.Modules.ApiModule; using YLErp.Modules.CalculationModule; using YLErp.Modules.ClientModule; using YLErp.Modules.DataProviderModule; using YLErp.Modules.EodModule; using YLErp.Modules.EodModule.QueryModule; using YLErp.Modules.EodModule.SettlementModule; using YLErp.Modules.MarketRiskMoudule; using YLErp.Modules.MarketRiskMoudule.Dto; using YLErp.Modules.RiskExposure; using YLErp.Modules.SwapModule; using YLErp.Modules.TradeDalModule; using YLErp.Modules.TradeModule; using YLErp.Modules.TradeModule.DealModule; using YLErp.QdpModule; using static YLErp.ConsGlobal; namespace YLErp.Modules.RiskModule { public class QuotaMonitorService : YLBaseService { IYcLogger _logger = LogFactory.GetLogger("QuotaMonitorService"); static QuotaMonitorService() { initSetting(); } public QuotaMonitorService(OptUserInfo userInfo) : base(userInfo) { } public QuotaMonitorService(YLBaseService baseService) : base(baseService) { } public QuotaMonitorService(OptUserInfo optUser, YLContext dbContext) : base(optUser, dbContext) { } /// /// 当日收盘时限额监控数据落地 /// 配合国海风控数据采集 /// 只有当日收盘时才运行,防止数据错误 /// /// public void EodSettlementExecute(DateTime settlementDate) { var req = new QuotaMonitorReq { PvPercent = PS.Config.ErpElement.EodVarPvPercent, UseCalcCreditExposure = true }; //全局 { var list = QueryGlobalFromCalc(req); list.ForEach(O => { O.id = 0; O.ValueDate = settlementDate; O.BusinessType ??= ""; O.EquityRiskScale = O.EquityRiskScale.IsNormalize() ? O.EquityRiskScale : null; O.Quota_EquityRiskScale_Upper = O.Quota_EquityRiskScale_Upper.IsNormalize() ? O.Quota_EquityRiskScale_Upper : null; O.Quota_EquityRiskScale_Lower = O.Quota_EquityRiskScale_Lower.IsNormalize() ? O.Quota_EquityRiskScale_Lower : null; O.Quota_EquityRiskScale_wUpper = O.Quota_EquityRiskScale_wUpper.IsNormalize() ? O.Quota_EquityRiskScale_wUpper : null; O.Quota_EquityRiskScale_wLower = O.Quota_EquityRiskScale_wLower.IsNormalize() ? O.Quota_EquityRiskScale_wLower : null; O.NonEquityRiskScale = O.NonEquityRiskScale.IsNormalize() ? O.NonEquityRiskScale : null; O.Quota_NonEquityRiskScale_Upper = O.Quota_NonEquityRiskScale_Upper.IsNormalize() ? O.Quota_NonEquityRiskScale_Upper : null; O.Quota_NonEquityRiskScale_Lower = O.Quota_NonEquityRiskScale_Lower.IsNormalize() ? O.Quota_NonEquityRiskScale_Lower : null; O.Quota_NonEquityRiskScale_wUpper = O.Quota_NonEquityRiskScale_wUpper.IsNormalize() ? O.Quota_NonEquityRiskScale_wUpper : null; O.Quota_NonEquityRiskScale_wLower = O.Quota_NonEquityRiskScale_wLower.IsNormalize() ? O.Quota_NonEquityRiskScale_wLower : null; O.StockEqvNotional = O.StockEqvNotional.IsNormalize() ? O.StockEqvNotional : null; O.Quota_StockEqvNotional_Upper = O.Quota_StockEqvNotional_Upper.IsNormalize() ? O.Quota_StockEqvNotional_Upper : null; O.Quota_StockEqvNotional_Lower = O.Quota_StockEqvNotional_Lower.IsNormalize() ? O.Quota_StockEqvNotional_Lower : null; O.Quota_StockEqvNotional_wUpper = O.Quota_StockEqvNotional_wUpper.IsNormalize() ? O.Quota_StockEqvNotional_wUpper : null; O.Quota_StockEqvNotional_wLower = O.Quota_StockEqvNotional_wLower.IsNormalize() ? O.Quota_StockEqvNotional_wLower : null; O.DeltaExposure = O.DeltaExposure.IsNormalize() ? O.DeltaExposure : null; O.Quota_DeltaExposure_Upper = O.Quota_DeltaExposure_Upper.IsNormalize() ? O.Quota_DeltaExposure_Upper : null; O.Quota_DeltaExposure_Lower = O.Quota_DeltaExposure_Lower.IsNormalize() ? O.Quota_DeltaExposure_Lower : null; O.Quota_DeltaExposure_wUpper = O.Quota_DeltaExposure_wUpper.IsNormalize() ? O.Quota_DeltaExposure_wUpper : null; O.Quota_DeltaExposure_wLower = O.Quota_DeltaExposure_wLower.IsNormalize() ? O.Quota_DeltaExposure_wLower : null; O.DeltaNetExposure = O.DeltaNetExposure.IsNormalize() ? O.DeltaNetExposure : null; O.Quota_DeltaNetExposure_Upper = O.Quota_DeltaNetExposure_Upper.IsNormalize() ? O.Quota_DeltaNetExposure_Upper : null; O.Quota_DeltaNetExposure_Lower = O.Quota_DeltaNetExposure_Lower.IsNormalize() ? O.Quota_DeltaNetExposure_Lower : null; O.Quota_DeltaNetExposure_wUpper = O.Quota_DeltaNetExposure_wUpper.IsNormalize() ? O.Quota_DeltaNetExposure_wUpper : null; O.Quota_DeltaNetExposure_wLower = O.Quota_DeltaNetExposure_wLower.IsNormalize() ? O.Quota_DeltaNetExposure_wLower : null; O.DeltaCash = O.DeltaCash.IsNormalize() ? O.DeltaCash : null; O.Quota_DeltaCash_Upper = O.Quota_DeltaCash_Upper.IsNormalize() ? O.Quota_DeltaCash_Upper : null; O.Quota_DeltaCash_Lower = O.Quota_DeltaCash_Lower.IsNormalize() ? O.Quota_DeltaCash_Lower : null; O.Quota_DeltaCash_wUpper = O.Quota_DeltaCash_wUpper.IsNormalize() ? O.Quota_DeltaCash_wUpper : null; O.Quota_DeltaCash_wLower = O.Quota_DeltaCash_wLower.IsNormalize() ? O.Quota_DeltaCash_wLower : null; O.GammaCash = O.GammaCash.IsNormalize() ? O.GammaCash : null; O.Quota_GammaCash_Upper = O.Quota_GammaCash_Upper.IsNormalize() ? O.Quota_GammaCash_Upper : null; O.Quota_GammaCash_Lower = O.Quota_GammaCash_Lower.IsNormalize() ? O.Quota_GammaCash_Lower : null; O.Quota_GammaCash_wUpper = O.Quota_GammaCash_wUpper.IsNormalize() ? O.Quota_GammaCash_wUpper : null; O.Quota_GammaCash_wLower = O.Quota_GammaCash_wLower.IsNormalize() ? O.Quota_GammaCash_wLower : null; O.Vega = O.Vega.IsNormalize() ? O.Vega : null; O.Quota_Vega_Upper = O.Quota_Vega_Upper.IsNormalize() ? O.Quota_Vega_Upper : null; O.Quota_Vega_Lower = O.Quota_Vega_Lower.IsNormalize() ? O.Quota_Vega_Lower : null; O.Quota_Vega_wUpper = O.Quota_Vega_wUpper.IsNormalize() ? O.Quota_Vega_wUpper : null; O.Quota_Vega_wLower = O.Quota_Vega_wLower.IsNormalize() ? O.Quota_Vega_wLower : null; O.VegaCash = O.VegaCash.IsNormalize() ? O.VegaCash : null; O.Quota_VegaCash_Upper = O.Quota_VegaCash_Upper.IsNormalize() ? O.Quota_VegaCash_Upper : null; O.Quota_VegaCash_Lower = O.Quota_VegaCash_Lower.IsNormalize() ? O.Quota_VegaCash_Lower : null; O.Quota_VegaCash_wUpper = O.Quota_VegaCash_wUpper.IsNormalize() ? O.Quota_VegaCash_wUpper : null; O.Quota_VegaCash_wLower = O.Quota_VegaCash_wLower.IsNormalize() ? O.Quota_VegaCash_wLower : null; O.Concentration = O.Concentration.IsNormalize() ? O.Concentration : null; O.Quota_Concentration_Upper = O.Quota_Concentration_Upper.IsNormalize() ? O.Quota_Concentration_Upper : null; O.Quota_Concentration_Lower = O.Quota_Concentration_Lower.IsNormalize() ? O.Quota_Concentration_Lower : null; O.Quota_Concentration_wUpper = O.Quota_Concentration_wUpper.IsNormalize() ? O.Quota_Concentration_wUpper : null; O.Quota_Concentration_wLower = O.Quota_Concentration_wLower.IsNormalize() ? O.Quota_Concentration_wLower : null; O.VaR = O.VaR.IsNormalize() ? O.VaR : null; O.Quota_VaR_Upper = O.Quota_VaR_Upper.IsNormalize() ? O.Quota_VaR_Upper : null; O.Quota_VaR_Lower = O.Quota_VaR_Lower.IsNormalize() ? O.Quota_VaR_Lower : null; O.Quota_VaR_wUpper = O.Quota_VaR_wUpper.IsNormalize() ? O.Quota_VaR_wUpper : null; O.Quota_VaR_wLower = O.Quota_VaR_wLower.IsNormalize() ? O.Quota_VaR_wLower : null; O.Theta = O.Theta.IsNormalize() ? O.Theta : null; O.Quota_Theta_Upper = O.Quota_Theta_Upper.IsNormalize() ? O.Quota_Theta_Upper : null; O.Quota_Theta_Lower = O.Quota_Theta_Lower.IsNormalize() ? O.Quota_Theta_Lower : null; O.Quota_Theta_wUpper = O.Quota_Theta_wUpper.IsNormalize() ? O.Quota_Theta_wUpper : null; O.Quota_Theta_wLower = O.Quota_Theta_wLower.IsNormalize() ? O.Quota_Theta_wLower : null; O.TotalPnL = O.Theta.IsNormalize() ? O.TotalPnL : null; O.Quota_TotalPnL_Upper = O.Quota_TotalPnL_Upper.IsNormalize() ? O.Quota_TotalPnL_Upper : null; O.Quota_TotalPnL_Lower = O.Quota_TotalPnL_Lower.IsNormalize() ? O.Quota_TotalPnL_Lower : null; O.Quota_TotalPnL_wUpper = O.Quota_TotalPnL_wUpper.IsNormalize() ? O.Quota_TotalPnL_wUpper : null; O.Quota_TotalPnL_wLower = O.Quota_TotalPnL_wLower.IsNormalize() ? O.Quota_TotalPnL_wLower : null; O.ThisYearTotalPnl = O.ThisYearTotalPnl.IsNormalize() ? O.ThisYearTotalPnl : null; O.Quota_ThisYearTotalPnl_Upper = O.Quota_ThisYearTotalPnl_Upper.IsNormalize() ? O.Quota_ThisYearTotalPnl_Upper : null; O.Quota_ThisYearTotalPnl_Lower = O.Quota_ThisYearTotalPnl_Lower.IsNormalize() ? O.Quota_ThisYearTotalPnl_Lower : null; O.Quota_ThisYearTotalPnl_wUpper = O.Quota_ThisYearTotalPnl_wUpper.IsNormalize() ? O.Quota_ThisYearTotalPnl_wUpper : null; O.Quota_ThisYearTotalPnl_wLower = O.Quota_ThisYearTotalPnl_wLower.IsNormalize() ? O.Quota_ThisYearTotalPnl_wLower : null; }); DbContext.QuotaMonitor_Global.AddRange(list); } //资产汇总 { var list = QueryAssetFromCalc(req); list.ForEach(O => { O.id = 0; O.ValueDate = settlementDate; O.UnderlyingCode ??= ""; O.StockEqvNotional = O.StockEqvNotional.IsNormalize() ? O.StockEqvNotional : null; O.Quota_StockEqvNotional_Upper = O.Quota_StockEqvNotional_Upper.IsNormalize() ? O.Quota_StockEqvNotional_Upper : null; O.Quota_StockEqvNotional_Lower = O.Quota_StockEqvNotional_Lower.IsNormalize() ? O.Quota_StockEqvNotional_Lower : null; O.Quota_StockEqvNotional_wUpper = O.Quota_StockEqvNotional_wUpper.IsNormalize() ? O.Quota_StockEqvNotional_wUpper : null; O.Quota_StockEqvNotional_wLower = O.Quota_StockEqvNotional_wLower.IsNormalize() ? O.Quota_StockEqvNotional_wLower : null; O.DeltaExposure = O.DeltaExposure.IsNormalize() ? O.DeltaExposure : null; O.Quota_DeltaExposure_Upper = O.Quota_DeltaExposure_Upper.IsNormalize() ? O.Quota_DeltaExposure_Upper : null; O.Quota_DeltaExposure_Lower = O.Quota_DeltaExposure_Lower.IsNormalize() ? O.Quota_DeltaExposure_Lower : null; O.Quota_DeltaExposure_wUpper = O.Quota_DeltaExposure_wUpper.IsNormalize() ? O.Quota_DeltaExposure_wUpper : null; O.Quota_DeltaExposure_wLower = O.Quota_DeltaExposure_wLower.IsNormalize() ? O.Quota_DeltaExposure_wLower : null; O.DeltaNetExposure = O.DeltaNetExposure.IsNormalize() ? O.DeltaNetExposure : null; O.Quota_DeltaNetExposure_Upper = O.Quota_DeltaNetExposure_Upper.IsNormalize() ? O.Quota_DeltaNetExposure_Upper : null; O.Quota_DeltaNetExposure_Lower = O.Quota_DeltaNetExposure_Lower.IsNormalize() ? O.Quota_DeltaNetExposure_Lower : null; O.Quota_DeltaNetExposure_wUpper = O.Quota_DeltaNetExposure_wUpper.IsNormalize() ? O.Quota_DeltaNetExposure_wUpper : null; O.Quota_DeltaNetExposure_wLower = O.Quota_DeltaNetExposure_wLower.IsNormalize() ? O.Quota_DeltaNetExposure_wLower : null; O.DeltaCash = O.DeltaCash.IsNormalize() ? O.DeltaCash : null; O.Quota_DeltaCash_Upper = O.Quota_DeltaCash_Upper.IsNormalize() ? O.Quota_DeltaCash_Upper : null; O.Quota_DeltaCash_Lower = O.Quota_DeltaCash_Lower.IsNormalize() ? O.Quota_DeltaCash_Lower : null; O.Quota_DeltaCash_wUpper = O.Quota_DeltaCash_wUpper.IsNormalize() ? O.Quota_DeltaCash_wUpper : null; O.Quota_DeltaCash_wLower = O.Quota_DeltaCash_wLower.IsNormalize() ? O.Quota_DeltaCash_wLower : null; O.GammaCash = O.GammaCash.IsNormalize() ? O.GammaCash : null; O.Quota_GammaCash_Upper = O.Quota_GammaCash_Upper.IsNormalize() ? O.Quota_GammaCash_Upper : null; O.Quota_GammaCash_Lower = O.Quota_GammaCash_Lower.IsNormalize() ? O.Quota_GammaCash_Lower : null; O.Quota_GammaCash_wUpper = O.Quota_GammaCash_wUpper.IsNormalize() ? O.Quota_GammaCash_wUpper : null; O.Quota_GammaCash_wLower = O.Quota_GammaCash_wLower.IsNormalize() ? O.Quota_GammaCash_wLower : null; O.Vega = O.Vega.IsNormalize() ? O.Vega : null; O.Quota_Vega_Upper = O.Quota_Vega_Upper.IsNormalize() ? O.Quota_Vega_Upper : null; O.Quota_Vega_Lower = O.Quota_Vega_Lower.IsNormalize() ? O.Quota_Vega_Lower : null; O.Quota_Vega_wUpper = O.Quota_Vega_wUpper.IsNormalize() ? O.Quota_Vega_wUpper : null; O.Quota_Vega_wLower = O.Quota_Vega_wLower.IsNormalize() ? O.Quota_Vega_wLower : null; O.VegaCash = O.VegaCash.IsNormalize() ? O.VegaCash : null; O.Quota_VegaCash_Upper = O.Quota_VegaCash_Upper.IsNormalize() ? O.Quota_VegaCash_Upper : null; O.Quota_VegaCash_Lower = O.Quota_VegaCash_Lower.IsNormalize() ? O.Quota_VegaCash_Lower : null; O.Quota_VegaCash_wUpper = O.Quota_VegaCash_wUpper.IsNormalize() ? O.Quota_VegaCash_wUpper : null; O.Quota_VegaCash_wLower = O.Quota_VegaCash_wLower.IsNormalize() ? O.Quota_VegaCash_wLower : null; O.Concentration = O.Concentration.IsNormalize() ? O.Concentration : null; O.Quota_Concentration_Upper = O.Quota_Concentration_Upper.IsNormalize() ? O.Quota_Concentration_Upper : null; O.Quota_Concentration_Lower = O.Quota_Concentration_Lower.IsNormalize() ? O.Quota_Concentration_Lower : null; O.Quota_Concentration_wUpper = O.Quota_Concentration_wUpper.IsNormalize() ? O.Quota_Concentration_wUpper : null; O.Quota_Concentration_wLower = O.Quota_Concentration_wLower.IsNormalize() ? O.Quota_Concentration_wLower : null; O.PositionPnl = O.PositionPnl.IsNormalize() ? O.PositionPnl : null; O.Quota_PositionPnl_Upper = O.Quota_PositionPnl_Upper.IsNormalize() ? O.Quota_PositionPnl_Upper : null; O.Quota_PositionPnl_Lower = O.Quota_PositionPnl_Lower.IsNormalize() ? O.Quota_PositionPnl_Lower : null; O.Quota_PositionPnl_wUpper = O.Quota_PositionPnl_wUpper.IsNormalize() ? O.Quota_PositionPnl_wUpper : null; O.Quota_PositionPnl_wLower = O.Quota_PositionPnl_wLower.IsNormalize() ? O.Quota_PositionPnl_wLower : null; }); DbContext.QuotaMonitor_Asset.AddRange(list); } //品种汇总 { var list = QueryVarietyFromCalc(req); list.ForEach(O => { O.id = 0; O.ValueDate = settlementDate; O.UnderlyingCode ??= ""; O.StockEqvNotional = O.StockEqvNotional.IsNormalize() ? O.StockEqvNotional : null; O.Quota_StockEqvNotional_Upper = O.Quota_StockEqvNotional_Upper.IsNormalize() ? O.Quota_StockEqvNotional_Upper : null; O.Quota_StockEqvNotional_Lower = O.Quota_StockEqvNotional_Lower.IsNormalize() ? O.Quota_StockEqvNotional_Lower : null; O.Quota_StockEqvNotional_wUpper = O.Quota_StockEqvNotional_wUpper.IsNormalize() ? O.Quota_StockEqvNotional_wUpper : null; O.Quota_StockEqvNotional_wLower = O.Quota_StockEqvNotional_wLower.IsNormalize() ? O.Quota_StockEqvNotional_wLower : null; O.DeltaExposure = O.DeltaExposure.IsNormalize() ? O.DeltaExposure : null; O.Quota_DeltaExposure_Upper = O.Quota_DeltaExposure_Upper.IsNormalize() ? O.Quota_DeltaExposure_Upper : null; O.Quota_DeltaExposure_Lower = O.Quota_DeltaExposure_Lower.IsNormalize() ? O.Quota_DeltaExposure_Lower : null; O.Quota_DeltaExposure_wUpper = O.Quota_DeltaExposure_wUpper.IsNormalize() ? O.Quota_DeltaExposure_wUpper : null; O.Quota_DeltaExposure_wLower = O.Quota_DeltaExposure_wLower.IsNormalize() ? O.Quota_DeltaExposure_wLower : null; O.DeltaNetExposure = O.DeltaNetExposure.IsNormalize() ? O.DeltaNetExposure : null; O.Quota_DeltaNetExposure_Upper = O.Quota_DeltaNetExposure_Upper.IsNormalize() ? O.Quota_DeltaNetExposure_Upper : null; O.Quota_DeltaNetExposure_Lower = O.Quota_DeltaNetExposure_Lower.IsNormalize() ? O.Quota_DeltaNetExposure_Lower : null; O.Quota_DeltaNetExposure_wUpper = O.Quota_DeltaNetExposure_wUpper.IsNormalize() ? O.Quota_DeltaNetExposure_wUpper : null; O.Quota_DeltaNetExposure_wLower = O.Quota_DeltaNetExposure_wLower.IsNormalize() ? O.Quota_DeltaNetExposure_wLower : null; O.DeltaCash = O.DeltaCash.IsNormalize() ? O.DeltaCash : null; O.Quota_DeltaCash_Upper = O.Quota_DeltaCash_Upper.IsNormalize() ? O.Quota_DeltaCash_Upper : null; O.Quota_DeltaCash_Lower = O.Quota_DeltaCash_Lower.IsNormalize() ? O.Quota_DeltaCash_Lower : null; O.Quota_DeltaCash_wUpper = O.Quota_DeltaCash_wUpper.IsNormalize() ? O.Quota_DeltaCash_wUpper : null; O.Quota_DeltaCash_wLower = O.Quota_DeltaCash_wLower.IsNormalize() ? O.Quota_DeltaCash_wLower : null; O.GammaCash = O.GammaCash.IsNormalize() ? O.GammaCash : null; O.Quota_GammaCash_Upper = O.Quota_GammaCash_Upper.IsNormalize() ? O.Quota_GammaCash_Upper : null; O.Quota_GammaCash_Lower = O.Quota_GammaCash_Lower.IsNormalize() ? O.Quota_GammaCash_Lower : null; O.Quota_GammaCash_wUpper = O.Quota_GammaCash_wUpper.IsNormalize() ? O.Quota_GammaCash_wUpper : null; O.Quota_GammaCash_wLower = O.Quota_GammaCash_wLower.IsNormalize() ? O.Quota_GammaCash_wLower : null; O.Vega = O.Vega.IsNormalize() ? O.Vega : null; O.Quota_Vega_Upper = O.Quota_Vega_Upper.IsNormalize() ? O.Quota_Vega_Upper : null; O.Quota_Vega_Lower = O.Quota_Vega_Lower.IsNormalize() ? O.Quota_Vega_Lower : null; O.Quota_Vega_wUpper = O.Quota_Vega_wUpper.IsNormalize() ? O.Quota_Vega_wUpper : null; O.Quota_Vega_wLower = O.Quota_Vega_wLower.IsNormalize() ? O.Quota_Vega_wLower : null; O.VegaCash = O.VegaCash.IsNormalize() ? O.VegaCash : null; O.Quota_VegaCash_Upper = O.Quota_VegaCash_Upper.IsNormalize() ? O.Quota_VegaCash_Upper : null; O.Quota_VegaCash_Lower = O.Quota_VegaCash_Lower.IsNormalize() ? O.Quota_VegaCash_Lower : null; O.Quota_VegaCash_wUpper = O.Quota_VegaCash_wUpper.IsNormalize() ? O.Quota_VegaCash_wUpper : null; O.Quota_VegaCash_wLower = O.Quota_VegaCash_wLower.IsNormalize() ? O.Quota_VegaCash_wLower : null; O.Concentration = O.Concentration.IsNormalize() ? O.Concentration : null; O.Quota_Concentration_Upper = O.Quota_Concentration_Upper.IsNormalize() ? O.Quota_Concentration_Upper : null; O.Quota_Concentration_Lower = O.Quota_Concentration_Lower.IsNormalize() ? O.Quota_Concentration_Lower : null; O.Quota_Concentration_wUpper = O.Quota_Concentration_wUpper.IsNormalize() ? O.Quota_Concentration_wUpper : null; O.Quota_Concentration_wLower = O.Quota_Concentration_wLower.IsNormalize() ? O.Quota_Concentration_wLower : null; O.PositionPnl = O.PositionPnl.IsNormalize() ? O.PositionPnl : null; O.Quota_PositionPnl_Upper = O.Quota_PositionPnl_Upper.IsNormalize() ? O.Quota_PositionPnl_Upper : null; O.Quota_PositionPnl_Lower = O.Quota_PositionPnl_Lower.IsNormalize() ? O.Quota_PositionPnl_Lower : null; O.Quota_PositionPnl_wUpper = O.Quota_PositionPnl_wUpper.IsNormalize() ? O.Quota_PositionPnl_wUpper : null; O.Quota_PositionPnl_wLower = O.Quota_PositionPnl_wLower.IsNormalize() ? O.Quota_PositionPnl_wLower : null; O.Theta = O.Theta.IsNormalize() ? O.Theta : null; O.Quota_Theta_Upper = O.Quota_Theta_Upper.IsNormalize() ? O.Quota_Theta_Upper : null; O.Quota_Theta_Lower = O.Quota_Theta_Lower.IsNormalize() ? O.Quota_Theta_Lower : null; O.Quota_Theta_wUpper = O.Quota_Theta_wUpper.IsNormalize() ? O.Quota_Theta_wUpper : null; O.Quota_Theta_wLower = O.Quota_Theta_wLower.IsNormalize() ? O.Quota_Theta_wLower : null; }); DbContext.QuotaMonitor_Variety.AddRange(list); } //标的汇总 { var list = QueryUnderlyingFromCalc(req); list.ForEach(O => { O.id = 0; O.ValueDate = settlementDate; O.UnderlyingCode ??= ""; O.StockEqvNotional = O.StockEqvNotional.IsNormalize() ? O.StockEqvNotional : null; O.Quota_StockEqvNotional_Upper = O.Quota_StockEqvNotional_Upper.IsNormalize() ? O.Quota_StockEqvNotional_Upper : null; O.Quota_StockEqvNotional_Lower = O.Quota_StockEqvNotional_Lower.IsNormalize() ? O.Quota_StockEqvNotional_Lower : null; O.Quota_StockEqvNotional_wUpper = O.Quota_StockEqvNotional_wUpper.IsNormalize() ? O.Quota_StockEqvNotional_wUpper : null; O.Quota_StockEqvNotional_wLower = O.Quota_StockEqvNotional_wLower.IsNormalize() ? O.Quota_StockEqvNotional_wLower : null; O.DeltaExposure = O.DeltaExposure.IsNormalize() ? O.DeltaExposure : null; O.Quota_DeltaExposure_Upper = O.Quota_DeltaExposure_Upper.IsNormalize() ? O.Quota_DeltaExposure_Upper : null; O.Quota_DeltaExposure_Lower = O.Quota_DeltaExposure_Lower.IsNormalize() ? O.Quota_DeltaExposure_Lower : null; O.Quota_DeltaExposure_wUpper = O.Quota_DeltaExposure_wUpper.IsNormalize() ? O.Quota_DeltaExposure_wUpper : null; O.Quota_DeltaExposure_wLower = O.Quota_DeltaExposure_wLower.IsNormalize() ? O.Quota_DeltaExposure_wLower : null; O.DeltaNetExposure = O.DeltaNetExposure.IsNormalize() ? O.DeltaNetExposure : null; O.Quota_DeltaNetExposure_Upper = O.Quota_DeltaNetExposure_Upper.IsNormalize() ? O.Quota_DeltaNetExposure_Upper : null; O.Quota_DeltaNetExposure_Lower = O.Quota_DeltaNetExposure_Lower.IsNormalize() ? O.Quota_DeltaNetExposure_Lower : null; O.Quota_DeltaNetExposure_wUpper = O.Quota_DeltaNetExposure_wUpper.IsNormalize() ? O.Quota_DeltaNetExposure_wUpper : null; O.Quota_DeltaNetExposure_wLower = O.Quota_DeltaNetExposure_wLower.IsNormalize() ? O.Quota_DeltaNetExposure_wLower : null; O.DeltaCash = O.DeltaCash.IsNormalize() ? O.DeltaCash : null; O.Quota_DeltaCash_Upper = O.Quota_DeltaCash_Upper.IsNormalize() ? O.Quota_DeltaCash_Upper : null; O.Quota_DeltaCash_Lower = O.Quota_DeltaCash_Lower.IsNormalize() ? O.Quota_DeltaCash_Lower : null; O.Quota_DeltaCash_wUpper = O.Quota_DeltaCash_wUpper.IsNormalize() ? O.Quota_DeltaCash_wUpper : null; O.Quota_DeltaCash_wLower = O.Quota_DeltaCash_wLower.IsNormalize() ? O.Quota_DeltaCash_wLower : null; O.GammaCash = O.GammaCash.IsNormalize() ? O.GammaCash : null; O.Quota_GammaCash_Upper = O.Quota_GammaCash_Upper.IsNormalize() ? O.Quota_GammaCash_Upper : null; O.Quota_GammaCash_Lower = O.Quota_GammaCash_Lower.IsNormalize() ? O.Quota_GammaCash_Lower : null; O.Quota_GammaCash_wUpper = O.Quota_GammaCash_wUpper.IsNormalize() ? O.Quota_GammaCash_wUpper : null; O.Quota_GammaCash_wLower = O.Quota_GammaCash_wLower.IsNormalize() ? O.Quota_GammaCash_wLower : null; O.Vega = O.Vega.IsNormalize() ? O.Vega : null; O.Quota_Vega_Upper = O.Quota_Vega_Upper.IsNormalize() ? O.Quota_Vega_Upper : null; O.Quota_Vega_Lower = O.Quota_Vega_Lower.IsNormalize() ? O.Quota_Vega_Lower : null; O.Quota_Vega_wUpper = O.Quota_Vega_wUpper.IsNormalize() ? O.Quota_Vega_wUpper : null; O.Quota_Vega_wLower = O.Quota_Vega_wLower.IsNormalize() ? O.Quota_Vega_wLower : null; O.VegaCash = O.VegaCash.IsNormalize() ? O.VegaCash : null; O.Quota_VegaCash_Upper = O.Quota_VegaCash_Upper.IsNormalize() ? O.Quota_VegaCash_Upper : null; O.Quota_VegaCash_Lower = O.Quota_VegaCash_Lower.IsNormalize() ? O.Quota_VegaCash_Lower : null; O.Quota_VegaCash_wUpper = O.Quota_VegaCash_wUpper.IsNormalize() ? O.Quota_VegaCash_wUpper : null; O.Quota_VegaCash_wLower = O.Quota_VegaCash_wLower.IsNormalize() ? O.Quota_VegaCash_wLower : null; O.Concentration = O.Concentration.IsNormalize() ? O.Concentration : null; O.Quota_Concentration_Upper = O.Quota_Concentration_Upper.IsNormalize() ? O.Quota_Concentration_Upper : null; O.Quota_Concentration_Lower = O.Quota_Concentration_Lower.IsNormalize() ? O.Quota_Concentration_Lower : null; O.Quota_Concentration_wUpper = O.Quota_Concentration_wUpper.IsNormalize() ? O.Quota_Concentration_wUpper : null; O.Quota_Concentration_wLower = O.Quota_Concentration_wLower.IsNormalize() ? O.Quota_Concentration_wLower : null; O.PositionPnl = O.PositionPnl.IsNormalize() ? O.PositionPnl : null; O.Quota_PositionPnl_Upper = O.Quota_PositionPnl_Upper.IsNormalize() ? O.Quota_PositionPnl_Upper : null; O.Quota_PositionPnl_Lower = O.Quota_PositionPnl_Lower.IsNormalize() ? O.Quota_PositionPnl_Lower : null; O.Quota_PositionPnl_wUpper = O.Quota_PositionPnl_wUpper.IsNormalize() ? O.Quota_PositionPnl_wUpper : null; O.Quota_PositionPnl_wLower = O.Quota_PositionPnl_wLower.IsNormalize() ? O.Quota_PositionPnl_wLower : null; O.Theta = O.Theta.IsNormalize() ? O.Theta : null; O.Quota_Theta_Upper = O.Quota_Theta_Upper.IsNormalize() ? O.Quota_Theta_Upper : null; O.Quota_Theta_Lower = O.Quota_Theta_Lower.IsNormalize() ? O.Quota_Theta_Lower : null; O.Quota_Theta_wUpper = O.Quota_Theta_wUpper.IsNormalize() ? O.Quota_Theta_wUpper : null; O.Quota_Theta_wLower = O.Quota_Theta_wLower.IsNormalize() ? O.Quota_Theta_wLower : null; O.TotalPnL = O.Theta.IsNormalize() ? O.TotalPnL : null; O.Quota_TotalPnL_Upper = O.Quota_TotalPnL_Upper.IsNormalize() ? O.Quota_TotalPnL_Upper : null; O.Quota_TotalPnL_Lower = O.Quota_TotalPnL_Lower.IsNormalize() ? O.Quota_TotalPnL_Lower : null; O.Quota_TotalPnL_wUpper = O.Quota_TotalPnL_wUpper.IsNormalize() ? O.Quota_TotalPnL_wUpper : null; O.Quota_TotalPnL_wLower = O.Quota_TotalPnL_wLower.IsNormalize() ? O.Quota_TotalPnL_wLower : null; }); DbContext.QuotaMonitor_Underlying.AddRange(list); } //客户汇总 { var list = QueryClientFromCalc(req); list.ForEach(O => { O.id = 0; O.ValueDate = settlementDate; O.ClientNumber ??= ""; O.ClientName ??= ""; O.StockEqvNotionalScale = O.StockEqvNotionalScale.IsNormalize() ? O.StockEqvNotionalScale : null; O.AvailableStockEqvNotional = O.AvailableStockEqvNotional.IsNormalize() ? O.AvailableStockEqvNotional : null; O.StockEqvNotional = O.StockEqvNotional.IsNormalize() ? O.StockEqvNotional : null; O.Quota_StockEqvNotional_Upper = O.Quota_StockEqvNotional_Upper.IsNormalize() ? O.Quota_StockEqvNotional_Upper : null; O.Quota_StockEqvNotional_Lower = O.Quota_StockEqvNotional_Lower.IsNormalize() ? O.Quota_StockEqvNotional_Lower : null; O.Quota_StockEqvNotional_wUpper = O.Quota_StockEqvNotional_wUpper.IsNormalize() ? O.Quota_StockEqvNotional_wUpper : null; O.Quota_StockEqvNotional_wLower = O.Quota_StockEqvNotional_wLower.IsNormalize() ? O.Quota_StockEqvNotional_wLower : null; O.PositionMargin = O.PositionMargin.IsNormalize() ? O.PositionMargin : null; O.AvailableFund = O.AvailableFund.IsNormalize() ? O.AvailableFund : null; O.Credit = O.Credit.IsNormalize() ? O.Credit : null; O.PayableFund = O.PayableFund.IsNormalize() ? O.PayableFund : null; O.HoldingFund = O.HoldingFund.IsNormalize() ? O.HoldingFund : null; O.ProductScale = O.ProductScale.IsNormalize() ? O.ProductScale : null; O.HoldingRate = O.HoldingRate.IsNormalize() ? O.HoldingRate : null; O.Quota_HoldingFund_Upper = O.Quota_HoldingFund_Upper.IsNormalize() ? O.Quota_HoldingFund_Upper : null; O.Quota_HoldingFund_Lower = O.Quota_HoldingFund_Lower.IsNormalize() ? O.Quota_HoldingFund_Lower : null; O.Quota_HoldingFund_wUpper = O.Quota_HoldingFund_wUpper.IsNormalize() ? O.Quota_HoldingFund_wUpper : null; O.Quota_HoldingFund_wLower = O.Quota_HoldingFund_wLower.IsNormalize() ? O.Quota_HoldingFund_wLower : null; }); DbContext.QuotaMonitor_Client.AddRange(list); } //单笔交易 { var list = QueryTradeFromCalc(req); list.ForEach(O => { O.id = 0; O.ValueDate = settlementDate; O.TradeNumber ??= ""; O.ClientName ??= ""; O.StockEqvNotional = O.StockEqvNotional.IsNormalize() ? O.StockEqvNotional : null; O.Quota_StockEqvNotional_Upper = O.Quota_StockEqvNotional_Upper.IsNormalize() ? O.Quota_StockEqvNotional_Upper : null; O.Quota_StockEqvNotional_Lower = O.Quota_StockEqvNotional_Lower.IsNormalize() ? O.Quota_StockEqvNotional_Lower : null; O.Quota_StockEqvNotional_wUpper = O.Quota_StockEqvNotional_wUpper.IsNormalize() ? O.Quota_StockEqvNotional_wUpper : null; O.Quota_StockEqvNotional_wLower = O.Quota_StockEqvNotional_wLower.IsNormalize() ? O.Quota_StockEqvNotional_wLower : null; O.DeltaCash = O.DeltaCash.IsNormalize() ? O.DeltaCash : null; O.Quota_DeltaCash_Upper = O.Quota_DeltaCash_Upper.IsNormalize() ? O.Quota_DeltaCash_Upper : null; O.Quota_DeltaCash_Lower = O.Quota_DeltaCash_Lower.IsNormalize() ? O.Quota_DeltaCash_Lower : null; O.Quota_DeltaCash_wUpper = O.Quota_DeltaCash_wUpper.IsNormalize() ? O.Quota_DeltaCash_wUpper : null; O.Quota_DeltaCash_wLower = O.Quota_DeltaCash_wLower.IsNormalize() ? O.Quota_DeltaCash_wLower : null; O.GammaCash = O.GammaCash.IsNormalize() ? O.GammaCash : null; O.Quota_GammaCash_Upper = O.Quota_GammaCash_Upper.IsNormalize() ? O.Quota_GammaCash_Upper : null; O.Quota_GammaCash_Lower = O.Quota_GammaCash_Lower.IsNormalize() ? O.Quota_GammaCash_Lower : null; O.Quota_GammaCash_wUpper = O.Quota_GammaCash_wUpper.IsNormalize() ? O.Quota_GammaCash_wUpper : null; O.Quota_GammaCash_wLower = O.Quota_GammaCash_wLower.IsNormalize() ? O.Quota_GammaCash_wLower : null; O.Vega = O.Vega.IsNormalize() ? O.Vega : null; O.Quota_Vega_Upper = O.Quota_Vega_Upper.IsNormalize() ? O.Quota_Vega_Upper : null; O.Quota_Vega_Lower = O.Quota_Vega_Lower.IsNormalize() ? O.Quota_Vega_Lower : null; O.Quota_Vega_wUpper = O.Quota_Vega_wUpper.IsNormalize() ? O.Quota_Vega_wUpper : null; O.Quota_Vega_wLower = O.Quota_Vega_wLower.IsNormalize() ? O.Quota_Vega_wLower : null; O.VegaCash = O.VegaCash.IsNormalize() ? O.VegaCash : null; O.Quota_VegaCash_Upper = O.Quota_VegaCash_Upper.IsNormalize() ? O.Quota_VegaCash_Upper : null; O.Quota_VegaCash_Lower = O.Quota_VegaCash_Lower.IsNormalize() ? O.Quota_VegaCash_Lower : null; O.Quota_VegaCash_wUpper = O.Quota_VegaCash_wUpper.IsNormalize() ? O.Quota_VegaCash_wUpper : null; O.Quota_VegaCash_wLower = O.Quota_VegaCash_wLower.IsNormalize() ? O.Quota_VegaCash_wLower : null; O.PnL = O.PnL.IsNormalize() ? O.PnL : null; O.Quota_Loss_Upper = O.Quota_Loss_Upper.IsNormalize() ? O.Quota_Loss_Upper : null; O.Quota_Loss_Lower = O.Quota_Loss_Lower.IsNormalize() ? O.Quota_Loss_Lower : null; O.Quota_Loss_wUpper = O.Quota_Loss_wUpper.IsNormalize() ? O.Quota_Loss_wUpper : null; O.Quota_Loss_wLower = O.Quota_Loss_wLower.IsNormalize() ? O.Quota_Loss_wLower : null; O.Quota_CCR = O.Quota_CCR.IsNormalize() ? O.Quota_CCR : null; O.Quota_SwapPercent = O.Quota_SwapPercent.IsNormalize() ? O.Quota_SwapPercent : null; O.Quota_SwapPercent_Upper = O.Quota_SwapPercent_Upper.IsNormalize() ? O.Quota_SwapPercent_Upper : null; O.Quota_SwapPercent_Lower = O.Quota_SwapPercent_Lower.IsNormalize() ? O.Quota_SwapPercent_Lower : null; O.Quota_SwapPercent_wUpper = O.Quota_SwapPercent_wUpper.IsNormalize() ? O.Quota_SwapPercent_wUpper : null; O.Quota_SwapPercent_wLower = O.Quota_SwapPercent_wLower.IsNormalize() ? O.Quota_SwapPercent_wLower : null; }); DbContext.QuotaMonitor_Trade.AddRange(list); } #region 落库 DbContext.BulkDelete($"{nameof(QuotaMonitorBase.ValueDate)}='{settlementDate.ToString("yyyy-MM-dd")}'"); DbContext.BulkDelete($"{nameof(QuotaMonitorBase.ValueDate)}='{settlementDate.ToString("yyyy-MM-dd")}'"); DbContext.BulkDelete($"{nameof(QuotaMonitorBase.ValueDate)}='{settlementDate.ToString("yyyy-MM-dd")}'"); DbContext.BulkDelete($"{nameof(QuotaMonitorBase.ValueDate)}='{settlementDate.ToString("yyyy-MM-dd")}'"); DbContext.BulkDelete($"{nameof(QuotaMonitorBase.ValueDate)}='{settlementDate.ToString("yyyy-MM-dd")}'"); DbContext.BulkDelete($"{nameof(QuotaMonitorBase.ValueDate)}='{settlementDate.ToString("yyyy-MM-dd")}'"); DbContext.SaveChanges(); #endregion } public void EodSensitiveDataCalc(EodSettlementContextBase context) { //获取持仓数据 var clienIds = context.Request.ClientIds; var predicte = context.PredicateBuilder.GetOtcTradePredicate(); var tradeList = DbContext.trade.Where(predicte).ToList(); //去掉互换 远期 掉期 "现金流交易" var types = ConsTrade.OptionTradeTypes.Except(new List { "现金流交易" }); tradeList = tradeList.Where(o => types.Contains(o.TradeType)).ToList(); context.LogInfo($"SensitiveDataCalc场外交易 : {tradeList.Count()} "); var trades = DbContext.eod_trade_position .Where(t => t.ValueDate == context.SettleDate && ConsTrade.TradeTypesForHedge.Contains(t.TradeType)) .Select(et => new { id = et.TradeId, ClientId = et.ClientId, AssetId = et.BookId, TradeType = et.TradeType, UnderlyingId = et.UnderlyingId, UnderlyingCode = et.UnderlyingCode, BuySell = et.PositionType == "long" ? "买入" : "卖出", IsMoneynessOption = "否", Notional = et.PositionType == "long" ? et.Amount : -et.Amount, ExchangeOptionCode = et.ExchangeOptionCode, UniqueCode = et.HedgeUniqueCode, }).ToList(); #region 新增客户筛选 tw if (clienIds != null) { trades = trades.Where(l => clienIds.Contains(l.ClientId)).ToList(); } #endregion //期权合并 foreach (var et in trades) { var trade = new tradeDto { id = et.id, ClientId = et.ClientId, AssetId = et.AssetId, TradeType = et.TradeType, UnderlyingId = et.UnderlyingId, UnderlyingCode = et.UnderlyingCode, BuySell = et.BuySell == "long" ? "买入" : "卖出", IsMoneynessOption = "否", Notional = et.Notional, ExchangeOptionCode = et.ExchangeOptionCode, MetaDic = new Dictionary { { "UniqueCode", et.UniqueCode } } }; tradeList.Add(trade); } context.LogInfo($"SensitiveDataCalc全部交易 : {tradeList.Count()} "); var priceProvider = new EodPriceProvider(context.SettleDate).GetPriceProvider(SettlementTypeEnum.SettlePrice); try { SensitiveDataCalc(tradeList, priceProvider, context.SettleDate); } catch (Exception e) { context.LogInfo($"SensitiveDataCalc计算出错 : {e.Message + (e.InnerException == null ? string.Empty : e.InnerException.Message)} "); } context.LogInfo($"SensitiveDataCalc计算完成 : {tradeList.Count()} "); DbContext.SaveChanges(); } public void SensitiveDataCalc(IEnumerable tradeList, IPriceProvider priceProvider, DateTime SettleDate) { var tradeIds = tradeList.Where(o => o.id != 0).Select(o => o.id); //获取Delta Gamma var eodrisks = DbContext.eod_trade_risk.Where(o => o.ValueDate == SettleDate && tradeIds.Contains(o.TradeId)).ToList(); foreach (var trade in tradeList) { var list = new List() { trade }; var eodRisk = eodrisks.FirstOrDefault(o => o.TradeId == trade.id); var dataType = DataType.期权类; switch (trade.TradeType) { case "股票": dataType = DataType.股票类; break; case "商品期货": dataType = DataType.期货类; break; default: dataType = DataType.期权类; break; } GetUpDownLimitPrices(trade, eodRisk, priceProvider, dataType, out var upPercent, out var downPercent, out var upLimitPrices, out var downLimitPrices); //涨幅 var calcReq = GetCalculateRisksForTradesReq(upLimitPrices, SettleDate, list); var tradeRiskResult = CalculatorHelper.CalculateRisksForTrades(calcReq); if (tradeRiskResult.Results != null && tradeRiskResult.Results.Count > 0) { foreach (var item in tradeRiskResult.Results) { var id = item.Trade.id == 0 ? (trade.MetaDic.ContainsKey("UniqueCode") ? "" : trade.MetaDic["UniqueCode"]) : item.Trade.id.ToString(); var data = DbContext.SensitiveData.FirstOrDefault(o => o.ValueDate == SettleDate && o.tradeId == id && o.dataType == dataType); if (data == null) { data = new SensitiveData() { ValueDate = SettleDate, tradeId = id, dataType = dataType, }; DbContext.SensitiveData.Add(data); } data.param1 = upPercent; data.param2 = item.ValueResult.Pv; if (double.IsNaN(upPercent) || double.IsInfinity(upPercent)) { data.param1 = 0; } if (double.IsNaN(item.ValueResult.Pv) || double.IsInfinity(item.ValueResult.Pv)) { data.param2 = 0; } } } DbContext.SaveChanges(); //跌幅 calcReq = GetCalculateRisksForTradesReq(downLimitPrices, SettleDate, list); tradeRiskResult = CalculatorHelper.CalculateRisksForTrades(calcReq); if (tradeRiskResult.Results != null && tradeRiskResult.Results.Count > 0) { foreach (var item in tradeRiskResult.Results) { var id = item.Trade.id == 0 ? (trade.MetaDic.ContainsKey("UniqueCode") ? "" : trade.MetaDic["UniqueCode"]) : item.Trade.id.ToString(); var data = DbContext.SensitiveData.FirstOrDefault(o => o.ValueDate == SettleDate && o.tradeId == id && o.dataType == dataType); if (data == null) { data = new SensitiveData() { ValueDate = SettleDate, tradeId = id, dataType = dataType }; DbContext.SensitiveData.Add(data); } data.param3 = downPercent; data.param4 = item.ValueResult.Pv; if (double.IsNaN(downPercent) || double.IsInfinity(downPercent)) { data.param3 = 0; } if (double.IsNaN(item.ValueResult.Pv) || double.IsInfinity(item.ValueResult.Pv)) { data.param4 = 0; } } } DbContext.SaveChanges(); } } public void GetUpDownLimitPrices(trade t, eod_trade_risk r, IPriceProvider priceProvider, DataType datatype, out double upPercent, out double downPercent, out IPriceProvider upLimitPrices, out IPriceProvider downLimitPrices) { r ??= new eod_trade_risk { Delta = t.Notional, Gamma = 0 }; var code = t.UnderlyingCode; var basePercent = 0.1; upPercent = basePercent; downPercent = basePercent * -1; switch (datatype) { case DataType.期权类: upPercent = (upPercent * r.Delta) + (r.Gamma == 0 ? 0 : (upPercent * upPercent * r.Gamma / 2)); downPercent = (downPercent * r.Delta) + (r.Gamma == 0 ? 0 : (downPercent * downPercent * r.Gamma / 2)); break; case DataType.期货类: upPercent = basePercent * r.Delta; downPercent = -basePercent * r.Delta; break; case DataType.股票类: break; default: break; } var upLimitPrices2 = new ManualPriceProvider(); var downLimitPrices2 = new ManualPriceProvider(); //根据涨跌幅限制以及当日结算价计算涨停价以及跌停价 var price = priceProvider.GetPrice(code); var updown = new { UpLimitPrice = price + (price * upPercent), DownLimitPrice = price + (price * downPercent) }; upLimitPrices2.SetPrice(code, updown.UpLimitPrice); downLimitPrices2.SetPrice(code, updown.DownLimitPrice); upLimitPrices = upLimitPrices2; downLimitPrices = downLimitPrices2; } public CalculateRisksForTradesReq GetCalculateRisksForTradesReq(IPriceProvider priceProvider, DateTime ValueDate, IEnumerable tradeList) { var reqConv = new CalculateRisksForTradesReq { valueDate = ValueDate, tradeList = tradeList, priceProvider = priceProvider, pricingRequest = QdpPricingRequest.PV_ONLY, addVolRateDic = null, volType = "交易", isUseTradeVol = PS.Config.IsTradeVol, PreciseTimeMode = false, isAddVolPercent = true, overrideVolsForTrade = null, isMarginCalc = true, calcScenario = CalcScenarioEnum.EodSettlement, canUseManual = true }; return reqConv; } /// /// 从数据库中查询限额监控数据 /// /// /// /// private List QueryFromDb(QuotaMonitorReq req) where T : QuotaMonitorBase { var db = DbContext.Set(); return db.Where(O => O.ValueDate == req.ValueDate).ToList(); } /// /// 查询限额监控-全局数据 /// /// /// public List QueryGlobalFromDb(QuotaMonitorReq req) { //SetDebugSqlLog(); var list = QueryFromDb(req); list.Reverse(); return list; } /// /// 查询限额监控-客户数据 /// /// /// public List QueryClientFromDb(QuotaMonitorReq req) { var list = QueryFromDb(req); var types = new[] { "互换" }; if (req.ClientId > 0) { var clientName = DataCacheProvider.GetClientDataSource().GetData(req.ClientId)?.Name; list = list.Where(O => O.ClientName == clientName).ToList(); } if (req.ClientNumber > 0) { var clientName = DataCacheProvider.GetClientDataSource().GetData(req.ClientNumber)?.Name; list = list.Where(O => O.ClientName == clientName).ToList(); } return list; } /// /// 查询限额监控-资产汇总数据 /// /// /// public List QueryAssetFromDb(QuotaMonitorReq req) { return QueryFromDb(req); } /// /// 查询限额监控-品种汇总数据 /// /// /// public List QueryVarietyFromDb(QuotaMonitorReq req) { return QueryFromDb(req); } /// /// 查询限额监控-单笔交易数据 /// /// /// public List QueryTradeFromDb(QuotaMonitorReq req) { var queryStatus = false; var list = QueryFromDb(req); var tradeNumberList = list.Select(O => O.TradeNumber); var query = DbContext.trade.Where(O => tradeNumberList.Contains(O.TradeNumber)); if (!req.TradeNumber.IsNullOrWhiteSpace()) { queryStatus = true; query = query.Where(O => O.TradeNumber == req.TradeNumber); } if (req.ClientId > 0) { queryStatus = true; query = query.Where(O => O.ClientId == req.ClientId); } if (req.UnderlyingId?.Count > 0) { queryStatus = true; query = query.Where(O => req.UnderlyingId.Contains(O.UnderlyingId)); } if (req.VarietyId > 0) { queryStatus = true; query = from q in query join um in DbContext.underlying_manager on q.UnderlyingCode equals um.UnderlyingCode select q; } if (!req.TradeType.IsNullOrWhiteSpace()) { queryStatus = true; query = query.Where(O => req.TradeType == O.TradeType || req.TradeType == O.StructureType); } if (req.AssetId != 0) { queryStatus = true; query = query.Where(O => req.AssetId == O.AssetId); } if (req.TradeDateStart != default) { queryStatus = true; query = query.Where(O => req.TradeDateStart <= O.TradeDate); } if (req.TradeDateEnd != default) { queryStatus = true; var date = req.TradeDateEnd; if (date < DateTime.MaxValue.Date) { date = date.AddDays(1); } query = query.Where(O => date >= O.TradeDate); } if (!req.TradeStatus.IsNullOrWhiteSpace()) { queryStatus = true; query = query.Where(O => req.TradeStatus == O.TradeStatus); } if (req.ActualSubject != null && req.ActualSubject.Any()) { queryStatus = true; var clientDb = DbContextFactory.GetClientDbContext(OptUser); var dict = clientDb.ClientMeta.Where(O => O.MetaKey == "同一主体").Select(O => new { O.ClientId, O.MetaValue }); var clientId = new List(); foreach (var item in dict) { var arr = item.MetaValue.Split(new[] { ',' }, StringSplitOptions.RemoveEmptyEntries); if (req.ActualSubject.Intersect(arr).Any()) { clientId.Add(item.ClientId); } } query = query.Where(O => clientId.Contains(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)); } if (queryStatus) { tradeNumberList = query.Select(O => O.TradeNumber).ToList(); list = list.Where(O => tradeNumberList.Contains(O.TradeNumber)).ToList(); } return list; } /// /// 查询限额监控-标的汇总数据 /// /// /// public List QueryUnderlyingFromDb(QuotaMonitorReq req) { var list = QueryFromDb(req); if (req.UnderlyingId?.Count > 0) { list = list.Where(O => req.UnderlyingId.Contains(O.UnderlyingId)).ToList(); } return list; } /// /// 查询限额监控-簿记汇总数据 /// /// /// public List QueryAssetUnitFromDb(QuotaMonitorReq req) { var list = QueryFromDb(req); if (req.AssetId > 0) { list = list.Where(O => O.AssetBookID == req.AssetId).ToList(); } return list; } /// /// 查询限额监控-全局数据 /// /// /// public List QueryGlobalFromCalc(QuotaMonitorReq req) { var positionTradeQuery = DbContext.trade.Where(O => ConsTrade.PositionTradeStatusList.Contains(O.TradeStatus) && O.ValidState != "InValid" && O.ExerciseDate >= SystemValueDate && O.TradeType == "收益互换"); req.PvPercent = 1 - req.PvPercent; var arr = (from risk in DbContext.realtime_trade_risk join t in positionTradeQuery on risk.TradeId equals t.id where risk.VolType == "持仓" && t.TradeType == "收益互换" select new { risk, t }).ToArray(); var dict = (from o in arr group o by (o.t.TradeType == "收益互换" ? "互换" : "") into info select new { Key = info.Key, list = info.ToList() }).ToDictionary(K => K.Key, V => V.list); var posiQuery = from t in positionTradeQuery join p in DbContext.swap_position on t.id equals p.SwapTradeId where !p.IsInitial && p.PosiDirection > 0 && !p.Invalid select p; var setValue = new Action>((obj, settings) => { obj.StockEqvNotional = Convert.ToDouble(posiQuery.Sum(s => s.PosiNotionalValue)); if (!dict.ContainsKey(obj.BusinessType)) { return; } obj.DeltaCash = dict[obj.BusinessType].Sum(O => O.risk.DeltaCash.Normalize()); obj.Vega = dict[obj.BusinessType].Sum(O => O.risk.Vega.Normalize()); obj.VegaCash = dict[obj.BusinessType].Sum(O => O.risk.VegaCash.Normalize()); obj.GammaCash = dict[obj.BusinessType].Sum(O => O.risk.GammaCash.Normalize()); obj.Theta = dict[obj.BusinessType].Sum(O => O.risk.Theta.Normalize()); var equitySettings = settings.Where(O => O.IsValid && O.Status == QuotaSettingApprovalStatus.Valid && O.QuotaRange == 0 && O.QuotaIndex == "权益类业务规模").FirstOrDefault()?.Clone(); obj.Quota_EquityRiskScale_Upper = equitySettings?.QuotaUpperLimit ?? double.NaN; obj.Quota_EquityRiskScale_Lower = equitySettings?.QuotaLowerLimit ?? double.NaN; obj.Quota_EquityRiskScale_wUpper = equitySettings?.WarningUpperLimit ?? double.NaN; obj.Quota_EquityRiskScale_wLower = equitySettings?.WarningLowerLimit ?? double.NaN; var nonEquitySettings = settings.Where(O => O.IsValid && O.Status == QuotaSettingApprovalStatus.Valid && O.QuotaRange == 0 && O.QuotaIndex == "非权益类业务规模").FirstOrDefault()?.Clone(); obj.Quota_NonEquityRiskScale_Upper = nonEquitySettings?.QuotaUpperLimit ?? double.NaN; obj.Quota_NonEquityRiskScale_Lower = nonEquitySettings?.QuotaLowerLimit ?? double.NaN; obj.Quota_NonEquityRiskScale_wUpper = nonEquitySettings?.WarningUpperLimit ?? double.NaN; obj.Quota_NonEquityRiskScale_wLower = nonEquitySettings?.WarningLowerLimit ?? double.NaN; var stockEqvNotionalSettings = settings.Where(O => O.IsValid && O.Status == QuotaSettingApprovalStatus.Valid && O.QuotaRange == 0 && O.QuotaIndex == "名义本金").FirstOrDefault()?.Clone(); stockEqvNotionalSettings?.ConvertToAbs(valuedateBLL.SystemDate.BusinessTotalScale ?? 0); obj.Quota_StockEqvNotional_Upper = stockEqvNotionalSettings?.QuotaUpperLimit ?? double.NaN; 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 totalPnLSettings = settings.Where(O => O.IsValid && O.Status == QuotaSettingApprovalStatus.Valid && O.QuotaRange == 0 && O.QuotaIndex == "止损金额").FirstOrDefault()?.Clone(); obj.Quota_TotalPnL_Upper = totalPnLSettings?.QuotaUpperLimit ?? double.NaN; obj.Quota_TotalPnL_Lower = totalPnLSettings?.QuotaLowerLimit ?? double.NaN; obj.Quota_TotalPnL_wUpper = totalPnLSettings?.WarningUpperLimit ?? double.NaN; obj.Quota_TotalPnL_wLower = totalPnLSettings?.WarningLowerLimit ?? double.NaN; var deltaExposureSettings = settings.Where(O => O.IsValid && O.Status == QuotaSettingApprovalStatus.Valid && O.QuotaRange == 0 && O.QuotaIndex == "Delta敞口(多空比)").FirstOrDefault()?.Clone(); obj.Quota_DeltaExposure_Upper = deltaExposureSettings?.QuotaUpperLimit ?? double.NaN; obj.Quota_DeltaExposure_Lower = deltaExposureSettings?.QuotaLowerLimit ?? double.NaN; obj.Quota_DeltaExposure_wUpper = deltaExposureSettings?.WarningUpperLimit ?? double.NaN; obj.Quota_DeltaExposure_wLower = deltaExposureSettings?.WarningLowerLimit ?? double.NaN; var deltaNetExposureSettings = settings.Where(O => O.IsValid && O.Status == QuotaSettingApprovalStatus.Valid && O.QuotaRange == 0 && O.QuotaIndex == "Delta净敞口比例").FirstOrDefault()?.Clone(); obj.Quota_DeltaNetExposure_Upper = deltaNetExposureSettings?.QuotaUpperLimit ?? double.NaN; obj.Quota_DeltaNetExposure_Lower = deltaNetExposureSettings?.QuotaLowerLimit ?? double.NaN; obj.Quota_DeltaNetExposure_wUpper = deltaNetExposureSettings?.WarningUpperLimit ?? double.NaN; obj.Quota_DeltaNetExposure_wLower = deltaNetExposureSettings?.WarningLowerLimit ?? double.NaN; var deltaCashSettings = settings.Where(O => O.IsValid && O.Status == QuotaSettingApprovalStatus.Valid && O.QuotaRange == 0 && O.QuotaIndex == "Delta金额").FirstOrDefault()?.Clone(); deltaCashSettings?.ConvertToAbs(valuedateBLL.SystemDate.BusinessAvailableFunds ?? 0); obj.Quota_DeltaCash_Upper = deltaCashSettings?.QuotaUpperLimit ?? double.NaN; obj.Quota_DeltaCash_Lower = deltaCashSettings?.QuotaLowerLimit ?? double.NaN; obj.Quota_DeltaCash_wUpper = deltaCashSettings?.WarningUpperLimit ?? double.NaN; obj.Quota_DeltaCash_wLower = deltaCashSettings?.WarningLowerLimit ?? double.NaN; var gammaCashSettings = settings.Where(O => O.IsValid && O.Status == QuotaSettingApprovalStatus.Valid && O.QuotaRange == 0 && O.QuotaIndex == "Gamma金额").FirstOrDefault()?.Clone(); gammaCashSettings?.ConvertToAbs(valuedateBLL.SystemDate.BusinessAvailableFunds ?? 0); obj.Quota_GammaCash_Upper = gammaCashSettings?.QuotaUpperLimit ?? double.NaN; obj.Quota_GammaCash_Lower = gammaCashSettings?.QuotaLowerLimit ?? double.NaN; obj.Quota_GammaCash_wUpper = gammaCashSettings?.WarningUpperLimit ?? double.NaN; obj.Quota_GammaCash_wLower = gammaCashSettings?.WarningLowerLimit ?? double.NaN; var vegaSettings = settings.Where(O => O.IsValid && O.Status == QuotaSettingApprovalStatus.Valid && O.QuotaRange == 0 && O.QuotaIndex == "Vega").FirstOrDefault()?.Clone(); vegaSettings?.ConvertToAbs(valuedateBLL.SystemDate.BusinessAvailableFunds ?? 0); obj.Quota_Vega_Upper = vegaSettings?.QuotaUpperLimit ?? double.NaN; obj.Quota_Vega_Lower = vegaSettings?.QuotaLowerLimit ?? double.NaN; obj.Quota_Vega_wUpper = vegaSettings?.WarningUpperLimit ?? double.NaN; obj.Quota_Vega_wLower = vegaSettings?.WarningLowerLimit ?? double.NaN; var vegaCashSettings = settings.Where(O => O.IsValid && O.Status == QuotaSettingApprovalStatus.Valid && O.QuotaRange == 0 && O.QuotaIndex == "Vega金额").FirstOrDefault()?.Clone(); vegaCashSettings?.ConvertToAbs(valuedateBLL.SystemDate.BusinessAvailableFunds ?? 0); obj.Quota_VegaCash_Upper = vegaCashSettings?.QuotaUpperLimit ?? double.NaN; obj.Quota_VegaCash_Lower = vegaCashSettings?.QuotaLowerLimit ?? double.NaN; obj.Quota_VegaCash_wUpper = vegaCashSettings?.WarningUpperLimit ?? double.NaN; obj.Quota_VegaCash_wLower = vegaCashSettings?.WarningLowerLimit ?? double.NaN; var concentrationSettings = settings.Where(O => O.IsValid && O.Status == QuotaSettingApprovalStatus.Valid && O.QuotaRange == 0 && O.QuotaIndex == "集中度").FirstOrDefault()?.Clone(); obj.Quota_Concentration_Upper = concentrationSettings?.QuotaUpperLimit ?? double.NaN; obj.Quota_Concentration_Lower = concentrationSettings?.QuotaLowerLimit ?? double.NaN; obj.Quota_Concentration_wUpper = concentrationSettings?.WarningUpperLimit ?? double.NaN; obj.Quota_Concentration_wLower = concentrationSettings?.WarningLowerLimit ?? double.NaN; var vaRSettings = settings.Where(O => O.IsValid && O.Status == QuotaSettingApprovalStatus.Valid && O.QuotaRange == 0 && O.QuotaIndex == "VaR值").FirstOrDefault()?.Clone(); obj.Quota_VaR_Upper = vaRSettings?.QuotaUpperLimit ?? double.NaN; obj.Quota_VaR_Lower = vaRSettings?.QuotaLowerLimit ?? double.NaN; obj.Quota_VaR_wUpper = vaRSettings?.WarningUpperLimit ?? double.NaN; obj.Quota_VaR_wLower = vaRSettings?.WarningLowerLimit ?? double.NaN; var thetaSettings = settings.Where(O => O.IsValid && O.Status == QuotaSettingApprovalStatus.Valid && O.QuotaRange == 0 && O.QuotaIndex == "Theta").FirstOrDefault()?.Clone(); obj.Quota_Theta_Upper = thetaSettings?.QuotaUpperLimit ?? double.NaN; obj.Quota_Theta_Lower = thetaSettings?.QuotaLowerLimit ?? double.NaN; obj.Quota_Theta_wUpper = thetaSettings?.WarningUpperLimit ?? double.NaN; obj.Quota_Theta_wLower = thetaSettings?.WarningLowerLimit ?? double.NaN; var positionPnlSettings = settings.Where(O => O.IsValid && O.Status == QuotaSettingApprovalStatus.Valid && O.QuotaRange == 0 && O.QuotaIndex == "浮动盈亏").FirstOrDefault()?.Clone(); obj.Quota_PositionPnl_Upper = positionPnlSettings?.QuotaUpperLimit ?? double.NaN; obj.Quota_PositionPnl_Lower = positionPnlSettings?.QuotaLowerLimit ?? double.NaN; obj.Quota_PositionPnl_wUpper = positionPnlSettings?.WarningUpperLimit ?? double.NaN; obj.Quota_PositionPnl_wLower = positionPnlSettings?.WarningLowerLimit ?? double.NaN; var totalPnlSettings = settings.Where(O => O.IsValid && O.Status == QuotaSettingApprovalStatus.Valid && O.QuotaRange == 0 && O.QuotaIndex == "本年累计盈亏").FirstOrDefault()?.Clone(); obj.Quota_ThisYearTotalPnl_Upper = totalPnlSettings?.QuotaUpperLimit ?? double.NaN; obj.Quota_ThisYearTotalPnl_Lower = totalPnlSettings?.QuotaLowerLimit ?? double.NaN; obj.Quota_ThisYearTotalPnl_wUpper = totalPnlSettings?.WarningUpperLimit ?? double.NaN; obj.Quota_ThisYearTotalPnl_wLower = totalPnlSettings?.WarningLowerLimit ?? double.NaN; }); var allSetting = QueryCurrentQuotaSetting(QuotaTypeEnum.GLOBAL_ALL); allSetting = MargeQuotaSetting(allSetting, 0, 0); var swapSetting = QueryCurrentQuotaSetting(QuotaTypeEnum.GLOBAL_SWAP); swapSetting = MargeQuotaSetting(swapSetting, 0, 0); var swap = new QuotaMonitor_Global() { ParentKey = "场外", BusinessType = "互换" }; setValue(swap, swapSetting); var positionList = new List>(); var gloabPnl = GetTradePositionPnl(); if (dict.ContainsKey("互换")) { var swapPositionList = dict["互换"].Select(O => new KeyValuePair(O.t, O.risk)); swap.TotalPnL = swapPositionList.Sum(O => O.Value.PositionPnl.Normalize()); if (PS.Config.Is国信金阳) { swap.PositionPnl = swapPositionList.Sum(O => O.Value.PositionPnl.Normalize() < 0 ? O.Value.PositionPnl.Normalize() : 0); } else { swap.PositionPnl = swapPositionList.Sum(O => O.Value.PositionPnl.Normalize()); } positionList.AddRange(swapPositionList); } swap.DeltaNetExposure = double.NaN; var checkPosiList = GetCheckPosiList(); var posiStockEqvNotional = checkPosiList.Sum(s => s.Pv); var underly = new QuotaMonitor_Global() { ParentKey = "场外", BusinessType = "标的交易", PositionPnl = gloabPnl }; var unTrade = new QuotaMonitor_Global() { ParentKey = "场外", BusinessType = "未簿记合约", StockEqvNotional = Convert.ToDouble(posiStockEqvNotional) - swap.StockEqvNotional.Normalize() }; var all = new QuotaMonitor_Global() { BusinessType = "全局", StockEqvNotional = Convert.ToDouble(posiStockEqvNotional), PositionPnl = underly.PositionPnl + swap.PositionPnl }; var list = new List { swap, underly, unTrade, all }; for (var i = 0; i < list.Count; i++) { list[i].id = -1 - i; } return list; } private static void GuangFaOnOptionHandleGammaAndTheta(trade t, realtime_trade_risk risk) { if (t.ExerciseDate.HasValue) { DateTime dateTime = t.ExerciseDate.Value; for (var i = 0; i < 4; i++) { dateTime = QdpCalendarHelper.GetNonHolidayDefore(dateTime.AddDays(-1)); } if (valuedateBLL.ValueDate >= dateTime && t.ExerciseDate.Value >= valuedateBLL.ValueDate) { risk.GammaCash = 0; risk.Gamma = 0; risk.Theta = 0; } } } private static void GuangFaOnExchangeHandleGammaAndTheta(IEnumerable hedgeList) { if (PS.Config.Is广发商贸) { foreach (var item in hedgeList) { if (item.ExerciseDate.HasValue) { var maturityDate = item.ExerciseDate.Value; DateTime? dateTime = maturityDate; for (var i = 0; i < 4; i++) { dateTime = QdpCalendarHelper.GetNonHolidayDefore(dateTime.Value.AddDays(-1)); } if (valuedateBLL.ValueDate >= dateTime && maturityDate >= valuedateBLL.ValueDate) { item.GammaCash = 0; item.Gamma = 0; item.Theta = 0; } } } } } /// /// 查询限额监控-客户数据 /// /// /// public List QueryClientFromCalc(QuotaMonitorReq req) { 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)); var stockEqvNotionalSettings = temp.Where(O => O.QuotaIndex == "名义本金" && O.QuotaRange == 0).FirstOrDefault()?.Clone(); var stockEqvNotionalSettingsClient = temp.Where(O => O.QuotaIndex == "名义本金" && O.QuotaRange == obj.ClientId).FirstOrDefault()?.Clone(); if (stockEqvNotionalSettingsClient != null) { stockEqvNotionalSettings = stockEqvNotionalSettingsClient.Clone(); } stockEqvNotionalSettings?.ConvertToAbs(obj.ProductScale.GetValueOrDefault()); obj.Quota_StockEqvNotional_Upper = stockEqvNotionalSettings?.QuotaUpperLimit ?? double.NaN; 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 list = new List(); var setting = QueryCurrentQuotaSetting(QuotaTypeEnum.CLIENT); var creditList = DbContext.credit.Where(t => t.ProcessStatus == "已审批" && (!t.CreditDeadLine.HasValue || t.CreditDeadLine >= SystemValueDate) && (!t.CreditStartDate.HasValue || t.CreditStartDate <= SystemValueDate)) .ToList(); var creditExposureDict = new Dictionary(); var todayClientBalance = new RealTimeClientBanlanceService(OptUserInfo.SystemUser) .GetBanlances(clientIds, SystemValueDate); foreach (var item in todayClientBalance) { var c = new QuotaMonitor_Client(); var client = DataCacheProvider.GetClientDataSource().GetData(item.ClientId); var subclientList = getSubclientId(client.id); c.ClientId = item.ClientId; c.ClientName = client.Name; c.ClientNumber = client.Number; c.AvailableFund = item.AvailableAmount; c.Credit = item.TotalCredit; c.PayableFund = item.PayableFund; c.PositionMargin = item.MinusPayableMargin; c.StockEqvNotional = item.PositionNotionalPrincipal; 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; c.CreditExposure = creditExposureDict.ContainsKey(item.ClientId) ? creditExposureDict[item.ClientId] : null; list.Add(c); } var totalProductScale = list.Sum(O => O.ProductScale) ?? 0; foreach (var item in list) { setValue(item, setting); } var totalscale = list.Where(O => O.StockEqvNotionalScale.IsNormalize()).Select(O => O.StockEqvNotionalScale); var total = new QuotaMonitor_Client() { ClientNumber = "合计", StockEqvNotionalScale = totalscale.Any() ? totalscale.Sum() : double.NaN, StockEqvNotional = list.Sum(O => O.StockEqvNotional), PositionMargin = list.Sum(O => O.PositionMargin), AvailableFund = list.Sum(O => O.AvailableFund), Credit = list.Sum(O => O.Credit), PayableFund = list.Sum(O => O.PayableFund), ProductScale = list.Sum(O => O.ProductScale), HoldingFund = list.Sum(O => O.HoldingFund), ClientId = 0 }; total.HoldingRate = (total.HoldingFund / total.ProductScale).Normalize(); total.AvailableStockEqvNotional = total.StockEqvNotionalScale - total.StockEqvNotional; setValue(total, setting); list.Add(total); return list; } /// /// 查询限额监控-单笔交易数据 /// /// /// public List QueryTradeFromCalc(QuotaMonitorReq req) { var result = new List(); req.sidx = "TradeNumber"; req.sord = "asc"; 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)); } if (req.ClientId != 0) { predicate = predicate.And(O => req.ClientId == O.ClientId); } if (req.VarietyId != 0) {//品种的筛选方式是取出该品种下所有标的后和指定的标的条件取交集 var unIds = DataCacheProvider.GetUnderlyingDataSource().AsQueryable().Where(O => O.UnderlyingTypeId == req.VarietyId).Select(O => O.id).ToList(); predicate = predicate.And(O => unIds.Contains(O.UnderlyingId)); } if (req.UnderlyingId?.Count > 0) { predicate = predicate.And(O => req.UnderlyingId.Contains(O.UnderlyingId)); } if (!req.TradeType.IsNullOrWhiteSpace()) { predicate = predicate.And(O => req.TradeType == O.TradeType || req.TradeType == O.StructureType); } if (req.AssetId != 0) { predicate = predicate.And(O => req.AssetId == O.AssetId); } if (req.TradeDateStart != default) { predicate = predicate.And(O => req.TradeDateStart <= O.TradeDate); } if (req.TradeDateEnd != default) { var date = req.TradeDateEnd; if (date < DateTime.MaxValue.Date) { date = date.AddDays(1); } predicate = predicate.And(O => date >= O.TradeDate); } if (!req.TradeStatus.IsNullOrWhiteSpace()) { predicate = predicate.And(O => req.TradeStatus == O.TradeStatus); } 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 new QuotaMonitor_TradeDto() { trade = t, ClientId = t.ClientId, TradeNumber = t.TradeNumber, ClientName = t.ClientName, StockEqvNotional = t.StockEqvNotional, }; var list = posiQuery.ToList(); var setValue = new Action>((obj, settings) => { var temp = settings.Where(O => O.IsValid && O.Status == QuotaSettingApprovalStatus.Valid && O.QuotaRange == 0); var stockEqvNotionalSetting = temp.Where(O => O.QuotaIndex == "名义本金").FirstOrDefault()?.Clone(); stockEqvNotionalSetting?.ConvertToAbs(valuedateBLL.SystemDate.BusinessTotalScale ?? 0); obj.Quota_StockEqvNotional_Upper = stockEqvNotionalSetting?.QuotaUpperLimit ?? double.NaN; 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 swapPercentSetting = temp.Where(O => O.QuotaIndex == "互换价格偏离比例").FirstOrDefault()?.Clone(); obj.Quota_SwapPercent_Upper = swapPercentSetting?.QuotaUpperLimit ?? double.NaN; obj.Quota_SwapPercent_Lower = swapPercentSetting?.QuotaLowerLimit ?? double.NaN; obj.Quota_SwapPercent_wUpper = swapPercentSetting?.WarningUpperLimit ?? double.NaN; obj.Quota_SwapPercent_wLower = swapPercentSetting?.WarningLowerLimit ?? double.NaN; }); var setting = QueryCurrentQuotaSetting(QuotaTypeEnum.TRADE); setting = MargeQuotaSetting(setting, 0, 0); var ccrDict = new Dictionary(); foreach (var item in list) { setValue(item, setting); 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); } var total = new QuotaMonitor_Trade() { TradeNumber = "合计", StockEqvNotional = result.Sum(O => O.StockEqvNotional), DeltaCash = result.Sum(O => O.DeltaCash), GammaCash = result.Sum(O => O.GammaCash), 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), }; result.Add(total); return result; } /// /// 查询限额监控-资产汇总数据 /// /// /// public List QueryAssetFromCalc(QuotaMonitorReq req) { var result = new List(); 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.TradeType != "现金流交易" && t.UnderlyingCode != null && ConsTrade.PositionTradeStatusList.Contains(t.TradeStatus) && t.ValidState != "InValid" && t.ExerciseDate >= SystemValueDate select new { t, risk }).ToArray(); if (PS.Config.Is广发商贸) { foreach (var q in query) { GuangFaOnOptionHandleGammaAndTheta(q.t, q.risk); } } var stockEqvNotionalWhere = DbContext.trade.Where(t => t.TradeType != "现金流交易" && t.UnderlyingCode != null && ConsTrade.PositionTradeStatusList.Contains(t.TradeStatus) && t.ValidState != "InValid" && t.ExerciseDate >= SystemValueDate); 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.t.id)).ToArray(); stockEqvNotionalWhere = stockEqvNotionalWhere.Where(p => tradeTagIdQuery.Contains(p.id)); } if (req.UnderlyingId != null && req.UnderlyingId.Count > 0) { query = query.Where(O => req.UnderlyingId.Contains(O.t.UnderlyingId)).ToArray(); } var quotaList = from t in query group new { t.risk, t.t } by t.t.UnderlyingCode into info select new QuotaMonitor_AssetDto() { UnderlyingId = info.FirstOrDefault().t.UnderlyingId, UnderlyingCode = info.Key, DeltaCash = info.Sum(O => O.risk.DeltaCash ?? 0), GammaCash = info.Sum(O => O.risk.GammaCash ?? 0), Vega = info.Sum(O => O.risk.Vega ?? 0), VegaCash = info.Sum(O => O.risk.VegaCash ?? 0), }; var stockEqvNotionalQuery = (from risk in DbContext.realtime_trade_risk join t in stockEqvNotionalWhere on risk.TradeId equals t.id join t1 in DbContext.trade on t.ParentTradeId == 0 || (t.ParentTradeId > 0 && t.TradeType == "收益互换") ? t.id : t.ParentTradeId equals t1.id where risk.VolType == "持仓" group t1 by t1.id into info select new { info.FirstOrDefault().UnderlyingId, info.FirstOrDefault().StockEqvNotional }).ToArray() .GroupBy(O => O.UnderlyingId).ToDictionary(K => K.Key, V => V.Sum(O => O.StockEqvNotional)); var setValue = new Action, double>((obj, settings, tStockEqvNotional) => { obj.Concentration = obj.StockEqvNotional / tStockEqvNotional; var stockEqvNotionalSetting = settings.Where(O => O.QuotaIndex == "名义本金").FirstOrDefault()?.Clone(); stockEqvNotionalSetting?.ConvertToAbs(valuedateBLL.SystemDate.BusinessTotalScale ?? 0); obj.Quota_StockEqvNotional_Upper = stockEqvNotionalSetting?.QuotaUpperLimit ?? double.NaN; 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 deltaExposureSetting = settings.Where(O => O.QuotaIndex == "Delta敞口(多空比)").FirstOrDefault()?.Clone(); obj.Quota_DeltaExposure_Upper = deltaExposureSetting?.QuotaUpperLimit ?? double.NaN; obj.Quota_DeltaExposure_Lower = deltaExposureSetting?.QuotaLowerLimit ?? double.NaN; obj.Quota_DeltaExposure_wUpper = deltaExposureSetting?.WarningUpperLimit ?? double.NaN; obj.Quota_DeltaExposure_wLower = deltaExposureSetting?.WarningLowerLimit ?? double.NaN; var deltaNetExposureSetting = settings.Where(O => O.QuotaIndex == "Delta净敞口比例").FirstOrDefault()?.Clone(); obj.Quota_DeltaNetExposure_Upper = deltaNetExposureSetting?.QuotaUpperLimit ?? double.NaN; obj.Quota_DeltaNetExposure_Lower = deltaNetExposureSetting?.QuotaLowerLimit ?? double.NaN; obj.Quota_DeltaNetExposure_wUpper = deltaNetExposureSetting?.WarningUpperLimit ?? double.NaN; obj.Quota_DeltaNetExposure_wLower = deltaNetExposureSetting?.WarningLowerLimit ?? double.NaN; var deltaCashSetting = settings.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 = settings.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 = settings.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 = settings.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 concentrationSetting = settings.Where(O => O.QuotaIndex == "集中度").FirstOrDefault()?.Clone(); obj.Quota_Concentration_Upper = concentrationSetting?.QuotaUpperLimit ?? double.NaN; obj.Quota_Concentration_Lower = concentrationSetting?.QuotaLowerLimit ?? double.NaN; obj.Quota_Concentration_wUpper = concentrationSetting?.WarningUpperLimit ?? double.NaN; obj.Quota_Concentration_wLower = concentrationSetting?.WarningLowerLimit ?? double.NaN; var positionPnlSetting = settings.Where(O => O.QuotaIndex == "浮动盈亏").FirstOrDefault()?.Clone(); obj.Quota_PositionPnl_Upper = concentrationSetting?.QuotaUpperLimit ?? double.NaN; obj.Quota_PositionPnl_Lower = concentrationSetting?.QuotaLowerLimit ?? double.NaN; obj.Quota_PositionPnl_wUpper = concentrationSetting?.WarningUpperLimit ?? double.NaN; obj.Quota_PositionPnl_wLower = concentrationSetting?.WarningLowerLimit ?? double.NaN; }); var totalStockEqvNotional = stockEqvNotionalQuery.Values.Sum(); var riskDict = query.ToDictionary(K => K.t, V => V.risk); var hedgeList = CalculateExchangeTrade(); GuangFaOnExchangeHandleGammaAndTheta(hedgeList); var equityList = new KeyValuePair, QuotaMonitor_Asset>( new List(), new QuotaMonitor_Asset() { DeltaCash = 0, GammaCash = 0, Vega = 0, VegaCash = 0 }); var commodityList = new KeyValuePair, QuotaMonitor_Asset>( new List(), new QuotaMonitor_Asset() { DeltaCash = 0, GammaCash = 0, Vega = 0, VegaCash = 0 }); var preciousMetalsList = new KeyValuePair, QuotaMonitor_Asset>( new List(), new QuotaMonitor_Asset() { DeltaCash = 0, GammaCash = 0, Vega = 0, VegaCash = 0 }); var codeList = new List(); var deltaDict = new Dictionary() { //double[]{ (场内+场外)多, (场内+场外)空, 场外净值 } {"贵金属",new double[]{ 0, 0, 0} }, {"商品",new double[]{ 0, 0, 0 } }, {"权益",new double[]{ 0, 0, 0 } }, }; foreach (var item in quotaList) { item.StockEqvNotional = stockEqvNotionalQuery.ContainsKey(item.UnderlyingId) ? stockEqvNotionalQuery[item.UnderlyingId] : 0; GetDeltaExposure(riskDict, hedgeList, item.UnderlyingCode, out var longDelta, out var shortDelta, out var allLongDelta, out var allShortDelta); var hedgeRisks = hedgeList.Where(O => O.UnderlyingCode == item.UnderlyingCode).ToList(); var um = DataCacheProvider.GetUnderlyingDataSource().GetData(item.UnderlyingCode); if (um.UnderlyingCode.ToUpper().StartsWith("AU") || um.UnderlyingCode.ToUpper().StartsWith("AG")) { item.ParentKey = "贵金属"; preciousMetalsList.Key.Add(item); preciousMetalsList.Value.DeltaCash += hedgeRisks.Sum(O => O.DeltaCash.Normalize()); preciousMetalsList.Value.GammaCash += hedgeRisks.Sum(O => O.GammaCash.Normalize()); preciousMetalsList.Value.Vega += hedgeRisks.Sum(O => O.Vega.Normalize()); preciousMetalsList.Value.VegaCash += hedgeRisks.Sum(O => (O.Vega * O.SettlePrice).Normalize()); } else if (um.IsCommodity()) { item.ParentKey = "商品"; commodityList.Key.Add(item); commodityList.Value.DeltaCash += hedgeRisks.Sum(O => O.DeltaCash.Normalize()); commodityList.Value.GammaCash += hedgeRisks.Sum(O => O.GammaCash.Normalize()); commodityList.Value.Vega += hedgeRisks.Sum(O => O.Vega.Normalize()); commodityList.Value.VegaCash += hedgeRisks.Sum(O => (O.Vega * O.SettlePrice).Normalize()); } else { item.ParentKey = "权益"; equityList.Key.Add(item); equityList.Value.DeltaCash += hedgeRisks.Sum(O => O.DeltaCash.Normalize()); equityList.Value.GammaCash += hedgeRisks.Sum(O => O.GammaCash.Normalize()); equityList.Value.Vega += hedgeRisks.Sum(O => O.Vega.Normalize()); equityList.Value.VegaCash += hedgeRisks.Sum(O => (O.Vega * O.SettlePrice).Normalize()); } deltaDict[item.ParentKey][0] += allLongDelta; deltaDict[item.ParentKey][1] += allShortDelta; deltaDict[item.ParentKey][2] += longDelta + shortDelta; codeList.Add(item.UnderlyingCode); } var hedgeDict = hedgeList.Where(O => !codeList.Contains(O.UnderlyingCode)).GroupBy(O => O.UnderlyingCode).ToDictionary(K => K.Key, V => V.ToList()); foreach (var item in hedgeDict) { var obj = new QuotaMonitor_Asset(); var um = DataCacheProvider.GetUnderlyingDataSource().GetData(item.Key); obj.UnderlyingId = um.id; obj.UnderlyingCode = item.Key; obj.StockEqvNotional = 0; if (um.UnderlyingCode.ToUpper().StartsWith("AU") || um.UnderlyingCode.ToUpper().StartsWith("AG")) { obj.ParentKey = "贵金属"; preciousMetalsList.Key.Add(obj); preciousMetalsList.Value.DeltaCash += item.Value.Sum(O => O.DeltaCash.Normalize()); preciousMetalsList.Value.GammaCash += item.Value.Sum(O => O.GammaCash.Normalize()); preciousMetalsList.Value.Vega += item.Value.Sum(O => O.Vega.Normalize()); preciousMetalsList.Value.VegaCash += item.Value.Sum(O => (O.Vega * O.SettlePrice).Normalize()); } else if (um.IsCommodity()) { obj.ParentKey = "商品"; commodityList.Key.Add(obj); commodityList.Value.DeltaCash += item.Value.Sum(O => O.DeltaCash.Normalize()); commodityList.Value.GammaCash += item.Value.Sum(O => O.GammaCash.Normalize()); commodityList.Value.Vega += item.Value.Sum(O => O.Vega.Normalize()); commodityList.Value.VegaCash += item.Value.Sum(O => (O.Vega * O.SettlePrice).Normalize()); } else { obj.ParentKey = "权益"; equityList.Key.Add(obj); equityList.Value.DeltaCash += item.Value.Sum(O => O.DeltaCash.Normalize()); equityList.Value.GammaCash += item.Value.Sum(O => O.GammaCash.Normalize()); equityList.Value.Vega += item.Value.Sum(O => O.Vega.Normalize()); equityList.Value.VegaCash += item.Value.Sum(O => (O.Vega * O.SettlePrice).Normalize()); } if (GetHedgeDeltaExposure(hedgeList, obj.ParentKey, out var longDelta, out var shortDelta)) { if (!deltaDict.ContainsKey(item.Key)) { //double[]{ (场内+场外)多, (场内+场外)空, 场外净值 } deltaDict[obj.ParentKey] = new double[] { 0, 0, 0 }; } deltaDict[obj.ParentKey][0] += longDelta; deltaDict[obj.ParentKey][1] += shortDelta; } } var equity = new QuotaMonitor_Asset() { UnderlyingCode = "权益", StockEqvNotional = equityList.Key.Sum(O => O.StockEqvNotional.Normalize()), DeltaExposure = equityList.Key.Sum(O => O.DeltaExposure.Normalize()), DeltaNetExposure = equityList.Key.Sum(O => O.DeltaNetExposure.Normalize()), DeltaCash = equityList.Key.Sum(O => O.DeltaCash.Normalize()) + equityList.Value.DeltaCash.Normalize(), GammaCash = equityList.Key.Sum(O => O.GammaCash.Normalize()) + equityList.Value.GammaCash.Normalize(), Vega = equityList.Key.Sum(O => O.Vega.Normalize()) + equityList.Value.Vega.Normalize(), VegaCash = equityList.Key.Sum(O => O.VegaCash.Normalize()) + equityList.Value.VegaCash.Normalize(), Concentration = equityList.Key.Sum(O => O.Concentration.Normalize()), UnderlyingId = 0, }; equity.DeltaExposure = Math.Abs(deltaDict[equity.UnderlyingCode][0] / deltaDict[equity.UnderlyingCode][1]); equity.DeltaNetExposure = (deltaDict[equity.UnderlyingCode][0] + deltaDict[equity.UnderlyingCode][1]) / -deltaDict[equity.UnderlyingCode][2]; var equitySetting = QueryCurrentQuotaSetting(QuotaTypeEnum.ASSET_EQUITY); equitySetting = MargeQuotaSetting(equitySetting, 0, 0); setValue(equity, equitySetting, totalStockEqvNotional); var preciousMetals = new QuotaMonitor_Asset() { UnderlyingCode = "贵金属", StockEqvNotional = preciousMetalsList.Key.Sum(O => O.StockEqvNotional.Normalize()), DeltaExposure = preciousMetalsList.Key.Sum(O => O.DeltaExposure.Normalize()), DeltaNetExposure = preciousMetalsList.Key.Sum(O => O.DeltaNetExposure.Normalize()), DeltaCash = preciousMetalsList.Key.Sum(O => O.DeltaCash.Normalize()) + preciousMetalsList.Value.DeltaCash.Normalize(), GammaCash = preciousMetalsList.Key.Sum(O => O.GammaCash.Normalize()) + preciousMetalsList.Value.GammaCash.Normalize(), Vega = preciousMetalsList.Key.Sum(O => O.Vega.Normalize()) + preciousMetalsList.Value.Vega.Normalize(), VegaCash = preciousMetalsList.Key.Sum(O => O.VegaCash.Normalize()) + preciousMetalsList.Value.VegaCash.Normalize(), Concentration = preciousMetalsList.Key.Sum(O => O.Concentration.Normalize()), UnderlyingId = 0, }; preciousMetals.DeltaExposure = Math.Abs(deltaDict[preciousMetals.UnderlyingCode][0] / deltaDict[preciousMetals.UnderlyingCode][1]); preciousMetals.DeltaNetExposure = (deltaDict[preciousMetals.UnderlyingCode][0] + deltaDict[preciousMetals.UnderlyingCode][1]) / -deltaDict[preciousMetals.UnderlyingCode][2]; var preciousMetalsSetting = QueryCurrentQuotaSetting(QuotaTypeEnum.ASSET_PRECIOUSMETALS); preciousMetalsSetting = MargeQuotaSetting(preciousMetalsSetting, 0, 0); setValue(preciousMetals, preciousMetalsSetting, totalStockEqvNotional); var commodity = new QuotaMonitor_Asset() { UnderlyingCode = "商品", StockEqvNotional = commodityList.Key.Sum(O => O.StockEqvNotional.Normalize()), DeltaExposure = commodityList.Key.Sum(O => O.DeltaExposure.Normalize()), DeltaNetExposure = commodityList.Key.Sum(O => O.DeltaNetExposure.Normalize()), DeltaCash = commodityList.Key.Sum(O => O.DeltaCash.Normalize()) + commodityList.Value.DeltaCash.Normalize(), GammaCash = commodityList.Key.Sum(O => O.GammaCash.Normalize()) + commodityList.Value.GammaCash.Normalize(), Vega = commodityList.Key.Sum(O => O.Vega.Normalize()) + commodityList.Value.Vega.Normalize(), VegaCash = commodityList.Key.Sum(O => O.VegaCash.Normalize()) + commodityList.Value.VegaCash.Normalize(), Concentration = commodityList.Key.Sum(O => O.Concentration.Normalize()), UnderlyingId = 0, }; commodity.DeltaExposure = Math.Abs(deltaDict[commodity.UnderlyingCode][0] / deltaDict[commodity.UnderlyingCode][1]); commodity.DeltaNetExposure = (deltaDict[commodity.UnderlyingCode][0] + deltaDict[commodity.UnderlyingCode][1]) / -deltaDict[commodity.UnderlyingCode][2]; var commoditySetting = QueryCurrentQuotaSetting(QuotaTypeEnum.ASSET_COMMODITY); commoditySetting = MargeQuotaSetting(commoditySetting, 0, 0); setValue(commodity, commoditySetting, totalStockEqvNotional); result.Add(equity); result.Add(preciousMetals); result.Add(commodity); return result; } /// /// 查询限额监控-品种汇总数据 /// /// /// public List QueryVarietyFromCalc(QuotaMonitorReq req) { var result = new List(); 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.TradeType != "现金流交易" && t.UnderlyingCode != null && ConsTrade.PositionTradeStatusList.Contains(t.TradeStatus) && t.ValidState != "InValid" && t.ExerciseDate >= SystemValueDate select new { t, risk }).ToArray(); if (PS.Config.Is广发商贸) { foreach (var q in query) { GuangFaOnOptionHandleGammaAndTheta(q.t, q.risk); } } if (req.UnderlyingId != null && req.UnderlyingId.Count > 0) { query = query.Where(O => req.UnderlyingId.Contains(O.t.UnderlyingId)).ToArray(); } var stockEqvNotionalWhere = DbContext.trade.Where(t => t.TradeType != "现金流交易" && t.UnderlyingCode != null && ConsTrade.PositionTradeStatusList.Contains(t.TradeStatus) && t.ValidState != "InValid" && t.ExerciseDate >= SystemValueDate); 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.t.id)).ToArray(); stockEqvNotionalWhere = stockEqvNotionalWhere.Where(p => tradeTagIdQuery.Contains(p.id)); } var quotaList = from t in query group new { t.risk, t.t } by t.t.UnderlyingCode into info select new QuotaMonitor_VarietyDto() { UnderlyingId = info.FirstOrDefault().t.UnderlyingId, UnderlyingCode = info.Key, DeltaCash = info.Sum(O => O.risk.DeltaCash ?? 0), GammaCash = info.Sum(O => O.risk.GammaCash ?? 0), Vega = info.Sum(O => O.risk.Vega ?? 0), VegaCash = info.Sum(O => O.risk.VegaCash ?? 0), Theta = info.Sum(O => O.risk.Theta ?? 0), PositionPnl = info.Sum(O => O.risk.PositionPnl ?? 0) }; var stockEqvNotionalQuery = (from risk in DbContext.realtime_trade_risk join t in stockEqvNotionalWhere on risk.TradeId equals t.id join t1 in DbContext.trade on t.ParentTradeId == 0 ? t.id : t.ParentTradeId equals t1.id where risk.VolType == "持仓" group t1 by t1.id into info select new { info.FirstOrDefault().UnderlyingId, info.FirstOrDefault().StockEqvNotional }).ToArray() .GroupBy(O => O.UnderlyingId).ToDictionary(K => K.Key, V => V.Sum(O => O.StockEqvNotional)); var setValue = new Action, double>((obj, settings, tStockEqvNotional) => { obj.Concentration = obj.StockEqvNotional / tStockEqvNotional; var stockEqvNotionalSetting = settings.Where(O => O.QuotaIndex == "名义本金").FirstOrDefault()?.Clone(); stockEqvNotionalSetting?.ConvertToAbs(valuedateBLL.SystemDate.BusinessTotalScale ?? 0); obj.Quota_StockEqvNotional_Upper = stockEqvNotionalSetting?.QuotaUpperLimit ?? double.NaN; 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 deltaExposureSetting = settings.Where(O => O.QuotaIndex == "Delta敞口(多空比)").FirstOrDefault()?.Clone(); obj.Quota_DeltaExposure_Upper = deltaExposureSetting?.QuotaUpperLimit ?? double.NaN; obj.Quota_DeltaExposure_Lower = deltaExposureSetting?.QuotaLowerLimit ?? double.NaN; obj.Quota_DeltaExposure_wUpper = deltaExposureSetting?.WarningUpperLimit ?? double.NaN; obj.Quota_DeltaExposure_wLower = deltaExposureSetting?.WarningLowerLimit ?? double.NaN; var deltaNetExposureSetting = settings.Where(O => O.QuotaIndex == "Delta净敞口比例").FirstOrDefault()?.Clone(); obj.Quota_DeltaNetExposure_Upper = deltaNetExposureSetting?.QuotaUpperLimit ?? double.NaN; obj.Quota_DeltaNetExposure_Lower = deltaNetExposureSetting?.QuotaLowerLimit ?? double.NaN; obj.Quota_DeltaNetExposure_wUpper = deltaNetExposureSetting?.WarningUpperLimit ?? double.NaN; obj.Quota_DeltaNetExposure_wLower = deltaNetExposureSetting?.WarningLowerLimit ?? double.NaN; var deltaCashSetting = settings.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 = settings.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 = settings.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 = settings.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 concentrationSetting = settings.Where(O => O.QuotaIndex == "集中度").FirstOrDefault()?.Clone(); obj.Quota_Concentration_Upper = concentrationSetting?.QuotaUpperLimit ?? double.NaN; obj.Quota_Concentration_Lower = concentrationSetting?.QuotaLowerLimit ?? double.NaN; obj.Quota_Concentration_wUpper = concentrationSetting?.WarningUpperLimit ?? double.NaN; obj.Quota_Concentration_wLower = concentrationSetting?.WarningLowerLimit ?? double.NaN; var positionPnlSetting = settings.Where(O => O.QuotaIndex == "浮动盈亏").FirstOrDefault()?.Clone(); obj.Quota_PositionPnl_Upper = positionPnlSetting?.QuotaUpperLimit ?? double.NaN; obj.Quota_PositionPnl_Lower = positionPnlSetting?.QuotaLowerLimit ?? double.NaN; obj.Quota_PositionPnl_wUpper = positionPnlSetting?.WarningUpperLimit ?? double.NaN; obj.Quota_PositionPnl_wLower = positionPnlSetting?.WarningLowerLimit ?? double.NaN; var thetaSettings = settings.Where(O => O.QuotaIndex == "Theta").FirstOrDefault()?.Clone(); obj.Quota_Theta_Upper = thetaSettings?.QuotaUpperLimit ?? double.NaN; obj.Quota_Theta_Lower = thetaSettings?.QuotaLowerLimit ?? double.NaN; obj.Quota_Theta_wUpper = thetaSettings?.WarningUpperLimit ?? double.NaN; obj.Quota_Theta_wLower = thetaSettings?.WarningLowerLimit ?? double.NaN; }); var totalStockEqvNotional = stockEqvNotionalQuery.Values.Sum(); var riskDict = query.ToDictionary(K => K.t, V => V.risk); var hedgeList = CalculateExchangeTrade(); GuangFaOnExchangeHandleGammaAndTheta(hedgeList); var dict = new Dictionary, QuotaMonitor_Variety>>(); var codeList = new List(); //double[]{ (场内+场外)多, (场内+场外)空, 场外净值 } var deltaDict = new Dictionary(); foreach (var item in quotaList) { item.StockEqvNotional = stockEqvNotionalQuery.ContainsKey(item.UnderlyingId) ? stockEqvNotionalQuery[item.UnderlyingId] : 0; GetDeltaExposure(riskDict, hedgeList, item.UnderlyingCode, out var longDelta, out var shortDelta, out var allLongDelta, out var allShortDelta); var hedgeRisks = hedgeList.Where(O => O.UnderlyingCode == item.UnderlyingCode).ToList(); var um = DataCacheProvider.GetUnderlyingDataSource().GetData(item.UnderlyingCode); var key = /*um.CommodityCode ??*/ um.UnderlyingType; if (!deltaDict.ContainsKey(key)) { //double[]{ (场内+场外)多, (场内+场外)空, 场外净值 } deltaDict[key] = new double[] { 0, 0, 0 }; } deltaDict[key][0] += allLongDelta; deltaDict[key][1] += allShortDelta; deltaDict[key][2] += longDelta + shortDelta; if (!dict.ContainsKey(key)) { dict[key] = new KeyValuePair, QuotaMonitor_Variety>( new List(), new QuotaMonitor_Variety() { DeltaCash = 0, GammaCash = 0, Vega = 0, Theta = 0, VegaCash = 0 }); } dict[key].Key.Add(item); dict[key].Value.DeltaCash += hedgeRisks.Sum(O => O.DeltaCash.Normalize()); dict[key].Value.GammaCash += hedgeRisks.Sum(O => O.GammaCash.Normalize()); dict[key].Value.Vega += hedgeRisks.Sum(O => O.Vega.Normalize()); dict[key].Value.VegaCash += hedgeRisks.Sum(O => (O.Vega * O.SettlePrice).Normalize()); dict[key].Value.Theta += hedgeRisks.Sum(O => O.Theta.Normalize()); codeList.Add(item.UnderlyingCode); } var hedgeDict = hedgeList.Where(O => !codeList.Contains(O.UnderlyingCode)).GroupBy(O => O.UnderlyingCode).ToDictionary(K => K.Key, V => V.ToList()); foreach (var item in hedgeDict) { var obj = new QuotaMonitor_Variety(); var um = DataCacheProvider.GetUnderlyingDataSource().GetData(item.Key); obj.UnderlyingId = um.id; obj.UnderlyingCode = item.Key; obj.StockEqvNotional = 0; var hedgeRisks = hedgeList.Where(O => O.UnderlyingCode == item.Key).ToList(); var key = /*um.CommodityCode ??*/ um.UnderlyingType; //if (GetHedgeDeltaExposure(hedgeList, item.Key, out var longDelta, out var shortDelta)) //{ // if (!deltaDict.ContainsKey(key)) // { // //double[]{ (场内+场外)多, (场内+场外)空, 场外净值 } // deltaDict[key] = new double[] { 0, 0, 0 }; // } // deltaDict[key][0] += longDelta; // deltaDict[key][1] += shortDelta; //} if (!dict.ContainsKey(key)) { dict[key] = new KeyValuePair, QuotaMonitor_Variety>( new List(), new QuotaMonitor_Variety() { DeltaCash = 0, GammaCash = 0, Vega = 0, Theta = 0, VegaCash = 0 }); } dict[key].Key.Add(obj); dict[key].Value.DeltaCash += item.Value.Sum(O => O.DeltaCash.Normalize()); dict[key].Value.GammaCash += item.Value.Sum(O => O.GammaCash.Normalize()); dict[key].Value.Vega += item.Value.Sum(O => O.Vega.Normalize()); dict[key].Value.VegaCash += item.Value.Sum(O => (O.Vega * O.SettlePrice).Normalize()); dict[key].Value.Theta += item.Value.Sum(O => O.Theta.Normalize()); } foreach (var item in dict) { var obj = new QuotaMonitor_Variety() { UnderlyingCode = item.Key, StockEqvNotional = PS.Config.Is国信金阳 ? Math.Abs(item.Value.Key.Sum(O => O.StockEqvNotional.Normalize())) : item.Value.Key.Sum(O => O.StockEqvNotional.Normalize()), DeltaExposure = item.Value.Key.Sum(O => O.DeltaExposure.Normalize()), DeltaNetExposure = item.Value.Key.Sum(O => O.DeltaNetExposure.Normalize()), DeltaCash = item.Value.Key.Sum(O => O.DeltaCash.Normalize()) + item.Value.Value.DeltaCash.Normalize(), GammaCash = item.Value.Key.Sum(O => O.GammaCash.Normalize()) + item.Value.Value.GammaCash.Normalize(), Vega = item.Value.Key.Sum(O => O.Vega.Normalize()) + item.Value.Value.Vega.Normalize(), VegaCash = item.Value.Key.Sum(O => O.VegaCash.Normalize()) + item.Value.Value.VegaCash.Normalize(), Theta = item.Value.Key.Sum(O => O.Theta.Normalize()) + item.Value.Value.Theta.Normalize(), Concentration = item.Value.Key.Sum(O => O.Concentration.Normalize()), UnderlyingId = 0, PositionPnl = item.Value.Key.Sum(O => O.PositionPnl.Normalize()), }; if (deltaDict.ContainsKey(obj.UnderlyingCode)) { obj.DeltaExposure = Math.Abs(deltaDict[obj.UnderlyingCode][0] / deltaDict[obj.UnderlyingCode][1]); obj.DeltaNetExposure = (deltaDict[obj.UnderlyingCode][0] + deltaDict[obj.UnderlyingCode][1]) / -deltaDict[obj.UnderlyingCode][2]; } var varietySetting = QueryCurrentQuotaSetting(QuotaTypeEnum.VARIETY); varietySetting = MargeQuotaSetting(varietySetting, 0, 0); setValue(obj, varietySetting, totalStockEqvNotional); if (obj.DeltaCash == 0 && obj.GammaCash == 0 && obj.Vega == 0 && obj.Theta == 0 && obj.StockEqvNotional == 0) { continue; } result.Add(obj); } return result; } /// /// 查询限额监控-标的汇总数据 /// /// /// public List QueryUnderlyingFromCalc(QuotaMonitorReq req) { var result = new List(); using var bondDb = new BondOmsDBContext(); var clientPositions = bondDb.client_position.AsNoTracking().AsEnumerable(); 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) { QuotaMonitor_UnderlyingDto dto = new QuotaMonitor_UnderlyingDto(); dto.UnderlyingCode = item.Key; dto.StockEqvNotional = Math.Abs(item.Sum(s => s.StockEqvNotional * (s.PositionType == (int)PositionTypeFlag.Long ? 1 : -1))); qutoList.Add(dto); } var underlyingCodes = qutoList.Select(s => s.UnderlyingCode).ToList(); var underlyings = DbContext.underlying_manager.Where(x => underlyingCodes.Contains(x.UnderlyingCode)).AsNoTracking().ToList(); foreach (var item in qutoList) { QuotaMonitor_Underlying quotaMonitor = new QuotaMonitor_Underlying(); var um = underlyings.FirstOrDefault(f => f.UnderlyingCode == item.UnderlyingCode); quotaMonitor.UnderlyingCode = item.UnderlyingCode; quotaMonitor.StockEqvNotional = Math.Abs(item.StockEqvNotional ?? 0); quotaMonitor.UnderlyingId = um?.id ?? 0; if (um != null && um.IsBond()) { var bond = JsonHelper.Deserialize(um.ExJson); quotaMonitor.Circulation = Convert.ToDouble((bond.IssueSize * 100000000m) ?? 0); } result.Add(quotaMonitor); } if (req.UnderlyingId != null && req.UnderlyingId.Count > 0) { result = result.Where(O => req.UnderlyingId.Contains(O.UnderlyingId)).ToList(); } var setValue = new Action, double>((obj, settings, tStockEqvNotional) => { obj.Concentration = obj.Circulation == 0 ? double.NaN : obj.StockEqvNotional / obj.Circulation; var stockEqvNotionalSetting = settings.Where(O => O.QuotaIndex == "轧差名义本金" && O.QuotaRange == obj.UnderlyingId).FirstOrDefault()?.Clone(); if (stockEqvNotionalSetting == null) { stockEqvNotionalSetting = settings.Where(O => O.QuotaIndex == "轧差名义本金" && O.QuotaRange == 0).FirstOrDefault()?.Clone(); } stockEqvNotionalSetting?.ConvertToAbs(valuedateBLL.SystemDate.BusinessTotalScale ?? 0); obj.Quota_StockEqvNotional_Upper = stockEqvNotionalSetting?.QuotaUpperLimit ?? double.NaN; 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 concentrationSetting = settings.Where(O => O.QuotaIndex == "轧差集中度" && O.QuotaRange == obj.UnderlyingId).FirstOrDefault()?.Clone(); if (concentrationSetting == null) { concentrationSetting = settings.Where(O => O.QuotaIndex == "轧差集中度" && O.QuotaRange == 0).FirstOrDefault()?.Clone(); } obj.Quota_Concentration_Upper = concentrationSetting?.QuotaUpperLimit ?? double.NaN; obj.Quota_Concentration_Lower = concentrationSetting?.QuotaLowerLimit ?? double.NaN; obj.Quota_Concentration_wUpper = concentrationSetting?.WarningUpperLimit ?? double.NaN; obj.Quota_Concentration_wLower = concentrationSetting?.WarningLowerLimit ?? double.NaN; }); var totalStockEqvNotional = result.Sum(s => s.StockEqvNotional ?? 0); var setting = QueryCurrentQuotaSetting(QuotaTypeEnum.UNDERLYING); //setting = MargeQuotaSetting(setting, 0, 0); var codeList = new List(); foreach (var item in result) { var tempSetting = setting.Where(O => O.IsValid && O.Status == QuotaSettingApprovalStatus.Valid && (O.QuotaRange == item.UnderlyingId || O.QuotaRange == 0)); setValue(item, tempSetting, totalStockEqvNotional); } result.Sort((X, Y) => X.UnderlyingCode.CompareTo(Y.UnderlyingCode)); //var total = new QuotaMonitor_Underlying() //{ // UnderlyingCode = "合计", // StockEqvNotional = totalStockEqvNotional, // // Concentration = result.Sum(O => O.Concentration), // UnderlyingId = 0, // Quota_Concentration_Upper = double.NaN, // Quota_Concentration_Lower = double.NaN, // Quota_Concentration_wUpper = double.NaN, // Quota_Concentration_wLower = double.NaN, // Quota_StockEqvNotional_Upper = double.NaN, // Quota_StockEqvNotional_Lower = double.NaN, // Quota_StockEqvNotional_wUpper = double.NaN, // Quota_StockEqvNotional_wLower = double.NaN, //}; //var totalSetting = setting.Where(O => O.IsValid && O.Status == QuotaSettingApprovalStatus.Valid && O.QuotaRange == total.UnderlyingId); //setValue(total, totalSetting, totalStockEqvNotional); //result.Add(total); return result; } /// /// 查询限额监控-簿记汇总数据 /// /// /// public List QueryAssetUnitFromCalc(QuotaMonitorReq req) { var result = new List(); 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.TradeType != "现金流交易" && t.UnderlyingCode != null && ConsTrade.PositionTradeStatusList.Contains(t.TradeStatus) && t.ValidState != "InValid" && t.ExerciseDate >= SystemValueDate select new { t, risk }).ToArray(); if (req.AssetId > 0) { query = query.Where(O => req.AssetId == O.t.AssetId).ToArray(); } if (PS.Config.Is广发商贸) { foreach (var q in query) { GuangFaOnOptionHandleGammaAndTheta(q.t, q.risk); } } var stockEqvNotionalWhere = DbContext.trade.Where(t => t.TradeType != "现金流交易" && t.UnderlyingCode != null && ConsTrade.PositionTradeStatusList.Contains(t.TradeStatus) && t.ValidState != "InValid" && t.ExerciseDate >= SystemValueDate); 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.t.id)).ToArray(); stockEqvNotionalWhere = stockEqvNotionalWhere.Where(p => tradeTagIdQuery.Contains(p.id)); } var quotaList = from t in query group t by t.t.AssetId into info select new QuotaMonitor_AssetunitDto() { AssetBookID = info.Key, AssetBookName = info.FirstOrDefault().t.AssetBookName, // StockEqvNotional = info.Sum(O => O.t.StockEqvNotional), DeltaCash = info.Sum(O => O.risk.DeltaCash ?? 0), GammaCash = info.Sum(O => O.risk.GammaCash ?? 0), Vega = info.Sum(O => O.risk.Vega ?? 0), Theta = info.Sum(O => O.risk.Theta ?? 0), VegaCash = info.Sum(O => O.risk.VegaCash ?? 0), PositionPnl = info.Sum(O => O.risk.PositionPnl ?? 0) }; var stockEqvNotionalQuery = (from risk in DbContext.realtime_trade_risk join t in stockEqvNotionalWhere on risk.TradeId equals t.id join t1 in DbContext.trade on t.ParentTradeId == 0 || (t.ParentTradeId > 0 && t.TradeType == "收益互换") ? t.id : t.ParentTradeId equals t1.id where risk.VolType == "持仓" group t1 by t1.id into info select new { info.FirstOrDefault().AssetId, info.FirstOrDefault().StockEqvNotional }).ToArray() .GroupBy(O => O.AssetId).ToDictionary(K => K.Key, V => V.Sum(O => O.StockEqvNotional)); var setValue = new Action, double>((obj, settings, tStockEqvNotional) => { obj.Concentration = obj.StockEqvNotional / tStockEqvNotional; var stockEqvNotionalSetting = settings.Where(O => O.QuotaIndex == "名义本金").FirstOrDefault()?.Clone(); stockEqvNotionalSetting?.ConvertToAbs(valuedateBLL.SystemDate.BusinessTotalScale ?? 0); obj.Quota_StockEqvNotional_Upper = stockEqvNotionalSetting?.QuotaUpperLimit ?? double.NaN; 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 deltaExposureSetting = settings.Where(O => O.QuotaIndex == "Delta敞口(多空比)").FirstOrDefault()?.Clone(); obj.Quota_DeltaExposure_Upper = deltaExposureSetting?.QuotaUpperLimit ?? double.NaN; obj.Quota_DeltaExposure_Lower = deltaExposureSetting?.QuotaLowerLimit ?? double.NaN; obj.Quota_DeltaExposure_wUpper = deltaExposureSetting?.WarningUpperLimit ?? double.NaN; obj.Quota_DeltaExposure_wLower = deltaExposureSetting?.WarningLowerLimit ?? double.NaN; var deltaNetExposureSetting = settings.Where(O => O.QuotaIndex == "Delta净敞口比例").FirstOrDefault()?.Clone(); obj.Quota_DeltaNetExposure_Upper = deltaNetExposureSetting?.QuotaUpperLimit ?? double.NaN; obj.Quota_DeltaNetExposure_Lower = deltaNetExposureSetting?.QuotaLowerLimit ?? double.NaN; obj.Quota_DeltaNetExposure_wUpper = deltaNetExposureSetting?.WarningUpperLimit ?? double.NaN; obj.Quota_DeltaNetExposure_wLower = deltaNetExposureSetting?.WarningLowerLimit ?? double.NaN; var deltaCashSetting = settings.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 = settings.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 = settings.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 = settings.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 concentrationSetting = settings.Where(O => O.QuotaIndex == "集中度").FirstOrDefault()?.Clone(); obj.Quota_Concentration_Upper = concentrationSetting?.QuotaUpperLimit ?? double.NaN; obj.Quota_Concentration_Lower = concentrationSetting?.QuotaLowerLimit ?? double.NaN; obj.Quota_Concentration_wUpper = concentrationSetting?.WarningUpperLimit ?? double.NaN; obj.Quota_Concentration_wLower = concentrationSetting?.WarningLowerLimit ?? double.NaN; var positionPnlSetting = settings.Where(O => O.QuotaIndex == "浮动盈亏").FirstOrDefault()?.Clone(); obj.Quota_PositionPnl_Upper = positionPnlSetting?.QuotaUpperLimit ?? double.NaN; obj.Quota_PositionPnl_Lower = positionPnlSetting?.QuotaLowerLimit ?? double.NaN; obj.Quota_PositionPnl_wUpper = positionPnlSetting?.WarningUpperLimit ?? double.NaN; obj.Quota_PositionPnl_wLower = positionPnlSetting?.WarningLowerLimit ?? double.NaN; var thetaSettings = settings.Where(O => O.QuotaIndex == "Theta").FirstOrDefault()?.Clone(); obj.Quota_Theta_Upper = thetaSettings?.QuotaUpperLimit ?? double.NaN; obj.Quota_Theta_Lower = thetaSettings?.QuotaLowerLimit ?? double.NaN; obj.Quota_Theta_wUpper = thetaSettings?.WarningUpperLimit ?? double.NaN; obj.Quota_Theta_wLower = thetaSettings?.WarningLowerLimit ?? double.NaN; var totalPnlSettings = settings.Where(O => O.QuotaIndex == "本年累计盈亏").FirstOrDefault()?.Clone(); obj.Quota_TotalPnL_Upper = totalPnlSettings?.QuotaUpperLimit ?? double.NaN; obj.Quota_TotalPnL_Lower = totalPnlSettings?.QuotaLowerLimit ?? double.NaN; obj.Quota_TotalPnL_wUpper = totalPnlSettings?.WarningUpperLimit ?? double.NaN; obj.Quota_TotalPnL_wLower = totalPnlSettings?.WarningLowerLimit ?? double.NaN; }); var totalStockEqvNotional = stockEqvNotionalQuery.Values.Sum(); var riskDict = query.ToDictionary(K => K.t, V => V.risk); var assetunitSetting = QueryCurrentQuotaSetting(QuotaTypeEnum.Assetunit); assetunitSetting = MargeQuotaSetting(assetunitSetting, 0, 0); var hedgeList = CalculateExchangeTrade(); GuangFaOnExchangeHandleGammaAndTheta(hedgeList); var dict = new Dictionary, QuotaMonitor_Assetunit>>(); var codeList = new List(); //double[]{ (场内+场外)多, (场内+场外)空, 场外净值 } var deltaDict = new Dictionary(); foreach (var item in quotaList) { item.StockEqvNotional = stockEqvNotionalQuery.ContainsKey(item.AssetBookID) ? stockEqvNotionalQuery[item.AssetBookID] : 0; var hedgeRisks = hedgeList.Where(O => O.BookId == item.AssetBookID).ToList(); var key = item.AssetBookID; if (!deltaDict.ContainsKey(key)) { //double[]{ (场内+场外)多, (场内+场外)空, 场外净值 } deltaDict[key] = new double[] { 0, 0, 0 }; } if (!dict.ContainsKey(key)) { dict[key] = new KeyValuePair, QuotaMonitor_Assetunit>( new List(), new QuotaMonitor_Assetunit() { DeltaCash = 0, GammaCash = 0, Vega = 0, Theta = 0, VegaCash = 0 }); } dict[key].Key.Add(item); dict[key].Value.DeltaCash += hedgeRisks.Sum(O => O.DeltaCash.Normalize()); dict[key].Value.GammaCash += hedgeRisks.Sum(O => O.GammaCash.Normalize()); dict[key].Value.Vega += hedgeRisks.Sum(O => O.Vega.Normalize()); dict[key].Value.VegaCash += hedgeRisks.Sum(O => (O.Vega * O.SettlePrice).Normalize()); dict[key].Value.Theta += hedgeRisks.Sum(O => O.Theta.Normalize()); codeList.Add(item.AssetBookID); } var hedgeDict = hedgeList.Where(O => !codeList.Contains(O.BookId)).GroupBy(O => O.BookId).ToDictionary(K => K.Key, V => V.ToList()); foreach (var item in hedgeDict) { var obj = new QuotaMonitor_Assetunit(); obj.AssetBookID = item.Key; obj.AssetBookName = DataCacheProvider.GetAssetUnitDataSource().GetData(item.Key).Name; obj.StockEqvNotional = 0; var hedgeRisks = hedgeList.Where(O => O.BookId == item.Key).ToList(); var key = item.Key; if (!dict.ContainsKey(key)) { dict[key] = new KeyValuePair, QuotaMonitor_Assetunit>( new List(), new QuotaMonitor_Assetunit() { DeltaCash = 0, GammaCash = 0, Vega = 0, Theta = 0, VegaCash = 0 }); } dict[key].Key.Add(obj); dict[key].Value.DeltaCash += item.Value.Sum(O => O.DeltaCash.Normalize()); dict[key].Value.GammaCash += item.Value.Sum(O => O.GammaCash.Normalize()); dict[key].Value.Vega += item.Value.Sum(O => O.Vega.Normalize()); dict[key].Value.VegaCash += item.Value.Sum(O => (O.Vega * O.SettlePrice).Normalize()); dict[key].Value.Theta += item.Value.Sum(O => O.Theta.Normalize()); } foreach (var item in dict) { var obj = new QuotaMonitor_Assetunit() { AssetBookID = item.Key, AssetBookName = DataCacheProvider.GetAssetUnitDataSource().GetData(item.Key).Name, StockEqvNotional = PS.Config.Is国信金阳 ? Math.Abs(item.Value.Key.Sum(O => O.StockEqvNotional.Normalize())) : item.Value.Key.Sum(O => O.StockEqvNotional.Normalize()), DeltaExposure = item.Value.Key.Sum(O => O.DeltaExposure.Normalize()), DeltaNetExposure = item.Value.Key.Sum(O => O.DeltaNetExposure.Normalize()), DeltaCash = item.Value.Key.Sum(O => O.DeltaCash.Normalize()) + item.Value.Value.DeltaCash.Normalize(), GammaCash = item.Value.Key.Sum(O => O.GammaCash.Normalize()) + item.Value.Value.GammaCash.Normalize(), Vega = item.Value.Key.Sum(O => O.Vega.Normalize()) + item.Value.Value.Vega.Normalize(), VegaCash = item.Value.Key.Sum(O => O.VegaCash.Normalize()) + item.Value.Value.VegaCash.Normalize(), Theta = item.Value.Key.Sum(O => O.Theta.Normalize()) + item.Value.Value.Theta.Normalize(), Concentration = item.Value.Key.Sum(O => O.Concentration.Normalize()), //UnderlyingId = 0, PositionPnl = item.Value.Key.Sum(O => O.PositionPnl.Normalize()), }; result.Add(obj); } //处理本年累计盈亏数据 if (PS.Config.Is广发商贸) { AssembleAssetunitTotalPnL(req, result); } result.ForEach(obj => { var tempSetting = assetunitSetting.Where(O => O.IsValid && O.Status == QuotaSettingApprovalStatus.Valid && O.QuotaRange == obj.AssetBookID); setValue(obj, tempSetting, totalStockEqvNotional); //if (obj.DeltaCash == 0 && obj.GammaCash == 0 && obj.Vega == 0 && obj.Theta == 0 && obj.StockEqvNotional == 0) //{ // continue; //} }); var total = new QuotaMonitor_Assetunit() { AssetBookName = "合计", StockEqvNotional = result.Sum(O => O.StockEqvNotional), DeltaCash = result.Sum(O => O.DeltaCash.Normalize()), GammaCash = result.Sum(O => O.GammaCash.Normalize()), Vega = result.Sum(O => O.Vega.Normalize()), VegaCash = result.Sum(O => O.VegaCash.Normalize()), Theta = result.Sum(O => O.Theta.Normalize()), Concentration = result.Sum(O => O.Concentration.Normalize()), PositionPnl = result.Sum(O => O.PositionPnl.Normalize()), TotalPnL = result.Sum(O => O.TotalPnL.Normalize()), AssetBookID = 0, }; var totalSetting = assetunitSetting.Where(O => O.IsValid && O.Status == QuotaSettingApprovalStatus.Valid && O.QuotaRange == total.AssetBookID); setValue(total, totalSetting, totalStockEqvNotional); result.Add(total); return result; } /// /// 本年累计盈亏组装标的维度 /// /// 查询日期 /// 已经查询好的标的源数据 public void AssembleUnderlyingTotalPnL(QuotaMonitorReq req, List _Underlyings) { List totalPnLModels = CaclYearTotalPnl(req, out List T2PositionDataDtos); //if (req.HasPosition.HasValue) //{ // var t2dic = T2PositionDataDtos.GroupBy(p => p.TradeId + "_" + (String.IsNullOrEmpty(p.HedgeUniqueCode) ? "" : p.HedgeUniqueCode)).Select(l => l.Key); // if (req.HasPosition == true) // { // totalPnLModels.RemoveAll(p => !t2dic.Contains(p.TradeId + "_" + (String.IsNullOrEmpty(p.HedgeUniqueCode) ? "" : p.HedgeUniqueCode))); // } // else // { // totalPnLModels.RemoveAll(p => t2dic.Contains(p.TradeId + "_" + (String.IsNullOrEmpty(p.HedgeUniqueCode) ? "" : p.HedgeUniqueCode))); // } //} var underIds = _Underlyings.Select(l => l.UnderlyingId).ToList(); var totalPnlListGroupBookID = totalPnLModels.GroupBy(l => l.UnderlyingId).Select(l => new { underlyingId = l.Key, sumBookTotalPnl = l.Sum(l => l.AccruedTotalPnL) }).ToList(); totalPnlListGroupBookID.ForEach(b => { var _AssetunitsHasBookID = _Underlyings.FirstOrDefault(l => l.UnderlyingId == b.underlyingId); if (_AssetunitsHasBookID != null) { _AssetunitsHasBookID.TotalPnL = b.sumBookTotalPnl; } else { var obj = new QuotaMonitor_Underlying() { UnderlyingId = b.underlyingId, UnderlyingCode = DataCacheProvider.GetUnderlyingDataSource().GetData(b.underlyingId).UnderlyingCode, TotalPnL = b.sumBookTotalPnl, }; _Underlyings.Add(obj); } }); if (req.HasPosition.HasValue) { if (req.HasPosition == true) { _Underlyings.RemoveAll(l => !underIds.Contains(l.UnderlyingId)); } else { _Underlyings.RemoveAll(l => underIds.Contains(l.UnderlyingId)); } } } /// /// 本年累计盈亏组装簿记维度 /// /// 查询日期 /// 已经查询好的簿记源数据 public void AssembleAssetunitTotalPnL(QuotaMonitorReq req, List _Assetunits) { List totalPnLModels = CaclYearTotalPnl(req, out List T2PositionDataDtos); //if (req.HasPosition.HasValue) //{ // var t2dic = T2PositionDataDtos.GroupBy(p => p.TradeId + "_" + (String.IsNullOrEmpty(p.HedgeUniqueCode) ? "" : p.HedgeUniqueCode)).Select(l => l.Key); // if (req.HasPosition == true) // { // totalPnLModels.RemoveAll(p => !t2dic.Contains(p.TradeId + "_" + (String.IsNullOrEmpty(p.HedgeUniqueCode) ? "" : p.HedgeUniqueCode))); // } // else // { // totalPnLModels.RemoveAll(p => t2dic.Contains(p.TradeId + "_" + (String.IsNullOrEmpty(p.HedgeUniqueCode) ? "" : p.HedgeUniqueCode))); // } //} var assetIds = _Assetunits.Select(l => l.AssetBookID).ToList(); var totalPnlListGroupBookID = totalPnLModels.GroupBy(l => l.BookId).Select(l => new { bookID = l.Key, sumBookTotalPnl = l.Sum(l => l.AccruedTotalPnL) }).ToList(); totalPnlListGroupBookID.ForEach(b => { var _AssetunitsHasBookID = _Assetunits.FirstOrDefault(l => l.AssetBookID == b.bookID); if (_AssetunitsHasBookID != null) { _AssetunitsHasBookID.TotalPnL = b.sumBookTotalPnl; } else { var obj = new QuotaMonitor_Assetunit() { AssetBookID = b.bookID, AssetBookName = DataCacheProvider.GetAssetUnitDataSource().GetData(b.bookID).Name, TotalPnL = b.sumBookTotalPnl, }; _Assetunits.Add(obj); } }); if (req.HasPosition.HasValue) { if (req.HasPosition == true) { _Assetunits.RemoveAll(l => !assetIds.Contains(l.AssetBookID)); } else { _Assetunits.RemoveAll(l => assetIds.Contains(l.AssetBookID)); } } } /// /// 计算本年累计盈亏 /// /// /// public List CaclYearTotalPnl(QuotaMonitorReq req, out List t2PositionDataDtos) { var SystemValueDate = valuedateBLL.ValueDate; // 1、确定三点 T1 T1~T2 T2 var yearStart = new DateTime(SystemValueDate.Year, 1, 1); var yearEnd = new DateTime(SystemValueDate.Year, 12, 31); //T1 本年初始持仓数据 var dateT1 = EodOperationBase.GetLastSettlementDate(yearStart); // T2 最近一次收盘持仓数据 var dateT2 = EodOperationBase.GetLastSettlementDate(yearEnd, true); RiskRequestModel reqModel = new RiskRequestModel() { ValueDateStart = dateT1, ValueDateEnd = dateT2, EodSettlePriceMode = "收盘价" }; return new AccruedTotalPnlService(UserInfo).CalcuTotalPnl(out t2PositionDataDtos, dateT1, dateT2, reqModel); } /// /// 查询限额指标设置 /// /// /// public SearchListResult QuerySetting(QuotaMonitorReq req) { var query = DbContext.quotaSetting.AsQueryable(); if (req.QuotaType >= 0) { query = query.Where(O => (int)O.QuotaType == req.QuotaType); } if (!string.IsNullOrWhiteSpace(req.QuotaIndex)) { query = query.Where(O => O.QuotaIndex == req.QuotaIndex); } if (req.Precheck != null) { query = query.Where(O => O.Precheck == req.Precheck); } if (req.UseValid != null) { var builder = PredicateBuilder.Create(o => false); foreach (var item in req.UseValid) { if (item.Contains("有效")) { builder = builder.Or(s => s.IsValid && s.Status == QuotaSettingApprovalStatus.Valid); // query = query.Where(s => s.IsValid && s.Status == QuotaSettingApprovalStatus.Valid); } else if (item.Contains("无效")) { //builder = builder.Or(s => s.IsValid && s.Status == QuotaSettingApprovalStatus.Valid); builder = builder.Or(s => (!s.IsValid && s.Status == QuotaSettingApprovalStatus.Valid || s.Status == QuotaSettingApprovalStatus.Rejected)); } else if (item.Contains("待删除")) { builder = builder.Or(s => s.Status != QuotaSettingApprovalStatus.Valid && s.Status != QuotaSettingApprovalStatus.Rejected && !s.IsValid); } else if (item.Contains("待生效")) { builder = builder.Or(s => s.Status != QuotaSettingApprovalStatus.Valid && s.Status != QuotaSettingApprovalStatus.Rejected && s.IsValid); } } query = query.Where(builder); } if (req.Status != null) { query = query.Where(t => req.Status.Contains((int)t.Status)); } var temp = query.ToList(); var remarkDict = new Dictionary(); var logService = new ClientProcessLogService(OptUser); var logReq = new ProcesslogReq() { rows = 1, ProcessType = "QuotaSetting", ProcessStatus = "修改", sord = "desc", }; temp.ForEach(O => { switch (O.QuotaType) { case QuotaTypeEnum.UNDERLYING: O.QuotaRangeStr = O.QuotaRange == 0 ? "全部" : DataCacheProvider.GetUnderlyingDataSource().GetData(O.QuotaRange).UnderlyingCode; break; case QuotaTypeEnum.STOCK_PERCENT: O.QuotaRangeStr = O.QuotaRange + "天"; break; case QuotaTypeEnum.CLIENT: O.QuotaRangeStr = O.QuotaRange == 0 ? "全部" : DataCacheProvider.GetClientDataSource().GetData(O.QuotaRange).Name; break; case QuotaTypeEnum.Assetunit: O.QuotaRangeStr = O.QuotaRange == 0 ? "全部" : DataCacheProvider.GetAssetUnitDataSource().GetData(O.QuotaRange).Name; break; case QuotaTypeEnum.GLOBAL_OFF_EXCHANGE: case QuotaTypeEnum.GLOBAL_OPTION: case QuotaTypeEnum.GLOBAL_SWAP: case QuotaTypeEnum.GLOBAL_FORWARD: case QuotaTypeEnum.GLOBAL_ON_EXCHANGE: case QuotaTypeEnum.ASSET_EQUITY: case QuotaTypeEnum.ASSET_COMMODITY: case QuotaTypeEnum.ASSET_PRECIOUSMETALS: case QuotaTypeEnum.VARIETY: case QuotaTypeEnum.TRADE: default: break; } if (O.Status == QuotaSettingApprovalStatus.Pending_Approval) { logReq.TypeId = O.id; var log = logService.SearchList(logReq); remarkDict[O.id] = log.rows.FirstOrDefault()?.Remark; } }); temp.Sort((x, y) => { var t = DateTime.Compare(y.OptDate.Value, x.OptDate.Value); if (t == 0) { t = x.QuotaType - y.QuotaType; } if (t == 0) { t = string.Compare(x.QuotaIndex, y.QuotaIndex); } if (t == 0) { t = x.QuotaRange - y.QuotaRange; } return t; }); var result = new SearchListResult() { rows = temp, Sum = new { RemarkDict = remarkDict, BusinessTotalScale = valuedateBLL.SystemDate.BusinessTotalScale, BusinessAvailableFunds = valuedateBLL.SystemDate.BusinessAvailableFunds, SingleUnderlyingRate = valuedateBLL.SystemDate.SingleUnderlyingRate, } }; return result; } /// /// 获取所有子客户id /// /// /// private List getSubclientId(int id) { var result = new List() { id }; var clientIdList = DataCacheProvider.GetClientDataSource().AsQueryable(O => O.ParentId == id).Select(O => O.id).ToList(); foreach (var c in clientIdList) { result.AddRange(getSubclientId(c)); } return result; } private IEnumerable CalculateExchangeTrade() { var preDate = DbContext.eod_trade_position.Max(O => (DateTime?)O.ValueDate) ?? valuedateBLL.SystemDate.AccruedTotalPnlStartDate ?? DateTime.MinValue; //昨日持仓信息 var lastEodPositions = DbContext.eod_trade_position.Where(O => O.ValueDate == preDate && ConsTrade.TradeTypesForHedge.Contains(O.TradeType)); //获取所有未结算过的对冲交易 var newTrades = DbContext.ExchangeTrade.Where(O => O.TradeDate > preDate && (O.MaturityDate == null || O.MaturityDate >= SystemValueDate) && O.IsValid).ToList(); var context = new HedgePnlCalcContext(CalcScenarioEnum.RealtimeRisk, SystemValueDate, "持仓", false, DataCacheProvider.GetUnderlyingDataSource() , DataCacheProvider.GetUnderlyingDataSource(), new ExchangeOptionPriceProvider(), UserInfo); var hedgePnlList = new HedgePnlCalc(context).Calculate(newTrades, lastEodPositions); return hedgePnlList; } /// /// /// /// /// 是否要包含对冲交易 /// 场外delta敞口 /// 互换delta敞口 /// 远期delta敞口 /// 场外期权delta净敞口 /// 场外delta净敞口(包括互换) /// 要包括的成交待确认状态的交易id /// private void GetDeltaExposure(IEnumerable> otcTradeList, IEnumerable hedgePnl, out double optionExposure, out double swapExposure, out double forwardExposure, out double offExchangeExposure, out double optionNetExposure, out double offExchangeNetExposure, IEnumerable tradeIds = null) { optionExposure = double.NaN; swapExposure = double.NaN; forwardExposure = double.NaN; offExchangeExposure = double.NaN; var index = 0; string optionType; var swapIds = otcTradeList.Where(O => O.Key.TradeType == "收益互换").Select(O => O.Key.id); var swapDict = DbContext.swap_position .Where(O => swapIds.Contains(O.SwapTradeId) && O.IsInitial && O.PosiDirection > 0 && !O.Invalid) .Select(O => new { O.id, O.SwapTradeId, longShortStatus = O.PositionType == 1 }).ToList(); // 期权or互换 标的代码 delta[0]多头;[1]空头 var allDict = new Dictionary>() { {"期权",new Dictionary() }, {"互换",new Dictionary() }, {"远期",new Dictionary() } }; var temp = new List(); foreach (var item in otcTradeList) { var underlyingCode = FormatUnderlyingCode(item.Key.UnderlyingCode); if (string.IsNullOrEmpty(item.Key.UnderlyingCode)) { if (item.Key.TradeType == ConsGlobal.TradeType.CashFlow) { underlyingCode = "现金流"; } else { underlyingCode = "未知"; } continue; } if (!ConsTrade.PositionTradeStatusList.Contains(item.Key.TradeStatus) && !(tradeIds?.Contains(item.Key.id) ?? false)) { continue; } index = 0; optionType = "期权"; if (item.Key.TradeType == "收益互换") { optionType = "互换"; if (!swapDict.Any(d => d.SwapTradeId == item.Key.id)) { continue; } index = swapDict.First(d => d.SwapTradeId == item.Key.id).longShortStatus ? 0 : 1; } else { if (item.Key.TradeType == "远期") { optionType = "远期"; } index = ((item.Key.BuySell == "买入" && (item.Key.CallPut == "看涨" || item.Key.CallPut == "Call")) || (item.Key.BuySell == "卖出" && (item.Key.CallPut == "看跌" || item.Key.CallPut == "Put"))) ? 0 : 1; } if (!allDict[optionType].ContainsKey(underlyingCode)) { allDict[optionType][underlyingCode] = new double[2] { 0, 0 }; } allDict[optionType][underlyingCode][index] += item.Value.Delta.Normalize(); temp.Add($"{item.Key}\t{item.Value.Delta.Normalize()}"); } optionType = "期权"; var optionExposureValue = allDict["期权"].Values.Sum(O => O.Sum().Normalize()); var swapExposureValue = allDict["互换"].Values.Sum(O => O.Sum().Normalize()); var forwardExposureValue = allDict["远期"].Values.Sum(O => O.Sum().Normalize()); if (hedgePnl != null) { foreach (var item in hedgePnl) { var code = FormatUnderlyingCode(item.UnderlyingCode); if (!allDict[optionType].ContainsKey(code)) { allDict[optionType][code] = new double[2] { 0, 0 }; } index = item.PositionType == "long" ? 0 : 1; allDict[optionType][code][index] += item.Delta; temp.Add($"{item.UnderlyingCode}\t{item.Delta.Normalize()}"); } } var longNum = allDict["期权"].Sum(O => O.Value[0].Normalize()); var shortNum = allDict["期权"].Sum(O => O.Value[1].Normalize()); optionExposure = Math.Abs((longNum / shortNum).Normalize()); swapExposure = Math.Abs((allDict["互换"].Sum(O => O.Value[0].Normalize()) / allDict["互换"].Sum(O => O.Value[1].Normalize())).Normalize()); forwardExposure = Math.Abs((allDict["远期"].Sum(O => O.Value[0].Normalize()) / allDict["远期"].Sum(O => O.Value[1].Normalize())).Normalize()); offExchangeExposure = Math.Abs(((allDict["互换"].Sum(O => O.Value[0].Normalize()) + allDict["远期"].Sum(O => O.Value[0].Normalize()) + longNum) / (allDict["互换"].Sum(O => O.Value[1].Normalize()) + allDict["远期"].Sum(O => O.Value[1].Normalize()) + shortNum)).Normalize()); optionNetExposure = ((longNum + shortNum).Normalize() / -optionExposureValue).Normalize(); offExchangeNetExposure = ((longNum + shortNum + swapExposureValue + forwardExposureValue).Normalize() / -(optionExposureValue + swapExposureValue + forwardExposureValue)).Normalize(); } /// /// Delte敞口算法 /// /// 场外交易 /// 场内交易 /// 要计算的标的 /// Delta敞口(多空比):多头/空头 /// Delta净敞口比例:场内外净敞口/(-场外期权Delta) /// 一定要包含的交易Id,否则会因为交易状态为非持仓交易而忽略掉 private void GetDeltaExposure(IEnumerable> otcTradeList, IEnumerable hedgePnl, string underlyingCode, out double longDelta, out double shortDelta, out double allLongDelta, out double allShortDelta, IEnumerable tradeIds = null) { longDelta = 0; shortDelta = 0; allLongDelta = shortDelta = 0; allShortDelta = 0; var isLong = false; underlyingCode = FormatUnderlyingCode(underlyingCode); var swapIds = otcTradeList.Where(O => O.Key.TradeType == "收益互换").Select(O => O.Key.id); var swapDict = DbContext.trade_swap .Where(O => swapIds.Contains(O.TradeId)) .Select(O => new { O.TradeId, longShortStatus = (O.GetLongShort != null && O.GetLongShort == "多头") || (O.PayLongShort != null && O.PayLongShort == "空头") }) .ToDictionary(K => K.TradeId, V => V.longShortStatus); foreach (var item in otcTradeList) { if (FormatUnderlyingCode(item.Key.UnderlyingCode) != underlyingCode) { continue; } if (string.IsNullOrEmpty(item.Key.UnderlyingCode)) { if (item.Key.TradeType == ConsGlobal.TradeType.CashFlow) { underlyingCode = "现金流"; } else { underlyingCode = "未知"; } continue; } if (!ConsTrade.PositionTradeStatusList.Contains(item.Key.TradeStatus) && !(tradeIds?.Contains(item.Key.id) ?? false)) { continue; } isLong = false; if (item.Key.TradeType == "收益互换") { if (!swapDict.ContainsKey(item.Key.id)) { continue; } isLong = swapDict[item.Key.id] ? true : false; } else { isLong = ((item.Key.BuySell == "买入" && (item.Key.CallPut == "看涨" || item.Key.CallPut == "Call")) || (item.Key.BuySell == "卖出" && (item.Key.CallPut == "看跌" || item.Key.CallPut == "Put"))) ? true : false; } if (isLong) { longDelta += item.Value.Delta.Normalize(); allLongDelta += item.Value.Delta.Normalize(); } else { shortDelta += item.Value.Delta.Normalize(); allShortDelta += item.Value.Delta.Normalize(); } } if (GetHedgeDeltaExposure(hedgePnl, underlyingCode, out var longValue, out var shortValue)) { allLongDelta += longValue; allShortDelta += shortValue; } } /// /// 获取指定标的场内持仓的Delta多空头合计值 /// /// 场内对冲交易 /// 标的代码 /// 多头Delta /// 空头Delta /// private bool GetHedgeDeltaExposure(IEnumerable hedgePnl, string underlyingCode, out double longDelta, out double shortDelta) { var result = false; longDelta = 0; shortDelta = 0; if (hedgePnl != null) { foreach (var item in hedgePnl) { if (item.UnderlyingCode != underlyingCode && FormatUnderlyingCode(item.UnderlyingCode) != underlyingCode) { continue; } result = true; if (item.PositionType == "long") { longDelta += item.Delta; } else { shortDelta += item.Delta; } } } return result; } private double? GetEodVar(string tradeType, double pvPercent) { var date = DbContext.eod_var.Where(O => O.TradeType == tradeType).OrderByDescending(O => O.ValueDate).Select(O => (DateTime?)O.ValueDate).FirstOrDefault(); double? calcPv = null; if (date == null) { return null; } var eodVaRs = DbContext.eod_var.Where(x => x.ValueDate == date && x.VaRType == (int)EnumVaRType.HistoricalSimulation && x.TradeType == tradeType); var totalVaR = eodVaRs.FirstOrDefault(o => o.UnderlyingCode == null || o.UnderlyingCode == ""); if (totalVaR != null && !string.IsNullOrEmpty(totalVaR.PvObject)) { var pvList = totalVaR.PvObject.Split(',').Select(x => double.TryParse(x, out var temp) ? temp : 0).OrderBy(x => x).ToArray(); var index = (int)Math.Floor(pvList.Length * pvPercent) - 1; index = Math.Max(0, index); calcPv = pvList[index]; } return calcPv; } /// /// /// /// /// private string FormatUnderlyingCode(string code) { //Match mc; //code = (code ?? "").ToUpper(); //if ((mc = Regex.Match(code, @"^I[F|C|H](?=\d+$)")).Success) //{ // code = mc.Value; //} switch (code) { case "000300.SH": case "399300.SZ": code = "IF"; break; case "000905.SH": case "399905.SZ": code = "IC"; break; case "000016.SH": code = "IH"; break; default: break; } return code; } /// /// 获取指定标的,指定簿记下的对冲交易 /// /// /// /// private List GetHedgeTradeList(IEnumerable codes, IEnumerable assetIds) { var tradePosition = DbContext.TradePosition //.Where(O => assetIds.Contains(O.BookId) && codes.Contains(O.UnderlyingCode)) .Select(O => new tradeDto { AssetId = O.BookId, BuySell = O.PositionType == PositionTypeFlag.Long ? "买入" : "卖出", ExerciseDate = O.MaturityDate, TradePrice = O.PositionCost, //OptionType, //Strike, IsMoneynessOption = "否", Notional = O.Position, TradeAmount = O.Position, TradeDate = O.CreateTime, StartDate = O.CreateTime, TradeType = O.TradeType, UnderlyingId = O.UnderlyingId, UnderlyingCode = O.UnderlyingCode, StockEqvNotional = O.PositionCost, SpotPrice = O.PositionCost / O.Position, //UnderlyingAssetClass, //UnderlyingInstrumentType, MaturityDate = O.MaturityDate, ExchangeOptionCode = O.InstrumentCode }).ToList(); foreach (var item in tradePosition) { item.Notional = Math.Abs(item.Notional); item.TradeAmount = Math.Abs(item.TradeAmount); item.TradePrice = item.TradePrice.HasValue ? Math.Abs(item.TradePrice.Value) : item.TradePrice; item.StockEqvNotional = Math.Abs(item.StockEqvNotional); var exchangeOption = DataCacheProvider.GetExchangeListOptionDataSource().GetData(item.ExchangeOptionCode); if (exchangeOption == null) { continue; } item.ExerciseDate = exchangeOption.MaturityDate; item.ExerciseMode = exchangeOption.ExerciseMode; item.OptionType = exchangeOption.OptionType; item.Strike = exchangeOption.Strike; item.TradeDate = item.StartDate = exchangeOption.OpenDate; } return tradePosition; } /// /// 获取交易对冲情况 /// 算法由国海提供 /// /// /// /// /// /// private List GetHedgeGroup(List tradeList, IPriceProvider priceProvider, out HashSet hedgeGroup, out HashSet nonhedgeGroup) { hedgeGroup = new HashSet(); nonhedgeGroup = new HashSet(); var calcTradeList = new List(tradeList); var exchangeRisk = new TradeRiskResult() { Results = new List() }; var tradeRiskResult = CalculatorHelper.CalculateRisksForTrades( valueDate: SystemValueDate, tradeList: calcTradeList, calcScenario: CalcScenarioEnum.Default, priceProvider: priceProvider, pricingRequest: QdpPricingRequest.BASIC_PRICING, isUseTradeVol: PS.Config.IsTradeVol); exchangeRisk.Results.AddRange(tradeRiskResult.Results); //value[0]:多头;value[1]:空头; var dict = exchangeRisk.Results.GroupBy(O => FormatUnderlyingCode(O.Trade.UnderlyingCode)) .ToDictionary( K => K.Key, V => new[] { V.Where(O => ConsTrade.TradeTypesForHedge.Contains(O.Trade.TradeType) ? O.Trade.BuySell == "买入" : (O.Trade.BuySell == "买入" && (O.Trade.CallPut == "看涨" || O.Trade.CallPut == "Call")) || (O.Trade.BuySell == "卖出" && (O.Trade.CallPut == "看跌" || O.Trade.CallPut == "Put"))), V.Where(O => ConsTrade.TradeTypesForHedge.Contains(O.Trade.TradeType) ? O.Trade.BuySell != "买入" : !((O.Trade.BuySell == "买入" && (O.Trade.CallPut == "看涨" || O.Trade.CallPut == "Call")) || (O.Trade.BuySell == "卖出" && (O.Trade.CallPut == "看跌" || O.Trade.CallPut == "Put")))) }); foreach (var item in dict) { var longPostion = item.Value[0].Sum(O => O.ValueResult.Delta); var shortPostion = item.Value[1].Sum(O => O.ValueResult.Delta); var percent = Math.Abs((longPostion / shortPostion).Normalize()); HashSet list = null; if (percent > 1.25 || percent < 0.8) { list = nonhedgeGroup; } else { list = hedgeGroup; } list.Add(item.Key); } return exchangeRisk.Results; } /// /// 计算 Math.Max(计算场外卖出交易的最大损失的5倍,名义本金的千分之五) /// 算法由国海提供 /// /// /// /// /// private Dictionary GetMaxLoss(IEnumerable codes, List tradeList, IPriceProvider priceProvider) { var customPrice = new ManualPriceProvider(); foreach (var item in codes) { customPrice.SetPrice(item, priceProvider.GetPrice(item) * 1.2); } var upRiskResult = CalculatorHelper.CalculateRisksForTrades(new CalculateRisksForTradesReq { valueDate = SystemValueDate, tradeList = tradeList, calcScenario = CalcScenarioEnum.Default, priceProvider = customPrice, pricingRequest = QdpPricingRequest.PV_ONLY, isUseTradeVol = PS.Config.IsTradeVol }); foreach (var item in codes) { customPrice.SetPrice(item, priceProvider.GetPrice(item) * 0.8); } var downRiskResult = CalculatorHelper.CalculateRisksForTrades( valueDate: SystemValueDate, tradeList: tradeList, calcScenario: CalcScenarioEnum.Default, priceProvider: customPrice, pricingRequest: QdpPricingRequest.PV_ONLY, isUseTradeVol: PS.Config.IsTradeVol); var loss = new Dictionary(); codes = codes.Select(O => FormatUnderlyingCode(O)).Distinct(); foreach (var item in codes) { var upPv = upRiskResult.Results.Where(O => FormatUnderlyingCode(O.Trade.UnderlyingCode) == item).Sum(O => O.ValueResult.Pv.Normalize()); var downPv = downRiskResult.Results.Where(O => FormatUnderlyingCode(O.Trade.UnderlyingCode) == item).Sum(O => O.ValueResult.Pv.Normalize()); var stockEqvNotional = upRiskResult.Results.Where(O => FormatUnderlyingCode(O.Trade.UnderlyingCode) == item).Sum(O => (O.Trade.StockEqvNotionalReal * O.Trade.StockEqvNotional / O.Trade.OriginalStockEqvNotional).Normalize()); loss[item] = Math.Max(Math.Max(Math.Abs(upPv), Math.Abs(downPv)) * 5, stockEqvNotional * 0.0005); } return loss; } readonly string[] ignoreTradeTypes = new string[] { "雪球期权", "凤凰期权" }; readonly string[] underlyingInstrumentTypeList = new string[] { "CommodityFutures", "CommoditySpot" }; /// /// 计算业务规模 /// 根据传入的场外交易列表获取同标的同簿记账户下的场内持仓根据规则计算业务规模 /// /// 要计算的场外交易列表 /// 权益类场外期权 /// 非权益类场外期权 /// 权益类互换交易 /// 非权益类互换交易 /// 返回结果:(场内权益,场内非权益,场外权益,场外非权益) public (double onExchangeEquity, double onExchangeNonEquity, double offExchangeEquity, double offExchangeNonEquity) GetRiskScale(List tradeList, out double offExchangeEquity, out double offExchangeNonEquity, out double swapEquity, out double swapNonEquity, out double forwardEquity, out double forwardNonEquity) { var tempDict = new Dictionary(); var data = new Dictionary>(); var underlyingPrice = DataCacheProvider.GetUnderlyingDataSource(); var riskResult = GetHedgeGroup(tradeList, underlyingPrice, out var codes, out var totalTradeList, out var calcTradeList, out var hedgeGroup, out var nonhedgeGroup); var maxLoss = GetMaxLoss(codes, calcTradeList.Where(O => !ConsTrade.TradeTypesForHedge.Contains(O.TradeType)).ToList(), underlyingPrice); // 股票/股指 股指期货 商品期货 国债期货 卖出权益类场内期权 卖出非权益类场内期权 买入权益类场内期权 买入权益类场外期权 double stock = 0, stockIF = 0, commodity = 0, tbFutures = 0, sellOnExchange_Equity = 0, sellOnExchange_NonEquity = 0, buyOnExchange_Equity = 0, buyOffExchange_Equity = 0, //买入非权益类场内期权 买入非权益类场外期权 卖出权益类场外期权 卖出非权益类场外期权 权益类互换 非权益类互换 权益类互换 非权益类互换 buyOnExchange_NonEquity = 0, buyOffExchange_NonEquity = 0, sellOffExchange_Equity = 0, sellOffExchange_NonEquity = 0, swap_Equity = 0, swap_NonEquity = 0, forward_Equity = 0, forward_NonEquity = 0; riskResult.ForEach(O => { var um = O.Underlyings.FirstOrDefault() ?? new underlying_manager(); //国海强子说暂时不区分是否有效对冲,全部按照已有效对冲算; var hedgeStatus = true;//hedgeGroup.Contains(um.UnderlyingCode); if (O.Trade.TradeType != "场内期权" && ConsTrade.TradeTypesForHedge.Contains(O.Trade.TradeType)) { switch (um?.UnderlyingInstrumentType) { case "Stock"://股票交易 case "StockIndex"://股票交易 stock += O.ValueResult.Pv.Normalize() * (hedgeStatus ? 0.05 : 1); break; case "StockIF"://股指期货交易 stockIF += O.Trade.StockEqvNotional.Normalize() * 0.15 * (hedgeStatus ? 0.05 : 1); break; default: if (underlyingInstrumentTypeList.Contains(um.UnderlyingInstrumentType ?? "") || um.UnderlyingInstrumentType == ConsGlobal.InstrumentType.TBFutures) { if (Regex.IsMatch(O.Trade.UnderlyingCode ?? "", @"^T[F|S]??\d+$"))//国债期货交易 { tbFutures += O.Trade.StockEqvNotional.Normalize() * 0.05 * (hedgeStatus ? 0.01 : 1); } else//其他商品期货交易 { commodity += O.Trade.StockEqvNotional.Normalize() * 0.1 * (hedgeStatus ? 0.01 : 1); } } else { } break; } } else if (O.Trade.TradeType == "场内期权" && O.Trade.BuySell != "买入") { if (underlyingInstrumentTypeList.Contains(um.UnderlyingInstrumentType ?? ""))//卖出场内非权益期权 { sellOnExchange_NonEquity += O.ValueResult.DeltaCash.Normalize() * 0.15 * (hedgeStatus ? 0.01 : 1); } else//卖出场内权益期权 { sellOnExchange_Equity += O.ValueResult.DeltaCash.Normalize() * 0.15 * (hedgeStatus ? 0.05 : 1); } } }); totalTradeList.ForEach(O => { if (O.BuySell != "买入") { return; } var um = DataCacheProvider.GetUnderlyingDataSource().GetData(O.UnderlyingCode ?? "") ?? new underlying_manager(); //国海强子说暂时不区分是否有效对冲,全部按照已有效对冲算; var hedgeStatus = true;//hedgeGroup.Contains(um.UnderlyingCode); if (underlyingInstrumentTypeList.Contains(um.UnderlyingInstrumentType)) { if (O.TradeType == "场内期权")//买入非权益场内期权 { buyOnExchange_NonEquity += Math.Abs(O.TradePrice.Normalize()) * (hedgeStatus ? 0.01 : 1); } else if (!ConsTrade.TradeTypesForHedge.Contains(O.TradeType) && !ignoreTradeTypes.Contains(O.TradeType))//买入非权益场外期权 { buyOffExchange_NonEquity += Math.Abs(O.TradePrice.Normalize()) * (hedgeStatus ? 0.01 : 1); } } else { if (O.TradeType == "场内期权")//买入权益场内期权 { buyOnExchange_Equity += Math.Abs(O.TradePrice.Normalize()) * (hedgeStatus ? 0.05 : 1); } else if (!ConsTrade.TradeTypesForHedge.Contains(O.TradeType) && !ignoreTradeTypes.Contains(O.TradeType))//买入权益场外期权 { buyOffExchange_Equity += Math.Abs(O.TradePrice.Normalize()) * (hedgeStatus ? 0.05 : 1); } } }); tradeList.ForEach(O => { if (O.TradeType != "收益互换" && O.TradeType != "远期") { return; } var um = DataCacheProvider.GetUnderlyingDataSource().GetData(O.UnderlyingCode ?? "") ?? new underlying_manager(); //国海强子说暂时不区分是否有效对冲,全部按照已有效对冲算; var hedgeStatus = true;//hedgeGroup.Contains(um.UnderlyingCode); if (O.TradeType == "收益互换") { if (underlyingInstrumentTypeList.Contains(um?.UnderlyingInstrumentType ?? ""))//非权益 互换或远期 { swap_NonEquity += O.StockEqvNotional.Normalize() * 0.1 * (hedgeStatus ? 0.01 : 1); } else//权益 互换或远期 { swap_Equity += O.StockEqvNotional.Normalize() * 0.1 * (hedgeStatus ? 0.05 : 1); } } if (O.TradeType == "远期") { if (underlyingInstrumentTypeList.Contains(um?.UnderlyingInstrumentType ?? ""))//非权益 互换或远期 { forward_NonEquity += O.StockEqvNotional.Normalize() * 0.1 * (hedgeStatus ? 0.01 : 1); } else//权益 互换或远期 { forward_Equity += O.StockEqvNotional.Normalize() * 0.1 * (hedgeStatus ? 0.05 : 1); } } }); foreach (var item in maxLoss)//卖出权益/非权益场外期权 { var um = DataCacheProvider.GetUnderlyingDataSource().GetData(item.Key) ?? new underlying_manager(); //国海强子说暂时不区分是否有效对冲,全部按照已有效对冲算; var hedgeStatus = true;//hedgeGroup.Contains(um.UnderlyingCode); if (underlyingInstrumentTypeList.Contains(um.UnderlyingInstrumentType ?? "")) { sellOffExchange_NonEquity += item.Value.Normalize() * (hedgeStatus ? 0.01 : 1); } else { sellOffExchange_Equity += item.Value.Normalize() * (hedgeStatus ? 0.05 : 1); } } offExchangeEquity = buyOffExchange_Equity + sellOffExchange_Equity; offExchangeNonEquity = buyOffExchange_NonEquity + sellOffExchange_NonEquity; swapEquity = swap_Equity; swapNonEquity = swap_NonEquity; forwardEquity = forward_Equity; forwardNonEquity = forward_NonEquity; return (sellOnExchange_Equity + buyOnExchange_Equity + stock + stockIF, sellOnExchange_NonEquity + buyOnExchange_NonEquity + commodity + tbFutures, buyOffExchange_Equity + sellOffExchange_Equity + swap_Equity + forward_Equity, buyOffExchange_NonEquity + sellOffExchange_NonEquity + swap_NonEquity + forward_NonEquity); } /// /// 获取对冲组合 /// 只需要传入场外交易列表即可,函数内会提取同标的同簿记的场内交易计算 /// /// 场外交易列表 /// 价格提供者 /// 所有标的信息代码 /// 含场内场外的交易列表 /// 符合计算条件的交易列表 /// 已对冲的标的代码 /// 未对冲的标的代码 /// public List GetHedgeGroup(List tradeList, IPriceProvider underlyingPrice, out IEnumerable codes, out List totalTradeList, out List calcTradeList, out HashSet hedgeGroup, out HashSet nonhedgeGroup) { codes = tradeList.Where(O => !string.IsNullOrEmpty(O.UnderlyingCode)).Select(O => O.UnderlyingCode).Distinct(); var assetIds = tradeList.Select(O => O.AssetId).Distinct(); totalTradeList = new List(tradeList.Where(O => O.TradeType != "收益互换" && O.TradeType != "远期")); totalTradeList.AddRange(GetHedgeTradeList(codes, assetIds)); calcTradeList = totalTradeList.Where(O => ignoreTradeTypes.Contains(O.TradeType) || ConsTrade.TradeTypesForHedge.Contains(O.TradeType) || O.BuySell == "卖出").ToList(); //设置option info tradeBLL.SetFieldsByTradeType(calcTradeList); new TradeDalService(this).SetSubTradeList(calcTradeList); var riskResult = GetHedgeGroup(calcTradeList, underlyingPrice, out hedgeGroup, out nonhedgeGroup); return riskResult; } private static List _quotaSettings = null; private static List _quotaIndexList = null; /// /// 限额指标列表 /// public static List QuotaIndexList { get { if (_quotaIndexList == null) { _quotaIndexList = _quotaSettings.Select(O => O.QuotaIndex).Distinct().ToList(); _quotaIndexList.Sort(); } return _quotaIndexList; } } /// /// 状态 /// public static List StatusqutaIndexList { get { if (_quotaIndexList == null) { _quotaIndexList = _quotaSettings.Select(O => O.Status.ToString()).Distinct().ToList(); _quotaIndexList.Sort(); } return _quotaIndexList; } } public static List QueryQuotaIndexList { get { if (_quotaIndexList == null) { _quotaIndexList = _quotaSettings.Select(O => O.IsValid.ToString()).Distinct().ToList(); _quotaIndexList.Sort(); } return _quotaIndexList; } } private static double? businessTotalScale = 0; private static double? businessAvailableFunds = 0; private static double? singleUnderlyingRate = 0; private static void initSetting() { if (valuedateBLL.SystemDate.BusinessTotalScale != businessTotalScale || valuedateBLL.SystemDate.BusinessAvailableFunds != businessAvailableFunds || valuedateBLL.SystemDate.SingleUnderlyingRate != singleUnderlyingRate) { businessTotalScale = valuedateBLL.SystemDate.BusinessTotalScale; businessAvailableFunds = valuedateBLL.SystemDate.BusinessAvailableFunds; singleUnderlyingRate = valuedateBLL.SystemDate.SingleUnderlyingRate; _quotaSettings = new List(); #region 整体业务 #endregion #region 互换 //互换-名义本金 _quotaSettings.Add(new QuotaSetting() { QuotaType = QuotaTypeEnum.GLOBAL_SWAP, QuotaRange = 0, QuotaIndex = "名义本金", QuotaLowerLimit = null, QuotaUpperLimit = null, WarningLowerLimit = null, WarningUpperLimit = null, Percent = false, IsValid = true, Status = QuotaSettingApprovalStatus.Valid, }); ////互换-浮动盈亏 //_quotaSettings.Add(new QuotaSetting() //{ // QuotaType = QuotaTypeEnum.GLOBAL_SWAP, // QuotaRange = 0, // QuotaIndex = "浮动盈亏", // QuotaLowerLimit = null, // QuotaUpperLimit = null, // WarningLowerLimit = null, // WarningUpperLimit = null, // Percent = false, // IsValid = true, // Status = QuotaSettingApprovalStatus.Valid, //}); #endregion #region 标的资产 //标的资产-名义本金 _quotaSettings.Add(new QuotaSetting() { QuotaType = QuotaTypeEnum.UNDERLYING, QuotaRange = 0, QuotaIndex = "轧差名义本金", QuotaLowerLimit = null, QuotaUpperLimit = null, WarningLowerLimit = null, WarningUpperLimit = null, Percent = false, IsValid = true, Status = QuotaSettingApprovalStatus.Valid, }); //标的资产-集中度 _quotaSettings.Add(new QuotaSetting() { QuotaType = QuotaTypeEnum.UNDERLYING, QuotaRange = 0, QuotaIndex = "轧差集中度", QuotaLowerLimit = null, QuotaUpperLimit = null, WarningLowerLimit = null, WarningUpperLimit = null, Percent = true, IsValid = true, Status = QuotaSettingApprovalStatus.Valid, }); #endregion #region 单笔交易 //交易-名义本金 _quotaSettings.Add(new QuotaSetting() { QuotaType = QuotaTypeEnum.TRADE, QuotaRange = 0, QuotaIndex = "名义本金", QuotaLowerLimit = null, QuotaUpperLimit = null, WarningLowerLimit = null, WarningUpperLimit = null, Percent = false, IsValid = true, Status = QuotaSettingApprovalStatus.Valid, }); //交易-互换价格偏离比例 _quotaSettings.Add(new QuotaSetting() { QuotaType = QuotaTypeEnum.TRADE, QuotaRange = 0, QuotaIndex = "互换价格偏离比例", QuotaLowerLimit = null, QuotaUpperLimit = null, WarningLowerLimit = null, WarningUpperLimit = null, Percent = true, IsValid = true, Status = QuotaSettingApprovalStatus.Valid, }); #endregion #region 客户 //客户-名义本金 _quotaSettings.Add(new QuotaSetting() { QuotaType = QuotaTypeEnum.CLIENT, QuotaRange = 0, QuotaIndex = "名义本金", QuotaLowerLimit = null, QuotaUpperLimit = null, WarningLowerLimit = null, WarningUpperLimit = null, Percent = false, IsValid = true, Status = QuotaSettingApprovalStatus.Valid, }); #endregion if (PS.Config.Company != Configuration.CompanyEnum.天风) { _quotaSettings = _quotaSettings.Where(O => !(O.QuotaIndex.StartsWith("Delta敞口") || O.QuotaIndex.StartsWith("Delta净敞口"))).ToList(); } for (var i = 0; i < _quotaSettings.Count; i++) { _quotaSettings[i].id = 0 - i - 1; } } } private List formatDefaultQuotaSettingDetails(List settings, QuotaTypeEnum? quotatype = null) { if (PS.Config.Company != Configuration.CompanyEnum.天风) { settings = settings.Where(O => !(O.QuotaIndex.StartsWith("Delta敞口") || O.QuotaIndex.StartsWith("Delta净敞口"))).ToList(); } settings.ForEach(O => { var count = 2; if (O.Percent) { count = 4; } 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); O.WarningLowerLimit = O.WarningLowerLimit.FormatValue(count); O.WarningUpperLimit = O.WarningUpperLimit.FormatValue(count); }); var settingDict = settings.GroupBy(O => O.QuotaType).ToDictionary(K => K.Key, V => V.GroupBy(O => O.QuotaRange).ToDictionary(K => K.Key, V1 => V1.ToList())); foreach (var item in _quotaSettings) { if (quotatype == null || quotatype == item.QuotaType) { if (!settingDict.ContainsKey(item.QuotaType)) { settingDict[item.QuotaType] = new Dictionary> { { 0, new List() } }; } if (item.QuotaType != QuotaTypeEnum.STOCK_PERCENT && !settingDict[item.QuotaType].ContainsKey(0)) { var obj = item.Clone(); obj.QuotaRange = 0; obj.Precheck = item.Precheck; settings.Add(obj); } foreach (var o in settingDict[item.QuotaType]) { if (!o.Value.Exists(O => O.QuotaType == item.QuotaType && O.QuotaIndex == item.QuotaIndex)) { var obj = item.Clone(); obj.QuotaRange = o.Key; obj.Precheck = item.Precheck; settings.Add(obj); } } } } return settings; } /// /// 查询设置 /// /// public List QueryCurrentQuotaSetting() { var result = QueryPrecheckQuotaSetting(false); result = formatDefaultQuotaSettingDetails(result); result.Sort((X, Y) => string.Compare(X.QuotaIndex, Y.QuotaIndex)); return result; } /// /// 查询设置 /// /// /// /// public List QueryCurrentQuotaSetting(QuotaTypeEnum quotaType) { var list = DbContext.quotaSetting.AsNoTracking().Where(O => O.IsValid && O.Status == QuotaSettingApprovalStatus.Valid && O.QuotaType == quotaType).ToList(); //list = formatDefaultQuotaSettingDetails(list, quotaType); list.Sort((X, Y) => string.Compare(X.QuotaIndex, Y.QuotaIndex)); return list; } /// /// 查询设置 /// /// /// /// public List QueryPrecheckQuotaSetting(bool onlyPrecheck) { var query = DbContext.quotaSetting.AsNoTracking().Where(O => O.IsValid && O.Status == QuotaSettingApprovalStatus.Valid); if (onlyPrecheck) { query = query.Where(O => O.Precheck); } return query.ToList(); } /// /// 转换百分比指标为绝对值 /// /// private QuotaSetting ConvertToAbs(QuotaSetting setting, double totalProductScale = 0, double productScale = 0) { switch (setting.QuotaIndex) { case "名义本金": case "轧差名义本金": setting.ConvertToAbs(valuedateBLL.SystemDate.BusinessTotalScale.Normalize()); break; case "Delta金额": case "Gamma金额": case "Vega": case "Vega金额": case "Theta": setting.ConvertToAbs(valuedateBLL.SystemDate.BusinessAvailableFunds.Normalize()); break; case "产品占用资金限额": if (setting.QuotaRange == 0) { setting.ConvertToAbs(totalProductScale); } else { setting.ConvertToAbs(productScale); } break; default: break; } return setting; } /// /// 合并限额设置,没有设置的指标用默认值填充 /// /// /// /// /// private List MargeQuotaSetting(List list, double totalProductScale = 0, double productScale = 0) { var settings = new List(); if (PS.Config.Company != Configuration.CompanyEnum.天风) { list = list.Where(O => !(O.QuotaIndex.StartsWith("Delta敞口") || O.QuotaIndex.StartsWith("Delta净敞口"))).ToList(); } var dict = list.GroupBy(O => O.QuotaType + O.QuotaIndex) .ToDictionary(K => K.Where(O => O.QuotaRange == 0).FirstOrDefault() ?? new QuotaSetting() { QuotaIndex = K.Key, IsValid = false, Status = QuotaSettingApprovalStatus.Valid, }, V => V.Where(O => O.QuotaRange != 0).ToList()); foreach (var item in dict) { if (!item.Key.IsValid) { settings.AddRange(item.Value); continue; } ConvertToAbs(item.Key); foreach (var vItem in item.Value) { ConvertToAbs(vItem); if (vItem.QuotaLowerLimit.HasValue && item.Key.QuotaLowerLimit.HasValue && vItem.QuotaLowerLimit.Value < item.Key.QuotaLowerLimit.Value) { vItem.QuotaLowerLimit = item.Key.QuotaLowerLimit; } if (vItem.QuotaUpperLimit.HasValue && item.Key.QuotaUpperLimit.HasValue && vItem.QuotaUpperLimit.Value > item.Key.QuotaUpperLimit.Value) { vItem.QuotaUpperLimit = item.Key.QuotaUpperLimit; } if (vItem.WarningLowerLimit.HasValue && item.Key.WarningLowerLimit.HasValue && vItem.WarningLowerLimit.Value < item.Key.WarningLowerLimit.Value) { vItem.WarningLowerLimit = item.Key.WarningLowerLimit; } if (vItem.WarningUpperLimit.HasValue && item.Key.WarningUpperLimit.HasValue && vItem.WarningUpperLimit.Value > item.Key.WarningUpperLimit.Value) { vItem.WarningUpperLimit = item.Key.WarningUpperLimit; } } settings.Add(item.Key); settings.AddRange(item.Value); } return settings; } public void ApprovalQuotaSetting(List req) { foreach (var item in req) { ApprovalQuotaSetting(item); } } public void ApprovalQuotaSetting(ApprovalReqq req) { var obj = DbContext.quotaSetting.Where(O => O.id == req.id).FirstOrDefault(); if (obj.Status == QuotaSettingApprovalStatus.Pending_Approval) { if (obj == null) { throw new ServiceException("数据不存在"); } if (obj.OptDate != req.optDate) { throw new ServiceException("信息已被修改,请重新提交"); } var logArr = new List(); switch (req.optType) { case "pass": obj.Status = QuotaSettingApprovalStatus.Valid; var oldObjs = DbContext.quotaSetting .Where(O => O.IsValid && O.Status == QuotaSettingApprovalStatus.Valid && O.QuotaType == obj.QuotaType && (O.QuotaRange == obj.QuotaRange || obj.QuotaType == QuotaTypeEnum.STOCK_PERCENT) && O.QuotaIndex == obj.QuotaIndex); logArr.Add(new Processlog() { TypeId = req.id, ProcessType = "QuotaSetting", ProcessStatus = "确认", Log = "已确认", Remark = req.remark, OptId = UserId, CreateTime = DateTime.Now }); foreach (var item in oldObjs) { item.IsValid = false; logArr.Add(new Processlog() { TypeId = item.id, ProcessType = "QuotaSetting", ProcessStatus = "确认", Log = "已无效", Remark = req.remark, OptId = UserId, CreateTime = DateTime.Now }); } break; case "reject": obj.Status = QuotaSettingApprovalStatus.Rejected; obj.IsValid = false; logArr.Add(new Processlog() { TypeId = req.id, ProcessType = "QuotaSetting", ProcessStatus = "确认", Log = "已拒绝", Remark = req.remark, OptId = UserId, CreateTime = DateTime.Now }); break; default: throw new ServiceException("错误的操作类型"); } DbContext.SaveChanges(); var log = new ClientProcessLogService(OptUser); logArr.ForEach(O => log.SetLog(O)); } } /// /// 保存设置 /// /// public List SaveQuotaSettings(List settings) { var msgList = new List(); using var db = new YLContext(); var dbData = db.quotaSetting.Where(O => O.IsValid || (!O.IsValid && O.Status == QuotaSettingApprovalStatus.Pending_Approval)).ToArray(); var logTime = DateTime.Now; foreach (var item in settings) { var msgTextList = new HashSet(); var useEdit = false; var exist = false; var dbList = dbData.Where(O => O.QuotaType == item.QuotaType && (O.QuotaRange == item.QuotaRange || item.QuotaType == QuotaTypeEnum.STOCK_PERCENT) && O.QuotaIndex == item.QuotaIndex).ToArray(); var temp = dbList.Where(O => O.Status > QuotaSettingApprovalStatus.Valid).FirstOrDefault(); if (exist = temp == null) { temp = dbList.FirstOrDefault()?.Clone(); temp ??= _quotaSettings.Where(O => O.QuotaType == item.QuotaType && O.QuotaIndex == item.QuotaIndex).FirstOrDefault()?.Clone(); temp.id = 0; db.quotaSetting.Add(temp); } var quotaLowerLimit = item.QuotaLowerLimit.IsNormalize() ? item.QuotaLowerLimit : null; var quotaUpperLimit = item.QuotaUpperLimit.IsNormalize() ? item.QuotaUpperLimit : null; var warningLowerLimit = item.WarningLowerLimit.IsNormalize() ? item.WarningLowerLimit : null; var warningUpperLimit = item.WarningUpperLimit.IsNormalize() ? item.WarningUpperLimit : null; if (!quotaLowerLimit.IsNormalize() && !quotaUpperLimit.IsNormalize() && !warningLowerLimit.IsNormalize() && !warningUpperLimit.IsNormalize()) { if (exist && temp.OptDate == null)//optDate==null说明是初始数据,不需要保存 { db.quotaSetting.Remove(temp); continue; } if (temp.IsValid) { temp.IsValid = false; useEdit = true; } } else { if (!temp.IsValid) { temp.IsValid = true; useEdit = true; } } quotaLowerLimit = OtcFormatHelper.FormatValue(quotaLowerLimit, item.Percent ? 4 : 2); quotaUpperLimit = OtcFormatHelper.FormatValue(quotaUpperLimit, item.Percent ? 4 : 2); warningLowerLimit = OtcFormatHelper.FormatValue(warningLowerLimit, item.Percent ? 4 : 2); warningUpperLimit = OtcFormatHelper.FormatValue(warningUpperLimit, item.Percent ? 4 : 2); var range = ""; if (item.QuotaRange > 0) { if (item.QuotaType == QuotaTypeEnum.CLIENT) { range = DataCacheProvider.GetClientDataSource().GetData(item.QuotaRange)?.Name; } else if (item.QuotaType == QuotaTypeEnum.UNDERLYING) { range = DataCacheProvider.GetUnderlyingDataSource().GetData(item.QuotaRange)?.UnderlyingCode; } else if (item.QuotaType == QuotaTypeEnum.STOCK_PERCENT) { range = item.QuotaRange + "天"; } } var suffix_Old = temp.Percent ? "0.##%" : "N"; var suffix_New = item.Percent ? "0.##%" : "N"; if (item.QuotaType == QuotaTypeEnum.STOCK_PERCENT && temp.QuotaRange != item.QuotaRange) { useEdit = true; msgTextList.Add($"{EnumHelper.GetDescriptionByName(item.QuotaType)}-观察天数:{temp.QuotaRange}天->{item.QuotaRange}天"); } if (temp.Precheck != item.Precheck) { useEdit = true; msgTextList.Add($"{EnumHelper.GetDescriptionByName(item.QuotaType)}-{(string.IsNullOrWhiteSpace(range) ? "" : (range + "-"))}{item.QuotaIndex}-事前检查:{(temp.Precheck ? "启用" : "禁用")}->{(item.Precheck ? "启用" : "禁用")}"); } if (temp.QuotaLowerLimit != quotaLowerLimit) { useEdit = true; msgTextList.Add($"{EnumHelper.GetDescriptionByName(item.QuotaType)}-{(string.IsNullOrWhiteSpace(range) ? "" : (range + "-"))}{item.QuotaIndex}-限额下限:{temp.QuotaLowerLimit?.ToString(suffix_Old)}->{quotaLowerLimit?.ToString(suffix_New)}"); } if (temp.QuotaUpperLimit != quotaUpperLimit) { useEdit = true; msgTextList.Add($"{EnumHelper.GetDescriptionByName(item.QuotaType)}-{(string.IsNullOrWhiteSpace(range) ? "" : (range + "-"))}{item.QuotaIndex}-限额上限:{temp.QuotaUpperLimit?.ToString(suffix_Old)}->{quotaUpperLimit?.ToString(suffix_New)}"); } if (temp.WarningLowerLimit != warningLowerLimit) { useEdit = true; msgTextList.Add($"{EnumHelper.GetDescriptionByName(item.QuotaType)}-{(string.IsNullOrWhiteSpace(range) ? "" : (range + "-"))}{item.QuotaIndex}-预警下限:{temp.WarningLowerLimit?.ToString(suffix_Old)}->{warningLowerLimit?.ToString(suffix_New)}"); } if (temp.WarningUpperLimit != warningUpperLimit) { useEdit = true; msgTextList.Add($"{EnumHelper.GetDescriptionByName(item.QuotaType)}-{(string.IsNullOrWhiteSpace(range) ? "" : (range + "-"))}{item.QuotaIndex}-预警上限:{temp.WarningUpperLimit?.ToString(suffix_Old)}->{warningUpperLimit?.ToString(suffix_New)}"); } if (temp.Percent != item.Percent) { useEdit = true; msgTextList.Add($"{EnumHelper.GetDescriptionByName(item.QuotaType)}-{(string.IsNullOrWhiteSpace(range) ? "" : (range + "-"))}{item.QuotaIndex}-单位:{(temp.Percent ? "百分比" : "金额")}->{(item.Percent ? "百分比" : "金额")}"); } if (useEdit) { temp.Status = QuotaSettingApprovalStatus.Pending_Approval; temp.QuotaIndex = item.QuotaIndex; temp.QuotaRange = item.QuotaRange; temp.QuotaType = item.QuotaType; temp.Precheck = item.Precheck; temp.QuotaLowerLimit = OtcFormatHelper.FormatValue(quotaLowerLimit, item.Percent ? 4 : 2); temp.QuotaUpperLimit = OtcFormatHelper.FormatValue(quotaUpperLimit, item.Percent ? 4 : 2); temp.WarningLowerLimit = OtcFormatHelper.FormatValue(warningLowerLimit, item.Percent ? 4 : 2); temp.WarningUpperLimit = OtcFormatHelper.FormatValue(warningUpperLimit, item.Percent ? 4 : 2); temp.Percent = item.Percent; temp.OptId = UserId; temp.OptName = UserName; temp.OptDate = DateTime.Now; db.SaveChanges(); msgList.Add(new Processlog() { TypeId = temp.id, ProcessType = "QuotaSetting", ProcessStatus = "修改", Log = string.Join("\r\n", msgTextList), OptId = UserId, CreateTime = logTime }); } else if (exist) { db.quotaSetting.Remove(temp); } } return msgList.ToList(); } /// /// 导入规则设置 /// /// /// public bool ImportFromCsv(Stream stream) { try { List dc = new List(); var resDic = new Dictionary(); var underlyingName = ""; var quotaName = ""; var errMsgs = new List(); var stockBlackWhite = new List(); dc.Add(new ExcelHelper.DataColumnModel("挂钩标的", nameof(QuotaSetting.QuotaRange), (cv, obj) => { string code = (string)cv; if (code == "全部") { return 0; } var underlying = DataCacheProvider.GetUnderlyingDataSource().GetData(code ?? ""); if (underlying == null) { underlying = new underlying_manager(); string Msgs = "不存在该挂钩标的"; errMsgs.Add(Msgs); } else { underlyingName = underlying.UnderlyingCode; } return underlying.id; })); var quotaIndexList = _quotaSettings.Where(O => O.QuotaType == QuotaTypeEnum.UNDERLYING).Select(O => O.QuotaIndex).ToArray(); dc.Add(new ExcelHelper.DataColumnModel("限额指标", nameof(QuotaSetting.QuotaIndex), (cv, obj) => { string value = cv as string; if (!quotaIndexList.Contains(value ?? "")) { quotaIndexList = new string[0]; string Msgs = "限额指标不存在"; errMsgs.Add(Msgs); } else { quotaName = value; } return value; })); dc.Add(new ExcelHelper.DataColumnModel("事前检查", nameof(QuotaSetting.Precheck), (cv, obj) => { bool resulCheck = true; switch (cv) { case "是": resulCheck = true; break; case "否": resulCheck = false; break; default: string Msgs = "没有填写事前检查"; errMsgs.Add(Msgs); break; } return resulCheck; })); dc.Add(new ExcelHelper.DataColumnModel("限额下限", nameof(QuotaSetting.QuotaLowerLimit), (cv, obj) => { if (cv is DBNull) { return null; } string s = (string)cv; var res = GetDouble("限额下限", resDic, s, underlyingName, quotaName, out List messages); errMsgs.AddRange(messages); return res; })); dc.Add(new ExcelHelper.DataColumnModel("", nameof(QuotaSetting.Percent), (obj) => { var row = obj as DataRow; return row["限额下限"].ToString().Contains("%"); })); dc.Add(new ExcelHelper.DataColumnModel("限额上限", nameof(QuotaSetting.QuotaUpperLimit), (cv, obj) => { if (cv is DBNull) { return null; } string s = (string)cv; //var _ = obj as Datarow; //_.Percent = s.Contains("%"); var res = GetDouble("限额上限", resDic, s, underlyingName, quotaName, out List messages); errMsgs.AddRange(messages); return res; })); dc.Add(new ExcelHelper.DataColumnModel("", nameof(QuotaSetting.Percent), (obj) => { var row = obj as DataRow; return row["限额上限"].ToString().Contains("%"); })); dc.Add(new ExcelHelper.DataColumnModel("预警下限", nameof(QuotaSetting.WarningLowerLimit), (cv, obj) => { if (cv is DBNull) { return null; } string s = (string)cv; var res = GetDouble("预警下限", resDic, s, underlyingName, quotaName, out List messages); errMsgs.AddRange(messages); return res; })); dc.Add(new ExcelHelper.DataColumnModel("", nameof(QuotaSetting.Percent), (obj) => { var row = obj as DataRow; return row["预警下限"].ToString().Contains("%"); })); dc.Add(new ExcelHelper.DataColumnModel("预警上限", nameof(QuotaSetting.WarningUpperLimit), (cv, obj) => { if (cv is DBNull) { return null; } string s = (string)cv; var res = GetDouble("预警上限", resDic, s, underlyingName, quotaName, out List messages); errMsgs.AddRange(messages); return res; })); dc.Add(new ExcelHelper.DataColumnModel("", nameof(QuotaSetting.Percent), (obj) => { var row = obj as DataRow; return row["预警上限"].ToString().Contains("%"); })); var dic = new ExcelHelper().ExcelToListT(dc.ToArray(), stream, new[] { "Sheet1" }); if (dic.Count == 0 || dic["Sheet1"].Count == 0) { throw new ServiceException("导入失败:读取数据为空"); } foreach (var d in dic.Values) { d.ForEach(d => d.QuotaType = QuotaTypeEnum.UNDERLYING); var checkD = d.Select(s => new { s.QuotaIndex, s.QuotaLowerLimit }).ToList(); } if (errMsgs.Count > 0) { throw new ServiceException(string.Join("\r\n", errMsgs.Distinct())); } SaveQuotaSettings(dic["Sheet1"]); return true; } catch (ApiException ex) { LogFactory.GetLogger("QuotaSetting").Error(ex, "导入限额指标,操作出错"); throw; } } private double GetDouble(string columnName, Dictionary dic, string s, string underlyingName, string quotaName, out List messages) { var key = $"挂钩标的:{underlyingName},限额指标:{quotaName}"; messages = new List(); if (s.Contains("%")) { if (quotaName == "本年累计盈亏" || quotaName == "浮动盈亏" || quotaName == "名义本金") { messages.Add(key + $"{columnName}填写不正确必须为数值"); return double.NaN; } else { if (dic.ContainsKey(key)) { if (!dic[key]) { messages.Add(key + "格式不统一;"); } } if (double.TryParse(s.Replace("%", ""), out double sl)) { sl = sl / 100; dic[$"挂钩标的:{underlyingName},限额指标:{quotaName}"] = true; return sl; } else { messages.Add(key + $"{columnName}格式不正确"); return double.NaN; } } } else { if (quotaName == "集中度") { messages.Add(key + $"{columnName}填写不正确必须含有%"); return double.NaN; } else { if (dic.ContainsKey(key)) { if (dic[key]) { messages.Add(key + "格式不统一;"); } } if (double.TryParse(s, out double sl)) { dic[$"挂钩标的:{underlyingName},限额指标:{quotaName}"] = false; return sl; } else { messages.Add(key + $"{columnName}格式不正确"); return double.NaN; } } } } /// /// 删除设置 /// /// /// public void DeleteQuotaSettings(QuotaTypeEnum quotaType, int quotaRange) { var dbData = DbContext.quotaSetting.Where(O => O.QuotaType == quotaType && O.QuotaRange == quotaRange); DbContext.quotaSetting.RemoveRange(dbData); } /// /// 查询操作记录 /// /// /// public SearchListResult QueryQuotaSettingHistory(ProcesslogReq req) { var service = new ClientProcessLogService(OptUser); req.ProcessType = "QuotaSetting"; req.sord = "desc"; var result = service.SearchList(req); return result; } public bool QuotaCheck(ref TradeOpenResult res) { if (res == null && res.Trade == null) { throw new ArgumentNullException(nameof(res)); } var tradeId = (res.Trade.ParentTradeId == 0 || res.Trade.IsGroup != 0) ? res.Trade.id : res.Trade.ParentTradeId; var tradeNumber = DbContext.trade.Where(O => O.id == tradeId).Select(O => O.TradeNumber).FirstOrDefault(); var trialService = new QuotaMonitorService(this); var quotaObj = trialService.QuotaCheck(tradeId, 1); if (quotaObj.TrialStatus == QuotaTrialStatusEnum.Success)//这次计算结果是通过,直接通过 { if (!string.IsNullOrWhiteSpace(quotaObj.AvailableForClient)) // 返回用户资金情况 { res.RetCode = TradeOpenRetCode.FundStatus; res.TrialDataId = quotaObj.id; return false; } return true; } var lastTrial = trialService.QueryLastQuotaTrial(tradeId, true); if (quotaObj.TrialStatus == QuotaTrialStatusEnum.Success) { new TradeRiskCheckLogService(UserInfo).AddLog(quotaObj); } //否则的情况是上次没算,这次是预警,或上次算了,结果是不通过\通过或预警,这次是预警或不通过,提示用户; res.RetCode = TradeOpenRetCode.QuotaTrialError; res.TrialDataId = quotaObj.id; return false; } /// /// 校验电子盘下单风控 /// /// /// public List QuotaClientCheck(ClientRiskCheckItemParam clientRiskCheckReq) { List clientRiskCheckResps = new List(); var allList = QueryPrecheckQuotaSetting(false); var precheckQuotaSettingList = allList.Where(O => O.Precheck).ToList(); List quotaIndexs = new List() { "名义本金", "轧差集中度", "轧差名义本金" }; if (clientRiskCheckReq.isClient) { precheckQuotaSettingList = precheckQuotaSettingList.Where(x => quotaIndexs.Contains(x.QuotaIndex)).ToList(); } var client = DataCacheProvider.GetClientDataSource().GetData(clientRiskCheckReq.clientId); if (client == null) { throw new ServiceException("未找到客户信息"); } var dealDate = DateTime.Parse(clientRiskCheckReq.dealDate); using var bondDb = new BondOmsDBContext(); 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 checkPoisiList = BuildCheckQuotaMoitorModels(clientPositions, ums, dealDate); var underlyingPositions = checkPoisiList.Where(x => x.UnderlyingCode == clientRiskCheckReq.securityId).ToList(); var sameQty = underlyingPositions.Where(x=>x.ClientId== clientRiskCheckReq.clientId && x.Side== clientRiskCheckReq.side).Sum(s => s.Qty); var qty = underlyingPositions.Where(x => x.ClientId == clientRiskCheckReq.clientId && x.Side != clientRiskCheckReq.side).Sum(s => s.Qty); if (sameQty > qty) { //开仓需要校验适当性评估 if (client.EvaluateExpireDate < dealDate) { throw new ServiceException("适当性评估已经过期,只有在适当性有效期内才可以新开仓"); } } else { return clientRiskCheckResps; //平仓不过风控 } if (precheckQuotaSettingList.Count == 0) { return clientRiskCheckResps; } foreach (var settingItem in precheckQuotaSettingList) { var clientRiskCheckItem = CreateClientRiskCheckItem(settingItem); if (clientRiskCheckItem == null) continue; if (!settingItem.QuotaLowerLimit.HasValue && !settingItem.QuotaUpperLimit.HasValue && !settingItem.WarningUpperLimit.HasValue && !settingItem.WarningLowerLimit.HasValue) { continue; } clientRiskCheckItem = CheckQuota(checkPoisiList, settingItem.QuotaType, clientRiskCheckItem, settingItem.QuotaRange, allList); if (clientRiskCheckItem != null) { clientRiskCheckItem.quotaType = $"{EnumHelper.GetDescriptionByName(settingItem.QuotaType)}({settingItem.QuotaIndex})"; clientRiskCheckResps.Add(clientRiskCheckItem); } } 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 clientPositions = bondDb.client_position.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(); clientPositions.ForEach(x => { x.position_qty *= 10000; }); return (clientPositions, clientOrderPositions); } /// /// 过滤风控检查项 /// /// /// /// /// private List FilterPrecheckQuotaSettings(List precheckQuotaSettingList, ClientRiskCheckItemParam clientRiskCheckReq) { var clientId = clientRiskCheckReq.clientId; var um = DataCacheProvider.GetUnderlyingDataSource().GetData(clientRiskCheckReq.securityId); var underlyingId = um?.id; // 分离三种类型数据 var clientQuotas = precheckQuotaSettingList.Where(x => x.QuotaType == QuotaTypeEnum.CLIENT); var underlyingQuotas = precheckQuotaSettingList.Where(x => x.QuotaType == QuotaTypeEnum.UNDERLYING); var otherQuotas = precheckQuotaSettingList.Where(x => x.QuotaType != QuotaTypeEnum.CLIENT && x.QuotaType != QuotaTypeEnum.UNDERLYING ); // 1. CLIENT类型处理(按QuotaIndex分组+三层优先级) var filteredClient = clientQuotas .GroupBy(x => x.QuotaIndex) .SelectMany(g => { // 优先级1: QuotaRange=clientId var p1 = g.Where(x => x.QuotaRange == clientId); if (p1.Any()) return p1; // 优先级2: QuotaRange=0 return g.Where(x => x.QuotaRange == 0); }) .ToList(); // 2. UNDERLYING类型分层筛选 var filteredUnderlying = underlyingQuotas .GroupBy(x => x.QuotaIndex) .SelectMany(g => { // 优先级1: QuotaRange=underlyingId var p1 = g.Where(x => x.QuotaRange == underlyingId); if (p1.Any()) return p1; // 优先级2: QuotaRange=0 return g.Where(x => x.QuotaRange == 0); }).ToList(); // 3. 合并所有类型(保留其他类型原样) return filteredClient .Concat(filteredUnderlying) .Concat(otherQuotas) .ToList(); } /// /// 构建风控指标类 /// /// /// 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*10000, position_notional_principal = item.full_price * item.order_qty* ConsGlobal.bondPriceMultiple * 10000, 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, direction = (int)SwapDirectionEnum.支付, id = clientRiskCheckReq.orderId, Current = true, }; posi.position_notional_principal = posi.position_qty * posi.deal_full_price_avg* ConsGlobal.bondPriceMultiple; 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!=null&& um.IsBond()&&string.IsNullOrEmpty(um.ExJson)) { bond = JsonHelper.Deserialize(um.ExJson); } bond.IssueSize = bond.IssueSize ?? 0; 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 * 100000000) ?? 0, Pnl = (decimal)((item.full_price_now - item.deal_full_price_avg) * (item.position_qty ?? 0) * ConsGlobal.bondPriceMultiple), Current = item.Current, }; }).ToList(); } /// /// 获取实时持仓数据构建限额查询数据 /// private List GetCheckPosiList() { List checkPoisiList = new List(); var dealDate = valuedateBLL.ValueDate; using var bondDb = new BondOmsDBContext(); var clientPositions = bondDb.client_position.AsNoTracking().ToList();//所有持仓 Dictionary eodPriceDic = new Dictionary(); foreach (var item in clientPositions) { 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 = 1; checkQuotaMoitorModel.Pv = item.position_notional_principal ?? 0; checkQuotaMoitorModel.UnderlyingCode = item.security_id; checkQuotaMoitorModel.Side = item.side; checkQuotaMoitorModel.Commision = item.commission ?? 0; checkQuotaMoitorModel.Direction = item.direction; checkQuotaMoitorModel.ClientId = Convert.ToInt32(item.client_id ?? 0); checkQuotaMoitorModel.Delta = (checkQuotaMoitorModel.Side == 0 ? checkQuotaMoitorModel.Price : -checkQuotaMoitorModel.Price) * checkQuotaMoitorModel.Qty; EodPrice bondPrice = null; if (eodPriceDic.ContainsKey(checkQuotaMoitorModel.UnderlyingCode)) { bondPrice = eodPriceDic[checkQuotaMoitorModel.UnderlyingCode]; } else { bondPrice = EodPriceQueryService.GetBondPrice(dealDate, item.security_id); } var lastPrice = bondPrice != null ? bondPrice.ClosePrice : 0; decimal vobp = bondPrice != null ? bondPrice.Vobp ?? 0 : 0; var ratio = checkQuotaMoitorModel.Side == 0 ? 1 : -1; var directionRatio = checkQuotaMoitorModel.Direction == (int)SwapDirectionEnum.收取 ? 1 : -1; vobp = vobp * ratio * directionRatio; checkQuotaMoitorModel.LastPrice = Convert.ToDecimal(lastPrice); checkQuotaMoitorModel.Pnl = (checkQuotaMoitorModel.NowPrice - checkQuotaMoitorModel.Price) * checkQuotaMoitorModel.Qty; checkQuotaMoitorModel.Current = checkQuotaMoitorModel.id == 0; checkPoisiList.Add(checkQuotaMoitorModel); } return checkPoisiList; } /// /// 查询最新的试算结果 /// /// 要查询的交易 /// 是否忽略自动试算的记录(确认成交,交易复核等操作时,会自动调用试算) /// public QuotaTrial QueryLastQuotaTrial(int tradeId, bool ignoreAuto) { var query = DbContext.quotaTrial.Where(O => O.TradeId == tradeId); if (ignoreAuto) { query = query.Where(O => O.TrialSource == 0); } var result = query.OrderByDescending(O => O.OptDate).FirstOrDefault(); return result; } /// /// 查询试算结果 /// /// public QuotaTrial QueryQuotaTrial(int id) { var result = DbContext.quotaTrial.Where(O => O.id == id).FirstOrDefault(); return result; } /// /// 查询试算历史 /// /// public SearchListResult QueryQuotaTrial(QuotaTrialReq req) { var query = DbContext.quotaTrial.Where(O => O.TradeId == req.TradeId).AsQueryable(); var result = query.ToSearchList(req); return result; } /// /// 交易试算 /// /// 交易Id,结构化交易请传主交易Id /// 试算来源,0:手动点击试算按钮;1:确认成交时自动调用; /// public QuotaTrial QuotaCheck(int tradeId, int trialSource) { var result = new QuotaTrial(); var tradeObj = DbContext.trade.AsNoTracking().Where(O => O.id == tradeId).FirstOrDefault(); var tradeList = new List(); if (tradeObj.TradeType == "结构化交易") { tradeList.AddRange(DbContext.trade.AsNoTracking().Where(O => O.ParentTradeId == tradeId).ToArray()); } else { tradeList.Add(tradeObj); } var fundStatus = CheckFund(tradeList, tradeObj.StockEqvNotional, tradeObj.ClientId, out var fundMsg, out var availableMsg); //var riskWarningStatus = CheckRiskWarning(tradeList, out var riskWarningMsg); string riskWarningMsg = string.Empty; var riskWarningStatus = true; tradeList = tradeList.Where(O => !string.IsNullOrEmpty(O.UnderlyingCode) && O.TradeType != "结构化交易" && O.TradeType != "现金流交易").ToList(); if (tradeList.Count == 0) { //("当前交易不需要试算"); return new QuotaTrial() { TrialStatus = QuotaTrialStatusEnum.Success }; } string quotaWarningMsg = "", quotaMsg = ""; bool quotaWarningStatus = true, quotaStatus = true; var positions = DbContext.swap_position.Where(x => x.SwapTradeId == tradeId && x.PosiQuantity > 0 && x.IsInitial && !x.Invalid).ToList(); quotaStatus = CheckUnderlyingWhiteList(tradeObj.ClientId, positions, out quotaMsg); if (quotaStatus) { var allList = QueryPrecheckQuotaSetting(false); var list = allList.Where(O => O.Precheck).ToList(); if (list.Count > 0) { var clientPrecheckQuotaSettingList = list.Where(x => x.QuotaRange == tradeObj.ClientId && x.QuotaType == QuotaTypeEnum.CLIENT && x.QuotaIndex == "名义本金").ToList(); var allClientPrecheckQuotaSettingList = list.Where(x => x.QuotaRange == 0 && x.QuotaType == QuotaTypeEnum.CLIENT && x.QuotaIndex == "名义本金").ToList(); if (clientPrecheckQuotaSettingList.Count > 0) { foreach (var item in allClientPrecheckQuotaSettingList) { list.Remove(item); } } List clientRiskCheckResps = new List(); var floatPosi = DbContext.swap_position.Where(x => x.SwapTradeId == tradeId && x.PosiQuantity > 0 && x.IsInitial && !x.Invalid).FirstOrDefault(); if (floatPosi != null) { using var bondDb = new BondOmsDBContext(); var clientPositions = bondDb.client_position.AsNoTracking().ToList();//所有持仓 ClientPosition posi = new ClientPosition(); posi.commission = floatPosi.PosiTradingFee; posi.side = floatPosi.PositionType == 1 ? 0 : 1; posi.security_id = floatPosi.UnderlyingCode; posi.full_price_now = floatPosi.PosiNetPrice * ConsGlobal.bondShowPriceMultiple; posi.deal_full_price_avg = floatPosi.PosiNetPrice * ConsGlobal.bondShowPriceMultiple; posi.client_id = tradeObj.ClientId; posi.position_qty = floatPosi.PosiQuantity / 10000; posi.position_notional_principal = floatPosi.PosiNotionalValue; posi.direction = floatPosi.PosiDirection; clientPositions.Add(posi); var umCodes = clientPositions.Select(s => s.security_id).Distinct().ToList(); umCodes.Add(floatPosi.UnderlyingCode); umCodes = umCodes.Distinct().ToList(); var ums = DataCacheProvider.GetUnderlyingDataSource().AsQueryable().Where(x => umCodes.Contains(x.UnderlyingCode)); List checkPoisiList = new List(); var posiList = GetPosiQuotaMoitors(); foreach (var item in clientPositions) { var dealDate = tradeObj.TradeDate.Value; decimal vobp = 0; 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.UnderlyingCode = item.security_id; checkQuotaMoitorModel.Side = item.side; checkQuotaMoitorModel.Commision = item.commission ?? 0; checkQuotaMoitorModel.Direction = item.direction; 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); vobp = bondPrice != null ? bondPrice.Vobp ?? 0 : 0; 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; vobp = vobp * ratio * directionRatio; checkQuotaMoitorModel.LastPrice = Convert.ToDecimal(lastPrice); checkQuotaMoitorModel.Pnl = (checkQuotaMoitorModel.NowPrice - checkQuotaMoitorModel.Price) * checkQuotaMoitorModel.Qty; checkQuotaMoitorModel.Current = checkQuotaMoitorModel.id == 0; checkPoisiList.Add(checkQuotaMoitorModel); } quotaWarningStatus = CheckQuota(checkPoisiList, posiList, list, allList, true, out quotaWarningMsg); quotaStatus = CheckQuota(checkPoisiList, posiList, list, allList, false, out quotaMsg); } } } if (quotaWarningStatus && riskWarningStatus && fundStatus && quotaStatus) { result.TrialStatus = QuotaTrialStatusEnum.Success; } else { if (fundStatus && quotaStatus && (!quotaWarningStatus || !riskWarningStatus)) { result.TrialStatus = QuotaTrialStatusEnum.Warning; } else { result.TrialStatus = QuotaTrialStatusEnum.Error; } } result.TradeId = tradeObj.id; result.TradeNumber = tradeObj.TradeNumber; result.ClientName = tradeObj.ClientName; result.FundCheckDetails = fundMsg; result.QuotaCheckDetails = quotaMsg; result.RiskWarningDetails = riskWarningMsg; result.QuotaWarningDetails = quotaWarningMsg; result.AvailableForClient = availableMsg; result.TrialSource = trialSource; SaveQuotaTrial(result); return result; } /// /// 校验标的白名单 /// /// /// /// /// private bool CheckUnderlyingWhiteList(int clientId, List positions, out string quotaMsg) { quotaMsg = ""; if (positions.Count == 0) { return true; } var baseUrl = Environment.GetEnvironmentVariable("BondOmsInterface_BaseUrl"); var checkUrl = "/clientOrderAdmin/checkOrderBonds"; var clientOrderParams = new List(); foreach (var item in positions) { CheckUnderlyingRequest clientOrderParam = new CheckUnderlyingRequest() { clientId = clientId, securityId = item.UnderlyingCode, orderQty = item.PosiQuantity * 0.0001m, side = item.PositionType - 1 }; clientOrderParams.Add(clientOrderParam); } if (!string.IsNullOrEmpty(baseUrl) && !string.IsNullOrEmpty(checkUrl)) { var httpHelper = new HttpHelper(baseUrl, null); // http 请求 Web项目接口 var result = httpHelper.PostRequestNoAuth, ApiResponse>(checkUrl, clientOrderParams).Result; if (result != null && !result.success) { quotaMsg = result.message; return false; } } return true; } private bool CheckRiskWarning(List trades, out string msg) { msg = ""; var obj = new SubstandardTradeRuleService(OptUser).QueryCurrentRule(); var msgList = new HashSet(); foreach (var item in trades) { if (obj.StrikeOffset.HasValue && item.TradeType != "收益互换" && item.TradeType != "远期" && item.TradeType != "自定义交易" && item.TradeType != "现金流交易") { var strike = 1 - Math.Abs(item.Strike ?? 0); var s = (item.Strike ?? 0).ToString("0.00%"); var ss = (item.Strike ?? 0) / (item.SpotPrice ?? 0); if (item.IsMoneynessOptionData) { if (Math.Abs((item.Strike ?? 0) - 1) > obj.StrikeOffset) { msgList.Add($"行权价偏离:当前{s},偏离{Math.Abs(strike).ToString("0.00%")},预警:>{(obj.StrikeOffset ?? 0).ToString("0.00%")}"); } } else { if (item.SpotPrice.HasValue) { //var ss= (item.Strike ?? 0) / (item.SpotPrice ?? 0); var offsetPrice = Math.Abs(((item.Strike ?? 0) / item.SpotPrice.Value) - 1); if (offsetPrice > obj.StrikeOffset) { msgList.Add($"行权价偏离:当前{ss.ToString("0.00%")},偏离{Math.Abs(ss - 1).ToString("0.00%")},预警:>{obj.StrikeOffset.Value.ToString("0.00%")}"); } } else { msgList.Add($"期初价格不应为空"); } } } if (obj.LockTradingDay.HasValue && item.TradeType != "远期") { if (item.ExerciseDate.HasValue && item.TradeDate.HasValue) { var day = (item.ExerciseDate.Value - item.TradeDate.Value).Days; if (obj.MinTradingDay.HasValue) { if (day < obj.MinTradingDay.Value) { msgList.Add($"合约期限:当前存续{day}天,预警:<{obj.MinTradingDay.Value}"); } } } } } var tempList = new List(msgList); for (var i = 0; i < tempList.Count; i++) { tempList[i] = $"{i + 1}.{tempList[i]}"; } msg = string.Join("\r\n", tempList); return msgList.Count == 0; } private bool CheckFund(List trades, double stockEqvNotional, int clientId, out string msg, out string avmsg) { msg = ""; avmsg = ""; var totalCredit = 0.0; var availableStockEqvNotional = 0.0; var client = DataCacheProvider.GetClientDataSource().GetData(clientId); if (client == null) { msg = "获取客户信息失败!"; return false; } if (client.SwapTradeType == 0)// 非dma不做资金校验 { msg = ""; return true; } var clientBalances = RealtimePnlCalc.GetRealTimeClientBanlance(new List { clientId }); if (clientBalances == null || clientBalances.Count <= 0) { msg = "客户实时资金错误!"; return false; } var subClientIds = getSubclientId(clientId); var positionStockEqvNotional = DbContext.trade .Where(O => ConsTrade.PositionTradeStatusList.Contains(O.TradeStatus) && O.ValidState != "InValid" && subClientIds.Contains(O.ClientId)) .Select(O => (double?)(O.ParentTradeId > 0 ? 0 : O.StockEqvNotional))?.Sum() ?? 0; totalCredit = DbContext.credit.Where(t => t.ClientId == clientId && t.ProcessStatus == "已审批" && (!t.CreditDeadLine.HasValue || t.CreditDeadLine >= SystemValueDate) && (!t.CreditStartDate.HasValue || t.CreditStartDate <= SystemValueDate)) .Select(O => O.StockEqvNotional).Sum() ?? double.NaN; if (PS.Config.ErpElement.UseClientStockEqvNotional) { availableStockEqvNotional = totalCredit - positionStockEqvNotional; } else { totalCredit = double.MaxValue; availableStockEqvNotional = totalCredit - positionStockEqvNotional; } foreach (var t in trades) { if (t.Warning) { new TradeDalService(OptUserInfo.SystemUser).RollbackToBeforeSettle(t, valuedateBLL.ValueDate); } } var msgList = new List(); if (trades.Any(O => O.IsOption() || O.TradeType == "结构化交易" || O.TradeType == "自定义交易" || O.TradeType == "现金流交易")) { var margin = trades.Sum(O => O.InitialMargin) ?? 0; var tradePrice = trades.Sum(O => O.TradePrice * (-TradeCalcHelper.GetSign(O.BuySell))) ?? 0; var clientBalance = clientBalances[0]; if (tradePrice <= 0) { if (trades[0].ExerciseDate.Value.Date >= valuedateBLL.ValueDate.Date) { var margins = margin + tradePrice; var AvailableAmount = Math.Max(clientBalance.AvailableAmount + clientBalance.TotalCredit, 0); if (AvailableAmount < margins) { if (PS.Config.Is申万) { msgList.Add($"当前账户可用资金{AvailableAmount.ToString("#,##0.000")}元,不足以支付预付金{margin.ToString("#,##0.000")}元"); } else { msgList.Add($"当前交易应付预付金:{margin.ToString("#,##0.000")},应付成交金额:{tradePrice.ToString("#,##0.000")},总应付资金:{margins.ToString("#,##0.000")}"); msgList.Add($"当前剩余资金:{clientBalance.AmountFund.ToString("#,##0.000")},冻结资金:{clientBalance.AllFreezeBalance().ToString("#,##0.000")},抵押品价值:{clientBalance.GuaranteesTotalAmount.ToString("#,##0.000")},授信额度:{clientBalance.TotalCredit.ToString("#,##0.000")},可用总额度:{AvailableAmount.ToString("#,##0.000")}。不足以支付上述金额。"); } } if ((availableStockEqvNotional - stockEqvNotional) < 0) { msgList.Add($"当前名义本金可用规模:{availableStockEqvNotional.ToString("#,##0.000")}。不足抵充该交易名义本金:{stockEqvNotional.ToString("#,##0.000")}"); } var amountFund = AvailableAmount - margin; avmsg = $"当前可用总额度:{AvailableAmount.ToString("#,##0.000")},计入当前交易后,可用总额度为:{amountFund.ToString("#,##0.000")}"; } } else { var AvailableAmount = clientBalance.AvailablePremium(); if (AvailableAmount < tradePrice) { if (PS.Config.Is申万) { msgList.Add($"当前账户可用资金{AvailableAmount.ToString("#,##0.000")}元,不足以支付期权费{tradePrice.ToString("#,##0.000")}元"); } else { msgList.Add($"当前交易应付预付金:{0.ToString("#,##0.000")},应付成交金额:{tradePrice.ToString("#,##0.000")},总应付资金:{tradePrice.ToString("#,##0.000")}"); msgList.Add($"当前剩余资金:{clientBalance.AmountFund.ToString("#,##0.000")},冻结资金:{clientBalance.AllFreezeBalance().ToString("#,##0.000")},抵押品价值:{clientBalance.GuaranteesTotalAmount.ToString("#,##0.000")},授信额度:{clientBalance.TotalCredit.ToString("#,##0.000")},可用总额度:{AvailableAmount.ToString("#,##0.000")}。不足以支付上述金额。"); } } if ((availableStockEqvNotional - stockEqvNotional) < 0) { msgList.Add($"当前名义本金可用规模:{availableStockEqvNotional.ToString("#,##0.000")}。不足抵充该交易名义本金:{stockEqvNotional.ToString("#,##0.000")}"); } var amountFund = AvailableAmount - tradePrice; avmsg = $"当前可用总额度:{AvailableAmount.ToString("#,##0.000")},计入当前交易后,可用总额度为:{amountFund.ToString("#,##0.000")}"; } } else if (trades[0].TradeType == "收益互换") { var clientBalance = clientBalances[0]; using var db = new YLContext(); var id = trades[0].id; var tradePrice = (trades[0].TradePrice ?? 0) * (-TradeCalcHelper.GetSign(trades[0].BuySell)); var margin = trades[0].InitialMargin ?? 0; var AvailableAmount = clientBalance.AvailablePremium(); if (AvailableAmount < tradePrice && tradePrice > 0) { if (PS.Config.Is申万) { msgList.Add($"当前账户可用资金{AvailableAmount.ToString("#,##0.000")}元,不足以支付交易费用{tradePrice.ToString("#,##0.000")}元"); } else { msgList.Add($"当前交易应付预付金:{0.ToString("#,##0.000")},应付成交金额:{tradePrice.ToString("#,##0.000")},总应付资金:{tradePrice.ToString("#,##0.000")}"); msgList.Add($"当前剩余资金:{clientBalance.AmountFund.ToString("#,##0.000")},冻结资金:{clientBalance.AllFreezeBalance().ToString("#,##0.000")},抵押品价值:{clientBalance.GuaranteesTotalAmount.ToString("#,##0.000")},授信额度:{clientBalance.TotalCredit.ToString("#,##0.000")},可用总额度:{AvailableAmount.ToString("#,##0.000")}。不足以支付上述金额。"); } var amountFund = AvailableAmount - tradePrice; avmsg = $"当前可用总额度:{AvailableAmount.ToString("#,##0.000")},计入当前交易后,可用总额度为:{amountFund.ToString("#,##0.000")}"; } else { var margins = margin + tradePrice; if (trades[0].ExerciseDate.Value.Date >= valuedateBLL.ValueDate.Date) { AvailableAmount = Math.Max(clientBalance.AvailableAmount + clientBalance.TotalCredit, 0); if (AvailableAmount < margins) { if (PS.Config.Is申万) { msgList.Add($"当前账户可用资金{AvailableAmount.ToString("#,##0.000")}元,不足以支付预付金{margin.ToString("#,##0.000")}元和交易费用{tradePrice.ToString("#,##0.000")}元的加和"); } else { msgList.Add($"当前交易应付预付金:{margin.ToString("#,##0.000")},应付成交金额:{tradePrice.ToString("#,##0.000")},总应付资金:{margins.ToString("#,##0.000")}"); msgList.Add($"当前剩余资金:{clientBalance.AmountFund.ToString("#,##0.000")},冻结资金:{clientBalance.AllFreezeBalance().ToString("#,##0.000")},抵押品价值:{clientBalance.GuaranteesTotalAmount.ToString("#,##0.000")},授信额度:{clientBalance.TotalCredit.ToString("#,##0.000")},可用总额度:{AvailableAmount.ToString("#,##0.000")}。不足以支付上述金额。"); } } var amountFund = AvailableAmount - tradePrice - margin; avmsg = $"当前可用总额度:{AvailableAmount.ToString("#,##0.000")},计入当前交易后,可用总额度为:{amountFund.ToString("#,##0.000")}"; } } if ((availableStockEqvNotional - trades[0].StockEqvNotional) < 0) { msgList.Add($"当前名义本金可用规模:{availableStockEqvNotional.ToString("#,##0.000")}。不足抵充该交易名义本金:{trades[0].StockEqvNotional.ToString("#,##0.000")}"); } } else if (trades[0].TradeType == "远期") { using var db = new YLContext(); var margin = trades[0].InitialMargin ?? 0; var tradePrice = trades[0].TradePrice ?? 0; var margins = margin + tradePrice; var id = trades[0].id; var forwardTrade = db.trade_forward.Where(x => x.TradeId == id).FirstOrDefault(); if (forwardTrade != null) { //远期验资,验证客户余额>= 开仓总费用 var clientBalance = clientBalances[0]; var AvailableAmount = clientBalance.AvailablePremium(); if (AvailableAmount < margins) { msgList.Add($"当前交易应付预付金:{margin.ToString("#,##0.000")},应付成交金额:{tradePrice.ToString("#,##0.000")},总应付资金:{margins.ToString("#,##0.000")}"); msgList.Add($"当前剩余资金:{clientBalance.AmountFund.ToString("#,##0.000")},冻结资金:{clientBalance.AllFreezeBalance().ToString("#,##0.000")},抵押品价值:{clientBalance.GuaranteesTotalAmount.ToString("#,##0.000")},授信额度:{clientBalance.TotalCredit.ToString("#,##0.000")},可用总额度:{AvailableAmount.ToString("#,##0.000")}。不足以支付上述金额。"); } if ((availableStockEqvNotional - trades[0].StockEqvNotional) < 0) { msgList.Add($"当前名义本金可用规模:{availableStockEqvNotional:#,##0.000}。不足抵充该交易名义本金:{trades[0].StockEqvNotional:#,##0.000}"); msgList.Add($"当前剩余资金:{clientBalance.AmountFund.ToString("#,##0.000")},冻结资金:{clientBalance.AllFreezeBalance().ToString("#,##0.000")},抵押品价值:{clientBalance.GuaranteesTotalAmount.ToString("#,##0.000")},授信额度:{clientBalance.TotalCredit.ToString("#,##0.000")},可用总额度:{AvailableAmount.ToString("#,##0.000")}。不足以支付上述金额。"); } var amountFund = AvailableAmount - margins; avmsg = $"当前可用总额度:{AvailableAmount:#,##0.000},计入当前交易后,可用总额度为:{amountFund.ToString("#,##0.000")}"; } else { msgList.Add("客户远期交易数据未找到!"); } } msg = string.Join("\r\n", msgList); if (!PS.Config.IsAvailableForClient) { avmsg = ""; } return msgList.Count == 0; } /// /// 执行试算检查 /// /// 当前试算的交易和盈亏值 /// 当前持仓的所有场外(不包含远期)交易+当前试算的交易和盈亏值 /// 当前持仓的所有远期交易+当前试算的交易和盈亏值;//之所以加这个字段,是因为只有整体业务试算时要包含远期交易 /// 场内持仓 /// 名义本金(包含远期) /// 远期交易名义本金;//之所以加这个字段,是因为只有整体业务试算时要包含远期交易 /// 试算配置 /// 试算类型是预警还是警告 /// 试算结果描述 /// private bool CheckQuota(List positionList, List posiList, List setting, List settingAll, bool warning, out string msg) { msg = ""; var msgList = new List(); var tag_prefix = ""; var dict = setting.GroupBy(O => new { O.QuotaType, O.QuotaRange }).ToDictionary(K => K.Key, V => V.ToArray()); foreach (var item in dict) { switch (item.Key.QuotaType) { case QuotaTypeEnum.GLOBAL_SWAP: msgList.AddRange(checkGlobal(positionList, posiList, "场外业务-互换", item.Value, warning)); break; case QuotaTypeEnum.TRADE: msgList.AddRange(checkTrade(positionList, posiList, "单笔交易", item.Value, warning)); break; case QuotaTypeEnum.UNDERLYING: msgList.AddRange(checkUnderlying(positionList, posiList, "标的资产", item.Value, settingAll, warning)); break; case QuotaTypeEnum.CLIENT: CheckQuotaMoitorModel current = positionList.FirstOrDefault(x => x.Current); var clientRange = item.Value.Where(O => O.QuotaRange == current.ClientId || O.QuotaRange == 0).ToArray(); if (!clientRange.Any()) { continue; } else { foreach (var quota in clientRange) { tag_prefix = quota.QuotaRange == 0 ? "客户合计" : "当前客户"; msgList.AddRange(checkClient(positionList, posiList, tag_prefix, new QuotaSetting[] { quota }, settingAll, warning)); } } break; default: continue; } } for (var i = 0; i < msgList.Count; i++) { if (string.IsNullOrWhiteSpace(msgList[i])) { msgList.RemoveAt(i); i = --i; continue; } msgList[i] = $"{i + 1}.{msgList[i]}"; } msg = string.Join("\r\n", msgList); return msgList.Count == 0; } /// /// 限额事前检查校验 /// /// /// /// /// /// /// /// /// /// 单笔名义本金 /// private ClientRiskCheckItem CheckQuota(List positionList, QuotaTypeEnum quotaType, ClientRiskCheckItem checkItem, int quotaRange, List settingAll) { var tag_prefix = ""; var positionListAll = positionList; switch (quotaType) { case QuotaTypeEnum.GLOBAL_SWAP: checkItem = checkGlobal(positionList, "场外业务-互换", checkItem, quotaType); break; case QuotaTypeEnum.TRADE: checkItem = checkTrade(positionList, "单笔交易", checkItem); break; case QuotaTypeEnum.UNDERLYING: checkItem = checkUnderlying(positionList, "标的资产", checkItem, quotaRange, settingAll); break; case QuotaTypeEnum.CLIENT: CheckQuotaMoitorModel current = positionList.FirstOrDefault(x => x.Current); if (current.ClientId == quotaRange || quotaRange == 0) { tag_prefix = quotaRange == 0 ? "客户合计" : "当前客户"; checkItem = checkClient(positionList, tag_prefix, checkItem, quotaRange, settingAll); } else { return null; } break; default: return null; } return checkItem; } /// /// 算标的交易盈亏 /// /// private double GetTradePositionPnl() { double currentValue = 0; List tradetypes = new List { "利率债", "信用债", "其它债券" }; var tposis = DbContext.TradePosition.Where(x => tradetypes.Contains(x.TradeType)).AsNoTracking().ToList(); var umCodes = tposis.Select(x => x.UnderlyingCode).Distinct().ToList(); var ums = DataCacheProvider.GetUnderlyingDataSource().AsQueryable().Where(x => umCodes.Contains(x.UnderlyingCode)); foreach (var item in tposis) { double lastPrice = 0; var um = ums.FirstOrDefault(x => x.UnderlyingCode == item.UnderlyingCode); double contractSize = 0; if (um != null) { contractSize = um.ContractSize; if (!um.IsBond()) { lastPrice = EodPriceQueryService.GetClosePrice(valuedateBLL.ValueDate, item.UnderlyingCode); } else { var bondPrice = EodPriceQueryService.GetBondPrice(valuedateBLL.ValueDate, item.UnderlyingCode); lastPrice = bondPrice != null ? bondPrice.ClosePrice : (um.Price ?? 0) * Convert.ToDouble(ConsGlobal.bondPriceMultiple); } } currentValue += lastPrice * item.Position - item.PositionCost; } return currentValue; } private List checkGlobal(List positionList, List posiList, string tag_prefix, QuotaSetting[] settings, bool warning) { var messageList = new List(); double? upperLimit, lowerLimit, currentValue = null; foreach (var settingItem in settings) { if (warning) { upperLimit = settingItem.WarningUpperLimit; lowerLimit = settingItem.WarningLowerLimit; } else { upperLimit = settingItem.QuotaUpperLimit; lowerLimit = settingItem.QuotaLowerLimit; } if (upperLimit == null && lowerLimit == null) { continue; } currentValue = null; double posiVal = 0; decimal noPosiVal = 0; decimal tradePosiVal = 0; var tag = $"{tag_prefix}({settingItem.QuotaIndex})"; CheckQuotaMoitorModel current = positionList.FirstOrDefault(x => x.Current); switch (settingItem.QuotaIndex) { 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 posiPv = positionList.Where(s => !s.Current).Sum(s => s.Pv); posiVal = Convert.ToDouble(posiPv); tradePosiVal = posiList.Sum(s => s.Pv); noPosiVal = posiPv - tradePosiVal; messageList.Add(SetQuotaMsg(tag, settingItem.QuotaIndex, currentValue, Convert.ToDouble(tradePosiVal), posiVal, Convert.ToDouble(current.Pv), Convert.ToDouble(noPosiVal), null, upperLimit, lowerLimit, settingItem.Percent, warning)); break; case "Delta金额": currentValue = Convert.ToDouble(positionList.Sum(s => s.Delta)); messageList.Add(SetMsg(tag, currentValue, upperLimit, lowerLimit, settingItem.Percent, warning)); break; case "浮动盈亏": currentValue = Convert.ToDouble(positionList.Sum(s => s.Pnl)); messageList.Add(SetMsg(tag, currentValue, upperLimit, lowerLimit, settingItem.Percent, warning)); break; default: continue; } } return messageList; } private ClientRiskCheckItem checkGlobal(List positionList, string tag_prefix, ClientRiskCheckItem checkItem, QuotaTypeEnum quotaType) { double? currentValue = null; currentValue = null; var tag = $"{tag_prefix}({checkItem.quotaType})"; CheckQuotaMoitorModel current = positionList.FirstOrDefault(x => x.Current); double posiVal = 0; switch (checkItem.quotaType) { case "名义本金": 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)) { return checkItem; } return null; case "浮动盈亏": currentValue = Convert.ToDouble(positionList.Sum(O => O.Pnl)); break; default: return null; } checkItem.currentValue = Math.Round(currentValue ?? 0, 2); if (!ValidateQuoteResult(checkItem)) { return checkItem; } return null; } private List checkTrade(List positionList, List posiList, string tag_prefix, QuotaSetting[] settings, bool warning) { if (string.IsNullOrEmpty(tag_prefix)) { throw new ArgumentException($"“{nameof(tag_prefix)}”不能是 Null 或为空", nameof(tag_prefix)); } if (settings is null) { throw new ArgumentNullException(nameof(settings)); } var messageList = new List(); double? upperLimit, lowerLimit, currentValue = null; CheckQuotaMoitorModel current = positionList.FirstOrDefault(x => x.Current); foreach (var settingItem in settings) { if (warning) { upperLimit = settingItem.WarningUpperLimit; lowerLimit = settingItem.WarningLowerLimit; } else { upperLimit = settingItem.QuotaUpperLimit; lowerLimit = settingItem.QuotaLowerLimit; } if (upperLimit == null && lowerLimit == null) { continue; } currentValue = null; var tag = $"{tag_prefix}({settingItem.QuotaIndex})"; double posiVal = 0; decimal noPosiVal = 0; decimal tradePosiVal = 0; switch (settingItem.QuotaIndex) { case "名义本金": var clientPv = positionList.Where(s => s.ClientId == current.ClientId && s.UnderlyingCode == current.UnderlyingCode && s.Side != current.Side).Sum(s => s.Pv * (s.Side == 0 ? 1 : -1)); var currentPv = current.Pv * (current.Side == 0 ? 1 : -1); currentValue = Convert.ToDouble(Math.Abs(clientPv + currentPv)); var posiPv = positionList.Where(s => s.ClientId == current.ClientId && s.UnderlyingCode == current.UnderlyingCode && !s.Current).Sum(s => s.Pv); posiVal = Convert.ToDouble(posiPv); tradePosiVal = posiList.Where(s => s.ClientId == current.ClientId && s.UnderlyingCode == current.UnderlyingCode).Sum(s => s.Pv); noPosiVal = posiPv - tradePosiVal; messageList.Add(SetQuotaMsg(tag, settingItem.QuotaIndex, currentValue, Convert.ToDouble(tradePosiVal), posiVal, Convert.ToDouble(current.Pv), Convert.ToDouble(noPosiVal), null, upperLimit, lowerLimit, settingItem.Percent, warning)); break; case "止损金额": currentValue = Convert.ToDouble(current.Pnl); messageList.Add(SetMsg(tag, currentValue, upperLimit, lowerLimit, settingItem.Percent, warning)); break; case "Delta金额": currentValue = Convert.ToDouble(current.Delta); messageList.Add(SetMsg(tag, currentValue, upperLimit, lowerLimit, settingItem.Percent, warning)); break; default: continue; } } return messageList; } private ClientRiskCheckItem checkTrade(List positionList, string tag_prefix, ClientRiskCheckItem checkItem) { if (string.IsNullOrEmpty(tag_prefix)) { throw new ArgumentException($"“{nameof(tag_prefix)}”不能是 Null 或为空", nameof(tag_prefix)); } double? currentValue = null; currentValue = null; double posiVal = 0; CheckQuotaMoitorModel current = positionList.FirstOrDefault(x => x.Current); var tag = $"{tag_prefix}({checkItem.quotaType})"; switch (checkItem.quotaType) { case "名义本金": var clientPv = positionList.Where(s => s.ClientId == current.ClientId && s.UnderlyingCode == current.UnderlyingCode && s.Side != current.Side).Sum(s => s.Pv * (s.Side == 0 ? 1 : -1)); var currentPv = current.Pv * (current.Side == 0 ? 1 : -1); currentValue = Convert.ToDouble(Math.Abs(clientPv + currentPv)); var posiPv = positionList.Where(s => s.ClientId == current.ClientId && s.UnderlyingCode == current.UnderlyingCode && !s.Current).Sum(s => s.Pv); posiVal = Convert.ToDouble(posiPv); checkItem.currentValue = Math.Round(currentValue ?? 0, 2); if (!ValidateQuoteResult(checkItem, currentValue, posiVal)) { return checkItem; } return null; case "互换价格偏离比例": var sportPrice = Convert.ToDouble(current.Price); var basePrice = Convert.ToDouble(current.LastPrice); if (checkItem.isPercent) { currentValue = basePrice == 0 ? 0 : Math.Abs((sportPrice / basePrice) - 1); } else { currentValue = Math.Abs(sportPrice - basePrice); } checkItem.currentValue = currentValue; if (!ValidateQuoteResult(checkItem)) { return checkItem; } return null; default: return null; } } private List checkUnderlying(List positionList, List posiList, string tag_prefix, QuotaSetting[] settings, List settingAll, bool warning) { if (string.IsNullOrEmpty(tag_prefix)) { throw new ArgumentException($"“{nameof(tag_prefix)}”不能是 Null 或为空", nameof(tag_prefix)); } if (settings is null) { throw new ArgumentNullException(nameof(settings)); } var messageList = new List(); double? upperLimit, lowerLimit, currentValue = null; CheckQuotaMoitorModel current = positionList.FirstOrDefault(x => x.Current); //settings 可能包含全部 var setting = settings.OrderByDescending(o => o.QuotaRange).First(); var noQuoteSetting = settingAll.FirstOrDefault(x => x.QuotaType == setting.QuotaType && x.QuotaRange == setting.QuotaRange && x.QuotaIndex == setting.QuotaIndex && !x.Precheck);//是否设置了不事前检查 if (noQuoteSetting != null) { return messageList; } if (warning) { upperLimit = setting.WarningUpperLimit; lowerLimit = setting.WarningLowerLimit; } else { upperLimit = setting.QuotaUpperLimit; lowerLimit = setting.QuotaLowerLimit; } if (upperLimit == null && lowerLimit == null) { return messageList; } if (current.UnderlyingId != setting.QuotaRange && setting.QuotaRange != 0) { return messageList; } currentValue = null; var tag = $"{tag_prefix}({setting.QuotaIndex})"; double posiVal = 0; decimal noPosiVal = 0; decimal tradePosiVal = 0; decimal currentPv = 0; decimal posiPv = 0; tradePosiVal = posiList.Where(s => s.UnderlyingCode == current.UnderlyingCode).Sum(s => s.Pv * (s.Side == 0 ? 1 : -1)); tradePosiVal = Math.Abs(tradePosiVal); 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)); posiPv = Math.Abs(posiPv); posiVal = Convert.ToDouble(posiPv); noPosiVal = posiPv - tradePosiVal; noPosiVal = Math.Abs(noPosiVal); switch (setting.QuotaIndex) { case "轧差名义本金": messageList.Add(SetQuotaMsg(tag, setting.QuotaIndex, currentValue, Convert.ToDouble(tradePosiVal), posiVal, Convert.ToDouble(current.Pv), Convert.ToDouble(noPosiVal), null, upperLimit, lowerLimit, setting.Percent, warning)); break; case "轧差集中度": currentValue = current.Circulation == 0 ? 0 : currentValue / Convert.ToDouble(current.Circulation); tradePosiVal = current.Circulation == 0 ? 0 : tradePosiVal / current.Circulation; posiVal = current.Circulation == 0 ? 0 : posiVal / Convert.ToDouble(current.Circulation); var curentPv = current.Circulation == 0 ? 0 : current.Pv / current.Circulation; noPosiVal = current.Circulation == 0 ? 0 : noPosiVal / current.Circulation; messageList.Add(SetQuotaMsg(tag, setting.QuotaIndex, currentValue, Convert.ToDouble(tradePosiVal), posiVal, posiVal, Convert.ToDouble(noPosiVal), null, upperLimit, lowerLimit, setting.Percent, warning)); break; default: break; } return messageList; } private ClientRiskCheckItem checkUnderlying(List positionList, string tag_prefix, ClientRiskCheckItem checkItem, int quotaRange, List settingAll) { if (string.IsNullOrEmpty(tag_prefix)) { throw new ArgumentException($"“{nameof(tag_prefix)}”不能是 Null 或为空", nameof(tag_prefix)); } double? currentValue = null; currentValue = null; double posiVal = 0; decimal currentPv = 0; decimal posiPv = 0; CheckQuotaMoitorModel current = positionList.FirstOrDefault(x => x.Current); if (current.UnderlyingId != quotaRange && quotaRange != 0) { return null; } var noQuoteSetting = settingAll.FirstOrDefault(x => x.QuotaType == QuotaTypeEnum.UNDERLYING && x.QuotaRange == quotaRange && x.QuotaIndex == checkItem.quotaType && !x.Precheck);//是否设置了不事前检查 if (noQuoteSetting != null) { return null; } positionList = positionList.Where(s => s.UnderlyingCode == current.UnderlyingCode).ToList(); var _posiList = DataHelper.DeepCopyObject(positionList); 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 "轧差名义本金": currentValue = Math.Abs(Convert.ToDouble(currentPv)); if (!ValidateQuoteResult(checkItem, currentValue, posiVal)) { return checkItem; } return null; case "轧差集中度": currentValue = current.Circulation == 0 ? 0 : currentValue / Convert.ToDouble(current.Circulation); 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)) { return checkItem; } return null; default: return null; } } private List checkClient(List positionList, List posiList, string tag_prefix, QuotaSetting[] settings, List settingAll, bool warning) { if (string.IsNullOrEmpty(tag_prefix)) { throw new ArgumentException($"“{nameof(tag_prefix)}”不能是 Null 或为空", nameof(tag_prefix)); } if (settings is null) { throw new ArgumentNullException(nameof(settings)); } var messageList = new List(); double? upperLimit, lowerLimit, currentValue = null; CheckQuotaMoitorModel current = positionList.FirstOrDefault(x => x.Current); //settings 可能包含全部 var settingItem = settings.First(); var noQuoteSetting = settingAll.FirstOrDefault(x => x.QuotaType == settingItem.QuotaType && x.QuotaRange == settingItem.QuotaRange && x.QuotaIndex == settingItem.QuotaIndex && !x.Precheck);//是否设置了不事前检查 if (noQuoteSetting != null) { return messageList; } if (warning) { upperLimit = settingItem.WarningUpperLimit; lowerLimit = settingItem.WarningLowerLimit; } else { upperLimit = settingItem.QuotaUpperLimit; lowerLimit = settingItem.QuotaLowerLimit; } if (upperLimit == null && lowerLimit == null) { return messageList; } if (current.ClientId != settingItem.QuotaRange && settingItem.QuotaRange != 0)//不是某一客户的不走限额 { return messageList; } currentValue = null; double posiVal = 0; decimal noPosiVal = 0; decimal tradePosiVal = 0; var tag = $"{tag_prefix}({settingItem.QuotaIndex})"; var clientPositionList = positionList.Where(O => O.ClientId == current.ClientId).ToList(); var tradeIds = clientPositionList.Select(O => O.id); switch (settingItem.QuotaIndex) { 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 posiPv = positionList.Where(s => s.ClientId == current.ClientId && !s.Current).Sum(s => s.Pv); posiVal = Convert.ToDouble(posiPv); tradePosiVal = posiList.Where(s => s.ClientId == current.ClientId).Sum(s => s.Pv); noPosiVal = posiPv - tradePosiVal; messageList.Add(SetQuotaMsg(tag, settingItem.QuotaIndex, currentValue, Convert.ToDouble(tradePosiVal), posiVal, Convert.ToDouble(current.Pv), Convert.ToDouble(noPosiVal), null, upperLimit, lowerLimit, settingItem.Percent, warning)); break; default: break; } return messageList; } private ClientRiskCheckItem checkClient(List positionList, string tag_prefix, ClientRiskCheckItem checkItem, int quotaRange, List settingAll) { if (string.IsNullOrEmpty(tag_prefix)) { throw new ArgumentException($"“{nameof(tag_prefix)}”不能是 Null 或为空", nameof(tag_prefix)); } double? currentValue = null; double posiVal = 0; var tag = $"{tag_prefix}({checkItem.quotaType})"; CheckQuotaMoitorModel current = positionList.FirstOrDefault(x => x.Current); var clientPositionList = positionList.Where(O => O.ClientId == current.ClientId).ToList(); var _posiList = DataHelper.DeepCopyObject(clientPositionList); var _tradePosiList = clientPositionList.Where(s => !s.Current).ToList(); var clientPv = CalcPv(_posiList, true, true); var clientTradePv = CalcPv(_tradePosiList, true); clientPv = Math.Abs(clientPv); clientTradePv = Math.Abs(clientTradePv); switch (checkItem.quotaType) { case "名义本金": currentValue = Convert.ToDouble(Math.Abs(clientPv)); var posiPv = clientTradePv; posiVal = Convert.ToDouble(posiPv); if (!ValidateQuoteResult(checkItem, currentValue, posiVal)) { return checkItem; } return null; default: 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; } /// /// 计算现有持仓pv /// /// /// /// private decimal CalcPv(List positionList, bool roll) { // 按ClientId和UnderlyingCode分组 var groupedPositions = positionList.GroupBy(p => new { p.ClientId, p.UnderlyingCode }); decimal totalPv = 0; foreach (var group in groupedPositions) { // 计算每个分组的PV总和 decimal groupPv = 0; foreach (var position in group) { // 根据roll参数决定是否考虑方向 decimal pv = position.Qty * position.Price; if (roll) { pv *= (position.Side == 0 ? 1 : -1); // 根据Side调整符号 } groupPv += pv; } totalPv += Math.Abs(groupPv); // 累加各分组的PV } return totalPv; } private List checkStockPercent(IEnumerable> positionList, Dictionary stockEqvNotionalDict, string tag_prefix, QuotaSetting[] settings, bool warning) { if (string.IsNullOrEmpty(tag_prefix)) { throw new ArgumentException($"“{nameof(tag_prefix)}”不能是 Null 或为空", nameof(tag_prefix)); } if (settings is null) { throw new ArgumentNullException(nameof(settings)); } var messageList = new List(); double? upperLimit, lowerLimit, currentValue = null; var count = settings[0].QuotaRange; var code = positionList.First().Key.UnderlyingCode; var list = DbContext.Eod_Market_Sum.Where(O => O.ValueDate < SystemValueDate && O.UnderlyingCode == code).OrderByDescending(O => O.ValueDate).Take(count); if (!list.Any()) { messageList.Add($"{tag_prefix}-{count}天:试算失败,历史成交量不存在;"); return messageList; } var totalVolume = list.Sum(O => O.TotalValue) / list.Count(); foreach (var settingItem in settings) { if (warning) { upperLimit = settingItem.WarningUpperLimit; lowerLimit = settingItem.WarningLowerLimit; } else { upperLimit = settingItem.QuotaUpperLimit; lowerLimit = settingItem.QuotaLowerLimit; } if (upperLimit == null && lowerLimit == null) { continue; } currentValue = null; var tag = $"{tag_prefix}-{count}天({settingItem.QuotaIndex})"; switch (settingItem.QuotaIndex) { case "名义本金": var currentRangeTradeIds = positionList.Select(O => O.Key.ParentTradeId == 0 ? O.Key.id : O.Key.ParentTradeId); currentValue = stockEqvNotionalDict.Where(O => currentRangeTradeIds.Contains(O.Key)).Sum(O => O.Value) / totalVolume; break; case "Delta金额": currentValue = positionList.Sum(O => O.Value.DeltaCash) / totalVolume; break; default: continue; } messageList.Add(SetMsg(tag, currentValue, upperLimit, lowerLimit, settingItem.Percent, warning)); } return messageList; } private ClientRiskCheckItem checkStockPercent(IEnumerable> positionList, Dictionary stockEqvNotionalDict, string tag_prefix, ClientRiskCheckItem checkItem, int quotaRange) { if (string.IsNullOrEmpty(tag_prefix)) { throw new ArgumentException($"“{nameof(tag_prefix)}”不能是 Null 或为空", nameof(tag_prefix)); } double? currentValue = null; var count = quotaRange; var code = positionList.First().Key.UnderlyingCode; var list = DbContext.Eod_Market_Sum.Where(O => O.ValueDate < SystemValueDate && O.UnderlyingCode == code).OrderByDescending(O => O.ValueDate).Take(count); if (!list.Any()) { throw new ServiceException($"{tag_prefix}-{count}天:试算失败,历史成交量不存在;"); } var totalVolume = list.Sum(O => O.TotalValue) / list.Count(); currentValue = null; var tag = $"{tag_prefix}-{count}天({checkItem.quotaType})"; switch (checkItem.quotaType) { case "名义本金": var currentRangeTradeIds = positionList.Select(O => O.Key.ParentTradeId == 0 ? O.Key.id : O.Key.ParentTradeId); currentValue = stockEqvNotionalDict.Where(O => currentRangeTradeIds.Contains(O.Key)).Sum(O => O.Value) / totalVolume; break; case "Delta金额": currentValue = positionList.Sum(O => O.Value.DeltaCash) / totalVolume; break; default: return null; } checkItem.currentValue = currentValue; if (!ValidateQuoteResult(checkItem)) { return checkItem; } return null; } /// /// 已簿记持仓 /// /// private List GetPosiQuotaMoitors() { List list = new List(); var flows = DbContext.realtime_trade_risk.Where(x => x.Pv != 0).AsNoTracking().ToList(); var dealDate = QdpCalendarHelper.GetNonHolidayDefore(valuedateBLL.ValueDate.AddDays(-1)); Dictionary eodPriceDic = new Dictionary(); foreach (var item in flows) { CheckQuotaMoitorModel quotaMoitorModel = new CheckQuotaMoitorModel(); quotaMoitorModel.ClientId = item.ClientId ?? 0; quotaMoitorModel.Pv = Math.Abs(Convert.ToDecimal(item.Pv ?? 0)); quotaMoitorModel.UnderlyingCode = item.Exposure; list.Add(quotaMoitorModel); } return list; } /// /// 组织试算消息 /// /// 维度说明 /// 当前值 /// 上限 /// 下限 /// 限额是否为百分比 /// 是否为预警 /// private string SetMsg(string tag, double? value, double? upperNumber, double? lowerNumber, bool percent, bool warning) { var result = ""; if (value == null) { result = $"{tag}:试算失败,未能计算出当前持仓值;"; } else if ((upperNumber.HasValue && value > upperNumber) || (lowerNumber.HasValue && value < lowerNumber)) { result = $"{tag}:当前:{value.Value.ToString(percent ? "#,##0.00%" : "#,##0.00")},{(warning ? "预警" : "限额")}:{lowerNumber?.ToString(percent ? "#,##0.00%" : "#,##0.00")}~{upperNumber?.ToString(percent ? "#,##0.00%" : "#,##0.00")}"; } return result; } private string SetMsg(string tag, string quoteIndex, double? value, double posiedVal, double currVal, double noposi, double? uDv, double? upperNumber, double? lowerNumber, bool percent, bool warning) { var result = ""; if (value == null) { result = $"{tag}:试算失败,未能计算出当前持仓值;"; } else if ((upperNumber.HasValue && value > upperNumber) || (lowerNumber.HasValue && value < lowerNumber)) { result = $"{tag}:当前:{value.Value.ToString(percent ? "#,##0.00%" : "#,##0.00")},{"预警"}:{lowerNumber?.ToString(percent ? "#,##0.00%" : "#,##0.00")}~{upperNumber?.ToString(percent ? "#,##0.00%" : "#,##0.00")}"; if (!warning) { result = $"{tag}:累积:{(value == 0 ? "0.00" : value?.ToString(percent ? "#,##0.00%" : "#,##0.00"))},{"限额"}:{lowerNumber?.ToString(percent ? "#,##0.00%" : "#,##0.00")}~{upperNumber?.ToString(percent ? "#,##0.00%" : "#,##0.00")}"; result += $"\r\n本交易({quoteIndex}):{(currVal == 0 ? "0.00" : currVal.ToString(percent ? "#,##0.00%" : "#,##0.00"))};"; result += $"\r\n已簿记交易({quoteIndex}):{(posiedVal == 0 ? "0.00" : posiedVal.ToString(percent ? "#,##0.00%" : "#,##0.00"))};"; result += $"\r\n未簿记交易({quoteIndex}):{(noposi == 0 ? "0.00" : noposi.ToString(percent ? "#,##0.00%" : "#,##0.00"))};"; } } return result; } /// /// 限额检查消息 /// /// 维度 /// 限额检查项 /// 假设合成持仓后的值 /// 已簿记的值 /// 持仓值+未簿记的流水值 /// 当前交易 /// 未簿记的流水 /// /// /// /// /// private string SetQuotaMsg(string tag, string quoteIndex, double? value, double? posiedVal, double? posiVal, double? currVal, double noposi, double? uDv, double? upperNumber, double? lowerNumber, bool percent, bool warning) { var result = SetMsg(tag, quoteIndex, value, posiedVal ?? 0, currVal ?? 0, noposi, uDv, upperNumber, lowerNumber, percent, warning); var resultLast = $"{tag}:当前:{value?.ToString(percent ? "#,##0.00%" : "#,##0.00")},{"预警"}:{lowerNumber?.ToString(percent ? "#,##0.00%" : "#,##0.00")}~{upperNumber?.ToString(percent ? "#,##0.00%" : "#,##0.00")}"; if (!warning) { resultLast = $"{tag}:累积:{(value == 0 ? "0.00" : value?.ToString(percent ? "#,##0.00%" : "#,##0.00"))},{"限额"}:{lowerNumber?.ToString(percent ? "#,##0.00%" : "#,##0.00")}~{upperNumber?.ToString(percent ? "#,##0.00%" : "#,##0.00")}"; resultLast += $"\r\n本交易({quoteIndex}):{(currVal == 0 ? "0.00" : currVal?.ToString(percent ? "#,##0.00%" : "#,##0.00"))};"; resultLast += $"\r\n已簿记交易({quoteIndex}):{(posiedVal == 0 ? "0.00" : posiedVal?.ToString(percent ? "#,##0.00%" : "#,##0.00"))};"; resultLast += $"\r\n未簿记交易({quoteIndex}):{(noposi == 0 ? "0.00" : noposi.ToString(percent ? "#,##0.00%" : "#,##0.00"))};"; } if (!string.IsNullOrEmpty(result) && posiVal != 0) { var resultPosi = SetMsg(tag, posiVal, upperNumber, lowerNumber, percent, warning); if (string.IsNullOrEmpty(resultPosi)) { return resultLast; } if (upperNumber.HasValue && value > upperNumber) { if (posiVal > upperNumber) { if (value <= posiVal) { return ""; } } if (lowerNumber.HasValue && posiVal < lowerNumber) { if (upperNumber - posiVal >= value - upperNumber) { return ""; } } return resultLast; } if (lowerNumber.HasValue && value < lowerNumber) { if (upperNumber.HasValue && posiVal > upperNumber) { if (posiVal - lowerNumber >= lowerNumber - value) { return ""; } } if (posiVal < lowerNumber) { if (value >= posiVal) { return ""; } } return resultLast; } } return result; } /// /// 限额指标校验 /// /// /// private bool ValidateQuoteResult(ClientRiskCheckItem clientRiskCheckItem) { var value = clientRiskCheckItem.currentValue; if (value == null) { return false; } else if ((clientRiskCheckItem.quotaUpperLimit.HasValue && value > clientRiskCheckItem.quotaUpperLimit) || (clientRiskCheckItem.quotaLowerLimit.HasValue && value < clientRiskCheckItem.quotaLowerLimit)) { return false; } else if ((clientRiskCheckItem.warningUpperLimit.HasValue && value > clientRiskCheckItem.warningUpperLimit) || (clientRiskCheckItem.warningLowerLimit.HasValue && value < clientRiskCheckItem.warningLowerLimit)) { return false; } return true; } /// /// 校验风控结果是否好于当前持仓结果 /// /// /// /// /// private bool ValidateQuoteResult(ClientRiskCheckItem clientRiskCheckItem, double? curruentVal, double posiVal) { clientRiskCheckItem.currentValue = curruentVal; if (!ValidateQuoteResult(clientRiskCheckItem))//假设合成持仓成立,校验不通过 { if (posiVal == 0) { return false; } clientRiskCheckItem.currentValue = posiVal; if (ValidateQuoteResult(clientRiskCheckItem))//当前持仓通过,拦截 { clientRiskCheckItem.currentValue = curruentVal; return false; } clientRiskCheckItem.currentValue = curruentVal; var quoteResult = ValidateQuote(curruentVal ?? 0, posiVal, clientRiskCheckItem.quotaUpperLimit, clientRiskCheckItem.quotaLowerLimit); var warningResult = ValidateQuote(curruentVal ?? 0, posiVal, clientRiskCheckItem.warningUpperLimit, clientRiskCheckItem.warningLowerLimit); if (!quoteResult || !warningResult) { return false; } } return true; } /// /// 校验风控结果 /// /// /// /// /// /// private bool ValidateQuote(double curruentVal, double posiVal, double? upperLimit, double? lowerLimit) { if (upperLimit.HasValue && curruentVal > upperLimit)//超过上限情况 { if (posiVal > upperLimit) { if (curruentVal <= posiVal) { return true; } } if (lowerLimit.HasValue && posiVal < lowerLimit) { if (upperLimit - posiVal >= curruentVal - upperLimit) { return true; } } return false; } if (lowerLimit.HasValue && curruentVal < lowerLimit)//超过下限情况 { if (upperLimit.HasValue && posiVal > upperLimit) { if (lowerLimit.HasValue && posiVal - lowerLimit >= lowerLimit - curruentVal) { return true; } } if (posiVal < lowerLimit) { if (curruentVal >= posiVal) { return true; } } return false; } return true; } /// /// 保存试算结果 /// /// 试算结果 public void SaveQuotaTrial(QuotaTrial obj) { if (obj is null) { throw new ArgumentNullException(nameof(obj)); } var dbObj = DbContext.quotaTrial.Where(O => O.id == obj.id).FirstOrDefault(); if (dbObj == null) { dbObj = obj; DbContext.quotaTrial.Add(obj); } dbObj.FundCheckDetails ??= ""; dbObj.QuotaCheckDetails ??= ""; dbObj.QuotaWarningDetails ??= ""; dbObj.RiskWarningDetails ??= ""; dbObj.Remark = obj.Remark ?? ""; dbObj.TrialSource = obj.TrialSource; dbObj.OptId = UserId; dbObj.OptName = UserName; dbObj.OptDate = DateTime.Now; DbContext.SaveChanges(); } } }