using BaseOUDAL;
using ClosedXML.Report.Utils;
using Confluent.Kafka;
using CsvHelper;
using Dapper;
using DocumentFormat.OpenXml.Office.CustomUI;
using DocumentFormat.OpenXml.Office2010.Excel;
using DocumentFormat.OpenXml.Spreadsheet;
using Org.BouncyCastle.Asn1.Ocsp;
using Qdp.Foundation.Utilities;
using Qdp.Pricing.Ecosystem.Trade.FixedIncome;
using Qdp.Pricing.Library.Base.Utilities;
using Qdp.Pricing.Library.Common.Products.Rates;
using Snowflake.Core;
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.EodSettlement;
using YLErp.BLL.Hedge;
using YLErp.Cache;
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 YLErp.Modules.RiskEngine;
using static YLErp.ConsGlobal;
namespace YLErp.Modules.RiskModule
{
public class QuotaMonitorService : YLBaseService
{
private const string RiskCheckTriggerRemark = "触发风控";
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,
ValueDate= settlementDate
};
//全局
{
var list = QueryEodGlobalFromCalc(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 = QueryEodUnderlyingFromCalc(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 = QueryEodClientFromCalc(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 = QueryEodTradeFromCalc(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.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);
foreach (var item in list)
{
item.StockEqvNotional = item.StockEqvNotional.Normalize();
item.Quota_StockEqvNotional_Upper = item.Quota_StockEqvNotional_Upper?? double.NaN;
item.Quota_StockEqvNotional_Lower = item.Quota_StockEqvNotional_Lower?? double.NaN;
item.Quota_StockEqvNotional_wUpper = item.Quota_StockEqvNotional_wUpper?? double.NaN;
item.Quota_StockEqvNotional_wLower = item.Quota_StockEqvNotional_wLower?? double.NaN;
item.DeltaExposure = item.DeltaExposure.Normalize();
}
// 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();
}
foreach (var item in list)
{
item.StockEqvNotional = item.StockEqvNotional.Normalize();
item.Quota_StockEqvNotional_Upper = item.Quota_StockEqvNotional_Upper ?? double.NaN;
item.Quota_StockEqvNotional_Lower = item.Quota_StockEqvNotional_Lower ?? double.NaN;
item.Quota_StockEqvNotional_wUpper = item.Quota_StockEqvNotional_wUpper ?? double.NaN;
item.Quota_StockEqvNotional_wLower = item.Quota_StockEqvNotional_wLower ?? double.NaN;
item.PositionMargin = item.PositionMargin.Normalize();
item.AvailableFund = item.AvailableFund.Normalize();
}
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 (!string.IsNullOrEmpty(req.TradeNumber))
{
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 (!string.IsNullOrEmpty(req.TradeType))
{
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 (!string.IsNullOrEmpty(req.TradeStatus))
{
queryStatus = true;
query = query.Where(O => req.TradeStatus == O.TradeStatus);
}
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();
}
foreach (var item in list)
{
item.StockEqvNotional = item.StockEqvNotional.Normalize();
item.Quota_StockEqvNotional_Upper = item.Quota_StockEqvNotional_Upper ?? double.NaN;
item.Quota_StockEqvNotional_Lower = item.Quota_StockEqvNotional_Lower ?? double.NaN;
item.Quota_StockEqvNotional_wUpper = item.Quota_StockEqvNotional_wUpper ?? double.NaN;
item.Quota_StockEqvNotional_wLower = item.Quota_StockEqvNotional_wLower ?? double.NaN;
item.DeltaExposure = item.DeltaExposure.Normalize();
item.Concentration= item.Concentration.Normalize();
item.Quota_Concentration_Lower = item.Quota_Concentration_Lower ?? double.NaN;
item.Quota_Concentration_Upper = item.Quota_Concentration_Upper ?? double.NaN;
item.Quota_Concentration_wUpper = item.Quota_Concentration_wUpper ?? double.NaN;
item.Quota_Concentration_wLower = item.Quota_Concentration_wLower ?? double.NaN;
}
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;
// 计算互换DV汇总(按标的缓存bondPrice)
var swapPositions = posiQuery.ToList();
var swapDV = 0d;
var swapBPCache = swapPositions.Select(p => p.UnderlyingCode).Distinct()
.ToDictionary(code => code, code => EodPriceQueryService.GetBondPrice(valuedateBLL.ValueDate, code));
foreach (var pos in swapPositions)
{
if (!swapBPCache.TryGetValue(pos.UnderlyingCode, out var bondPrice) || bondPrice == null) continue;
var vobp = bondPrice.Vobp ?? 0;
if (vobp == 0) continue;
swapDV += Convert.ToDouble(Dv01Helper.CalcDv01(pos.UnderlyingCode, pos.PosiQuantity, pos.PosiDirection, pos.PositionType, vobp));
}
var setValue = new Action>((obj, settings) =>
{
obj.StockEqvNotional = Convert.ToDouble(posiQuery.Sum(s => s.PosiNotionalValue));
if (!dict.ContainsKey(obj.BusinessType))
{
return;
}
obj.DeltaExposure= dict[obj.BusinessType].Sum(O => O.risk.Delta.Normalize());
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 globalDvSetting = allSetting.Where(O => O.IsValid && O.Status == QuotaSettingApprovalStatus.Valid && O.QuotaRange == 0 && O.QuotaIndex == "DV").FirstOrDefault()?.Clone();
var tradeSetting = QueryCurrentQuotaSetting(QuotaTypeEnum.TRADE);
tradeSetting = MargeQuotaSetting(tradeSetting, 0, 0);
var tradeDvSetting = tradeSetting.Where(O => O.IsValid && O.Status == QuotaSettingApprovalStatus.Valid && O.QuotaRange == 0 && O.QuotaIndex == "DV").FirstOrDefault()?.Clone();
var swapSetting = QueryCurrentQuotaSetting(QuotaTypeEnum.GLOBAL_SWAP);
swapSetting = MargeQuotaSetting(swapSetting, 0, 0);
var swap = new QuotaMonitor_Global()
{
ParentKey = "场外",
BusinessType = "互换"
};
setValue(swap, swapSetting);
swap.DV = swapDV;
//swap.Quota_DV_Upper = tradeDvSetting?.QuotaUpperLimit ?? double.NaN;
//swap.Quota_DV_Lower = tradeDvSetting?.QuotaLowerLimit ?? double.NaN;
//swap.Quota_DV_wUpper = tradeDvSetting?.WarningUpperLimit ?? double.NaN;
//swap.Quota_DV_wLower = tradeDvSetting?.WarningLowerLimit ?? double.NaN;
var positionList = new List>();
var underly = GetTradePositionPnl();
if (dict.ContainsKey("互换"))
{
var swapPositionList = dict["互换"].Select(O => new KeyValuePair(O.t, O.risk));
swap.TotalPnL = swapPositionList.Sum(O => O.Value.PositionPnl.Normalize());
swap.PositionPnl = swapPositionList.Sum(O => O.Value.PositionPnl.Normalize());
positionList.AddRange(swapPositionList);
}
var checkPosiList = GetCheckPosiList();
// 计算checkPosiList中所有持仓的DV合计,用于算未簿记DV(按标的缓存bondPrice)
var totalCheckPosiDV = 0d;
if (checkPosiList.Any())
{
var ckBPCache = checkPosiList.Select(p => p.UnderlyingCode).Distinct()
.ToDictionary(code => code, code => EodPriceQueryService.GetBondPrice(valuedateBLL.ValueDate, code));
foreach (var pos in checkPosiList)
{
if (!ckBPCache.TryGetValue(pos.UnderlyingCode, out var bp) || bp == null) continue;
var vobp = bp.Vobp ?? 0;
if (vobp == 0) continue;
int positionType = pos.Side == 0 ? (int)PositionTypeFlag.Long : (int)PositionTypeFlag.Short;
totalCheckPosiDV += Convert.ToDouble(Dv01Helper.CalcDv01(pos.UnderlyingCode, pos.Qty, pos.Direction, positionType, vobp));
}
}
var posiStockEqvNotional = checkPosiList.Sum(s => s.Pv);
var delta = checkPosiList.Sum(s => s.Delta);
var unTrade = new QuotaMonitor_Global()
{
ParentKey = "场外",
BusinessType = "未簿记合约",
StockEqvNotional = Convert.ToDouble(posiStockEqvNotional) - swap.StockEqvNotional.Normalize(),
DeltaExposure=(double)delta- (swap.DeltaExposure??0),
DV = totalCheckPosiDV - (swap.DV ?? 0),
};
var all = new QuotaMonitor_Global()
{
BusinessType = "全局",
StockEqvNotional = Convert.ToDouble(posiStockEqvNotional),
PositionPnl = underly.PositionPnl + swap.PositionPnl,
DeltaExposure=(double)delta+ underly.DeltaExposure,
DV = (swap.DV ?? 0) + (underly.DV ?? 0) + (unTrade.DV ?? 0),
Quota_DV_Upper = globalDvSetting?.QuotaUpperLimit ?? double.NaN,
Quota_DV_Lower = globalDvSetting?.QuotaLowerLimit ?? double.NaN,
Quota_DV_wUpper = globalDvSetting?.WarningUpperLimit ?? double.NaN,
Quota_DV_wLower = globalDvSetting?.WarningLowerLimit ?? double.NaN,
};
var list = new List
{
swap,
underly,
unTrade,
all
};
for (var i = 0; i < list.Count; i++)
{
list[i].id = -1 - i;
}
return list;
}
///
/// 查询限额监控-全局数据-收盘
///
///
///
public List QueryEodGlobalFromCalc(QuotaMonitorReq req)
{
var maxDate = DbContext.eod_swap_position.Where(O => O.PosiQuantity > 0).Max(O => (DateTime?)O.ValueDate) ?? req.ValueDate;
var posiQuery = DbContext.eod_swap_position.Where(O =>O.ValueDate==maxDate&&O.PosiQuantity>0);
req.PvPercent = 1 - req.PvPercent;
// 互换DV汇总:直接取eod_swap_position中已计算好的dv01
var eodSwapDV = Convert.ToDouble(posiQuery.Sum(p => p.dv01));
var setValue = new Action>((obj, settings) =>
{
obj.StockEqvNotional = Convert.ToDouble(posiQuery.Sum(s => s.PosiNotionalValue));
obj.DeltaExposure = posiQuery.Sum(O => Convert.ToDouble(O.PosiQuantity*(O.PositionType==(int)PositionTypeFlag.Long?1:-1)));
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 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 allSetting = QueryCurrentQuotaSetting(QuotaTypeEnum.GLOBAL_ALL);
allSetting = MargeQuotaSetting(allSetting, 0, 0);
var globalDvSetting = allSetting.Where(O => O.IsValid && O.Status == QuotaSettingApprovalStatus.Valid && O.QuotaRange == 0 && O.QuotaIndex == "DV").FirstOrDefault()?.Clone();
var tradeSetting = QueryCurrentQuotaSetting(QuotaTypeEnum.TRADE);
tradeSetting = MargeQuotaSetting(tradeSetting, 0, 0);
var tradeDvSetting = tradeSetting.Where(O => O.IsValid && O.Status == QuotaSettingApprovalStatus.Valid && O.QuotaRange == 0 && O.QuotaIndex == "DV").FirstOrDefault()?.Clone();
var swapSetting = QueryCurrentQuotaSetting(QuotaTypeEnum.GLOBAL_SWAP);
swapSetting = MargeQuotaSetting(swapSetting, 0, 0);
var swap = new QuotaMonitor_Global()
{
ParentKey = "场外",
BusinessType = "互换",
};
setValue(swap, swapSetting);
swap.DV = eodSwapDV;
swap.Quota_DV_Upper = tradeDvSetting?.QuotaUpperLimit ?? double.NaN;
swap.Quota_DV_Lower = tradeDvSetting?.QuotaLowerLimit ?? double.NaN;
swap.Quota_DV_wUpper = tradeDvSetting?.WarningUpperLimit ?? double.NaN;
swap.Quota_DV_wLower = tradeDvSetting?.WarningLowerLimit ?? double.NaN;
var underly = GetEodTradePositionPnl(req.ValueDate);
var unTrade = new QuotaMonitor_Global()
{
ParentKey = "场外",
BusinessType = "未簿记合约",
StockEqvNotional = 0,
DeltaExposure = 0
};
var all = new QuotaMonitor_Global()
{
BusinessType = "全局",
StockEqvNotional = swap.StockEqvNotional,
PositionPnl = underly.PositionPnl + swap.PositionPnl,
DeltaExposure = swap.DeltaExposure+ underly.DeltaExposure,
DV = (swap.DV ?? 0) + (underly.DV ?? 0) + (unTrade.DV ?? 0),
Quota_DV_Upper = globalDvSetting?.QuotaUpperLimit ?? double.NaN,
Quota_DV_Lower = globalDvSetting?.QuotaLowerLimit ?? double.NaN,
Quota_DV_wUpper = globalDvSetting?.WarningUpperLimit ?? double.NaN,
Quota_DV_wLower = globalDvSetting?.WarningLowerLimit ?? double.NaN,
};
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 QueryEodClientFromCalc(QuotaMonitorReq req)
{
var clientIds = DbContext.eod_swap.Where(x => x.NotionalValue > 0&&x.ValueDate==req.ValueDate).AsNoTracking().Select(s=>s.ClientId).Distinct().ToList();
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 >= req.ValueDate) &&
(!t.CreditStartDate.HasValue ||
t.CreditStartDate <= req.ValueDate))
.ToList();
var creditExposureDict = new Dictionary();
var todayClientBalance = ClientBalanceUtility.GetClientBanlances(clientIds, DateTime.MinValue, req.ValueDate).ToList();
foreach (var item in todayClientBalance)
{
var c = new QuotaMonitor_Client();
var client = DataCacheProvider.GetClientDataSource().GetData(item.ClientId);
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 (!string.IsNullOrEmpty(req.TradeNumber))
{
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 (!string.IsNullOrEmpty(req.TradeType))
{
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 (!string.IsNullOrEmpty(req.TradeStatus))
{
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 dvSetting = temp.Where(O => O.QuotaIndex == "DV").FirstOrDefault()?.Clone();
obj.Quota_DV_Upper = dvSetting?.QuotaUpperLimit ?? double.NaN;
obj.Quota_DV_Lower = dvSetting?.QuotaLowerLimit ?? double.NaN;
obj.Quota_DV_wUpper = dvSetting?.WarningUpperLimit ?? double.NaN;
obj.Quota_DV_wLower = dvSetting?.WarningLowerLimit ?? double.NaN;
});
var setting = QueryCurrentQuotaSetting(QuotaTypeEnum.TRADE);
setting = MargeQuotaSetting(setting, 0, 0);
var ccrDict = new Dictionary();
// 批量获取swap_position方向/多空/数量,用于DV计算
var tradeIds = list.Select(O => O.trade.id).Distinct().ToList();
var swapPositionDict = DbContext.swap_position
.Where(p => tradeIds.Contains(p.SwapTradeId) && !p.IsInitial && p.PosiDirection > 0 && !p.Invalid)
.ToDictionary(p => p.SwapTradeId);
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;
// 计算DV
if (swapPositionDict.TryGetValue(item.trade.id, out var swapPos) && bondPrice != null && vobp != 0)
{
item.DV = Convert.ToDouble(Dv01Helper.CalcDv01(item.trade.UnderlyingCode, swapPos.PosiQuantity, swapPos.PosiDirection, swapPos.PositionType, Convert.ToDecimal(vobp)));
}
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),
DV = result.Sum(O => O.DV),
};
result.Add(total);
return result;
}
///
/// 查询限额监控-单笔交易数据
///
///
///
public List QueryEodTradeFromCalc(QuotaMonitorReq req)
{
var result = new List();
req.sidx = "TradeNumber";
req.sord = "asc";
var eodPredicate = PredicateBuilder.True().And(t => t.NotionalValue>0&&t.ValueDate==req.ValueDate);
var predicate = PredicateBuilder.True().And(t => t.ValidState != "InValid" && t.ExerciseDate >= req.ValueDate);
if (!string.IsNullOrEmpty(req.TradeNumber))
{
eodPredicate = eodPredicate.And(O => O.SwapTradeNo.Contains(req.TradeNumber));
predicate = predicate.And(O => O.TradeNumber.Contains(req.TradeNumber));
}
if (req.ClientId != 0)
{
eodPredicate = eodPredicate.And(O => O.ClientId==req.ClientId);
predicate = predicate.And(O => req.ClientId == O.ClientId);
}
if (req.UnderlyingId?.Count > 0)
{
predicate = predicate.And(O => req.UnderlyingId.Contains(O.UnderlyingId));
}
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 (!string.IsNullOrEmpty(req.TradeStatus))
{
predicate = predicate.And(O => req.TradeStatus == O.TradeStatus);
}
var posiQuery = from t in DbContext.trade.Where(predicate)
join p in DbContext.eod_swap.Where(eodPredicate) on t.id equals p.SwapTradeId
select new QuotaMonitor_TradeDto()
{
trade = t,
ClientId = t.ClientId,
TradeNumber = t.TradeNumber,
ClientName = t.ClientName,
StockEqvNotional = Convert.ToDouble(p.NotionalValue),
};
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 dvSetting = temp.Where(O => O.QuotaIndex == "DV").FirstOrDefault()?.Clone();
obj.Quota_DV_Upper = dvSetting?.QuotaUpperLimit ?? double.NaN;
obj.Quota_DV_Lower = dvSetting?.QuotaLowerLimit ?? double.NaN;
obj.Quota_DV_wUpper = dvSetting?.WarningUpperLimit ?? double.NaN;
obj.Quota_DV_wLower = dvSetting?.WarningLowerLimit ?? double.NaN;
});
var setting = QueryCurrentQuotaSetting(QuotaTypeEnum.TRADE);
setting = MargeQuotaSetting(setting, 0, 0);
var ccrDict = new Dictionary();
// 批量获取eod_swap_position方向/多空/数量,用于DV计算和dv01回写
var tradeIds = list.Select(O => O.trade.id).Distinct().ToList();
var eodSwapPosDict = DbContext.eod_swap_position
.Where(p => p.ValueDate == req.ValueDate && tradeIds.Contains(p.SwapTradeId) && p.PosiQuantity > 0)
.ToDictionary(p => p.SwapTradeId);
var eodSwapDict = DbContext.eod_swap
.Where(p => p.ValueDate == req.ValueDate && tradeIds.Contains(p.SwapTradeId))
.ToDictionary(p => p.SwapTradeId);
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;
// 计算DV,并回写dv01
if (eodSwapPosDict.TryGetValue(item.trade.id, out var eodSwapPos))
{
item.DV = Convert.ToDouble(eodSwapPos.dv01);
}
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),
DV = result.Sum(O => O.DV),
};
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 QueryEodUnderlyingFromCalc(QuotaMonitorReq req)
{
var result = new List();
var clientPositions = DbContext.eod_swap_position.Where(s=>s.ValueDate==req.ValueDate&&s.PosiQuantity>0).AsNoTracking().AsEnumerable();
List underlyingModels = new List();
foreach (var item in clientPositions)
{
var model = new QuotaMonitor_UnderlyingModel()
{
UnderlyingCode = item.UnderlyingCode,
StockEqvNotional = Convert.ToDouble(item.PosiNotionalValue),
PositionType = item.PositionType,
Qty = item.PosiQuantity,
};
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);
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));
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:
if (O.QuotaRange == 0)
{
O.QuotaRangeStr = "全部";
}
else
{
var underlying = DataCacheProvider.GetUnderlyingDataSource().GetData(O.QuotaRange);
if (underlying == null)
{
_logger.Info($"未找到ID为 {O.QuotaRange} 的标的,quotaSetting ID: {O.id}");
O.QuotaRangeStr = "未知标的";
}
else
{
O.QuotaRangeStr = underlying.UnderlyingCode;
}
}
break;
case QuotaTypeEnum.STOCK_PERCENT:
O.QuotaRangeStr = O.QuotaRange + "天";
break;
case QuotaTypeEnum.CLIENT:
if (O.QuotaRange == 0)
{
O.QuotaRangeStr = "全部";
}
else
{
var client = DataCacheProvider.GetClientDataSource().GetData(O.QuotaRange);
if (client == null)
{
_logger.Info($"未找到ID为 {O.QuotaRange} 的客户,quotaSetting ID: {O.id}");
O.QuotaRangeStr = "未知客户";
}
else
{
O.QuotaRangeStr = client.Name;
}
}
break;
case QuotaTypeEnum.Assetunit:
if (O.QuotaRange == 0)
{
O.QuotaRangeStr = "全部";
}
else
{
var assetUnit = DataCacheProvider.GetAssetUnitDataSource().GetData(O.QuotaRange);
if (assetUnit == null)
{
_logger.Info($"未找到ID为 {O.QuotaRange} 的簿记,quotaSetting ID: {O.id}");
O.QuotaRangeStr = "未知簿记";
}
else
{
O.QuotaRangeStr = assetUnit.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 整体业务
//全局-DV
_quotaSettings.Add(new QuotaSetting()
{
QuotaType = QuotaTypeEnum.GLOBAL_ALL,
QuotaRange = 0,
QuotaIndex = "DV",
QuotaLowerLimit = null,
QuotaUpperLimit = null,
WarningLowerLimit = null,
WarningUpperLimit = null,
Percent = false,
IsValid = true,
Status = QuotaSettingApprovalStatus.Valid,
});
#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,
});
//交易-DV
_quotaSettings.Add(new QuotaSetting()
{
QuotaType = QuotaTypeEnum.TRADE,
QuotaRange = 0,
QuotaIndex = "DV",
QuotaLowerLimit = null,
QuotaUpperLimit = null,
WarningLowerLimit = null,
WarningUpperLimit = null,
Percent = false,
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,IYLCache yLCache)
{
foreach (var item in req)
{
ApprovalQuotaSetting(item,yLCache);
}
}
public void ApprovalQuotaSetting(ApprovalReqq req, IYLCache yLCache)
{
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
});
}
//删除限额监控设置缓存
var redisKey = RiskCfgRedisKey.GetKey(obj.QuotaType,obj.QuotaIndex);
yLCache.DeleteKey(redisKey);
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;
}
///
/// 交易确认风控检查入口:执行完整风控试算(老风控 + 新风控),并把试算结果转换为交易确认结果。
/// 返回 true 表示确认流程可以继续;返回 false 表示需要阻断、提示或审批。
///
public bool CheckTradeConfirmRisk(ref TradeOpenResult res)
{
return CheckTradeConfirmRisk(ref res, false);
}
///
/// 交易确认风控检查入口:执行完整风控试算(老风控 + 新风控)。
/// ignoreRiskWarning/ignoreRiskRuleIds 只用于新风控“需审批”规则的二次确认,不影响老风控结果。
///
public bool CheckTradeConfirmRisk(ref TradeOpenResult res, bool ignoreRiskWarning, IEnumerable ignoreRiskRuleIds = null)
{
if (res == null)
{
throw new ArgumentNullException(nameof(res));
}
if (res.Trade == null)
{
throw new ArgumentNullException(nameof(res.Trade));
}
var tradeId = (res.Trade.ParentTradeId == 0 || res.Trade.IsGroup != 0) ? res.Trade.id : res.Trade.ParentTradeId;
_logger.Info($"TradeConfirmRisk.RunFullRisk tradeId:{tradeId}, sourceTradeId:{res.Trade.id}, tradeNumber:{res.Trade.TradeNumber}, ignoreRiskWarning:{ignoreRiskWarning}, ignoreRiskRuleIds:{string.Join(",", ignoreRiskRuleIds ?? Enumerable.Empty())}, userId:{OptUser?.UserId}");
var trialService = new QuotaMonitorService(this);
var quotaObj = trialService.RunQuotaTrial(tradeId, 1);
return ApplyQuotaCheckResult(ref res, quotaObj, ignoreRiskWarning, ignoreRiskRuleIds);
}
///
/// 交易确认新风控检查入口:只执行新风控试算。
/// 用于老风控已被特批放行(ignoreMoneyCheck = true)的场景,避免老风控特批误跳过新风控。
///
public bool CheckTradeConfirmNewRisk(ref TradeOpenResult res, bool ignoreRiskWarning, IEnumerable ignoreRiskRuleIds = null)
{
if (res == null)
{
throw new ArgumentNullException(nameof(res));
}
if (res.Trade == null)
{
throw new ArgumentNullException(nameof(res.Trade));
}
var tradeId = (res.Trade.ParentTradeId == 0 || res.Trade.IsGroup != 0) ? res.Trade.id : res.Trade.ParentTradeId;
_logger.Info($"TradeConfirmRisk.RunNewRisk tradeId:{tradeId}, sourceTradeId:{res.Trade.id}, tradeNumber:{res.Trade.TradeNumber}, ignoreRiskWarning:{ignoreRiskWarning}, ignoreRiskRuleIds:{string.Join(",", ignoreRiskRuleIds ?? Enumerable.Empty())}, userId:{OptUser?.UserId}");
var trialService = new QuotaMonitorService(this);
var quotaObj = trialService.RunNewRiskTrial(tradeId, 1);
return ApplyQuotaCheckResult(ref res, quotaObj, ignoreRiskWarning, ignoreRiskRuleIds);
}
///
/// 将风控试算结果转换为交易确认结果。
/// 这里不重新执行风控,只负责设置 RetCode、ErrorMsg、TipMsg、TrialDataId 等返回给确认流程的字段。
///
private bool ApplyQuotaCheckResult(ref TradeOpenResult res, QuotaTrial quotaObj, bool ignoreRiskWarning, IEnumerable ignoreRiskRuleIds = null)
{
_logger.Info($"TradeConfirmRisk.TrialResult tradeId:{res.Trade?.id}, tradeNumber:{res.Trade?.TradeNumber}, trialDataId:{quotaObj.id}, trialStatus:{quotaObj.TrialStatus}, oldRiskTrialStatus:{quotaObj.OldRiskTrialStatus}, newRiskNeedApproval:{quotaObj.NewRiskNeedApproval}, newRiskBlocked:{quotaObj.NewRiskBlocked}, approvalRuleIds:{string.Join(",", quotaObj.ApprovalRuleIds ?? new List())}, ignoreRiskWarning:{ignoreRiskWarning}, ignoreRiskRuleIds:{string.Join(",", ignoreRiskRuleIds ?? Enumerable.Empty())}, fundCheck:{!string.IsNullOrWhiteSpace(quotaObj.FundCheckDetails)}, quotaCheck:{!string.IsNullOrWhiteSpace(quotaObj.QuotaCheckDetails)}, riskWarning:{!string.IsNullOrWhiteSpace(quotaObj.RiskWarningDetails)}, quotaWarning:{!string.IsNullOrWhiteSpace(quotaObj.QuotaWarningDetails)}, userId:{OptUser?.UserId}");
if (quotaObj.TrialStatus == QuotaTrialStatusEnum.Success)//这次计算结果是通过,直接通过
{
if (!string.IsNullOrWhiteSpace(quotaObj.AvailableForClient)) // 返回用户资金情况
{
res.RetCode = TradeOpenRetCode.FundStatus;
res.TrialDataId = quotaObj.id;
return false;
}
//成功状态下,如果触发TIp检查,提示用户
if (!string.IsNullOrWhiteSpace(quotaObj.RiskWarningDetails))
{
res.TrialDataId = quotaObj.id;
res.TipMsg = quotaObj.RiskWarningDetails;
}
return true;
}
//否则的情况是上次没算,这次是预警,或上次算了,结果是不通过\通过或预警,这次是预警或不通过,提示用户;
res.RetCode = TradeOpenRetCode.QuotaTrialError;
res.TrialDataId = quotaObj.id;
//汇总各类检查详情,避免只展示RiskWarningDetails而漏掉其它老风控检查
var detailParts = new List();
if (!string.IsNullOrWhiteSpace(quotaObj.FundCheckDetails))
detailParts.Add($"应付资金检查:{quotaObj.FundCheckDetails}");
if (!string.IsNullOrWhiteSpace(quotaObj.QuotaCheckDetails))
detailParts.Add($"限额检查:{quotaObj.QuotaCheckDetails}");
if (!string.IsNullOrWhiteSpace(quotaObj.RiskWarningDetails))
detailParts.Add($"风险预警:{quotaObj.RiskWarningDetails}");
if (!string.IsNullOrWhiteSpace(quotaObj.QuotaWarningDetails))
detailParts.Add($"限额预警:{quotaObj.QuotaWarningDetails}");
res.ErrorMsg = string.Join("\n", detailParts);
// 向上层透传新风控原始结果,用于区分组合特批和禁止场景。
res.NewRiskBlocked = quotaObj.NewRiskBlocked;
res.NewRiskNeedApproval = quotaObj.NewRiskNeedApproval;
// 新风控禁止优先阻断;需审批状态独立判断,避免被老风控的Error或Warning状态覆盖。
var isRiskApprovalWarning = quotaObj.NewRiskNeedApproval
&& !quotaObj.NewRiskBlocked
&& !string.IsNullOrWhiteSpace(quotaObj.RiskWarningDetails)
&& (quotaObj.ApprovalRuleIds?.Any() ?? false);
// 老风控试算不通过时,延用master逻辑进入交易特批;与新风控需审批同时命中时保留两边状态。
res.OldRiskNeedSpecialApproval = quotaObj.OldRiskTrialStatus.HasValue
&& quotaObj.OldRiskTrialStatus.Value != QuotaTrialStatusEnum.Success;
if (isRiskApprovalWarning)
{
var currentApprovalRuleIds = quotaObj.ApprovalRuleIds ?? new List();
res.IgnoredRiskRuleIds = currentApprovalRuleIds;
//需清除ErrorMsg,不然外部调用会认为失败
//触发需审批,交易特批逻辑
if (ignoreRiskWarning)
{
var ignoredRuleIdSet = new HashSet((ignoreRiskRuleIds ?? Enumerable.Empty()).Where(o => !string.IsNullOrWhiteSpace(o)));
var newApprovalRuleIds = currentApprovalRuleIds.Where(o => !ignoredRuleIdSet.Contains(o)).ToList();
if (!newApprovalRuleIds.Any())
{
_logger.Info($"TradeConfirmRisk.ApprovalIgnored tradeId:{res.Trade?.id}, tradeNumber:{res.Trade?.TradeNumber}, trialDataId:{quotaObj.id}, ignoredRuleIds:{string.Join(",", ignoredRuleIdSet)}, userId:{OptUser?.UserId}");
res.ErrorMsg = string.Empty;
return true;
}
}
}
res.RetCode = isRiskApprovalWarning ? TradeOpenRetCode.RiskWarning : TradeOpenRetCode.QuotaTrialError;
var isOldRiskErrorSpecialApproval = !isRiskApprovalWarning
&& quotaObj.OldRiskTrialStatus == QuotaTrialStatusEnum.Error
&& res.OldRiskNeedSpecialApproval;
AddRiskCheckLog(quotaObj, isOldRiskErrorSpecialApproval);
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);
// 仅在限额设置中有DV指标时才计算DV
//var (totalDv, currentDv) = precheckQuotaSettingList.Any(s => s.QuotaIndex == "DV")
// ? CalcRiskCheckDv(checkPoisiList, dealDate) : (0d, 0d);
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, 0d, 0d);
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();
}
///
/// 计算风控检查用的DV(全局DV + 当前单笔DV)
///
private (double totalDv, double currentDv) CalcRiskCheckDv(List checkPoisiList, DateTime dealDate)
{
var totalDv = 0d;
var currentDv = 0d;
// 按标的缓存bondPrice
var bpCache = checkPoisiList.Select(p => p.UnderlyingCode).Distinct()
.ToDictionary(code => code, code => EodPriceQueryService.GetBondPrice(dealDate, code));
// 全部持仓DV
totalDv = checkPoisiList.Sum(pos =>
{
if (!bpCache.TryGetValue(pos.UnderlyingCode, out var bp) || bp == null) return 0d;
var vobp = bp.Vobp ?? 0;
if (vobp == 0) return 0d;
int posType = pos.Side == 0 ? (int)PositionTypeFlag.Long : (int)PositionTypeFlag.Short;
return Convert.ToDouble(Dv01Helper.CalcDv01(pos.UnderlyingCode, pos.Qty, pos.Direction, posType, vobp));
});
// 当前单笔DV
var cur = checkPoisiList.FirstOrDefault(x => x.Current);
if (cur != null && bpCache.TryGetValue(cur.UnderlyingCode, out var curBp) && curBp != null)
{
var vobp = curBp.Vobp ?? 0;
if (vobp != 0)
{
int pt = cur.Side == 0 ? (int)PositionTypeFlag.Long : (int)PositionTypeFlag.Short;
currentDv = Convert.ToDouble(Dv01Helper.CalcDv01(cur.UnderlyingCode, cur.Qty, cur.Direction, pt, vobp));
}
}
// 标的交易DV
List tradeTypes = new List { "利率债", "信用债", "其它债券" };
var tposis = DbContext.TradePosition.Where(x => tradeTypes.Contains(x.TradeType)).AsNoTracking().ToList();
foreach (var code in tposis.Select(x => x.UnderlyingCode).Distinct().Where(c => !bpCache.ContainsKey(c)))
bpCache[code] = EodPriceQueryService.GetBondPrice(dealDate, code);
foreach (var item in tposis)
{
if (!bpCache.TryGetValue(item.UnderlyingCode, out var tpBp) || tpBp == null) continue;
var vobp = tpBp.Vobp ?? 0;
if (vobp == 0) continue;
totalDv += Convert.ToDouble(Dv01Helper.CalcDv01(item.UnderlyingCode, Convert.ToDecimal(Math.Abs(item.Position)), (int)SwapDirectionEnum.收取, (int)item.PositionType, vobp));
}
return (totalDv, currentDv);
}
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 ? 1 : -1) * 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;
}
private DateTime? QueryLatestRiskCheckTriggerTime(int tradeId)
{
var tradeNumber = DbContext.trade.Where(x => x.id == tradeId).Select(x => x.TradeNumber).FirstOrDefault();
if (string.IsNullOrWhiteSpace(tradeNumber))
{
return null;
}
return DbContext.trade_risk_check_log
.Where(x => x.trade_number == tradeNumber && x.remark.StartsWith(RiskCheckTriggerRemark))
.OrderByDescending(x => x.create_time)
.Select(x => (DateTime?)x.create_time)
.FirstOrDefault();
}
private void AddRiskCheckLog(QuotaTrial quotaTrial, bool isOldRiskErrorSpecialApproval = false)
{
var td = DbContext.trade.FirstOrDefault(x => x.id == quotaTrial.TradeId);
var log = new trade_risk_check_log();
if (td != null)
{
var client = DataCacheProvider.GetClientDataSource().GetData(td.ClientId);
log.client_number = client?.Number;
}
var worker = new IdWorker(1, 1);
log.id = worker.NextId();
log.trial_result = (int)quotaTrial.TrialStatus;
log.client_name = quotaTrial.ClientName;
log.trader = td?.TraderName;
log.trade_number = quotaTrial.TradeNumber;
var riskWarning = string.Empty;
if (!string.IsNullOrWhiteSpace(quotaTrial.QuotaWarningDetails))
{
riskWarning = "限额预警: " + quotaTrial.QuotaWarningDetails;
}
if (!string.IsNullOrWhiteSpace(quotaTrial.RiskWarningDetails))
{
if (!string.IsNullOrWhiteSpace(riskWarning))
{
riskWarning += Environment.NewLine;
}
riskWarning += "风险预警: " + quotaTrial.RiskWarningDetails;
}
log.risk_warning = TruncateRiskCheckLogText(riskWarning);
log.limit_warning = quotaTrial.QuotaCheckDetails;
log.remark = RiskCheckTriggerRemark;
if (isOldRiskErrorSpecialApproval)
{
log.remark += ";老风控Error允许交易特批";
}
log.create_user = UserId;
log.create_time = DateTime.Now;
DbContext.trade_risk_check_log.Add(log);
DbContext.SaveChanges();
}
///
/// 按 quotaTrial.id 校验最近一次触发风控的记录是否已超过配置时效
///
/// quotaTrial.id
/// 有效时长(秒)
/// 查不到记录或已超时返回 true,否则返回 false
public bool IsQuotaTrialExpired(int trialDataId, int expireSeconds)
{
var quotaTrial = QueryQuotaTrial(trialDataId);
if (quotaTrial == null)
{
return true;
}
var latestRiskCheckTriggerTime = QueryLatestRiskCheckTriggerTime(quotaTrial.TradeId);
var riskCheckTime = latestRiskCheckTriggerTime ?? quotaTrial.OptDate;
if (!riskCheckTime.HasValue)
{
return true;
}
return DateTime.Now - riskCheckTime.Value > TimeSpan.FromSeconds(expireSeconds);
}
///
/// 查询试算历史
///
///
public SearchListResult QueryQuotaTrial(QuotaTrialReq req)
{
var query = DbContext.quotaTrial.Where(O => O.TradeId == req.TradeId).AsQueryable();
var result = query.ToSearchList(req);
return result;
}
///
/// 完整风控试算:先执行老风控资金/限额试算,再叠加新风控试算结果。
/// 后续老风控下线时,调用方可以保留该入口,内部改为直接调用 RunNewRiskTrial。
///
/// 交易Id,结构化交易请传主交易Id
/// 试算来源,0:手动点击试算按钮;1:确认成交时自动调用;
///
public QuotaTrial RunQuotaTrial(int tradeId, int trialSource)
{
_logger.Info($"[限额试算] 开始执行 - tradeId: {tradeId}, trialSource: {trialSource}");
var result = new QuotaTrial();
trade tradeObj = DbContext.trade.AsNoTracking().Where(O => O.id == tradeId).FirstOrDefault();
// 检查交易对象是否存在
if (tradeObj == null)
{
_logger.Info($"[限额试算] 找不到交易记录 - tradeId: {tradeId}");
throw new ArgumentNullException(nameof(tradeObj), $"找不到ID为 {tradeId} 的交易记录");
}
_logger.Info($"[限额试算] 交易信息 - tradeId: {tradeObj.id}, TradeNumber: {tradeObj.TradeNumber}, TradeType: {tradeObj.TradeType}, ClientId: {tradeObj.ClientId}");
//// 检查关键字段是否为null
if (string.IsNullOrEmpty(tradeObj.TradeType))
{
_logger.Info($"[限额试算] TradeType 为 null 或空 - tradeId: {tradeId}");
throw new ArgumentNullException(nameof(tradeObj.TradeType), "交易类型不能为空");
}
var tradeList = new List();
if (tradeObj.TradeType == "结构化交易")
{
tradeList.AddRange(DbContext.trade.AsNoTracking().Where(O => O.ParentTradeId == tradeId).ToArray());
}
else
{
tradeList.Add(tradeObj);
}
// 检查关键字段
if (!tradeObj.TradeDate.HasValue)
{
_logger.Info($"[限额试算] TradeDate 为 null - tradeId: {tradeId}");
throw new ArgumentNullException(nameof(tradeObj.TradeDate), "交易日期不能为空");
}
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)
{
// 老风控没有可试算的标的时,老逻辑认为试算通过;这里仍需继续执行新风控,避免普通确认漏掉新风控检查。
//("当前交易不需要试算");
result.TrialStatus = QuotaTrialStatusEnum.Success;
result.TradeId = tradeObj.id;
result.TradeNumber = tradeObj.TradeNumber ?? "";
result.ClientName = tradeObj.ClientName ?? "";
result.FundCheckDetails = fundMsg ?? "";
result.RiskWarningDetails = riskWarningMsg ?? "";
result.AvailableForClient = availableMsg ?? "";
result.TrialSource = trialSource;
result.OldRiskTrialStatus = result.TrialStatus;
result = ApplyNewRiskTrial(tradeId, result);
SaveQuotaTrial(result);
return result;
}
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();
_logger.Info($"[限额试算] 检查持仓 - positions.Count: {positions?.Count ?? 0}");
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();
_logger.Info($"[限额试算] floatPosi: {(floatPosi == null ? "null" : $"id={floatPosi.id}, UnderlyingCode={floatPosi.UnderlyingCode}")}");
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();
_logger.Info($"[限额试算] umCodes.Count: {umCodes?.Count ?? 0}");
var ums = DataCacheProvider.GetUnderlyingDataSource().AsQueryable().Where(x => umCodes.Contains(x.UnderlyingCode));
_logger.Info($"[限额试算] ums.Count: {ums?.Count() ?? 0}");
List checkPoisiList = new List();
var posiList = GetPosiQuotaMoitors();
_logger.Info($"[限额试算] posiList.Count: {posiList?.Count ?? 0}");
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);
_logger.Info($"[限额试算] 处理持仓 - security_id: {item.security_id}, um: {(um == null ? "null" : $"id={um.id}, UnderlyingCode={um.UnderlyingCode}")}");
if (um == null)
{
_logger.Info($"[限额试算] 找不到标的 - security_id: {item.security_id}");
continue;
}
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;
// 检查 ExJson 是否为 null 或空
if (string.IsNullOrEmpty(um.ExJson))
{
_logger.Info($"[限额试算] um.ExJson 为 null 或空 - um.id: {um.id}, UnderlyingCode: {um.UnderlyingCode}");
checkQuotaMoitorModel.Circulation = 0;
}
else
{
try
{
var bond = JsonHelper.Deserialize(um.ExJson);
if (bond == null)
{
_logger.Info($"[限额试算] 反序列化 bond 失败 - um.id: {um.id}, ExJson: {um.ExJson.Substring(0, Math.Min(100, um.ExJson.Length))}");
checkQuotaMoitorModel.Circulation = 0;
}
else
{
checkQuotaMoitorModel.Circulation = (bond.IssueSize * 100000000m) ?? 0;
}
}
catch (Exception ex)
{
_logger.Error($"[限额试算] 错误: 反序列化 bond 异常 - um.id: {um.id}", ex);
checkQuotaMoitorModel.Circulation = 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);
}
// 预计算DV,避免warning/error两次检查各算一遍
var (quotaTotalDv, quotaCurrentDv) = list.Any(s => s.QuotaIndex == "DV") ? CalcRiskCheckDv(checkPoisiList, tradeObj.TradeDate.Value) : (0d, 0d);
quotaWarningStatus = CheckQuota(checkPoisiList, posiList, list, allList, true, out quotaWarningMsg, quotaTotalDv, quotaCurrentDv);
quotaStatus = CheckQuota(checkPoisiList, posiList, list, allList, false, out quotaMsg, quotaTotalDv, quotaCurrentDv);
}
}
}
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;
result.OldRiskTrialStatus = result.TrialStatus;
// 保留老风控原始状态,随后叠加新风控结果;这样二次特批时可以区分是老风控失败还是新风控需审批。
result = ApplyNewRiskTrial(tradeId, result);
SaveQuotaTrial(result);
return result;
}
///
/// 新风控独立试算:不执行老风控资金/限额检查,只调用新风控引擎。
/// 用于老风控特批后的确认流程,也可作为后续老风控下线后的主试算入口。
///
public QuotaTrial RunNewRiskTrial(int tradeId, int trialSource)
{
var result = new QuotaTrial
{
TradeId = tradeId,
TrialSource = trialSource,
TrialStatus = QuotaTrialStatusEnum.Success,
OldRiskTrialStatus = QuotaTrialStatusEnum.Success
};
var tradeObj = DbContext.trade.AsNoTracking().Where(O => O.id == tradeId).FirstOrDefault();
if (tradeObj != null)
{
result.TradeNumber = tradeObj.TradeNumber ?? "";
result.ClientName = tradeObj.ClientName ?? "";
}
result = ApplyNewRiskTrial(tradeId, result);
SaveQuotaTrial(result);
return result;
}
///
/// 将新风控引擎结果合并到 QuotaTrial。
/// 如果传入的是完整试算结果,则是在老风控结果基础上叠加新风控;如果传入的是新建结果,则表示只跑新风控。
///
private QuotaTrial ApplyNewRiskTrial(int tradeId, QuotaTrial result)
{
// ===== 新风控引擎接入点(设计文档 4.6.1) =====
try
{
var riskEngine = new RiskEngineService(this);
var riskContext = new RiskContext
{
TradeId = tradeId,
TriggerPoint = "BOOK_CONFIRM"
};
var riskResult = riskEngine.EvaluateRisk(riskContext, "BOOK_CONFIRM");
_logger.Info($"[风控引擎] 簿记交易确认 - TradeId: {tradeId}, Passed: {riskResult.Passed}, Blocked: {riskResult.Blocked}, NeedApproval: {riskResult.NeedApproval}, ShowTip: {riskResult.ShowTip}");
// 单独保留新风控原始结果,避免与老风控状态合并后无法区分组合特批和禁止场景。
result.NewRiskBlocked = riskResult.Blocked;
result.NewRiskNeedApproval = riskResult.NeedApproval;
// 按策略映射回 QuotaTrialStatusEnum
if (riskResult.Blocked)
{
result.TrialStatus = QuotaTrialStatusEnum.Error;
result.RiskWarningDetails += "[风控引擎] 规则触发:禁止\n";
foreach (var triggeredRule in riskResult.TriggeredRules.Where(r => r.ControlStrategy == RiskControlStrategy.Block))
{
result.RiskWarningDetails += BuildTriggeredRuleDetail(triggeredRule);
}
}
if (riskResult.NeedApproval)
{
if (result.TrialStatus != QuotaTrialStatusEnum.Error && result.TrialStatus != QuotaTrialStatusEnum.Warning)
{
result.TrialStatus = QuotaTrialStatusEnum.RiskWarning;
}
result.RiskWarningDetails += "[风控引擎] 规则触发:需审批\n";
var approvalTriggeredRules = riskResult.TriggeredRules.Where(r => r.ControlStrategy == RiskControlStrategy.Approval).ToList();
var approvalTriggeredRuleIds = approvalTriggeredRules.Select(r => r.RuleId)
.Where(r => !string.IsNullOrWhiteSpace(r))
.ToList();
var showTipTriggeredRules = riskResult.TriggeredRules.Where(r => r.ControlStrategy == RiskControlStrategy.ShowTip).ToList();
var showTipTriggeredRuleIds = showTipTriggeredRules.Select(r => r.RuleId)
.Where(r => !string.IsNullOrWhiteSpace(r))
.ToList();
// 二次确认时,需审批规则允许按首次命中结果忽略;同时已展示过的提示规则也不再重复展示
var processedRiskRuleIds = approvalTriggeredRuleIds.Concat(showTipTriggeredRuleIds)
.Distinct()
.ToList();
result.ApprovalRuleIds = processedRiskRuleIds;
foreach (var triggeredRule in approvalTriggeredRules)
{
result.RiskWarningDetails += BuildTriggeredRuleDetail(triggeredRule);
}
}
if (riskResult.ShowTip)
{
result.RiskWarningDetails += "[风控引擎] 规则触发:提示\n";
foreach (var triggeredRule in riskResult.TriggeredRules.Where(r => r.ControlStrategy == RiskControlStrategy.ShowTip))
{
result.RiskWarningDetails += BuildTriggeredRuleDetail(triggeredRule);
}
}
}
catch (Exception ex)
{
_logger.Error($"[风控引擎] 执行异常 - TradeId: {tradeId}, Error: {ex.Message}");
// 第一版:异常不阻断原有流程,只记录日志
}
// ===== 新风控引擎接入结束 =====
return result;
}
///
/// 构建新风控触发规则明细。禁止、审批、提示三类规则都可能带结构化命中明细,统一格式避免不同策略展示字段不一致。
///
private static string BuildTriggeredRuleDetail(TriggeredRuleInfo triggeredRule)
{
var applicationIdText = triggeredRule.ApplicationId?.ToString() ?? "-";
var detail = $"应用ID:{applicationIdText};规则ID:{triggeredRule.RuleId};规则名称:{triggeredRule.RuleName};规则说明:{triggeredRule.RuleText}";
if (!string.IsNullOrWhiteSpace(triggeredRule.Message))
{
detail += $";信息:{triggeredRule.Message}";
}
return detail + "\n";
}
private const int RiskCheckLogTextMaxLength = 500;
private static string TruncateRiskCheckLogText(string text)
{
if (string.IsNullOrEmpty(text) || text.Length <= RiskCheckLogTextMaxLength)
return text;
const string suffix = "……(日志内容过长已截断)";
return text.Substring(0, RiskCheckLogTextMaxLength - suffix.Length) + suffix;
}
///
/// 校验标的白名单
///
///
///
///
///
private bool CheckUnderlyingWhiteList(int clientId, List positions, out string quotaMsg)
{
quotaMsg = "";
if (positions.Count == 0)
{
return true;
}
// 检查 positions 中是否有 null 元素或 UnderlyingCode 为 null
for (int i = 0; i < positions.Count; i++)
{
var item = positions[i];
if (item == null)
{
_logger.Info($"[CheckUnderlyingWhiteList] positions[{i}] 为 null");
quotaMsg = "持仓数据异常:存在空记录";
return false;
}
if (string.IsNullOrEmpty(item.UnderlyingCode))
{
_logger.Info($"[CheckUnderlyingWhiteList] positions[{i}].UnderlyingCode 为 null 或空 - id: {item.id}, SwapTradeId: {item.SwapTradeId}");
quotaMsg = "持仓数据异常:标的代码为空";
return false;
}
}
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 };
_logger.Info($"[CheckUnderlyingWhiteList] 构建请求参数 - clientId: {clientId}, securityId: {item.UnderlyingCode}, orderQty: {item.PosiQuantity * 0.0001m}, side: {item.PositionType - 1}");
clientOrderParams.Add(clientOrderParam);
}
if (!string.IsNullOrEmpty(baseUrl) && !string.IsNullOrEmpty(checkUrl))
{
try
{
_logger.Info($"[CheckUnderlyingWhiteList] 开始调用外部接口 - baseUrl: {baseUrl}, checkUrl: {checkUrl}, clientOrderParams.Count: {clientOrderParams?.Count ?? 0}");
var httpHelper = new HttpHelper(baseUrl, null);
// http 请求 Web项目接口
var result = httpHelper.PostRequestNoAuth, ApiResponse>(checkUrl, clientOrderParams).Result;
_logger.Info($"[CheckUnderlyingWhiteList] 接口调用完成 - result: {(result == null ? "null" : $"success={result.success}")}");
if (result != null && !result.success)
{
quotaMsg = result.message;
return false;
}
}
catch (Exception ex)
{
_logger.Error($"[CheckUnderlyingWhiteList] 调用外部接口异常 - baseUrl: {baseUrl}, checkUrl: {checkUrl}", ex);
quotaMsg = "调用外部接口失败:" + ex.Message;
return false;
}
}
else
{
_logger.Info($"[CheckUnderlyingWhiteList] 跳过外部接口调用 - baseUrl: {(string.IsNullOrEmpty(baseUrl) ? "空" : baseUrl)}, checkUrl: {(string.IsNullOrEmpty(checkUrl) ? "空" : checkUrl)}");
}
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)
{
_logger.Info($"[CheckFund] 开始资金检查 - clientId: {clientId}, stockEqvNotional: {stockEqvNotional}, trades.Count: {trades?.Count ?? 0}");
msg = "";
avmsg = "";
var totalCredit = 0.0;
var availableStockEqvNotional = 0.0;
if (trades == null || trades.Count == 0)
{
_logger.Info("[CheckFund] trades 为 null 或空列表");
msg = "交易列表为空";
return false;
}
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;
_logger.Info($"[CheckFund] 资金计算 - totalCredit: {totalCredit}, positionStockEqvNotional: {positionStockEqvNotional}, UseClientStockEqvNotional: {PS.Config.ErpElement.UseClientStockEqvNotional}");
if (PS.Config.ErpElement.UseClientStockEqvNotional)
{
availableStockEqvNotional = totalCredit - positionStockEqvNotional;
_logger.Info($"[CheckFund] 可用名义本金 - availableStockEqvNotional: {availableStockEqvNotional}");
}
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)
{
// 检查 ExerciseDate 是否为 null
if (!trades[0].ExerciseDate.HasValue)
{
_logger.Info($"[CheckFund] trades[0].ExerciseDate 为 null - tradeId: {trades[0].id}");
msg = "交易行权日期不能为空";
return false;
}
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, double totalDv = 0, double currentDv = 0)
{
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_ALL:
msgList.AddRange(checkGlobalDv(positionList, item.Value, warning, totalDv));
break;
case QuotaTypeEnum.GLOBAL_SWAP:
msgList.AddRange(checkGlobal(positionList, posiList, "场外业务-互换", item.Value, warning));
break;
case QuotaTypeEnum.TRADE:
msgList.AddRange(checkTrade(positionList, posiList, "单笔交易", item.Value, warning, currentDv));
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;
}
///