6199 lines
331 KiB
C#
6199 lines
331 KiB
C#
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 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 static YLErp.ConsGlobal;
|
|
|
|
namespace YLErp.Modules.RiskModule
|
|
{
|
|
public class QuotaMonitorService : YLBaseService
|
|
{
|
|
IYcLogger _logger = LogFactory.GetLogger("QuotaMonitorService");
|
|
static QuotaMonitorService()
|
|
{
|
|
initSetting();
|
|
}
|
|
|
|
public QuotaMonitorService(OptUserInfo userInfo) : base(userInfo)
|
|
{
|
|
}
|
|
|
|
public QuotaMonitorService(YLBaseService baseService) : base(baseService)
|
|
{
|
|
}
|
|
|
|
public QuotaMonitorService(OptUserInfo optUser, YLContext dbContext) : base(optUser, dbContext)
|
|
{
|
|
}
|
|
|
|
/// <summary>
|
|
/// 当日收盘时限额监控数据落地
|
|
/// <para>配合国海风控数据采集</para>
|
|
/// <para>只有当日收盘时才运行,防止数据错误</para>
|
|
/// </summary>
|
|
/// <param name="settlementDate"></param>
|
|
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<QuotaMonitor_Global>($"{nameof(QuotaMonitorBase.ValueDate)}='{settlementDate.ToString("yyyy-MM-dd")}'");
|
|
DbContext.BulkDelete<QuotaMonitor_Underlying>($"{nameof(QuotaMonitorBase.ValueDate)}='{settlementDate.ToString("yyyy-MM-dd")}'");
|
|
DbContext.BulkDelete<QuotaMonitor_Client>($"{nameof(QuotaMonitorBase.ValueDate)}='{settlementDate.ToString("yyyy-MM-dd")}'");
|
|
DbContext.BulkDelete<QuotaMonitor_Trade>($"{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<string> { "现金流交易" });
|
|
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<string, string> { { "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<trade> 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>() { 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<trade> 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;
|
|
}
|
|
|
|
/// <summary>
|
|
/// 从数据库中查询限额监控数据
|
|
/// </summary>
|
|
/// <typeparam name="T"></typeparam>
|
|
/// <param name="req"></param>
|
|
/// <returns></returns>
|
|
private List<T> QueryFromDb<T>(QuotaMonitorReq req) where T : QuotaMonitorBase
|
|
{
|
|
var db = DbContext.Set<T>();
|
|
return db.Where(O => O.ValueDate == req.ValueDate).ToList();
|
|
}
|
|
|
|
/// <summary>
|
|
/// 查询限额监控-全局数据
|
|
/// </summary>
|
|
/// <param name="req"></param>
|
|
/// <returns></returns>
|
|
public List<QuotaMonitor_Global> QueryGlobalFromDb(QuotaMonitorReq req)
|
|
{
|
|
//SetDebugSqlLog();
|
|
var list = QueryFromDb<QuotaMonitor_Global>(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;
|
|
}
|
|
|
|
/// <summary>
|
|
/// 查询限额监控-客户数据
|
|
/// </summary>
|
|
/// <param name="req"></param>
|
|
/// <returns></returns>
|
|
public List<QuotaMonitor_Client> QueryClientFromDb(QuotaMonitorReq req)
|
|
{
|
|
var list = QueryFromDb<QuotaMonitor_Client>(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;
|
|
}
|
|
|
|
/// <summary>
|
|
/// 查询限额监控-资产汇总数据
|
|
/// </summary>
|
|
/// <param name="req"></param>
|
|
/// <returns></returns>
|
|
public List<QuotaMonitor_Asset> QueryAssetFromDb(QuotaMonitorReq req)
|
|
{
|
|
return QueryFromDb<QuotaMonitor_Asset>(req);
|
|
}
|
|
|
|
/// <summary>
|
|
/// 查询限额监控-品种汇总数据
|
|
/// </summary>
|
|
/// <param name="req"></param>
|
|
/// <returns></returns>
|
|
public List<QuotaMonitor_Variety> QueryVarietyFromDb(QuotaMonitorReq req)
|
|
{
|
|
return QueryFromDb<QuotaMonitor_Variety>(req);
|
|
}
|
|
|
|
/// <summary>
|
|
/// 查询限额监控-单笔交易数据
|
|
/// </summary>
|
|
/// <param name="req"></param>
|
|
/// <returns></returns>
|
|
public List<QuotaMonitor_Trade> QueryTradeFromDb(QuotaMonitorReq req)
|
|
{
|
|
var queryStatus = false;
|
|
var list = QueryFromDb<QuotaMonitor_Trade>(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;
|
|
}
|
|
|
|
/// <summary>
|
|
/// 查询限额监控-标的汇总数据
|
|
/// </summary>
|
|
/// <param name="req"></param>
|
|
/// <returns></returns>
|
|
public List<QuotaMonitor_Underlying> QueryUnderlyingFromDb(QuotaMonitorReq req)
|
|
{
|
|
var list = QueryFromDb<QuotaMonitor_Underlying>(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;
|
|
}
|
|
|
|
/// <summary>
|
|
/// 查询限额监控-簿记汇总数据
|
|
/// </summary>
|
|
/// <param name="req"></param>
|
|
/// <returns></returns>
|
|
public List<QuotaMonitor_Assetunit> QueryAssetUnitFromDb(QuotaMonitorReq req)
|
|
{
|
|
var list = QueryFromDb<QuotaMonitor_Assetunit>(req);
|
|
if (req.AssetId > 0)
|
|
{
|
|
list = list.Where(O => O.AssetBookID == req.AssetId).ToList();
|
|
}
|
|
return list;
|
|
}
|
|
|
|
/// <summary>
|
|
/// 查询限额监控-全局数据
|
|
/// </summary>
|
|
/// <param name="req"></param>
|
|
/// <returns></returns>
|
|
public List<QuotaMonitor_Global> QueryGlobalFromCalc(QuotaMonitorReq req)
|
|
{
|
|
var positionTradeQuery = DbContext.trade.Where(O =>
|
|
ConsTrade.PositionTradeStatusList.Contains(O.TradeStatus) && O.ValidState != "InValid" &&
|
|
O.ExerciseDate >= SystemValueDate && O.TradeType == "收益互换");
|
|
|
|
req.PvPercent = 1 - req.PvPercent;
|
|
var arr = (from risk in DbContext.realtime_trade_risk
|
|
join t in positionTradeQuery
|
|
on risk.TradeId equals t.id
|
|
where risk.VolType == "持仓" && t.TradeType == "收益互换"
|
|
select new { risk, t }).ToArray();
|
|
var dict = (from o in arr
|
|
group o by (o.t.TradeType == "收益互换" ? "互换" : "") into info
|
|
select new { Key = info.Key, list = info.ToList() }).ToDictionary(K => K.Key, V => V.list);
|
|
var posiQuery = from t in positionTradeQuery
|
|
join p in DbContext.swap_position on t.id equals p.SwapTradeId
|
|
where !p.IsInitial && p.PosiDirection > 0 && !p.Invalid
|
|
select p;
|
|
var setValue = new Action<QuotaMonitor_Global, List<QuotaSetting>>((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 swapSetting = QueryCurrentQuotaSetting(QuotaTypeEnum.GLOBAL_SWAP);
|
|
swapSetting = MargeQuotaSetting(swapSetting, 0, 0);
|
|
var swap = new QuotaMonitor_Global()
|
|
{
|
|
ParentKey = "场外",
|
|
BusinessType = "互换"
|
|
};
|
|
setValue(swap, swapSetting);
|
|
|
|
var positionList = new List<KeyValuePair<trade, realtime_trade_risk>>();
|
|
var underly = GetTradePositionPnl();
|
|
if (dict.ContainsKey("互换"))
|
|
{
|
|
var swapPositionList = dict["互换"].Select(O => new KeyValuePair<trade, realtime_trade_risk>(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();
|
|
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)
|
|
};
|
|
var all = new QuotaMonitor_Global()
|
|
{
|
|
BusinessType = "全局",
|
|
StockEqvNotional = Convert.ToDouble(posiStockEqvNotional),
|
|
PositionPnl = underly.PositionPnl + swap.PositionPnl,
|
|
DeltaExposure=(double)delta+ underly.DeltaExposure
|
|
};
|
|
var list = new List<QuotaMonitor_Global>
|
|
{
|
|
swap,
|
|
underly,
|
|
unTrade,
|
|
all
|
|
};
|
|
for (var i = 0; i < list.Count; i++)
|
|
{
|
|
list[i].id = -1 - i;
|
|
}
|
|
return list;
|
|
}
|
|
|
|
/// <summary>
|
|
/// 查询限额监控-全局数据-收盘
|
|
/// </summary>
|
|
/// <param name="req"></param>
|
|
/// <returns></returns>
|
|
public List<QuotaMonitor_Global> QueryEodGlobalFromCalc(QuotaMonitorReq req)
|
|
{
|
|
var posiQuery = DbContext.eod_swap_position.Where(O =>O.ValueDate==req.ValueDate&&O.PosiQuantity>0);
|
|
|
|
req.PvPercent = 1 - req.PvPercent;
|
|
|
|
var setValue = new Action<QuotaMonitor_Global, List<QuotaSetting>>((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 swapSetting = QueryCurrentQuotaSetting(QuotaTypeEnum.GLOBAL_SWAP);
|
|
swapSetting = MargeQuotaSetting(swapSetting, 0, 0);
|
|
var swap = new QuotaMonitor_Global()
|
|
{
|
|
ParentKey = "场外",
|
|
BusinessType = "互换",
|
|
};
|
|
setValue(swap, swapSetting);
|
|
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
|
|
};
|
|
var list = new List<QuotaMonitor_Global>
|
|
{
|
|
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<HedgePnl> 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;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
/// <summary>
|
|
/// 查询限额监控-客户数据
|
|
/// </summary>
|
|
/// <param name="req"></param>
|
|
/// <returns></returns>
|
|
public List<QuotaMonitor_Client> 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<int> clientIds = new List<int>();
|
|
clientIdList.ForEach(item =>
|
|
{
|
|
clientIds.Add(Convert.ToInt32(item.Value));
|
|
});
|
|
var setValue = new Action<QuotaMonitor_Client, List<QuotaSetting>>((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<QuotaMonitor_Client>();
|
|
|
|
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<int, double>();
|
|
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;
|
|
}
|
|
|
|
/// <summary>
|
|
/// 查询限额监控-客户数据
|
|
/// </summary>
|
|
/// <param name="req"></param>
|
|
/// <returns></returns>
|
|
public List<QuotaMonitor_Client> 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<QuotaMonitor_Client, List<QuotaSetting>>((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<QuotaMonitor_Client>();
|
|
|
|
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<int, double>();
|
|
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;
|
|
}
|
|
|
|
/// <summary>
|
|
/// 查询限额监控-单笔交易数据
|
|
/// </summary>
|
|
/// <param name="req"></param>
|
|
/// <returns></returns>
|
|
public List<QuotaMonitor_Trade> QueryTradeFromCalc(QuotaMonitorReq req)
|
|
{
|
|
var result = new List<QuotaMonitor_Trade>();
|
|
req.sidx = "TradeNumber";
|
|
req.sord = "asc";
|
|
var predicate = PredicateBuilder.True<trade>().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<QuotaMonitor_Trade, List<QuotaSetting>>((obj, settings) =>
|
|
{
|
|
var temp = settings.Where(O => O.IsValid && O.Status == QuotaSettingApprovalStatus.Valid && O.QuotaRange == 0);
|
|
var stockEqvNotionalSetting = temp.Where(O => O.QuotaIndex == "名义本金").FirstOrDefault()?.Clone();
|
|
stockEqvNotionalSetting?.ConvertToAbs(valuedateBLL.SystemDate.BusinessTotalScale ?? 0);
|
|
obj.Quota_StockEqvNotional_Upper = stockEqvNotionalSetting?.QuotaUpperLimit ?? double.NaN;
|
|
obj.Quota_StockEqvNotional_Lower = stockEqvNotionalSetting?.QuotaLowerLimit ?? double.NaN;
|
|
obj.Quota_StockEqvNotional_wUpper = stockEqvNotionalSetting?.WarningUpperLimit ?? double.NaN;
|
|
obj.Quota_StockEqvNotional_wLower = stockEqvNotionalSetting?.WarningLowerLimit ?? double.NaN;
|
|
var swapPercentSetting = temp.Where(O => O.QuotaIndex == "互换价格偏离比例").FirstOrDefault()?.Clone();
|
|
obj.Quota_SwapPercent_Upper = swapPercentSetting?.QuotaUpperLimit ?? double.NaN;
|
|
obj.Quota_SwapPercent_Lower = swapPercentSetting?.QuotaLowerLimit ?? double.NaN;
|
|
obj.Quota_SwapPercent_wUpper = swapPercentSetting?.WarningUpperLimit ?? double.NaN;
|
|
obj.Quota_SwapPercent_wLower = swapPercentSetting?.WarningLowerLimit ?? double.NaN;
|
|
});
|
|
|
|
var setting = QueryCurrentQuotaSetting(QuotaTypeEnum.TRADE);
|
|
setting = MargeQuotaSetting(setting, 0, 0);
|
|
var ccrDict = new Dictionary<int, double>();
|
|
foreach (var item in list)
|
|
{
|
|
setValue(item, setting);
|
|
var sportPrice = item.trade.SpotPrice ?? 0;
|
|
var bondPrice = EodPriceQueryService.GetBondPrice(valuedateBLL.ValueDate, item.trade.UnderlyingCode);
|
|
var basePrice = bondPrice == null ? 0 : bondPrice.ClosePrice;
|
|
var vobp = bondPrice == null ? 0 : Convert.ToDouble(bondPrice.Vobp);
|
|
var pricePercent = basePrice == 0 ? 0 : Math.Abs((sportPrice / basePrice) - 1);
|
|
item.Quota_SwapPercent = pricePercent;
|
|
var obj = new QuotaMonitor_Trade();
|
|
ObjectHelper.MapValues(obj, item);
|
|
result.Add(obj);
|
|
}
|
|
var total = new QuotaMonitor_Trade()
|
|
{
|
|
TradeNumber = "合计",
|
|
StockEqvNotional = result.Sum(O => O.StockEqvNotional),
|
|
DeltaCash = result.Sum(O => O.DeltaCash),
|
|
GammaCash = result.Sum(O => O.GammaCash),
|
|
Vega = result.Sum(O => O.Vega),
|
|
VegaCash = result.Sum(O => O.VegaCash),
|
|
Quota_CCR = result.Sum(O => O.Quota_CCR),
|
|
PnL = result.Sum(O => O.PnL),
|
|
};
|
|
result.Add(total);
|
|
|
|
return result;
|
|
}
|
|
/// <summary>
|
|
/// 查询限额监控-单笔交易数据
|
|
/// </summary>
|
|
/// <param name="req"></param>
|
|
/// <returns></returns>
|
|
public List<QuotaMonitor_Trade> QueryEodTradeFromCalc(QuotaMonitorReq req)
|
|
{
|
|
var result = new List<QuotaMonitor_Trade>();
|
|
req.sidx = "TradeNumber";
|
|
req.sord = "asc";
|
|
var eodPredicate = PredicateBuilder.True<eod_swap>().And(t => t.NotionalValue>0&&t.ValueDate==req.ValueDate);
|
|
var predicate = PredicateBuilder.True<trade>().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<QuotaMonitor_Trade, List<QuotaSetting>>((obj, settings) =>
|
|
{
|
|
var temp = settings.Where(O => O.IsValid && O.Status == QuotaSettingApprovalStatus.Valid && O.QuotaRange == 0);
|
|
var stockEqvNotionalSetting = temp.Where(O => O.QuotaIndex == "名义本金").FirstOrDefault()?.Clone();
|
|
stockEqvNotionalSetting?.ConvertToAbs(valuedateBLL.SystemDate.BusinessTotalScale ?? 0);
|
|
obj.Quota_StockEqvNotional_Upper = stockEqvNotionalSetting?.QuotaUpperLimit ?? double.NaN;
|
|
obj.Quota_StockEqvNotional_Lower = stockEqvNotionalSetting?.QuotaLowerLimit ?? double.NaN;
|
|
obj.Quota_StockEqvNotional_wUpper = stockEqvNotionalSetting?.WarningUpperLimit ?? double.NaN;
|
|
obj.Quota_StockEqvNotional_wLower = stockEqvNotionalSetting?.WarningLowerLimit ?? double.NaN;
|
|
var swapPercentSetting = temp.Where(O => O.QuotaIndex == "互换价格偏离比例").FirstOrDefault()?.Clone();
|
|
obj.Quota_SwapPercent_Upper = swapPercentSetting?.QuotaUpperLimit ?? double.NaN;
|
|
obj.Quota_SwapPercent_Lower = swapPercentSetting?.QuotaLowerLimit ?? double.NaN;
|
|
obj.Quota_SwapPercent_wUpper = swapPercentSetting?.WarningUpperLimit ?? double.NaN;
|
|
obj.Quota_SwapPercent_wLower = swapPercentSetting?.WarningLowerLimit ?? double.NaN;
|
|
});
|
|
|
|
var setting = QueryCurrentQuotaSetting(QuotaTypeEnum.TRADE);
|
|
setting = MargeQuotaSetting(setting, 0, 0);
|
|
var ccrDict = new Dictionary<int, double>();
|
|
foreach (var item in list)
|
|
{
|
|
setValue(item, setting);
|
|
var sportPrice = item.trade.SpotPrice ?? 0;
|
|
var bondPrice = EodPriceQueryService.GetBondPrice(valuedateBLL.ValueDate, item.trade.UnderlyingCode);
|
|
var basePrice = bondPrice == null ? 0 : bondPrice.ClosePrice;
|
|
var vobp = bondPrice == null ? 0 : Convert.ToDouble(bondPrice.Vobp);
|
|
var pricePercent = basePrice == 0 ? 0 : Math.Abs((sportPrice / basePrice) - 1);
|
|
item.Quota_SwapPercent = pricePercent;
|
|
var obj = new QuotaMonitor_Trade();
|
|
ObjectHelper.MapValues(obj, item);
|
|
result.Add(obj);
|
|
}
|
|
var total = new QuotaMonitor_Trade()
|
|
{
|
|
TradeNumber = "合计",
|
|
StockEqvNotional = result.Sum(O => O.StockEqvNotional),
|
|
DeltaCash = result.Sum(O => O.DeltaCash),
|
|
GammaCash = result.Sum(O => O.GammaCash),
|
|
Vega = result.Sum(O => O.Vega),
|
|
VegaCash = result.Sum(O => O.VegaCash),
|
|
Quota_CCR = result.Sum(O => O.Quota_CCR),
|
|
PnL = result.Sum(O => O.PnL),
|
|
};
|
|
result.Add(total);
|
|
|
|
return result;
|
|
}
|
|
/// <summary>
|
|
/// 查询限额监控-资产汇总数据
|
|
/// </summary>
|
|
/// <param name="req"></param>
|
|
/// <returns></returns>
|
|
public List<QuotaMonitor_Asset> QueryAssetFromCalc(QuotaMonitorReq req)
|
|
{
|
|
var result = new List<QuotaMonitor_Asset>();
|
|
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<QuotaMonitor_Asset, IEnumerable<QuotaSetting>, 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<List<QuotaMonitor_Asset>, QuotaMonitor_Asset>(
|
|
new List<QuotaMonitor_Asset>(),
|
|
new QuotaMonitor_Asset()
|
|
{
|
|
DeltaCash = 0,
|
|
GammaCash = 0,
|
|
Vega = 0,
|
|
VegaCash = 0
|
|
});
|
|
var commodityList = new KeyValuePair<List<QuotaMonitor_Asset>, QuotaMonitor_Asset>(
|
|
new List<QuotaMonitor_Asset>(),
|
|
new QuotaMonitor_Asset()
|
|
{
|
|
DeltaCash = 0,
|
|
GammaCash = 0,
|
|
Vega = 0,
|
|
VegaCash = 0
|
|
});
|
|
var preciousMetalsList = new KeyValuePair<List<QuotaMonitor_Asset>, QuotaMonitor_Asset>(
|
|
new List<QuotaMonitor_Asset>(),
|
|
new QuotaMonitor_Asset()
|
|
{
|
|
DeltaCash = 0,
|
|
GammaCash = 0,
|
|
Vega = 0,
|
|
VegaCash = 0
|
|
});
|
|
var codeList = new List<string>();
|
|
var deltaDict = new Dictionary<string, double[]>()
|
|
{
|
|
//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;
|
|
}
|
|
|
|
|
|
/// <summary>
|
|
/// 查询限额监控-品种汇总数据
|
|
/// </summary>
|
|
/// <param name="req"></param>
|
|
/// <returns></returns>
|
|
public List<QuotaMonitor_Variety> QueryVarietyFromCalc(QuotaMonitorReq req)
|
|
{
|
|
var result = new List<QuotaMonitor_Variety>();
|
|
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<QuotaMonitor_Variety, IEnumerable<QuotaSetting>, 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<string, KeyValuePair<List<QuotaMonitor_Variety>, QuotaMonitor_Variety>>();
|
|
var codeList = new List<string>();
|
|
//double[]{ (场内+场外)多, (场内+场外)空, 场外净值 }
|
|
var deltaDict = new Dictionary<string, double[]>();
|
|
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<List<QuotaMonitor_Variety>, QuotaMonitor_Variety>(
|
|
new List<QuotaMonitor_Variety>(),
|
|
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<List<QuotaMonitor_Variety>, QuotaMonitor_Variety>(
|
|
new List<QuotaMonitor_Variety>(),
|
|
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;
|
|
}
|
|
|
|
/// <summary>
|
|
/// 查询限额监控-标的汇总数据
|
|
/// </summary>
|
|
/// <param name="req"></param>
|
|
/// <returns></returns>
|
|
public List<QuotaMonitor_Underlying> QueryUnderlyingFromCalc(QuotaMonitorReq req)
|
|
{
|
|
var result = new List<QuotaMonitor_Underlying>();
|
|
using var bondDb = new BondOmsDBContext();
|
|
var clientPositions = bondDb.client_position.AsNoTracking().AsEnumerable();
|
|
List<QuotaMonitor_UnderlyingModel> underlyingModels = new List<QuotaMonitor_UnderlyingModel>();
|
|
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<QuotaMonitor_UnderlyingDto>();
|
|
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<UnderlyingBond>(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<QuotaMonitor_Underlying, IEnumerable<QuotaSetting>, 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<string>();
|
|
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;
|
|
}
|
|
/// <summary>
|
|
/// 查询限额监控-标的汇总数据-收盘
|
|
/// </summary>
|
|
/// <param name="req"></param>
|
|
/// <returns></returns>
|
|
public List<QuotaMonitor_Underlying> QueryEodUnderlyingFromCalc(QuotaMonitorReq req)
|
|
{
|
|
var result = new List<QuotaMonitor_Underlying>();
|
|
var clientPositions = DbContext.eod_swap_position.Where(s=>s.ValueDate==req.ValueDate&&s.PosiQuantity>0).AsNoTracking().AsEnumerable();
|
|
List<QuotaMonitor_UnderlyingModel> underlyingModels = new List<QuotaMonitor_UnderlyingModel>();
|
|
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<QuotaMonitor_UnderlyingDto>();
|
|
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<UnderlyingBond>(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<QuotaMonitor_Underlying, IEnumerable<QuotaSetting>, 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<string>();
|
|
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;
|
|
}
|
|
/// <summary>
|
|
/// 查询限额监控-簿记汇总数据
|
|
/// </summary>
|
|
/// <param name="req"></param>
|
|
/// <returns></returns>
|
|
public List<QuotaMonitor_Assetunit> QueryAssetUnitFromCalc(QuotaMonitorReq req)
|
|
{
|
|
var result = new List<QuotaMonitor_Assetunit>();
|
|
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<QuotaMonitor_Assetunit, IEnumerable<QuotaSetting>, 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<int, KeyValuePair<List<QuotaMonitor_Assetunit>, QuotaMonitor_Assetunit>>();
|
|
var codeList = new List<int>();
|
|
//double[]{ (场内+场外)多, (场内+场外)空, 场外净值 }
|
|
var deltaDict = new Dictionary<int, double[]>();
|
|
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<List<QuotaMonitor_Assetunit>, QuotaMonitor_Assetunit>(
|
|
new List<QuotaMonitor_Assetunit>(),
|
|
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<List<QuotaMonitor_Assetunit>, QuotaMonitor_Assetunit>(
|
|
new List<QuotaMonitor_Assetunit>(),
|
|
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;
|
|
}
|
|
/// <summary>
|
|
/// 本年累计盈亏组装标的维度
|
|
/// </summary>
|
|
/// <param name="reqDate">查询日期</param>
|
|
/// <param name="_Assetunits">已经查询好的标的源数据</param>
|
|
public void AssembleUnderlyingTotalPnL(QuotaMonitorReq req, List<QuotaMonitor_Underlying> _Underlyings)
|
|
{
|
|
List<TradePositionTotalPnLModel> totalPnLModels = CaclYearTotalPnl(req, out List<T2PositionDataDto> 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));
|
|
}
|
|
}
|
|
}
|
|
/// <summary>
|
|
/// 本年累计盈亏组装簿记维度
|
|
/// </summary>
|
|
/// <param name="reqDate">查询日期</param>
|
|
/// <param name="_Assetunits">已经查询好的簿记源数据</param>
|
|
public void AssembleAssetunitTotalPnL(QuotaMonitorReq req, List<QuotaMonitor_Assetunit> _Assetunits)
|
|
{
|
|
List<TradePositionTotalPnLModel> totalPnLModels = CaclYearTotalPnl(req, out List<T2PositionDataDto> 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));
|
|
}
|
|
}
|
|
}
|
|
/// <summary>
|
|
/// 计算本年累计盈亏
|
|
/// </summary>
|
|
/// <param name="reqDate"></param>
|
|
/// <returns></returns>
|
|
public List<TradePositionTotalPnLModel> CaclYearTotalPnl(QuotaMonitorReq req, out List<T2PositionDataDto> 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<eod_trade_position>(UserInfo).CalcuTotalPnl(out t2PositionDataDtos, dateT1, dateT2, reqModel);
|
|
}
|
|
|
|
|
|
|
|
/// <summary>
|
|
/// 查询限额指标设置
|
|
/// </summary>
|
|
/// <param name="req"></param>
|
|
/// <returns></returns>
|
|
public SearchListResult<QuotaSetting> 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<QuotaSetting>(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<int, string>();
|
|
var logService = new ClientProcessLogService(OptUser);
|
|
var logReq = new ProcesslogReq()
|
|
{
|
|
rows = 1,
|
|
ProcessType = "QuotaSetting",
|
|
ProcessStatus = "修改",
|
|
sord = "desc",
|
|
};
|
|
|
|
temp.ForEach(O =>
|
|
{
|
|
switch (O.QuotaType)
|
|
{
|
|
case QuotaTypeEnum.UNDERLYING:
|
|
O.QuotaRangeStr = O.QuotaRange == 0 ? "全部" : DataCacheProvider.GetUnderlyingDataSource().GetData(O.QuotaRange).UnderlyingCode;
|
|
break;
|
|
case QuotaTypeEnum.STOCK_PERCENT:
|
|
O.QuotaRangeStr = O.QuotaRange + "天";
|
|
break;
|
|
case QuotaTypeEnum.CLIENT:
|
|
O.QuotaRangeStr = O.QuotaRange == 0 ? "全部" : DataCacheProvider.GetClientDataSource().GetData(O.QuotaRange).Name;
|
|
break;
|
|
case QuotaTypeEnum.Assetunit:
|
|
O.QuotaRangeStr = O.QuotaRange == 0 ? "全部" : DataCacheProvider.GetAssetUnitDataSource().GetData(O.QuotaRange).Name;
|
|
break;
|
|
case QuotaTypeEnum.GLOBAL_OFF_EXCHANGE:
|
|
case QuotaTypeEnum.GLOBAL_OPTION:
|
|
case QuotaTypeEnum.GLOBAL_SWAP:
|
|
case QuotaTypeEnum.GLOBAL_FORWARD:
|
|
case QuotaTypeEnum.GLOBAL_ON_EXCHANGE:
|
|
case QuotaTypeEnum.ASSET_EQUITY:
|
|
case QuotaTypeEnum.ASSET_COMMODITY:
|
|
case QuotaTypeEnum.ASSET_PRECIOUSMETALS:
|
|
case QuotaTypeEnum.VARIETY:
|
|
case QuotaTypeEnum.TRADE:
|
|
default:
|
|
break;
|
|
}
|
|
if (O.Status == QuotaSettingApprovalStatus.Pending_Approval)
|
|
{
|
|
logReq.TypeId = O.id;
|
|
var log = logService.SearchList(logReq);
|
|
remarkDict[O.id] = log.rows.FirstOrDefault()?.Remark;
|
|
}
|
|
});
|
|
temp.Sort((x, y) =>
|
|
{
|
|
var t = DateTime.Compare(y.OptDate.Value, x.OptDate.Value);
|
|
if (t == 0)
|
|
{
|
|
t = x.QuotaType - y.QuotaType;
|
|
}
|
|
if (t == 0)
|
|
{
|
|
t = string.Compare(x.QuotaIndex, y.QuotaIndex);
|
|
}
|
|
if (t == 0)
|
|
{
|
|
t = x.QuotaRange - y.QuotaRange;
|
|
}
|
|
return t;
|
|
});
|
|
var result = new SearchListResult<QuotaSetting>()
|
|
{
|
|
rows = temp,
|
|
Sum = new
|
|
{
|
|
RemarkDict = remarkDict,
|
|
BusinessTotalScale = valuedateBLL.SystemDate.BusinessTotalScale,
|
|
BusinessAvailableFunds = valuedateBLL.SystemDate.BusinessAvailableFunds,
|
|
SingleUnderlyingRate = valuedateBLL.SystemDate.SingleUnderlyingRate,
|
|
}
|
|
};
|
|
return result;
|
|
}
|
|
|
|
/// <summary>
|
|
/// 获取所有子客户id
|
|
/// </summary>
|
|
/// <param name="id"></param>
|
|
/// <returns></returns>
|
|
private List<int> getSubclientId(int id)
|
|
{
|
|
var result = new List<int>() { 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<HedgePnl> 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;
|
|
}
|
|
|
|
/// <summary>
|
|
///
|
|
/// </summary>
|
|
/// <param name="otcTradeList"></param>
|
|
/// <param name="useExchange">是否要包含对冲交易</param>
|
|
/// <param name="optionExposure">场外delta敞口</param>
|
|
/// <param name="swapExposure">互换delta敞口</param>
|
|
/// <param name="forwardExposure">远期delta敞口</param>
|
|
/// <param name="optionNetExposure">场外期权delta净敞口</param>
|
|
/// <param name="offExchangeNetExposure">场外delta净敞口(包括互换)</param>
|
|
/// <param name="tradeIds">要包括的成交待确认状态的交易id</param>
|
|
/// <returns></returns>
|
|
private void GetDeltaExposure(IEnumerable<KeyValuePair<trade, realtime_trade_risk>> otcTradeList, IEnumerable<HedgePnl> hedgePnl, out double optionExposure, out double swapExposure, out double forwardExposure, out double offExchangeExposure, out double optionNetExposure, out double offExchangeNetExposure, IEnumerable<int> 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<string, Dictionary<string, double[]>>()
|
|
{
|
|
{"期权",new Dictionary<string, double[]>() },
|
|
{"互换",new Dictionary<string, double[]>() },
|
|
{"远期",new Dictionary<string, double[]>() }
|
|
};
|
|
var temp = new List<string>();
|
|
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();
|
|
}
|
|
|
|
/// <summary>
|
|
/// Delte敞口算法
|
|
/// </summary>
|
|
/// <param name="otcTradeList">场外交易</param>
|
|
/// <param name="hedgePnl">场内交易</param>
|
|
/// <param name="underlyingCode">要计算的标的</param>
|
|
/// <param name="deltaExposure">Delta敞口(多空比):多头/空头</param>
|
|
/// <param name="deltaNetExposure">Delta净敞口比例:场内外净敞口/(-场外期权Delta)</param>
|
|
/// <param name="tradeIds">一定要包含的交易Id,否则会因为交易状态为非持仓交易而忽略掉</param>
|
|
private void GetDeltaExposure(IEnumerable<KeyValuePair<trade, realtime_trade_risk>> otcTradeList, IEnumerable<HedgePnl> hedgePnl, string underlyingCode, out double longDelta, out double shortDelta, out double allLongDelta, out double allShortDelta, IEnumerable<int> 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;
|
|
}
|
|
}
|
|
|
|
/// <summary>
|
|
/// 获取指定标的场内持仓的Delta多空头合计值
|
|
/// </summary>
|
|
/// <param name="hedgePnl">场内对冲交易</param>
|
|
/// <param name="underlyingCode">标的代码</param>
|
|
/// <param name="longDelta">多头Delta</param>
|
|
/// <param name="shortDelta">空头Delta</param>
|
|
/// <returns></returns>
|
|
private bool GetHedgeDeltaExposure(IEnumerable<HedgePnl> 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;
|
|
}
|
|
|
|
/// <summary>
|
|
///
|
|
/// </summary>
|
|
/// <param name="code"></param>
|
|
/// <returns></returns>
|
|
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;
|
|
}
|
|
|
|
/// <summary>
|
|
/// 获取指定标的,指定簿记下的对冲交易
|
|
/// </summary>
|
|
/// <param name="codes"></param>
|
|
/// <param name="assetIds"></param>
|
|
/// <returns></returns>
|
|
private List<tradeDto> GetHedgeTradeList(IEnumerable<string> codes, IEnumerable<int> 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;
|
|
}
|
|
|
|
/// <summary>
|
|
/// 获取交易对冲情况
|
|
/// <para>算法由国海提供</para>
|
|
/// </summary>
|
|
/// <param name="tradeList"></param>
|
|
/// <param name="priceProvider"></param>
|
|
/// <param name="hedgeGroup"></param>
|
|
/// <param name="nonhedgeGroup"></param>
|
|
/// <returns></returns>
|
|
private List<TradeRiskResultRecord> GetHedgeGroup(List<trade> tradeList, IPriceProvider priceProvider, out HashSet<string> hedgeGroup, out HashSet<string> nonhedgeGroup)
|
|
{
|
|
hedgeGroup = new HashSet<string>();
|
|
nonhedgeGroup = new HashSet<string>();
|
|
var calcTradeList = new List<trade>(tradeList);
|
|
var exchangeRisk = new TradeRiskResult() { Results = new List<TradeRiskResultRecord>() };
|
|
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<string> list = null;
|
|
if (percent > 1.25 || percent < 0.8)
|
|
{
|
|
list = nonhedgeGroup;
|
|
}
|
|
else
|
|
{
|
|
list = hedgeGroup;
|
|
}
|
|
list.Add(item.Key);
|
|
}
|
|
return exchangeRisk.Results;
|
|
}
|
|
|
|
/// <summary>
|
|
/// 计算 Math.Max(计算场外卖出交易的最大损失的5倍,名义本金的千分之五)
|
|
/// <para>算法由国海提供</para>
|
|
/// </summary>
|
|
/// <param name="codes"></param>
|
|
/// <param name="tradeList"></param>
|
|
/// <param name="priceProvider"></param>
|
|
/// <returns></returns>
|
|
private Dictionary<string, double> GetMaxLoss(IEnumerable<string> codes, List<trade> 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<string, double>();
|
|
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" };
|
|
/// <summary>
|
|
/// 计算业务规模
|
|
/// <para>根据传入的场外交易列表获取同标的同簿记账户下的场内持仓根据规则计算业务规模</para>
|
|
/// </summary>
|
|
/// <param name="tradeList">要计算的场外交易列表</param>
|
|
/// <param name="offExchangeEquity">权益类场外期权</param>
|
|
/// <param name="offExchangeNonEquity">非权益类场外期权</param>
|
|
/// <param name="swapEquity">权益类互换交易</param>
|
|
/// <param name="swapNonEquity">非权益类互换交易</param>
|
|
/// <returns>返回结果:(场内权益,场内非权益,场外权益,场外非权益)</returns>
|
|
public (double onExchangeEquity, double onExchangeNonEquity, double offExchangeEquity, double offExchangeNonEquity) GetRiskScale(List<trade> tradeList, out double offExchangeEquity, out double offExchangeNonEquity, out double swapEquity, out double swapNonEquity, out double forwardEquity, out double forwardNonEquity)
|
|
{
|
|
var tempDict = new Dictionary<string, string[]>();
|
|
var data = new Dictionary<int, Dictionary<string, (double onExchangeEquity, double onExchangeNonEquity, double offExchangeEquity, double offExchangeNonEquity)>>();
|
|
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);
|
|
}
|
|
|
|
/// <summary>
|
|
/// 获取对冲组合
|
|
/// <para>只需要传入场外交易列表即可,函数内会提取同标的同簿记的场内交易计算</para>
|
|
/// </summary>
|
|
/// <param name="tradeList">场外交易列表</param>
|
|
/// <param name="underlyingPrice">价格提供者</param>
|
|
/// <param name="codes">所有标的信息代码</param>
|
|
/// <param name="totalTradeList">含场内场外的交易列表</param>
|
|
/// <param name="calcTradeList">符合计算条件的交易列表</param>
|
|
/// <param name="hedgeGroup">已对冲的标的代码</param>
|
|
/// <param name="nonhedgeGroup">未对冲的标的代码</param>
|
|
/// <returns></returns>
|
|
public List<TradeRiskResultRecord> GetHedgeGroup(List<trade> tradeList, IPriceProvider underlyingPrice, out IEnumerable<string> codes, out List<trade> totalTradeList, out List<trade> calcTradeList, out HashSet<string> hedgeGroup, out HashSet<string> 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<trade>(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<QuotaSetting> _quotaSettings = null;
|
|
private static List<string> _quotaIndexList = null;
|
|
/// <summary>
|
|
/// 限额指标列表
|
|
/// </summary>
|
|
public static List<string> QuotaIndexList
|
|
{
|
|
get
|
|
{
|
|
if (_quotaIndexList == null)
|
|
{
|
|
_quotaIndexList = _quotaSettings.Select(O => O.QuotaIndex).Distinct().ToList();
|
|
_quotaIndexList.Sort();
|
|
}
|
|
return _quotaIndexList;
|
|
}
|
|
}
|
|
/// <summary>
|
|
/// 状态
|
|
/// </summary>
|
|
public static List<string> StatusqutaIndexList
|
|
{
|
|
get
|
|
{
|
|
if (_quotaIndexList == null)
|
|
{
|
|
_quotaIndexList = _quotaSettings.Select(O => O.Status.ToString()).Distinct().ToList();
|
|
_quotaIndexList.Sort();
|
|
}
|
|
return _quotaIndexList;
|
|
}
|
|
}
|
|
public static List<string> 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<QuotaSetting>();
|
|
#region 整体业务
|
|
#endregion
|
|
#region 互换
|
|
|
|
//互换-名义本金
|
|
_quotaSettings.Add(new QuotaSetting()
|
|
{
|
|
QuotaType = QuotaTypeEnum.GLOBAL_SWAP,
|
|
QuotaRange = 0,
|
|
QuotaIndex = "名义本金",
|
|
QuotaLowerLimit = null,
|
|
QuotaUpperLimit = null,
|
|
WarningLowerLimit = null,
|
|
WarningUpperLimit = null,
|
|
Percent = false,
|
|
IsValid = true,
|
|
Status = QuotaSettingApprovalStatus.Valid,
|
|
});
|
|
////互换-浮动盈亏
|
|
//_quotaSettings.Add(new QuotaSetting()
|
|
//{
|
|
// QuotaType = QuotaTypeEnum.GLOBAL_SWAP,
|
|
// QuotaRange = 0,
|
|
// QuotaIndex = "浮动盈亏",
|
|
// QuotaLowerLimit = null,
|
|
// QuotaUpperLimit = null,
|
|
// WarningLowerLimit = null,
|
|
// WarningUpperLimit = null,
|
|
// Percent = false,
|
|
// IsValid = true,
|
|
// Status = QuotaSettingApprovalStatus.Valid,
|
|
//});
|
|
|
|
#endregion
|
|
#region 标的资产
|
|
|
|
//标的资产-名义本金
|
|
_quotaSettings.Add(new QuotaSetting()
|
|
{
|
|
QuotaType = QuotaTypeEnum.UNDERLYING,
|
|
QuotaRange = 0,
|
|
QuotaIndex = "轧差名义本金",
|
|
QuotaLowerLimit = null,
|
|
QuotaUpperLimit = null,
|
|
WarningLowerLimit = null,
|
|
WarningUpperLimit = null,
|
|
Percent = false,
|
|
IsValid = true,
|
|
Status = QuotaSettingApprovalStatus.Valid,
|
|
});
|
|
//标的资产-集中度
|
|
_quotaSettings.Add(new QuotaSetting()
|
|
{
|
|
QuotaType = QuotaTypeEnum.UNDERLYING,
|
|
QuotaRange = 0,
|
|
QuotaIndex = "轧差集中度",
|
|
QuotaLowerLimit = null,
|
|
QuotaUpperLimit = null,
|
|
WarningLowerLimit = null,
|
|
WarningUpperLimit = null,
|
|
Percent = true,
|
|
IsValid = true,
|
|
Status = QuotaSettingApprovalStatus.Valid,
|
|
});
|
|
#endregion
|
|
#region 单笔交易
|
|
|
|
////交易-名义本金
|
|
//_quotaSettings.Add(new QuotaSetting()
|
|
//{
|
|
// QuotaType = QuotaTypeEnum.TRADE,
|
|
// QuotaRange = 0,
|
|
// QuotaIndex = "名义本金",
|
|
// QuotaLowerLimit = null,
|
|
// QuotaUpperLimit = null,
|
|
// WarningLowerLimit = null,
|
|
// WarningUpperLimit = null,
|
|
// Percent = false,
|
|
// IsValid = true,
|
|
// Status = QuotaSettingApprovalStatus.Valid,
|
|
//});
|
|
|
|
//交易-互换价格偏离比例
|
|
_quotaSettings.Add(new QuotaSetting()
|
|
{
|
|
QuotaType = QuotaTypeEnum.TRADE,
|
|
QuotaRange = 0,
|
|
QuotaIndex = "互换价格偏离比例",
|
|
QuotaLowerLimit = null,
|
|
QuotaUpperLimit = null,
|
|
WarningLowerLimit = null,
|
|
WarningUpperLimit = null,
|
|
Percent = true,
|
|
IsValid = true,
|
|
Status = QuotaSettingApprovalStatus.Valid,
|
|
});
|
|
|
|
#endregion
|
|
#region 客户
|
|
|
|
//客户-名义本金
|
|
_quotaSettings.Add(new QuotaSetting()
|
|
{
|
|
QuotaType = QuotaTypeEnum.CLIENT,
|
|
QuotaRange = 0,
|
|
QuotaIndex = "名义本金",
|
|
QuotaLowerLimit = null,
|
|
QuotaUpperLimit = null,
|
|
WarningLowerLimit = null,
|
|
WarningUpperLimit = null,
|
|
Percent = false,
|
|
IsValid = true,
|
|
Status = QuotaSettingApprovalStatus.Valid,
|
|
});
|
|
|
|
#endregion
|
|
if (PS.Config.Company != Configuration.CompanyEnum.天风)
|
|
{
|
|
_quotaSettings = _quotaSettings.Where(O => !(O.QuotaIndex.StartsWith("Delta敞口") || O.QuotaIndex.StartsWith("Delta净敞口"))).ToList();
|
|
}
|
|
for (var i = 0; i < _quotaSettings.Count; i++)
|
|
{
|
|
_quotaSettings[i].id = 0 - i - 1;
|
|
}
|
|
}
|
|
}
|
|
|
|
private List<QuotaSetting> formatDefaultQuotaSettingDetails(List<QuotaSetting> 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<int, List<QuotaSetting>> { { 0, new List<QuotaSetting>() } };
|
|
}
|
|
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;
|
|
}
|
|
|
|
/// <summary>
|
|
/// 查询设置
|
|
/// </summary>
|
|
/// <returns></returns>
|
|
public List<QuotaSetting> QueryCurrentQuotaSetting()
|
|
{
|
|
var result = QueryPrecheckQuotaSetting(false);
|
|
result = formatDefaultQuotaSettingDetails(result);
|
|
result.Sort((X, Y) => string.Compare(X.QuotaIndex, Y.QuotaIndex));
|
|
return result;
|
|
}
|
|
|
|
/// <summary>
|
|
/// 查询设置
|
|
/// </summary>
|
|
/// <param name="quotaType"></param>
|
|
/// <param name="id"></param>
|
|
/// <returns></returns>
|
|
public List<QuotaSetting> 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;
|
|
}
|
|
|
|
/// <summary>
|
|
/// 查询设置
|
|
/// </summary>
|
|
/// <param name="quotaType"></param>
|
|
/// <param name="id"></param>
|
|
/// <returns></returns>
|
|
public List<QuotaSetting> 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();
|
|
}
|
|
|
|
/// <summary>
|
|
/// 转换百分比指标为绝对值
|
|
/// </summary>
|
|
/// <returns></returns>
|
|
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;
|
|
}
|
|
|
|
/// <summary>
|
|
/// 合并限额设置,没有设置的指标用默认值填充
|
|
/// </summary>
|
|
/// <param name="list"></param>
|
|
/// <param name="totalProductScale"></param>
|
|
/// <param name="productScale"></param>
|
|
/// <returns></returns>
|
|
private List<QuotaSetting> MargeQuotaSetting(List<QuotaSetting> list, double totalProductScale = 0, double productScale = 0)
|
|
{
|
|
var settings = new List<QuotaSetting>();
|
|
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<ApprovalReqq> 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<Processlog>();
|
|
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));
|
|
}
|
|
|
|
}
|
|
|
|
/// <summary>
|
|
/// 保存设置
|
|
/// </summary>
|
|
/// <param name="settings"></param>
|
|
public List<Processlog> SaveQuotaSettings(List<QuotaSetting> settings)
|
|
{
|
|
var msgList = new List<Processlog>();
|
|
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<string>();
|
|
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();
|
|
}
|
|
|
|
/// <summary>
|
|
/// 导入规则设置
|
|
/// </summary>
|
|
/// <param name="stream"></param>
|
|
/// <returns></returns>
|
|
public bool ImportFromCsv(Stream stream)
|
|
{
|
|
try
|
|
{
|
|
List<ExcelHelper.DataColumnModel> dc = new List<ExcelHelper.DataColumnModel>();
|
|
var resDic = new Dictionary<string, bool>();
|
|
var underlyingName = "";
|
|
var quotaName = "";
|
|
var errMsgs = new List<string>();
|
|
var stockBlackWhite = new List<QuotaSetting>();
|
|
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<string> 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<string> 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<string> 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<string> 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<QuotaSetting>(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<string, bool> dic, string s, string underlyingName, string quotaName, out List<string> messages)
|
|
{
|
|
var key = $"挂钩标的:{underlyingName},限额指标:{quotaName}";
|
|
messages = new List<string>();
|
|
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;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
/// <summary>
|
|
/// 删除设置
|
|
/// </summary>
|
|
/// <param name="quotaType"></param>
|
|
/// <param name="quotaRange"></param>
|
|
public void DeleteQuotaSettings(QuotaTypeEnum quotaType, int quotaRange)
|
|
{
|
|
var dbData = DbContext.quotaSetting.Where(O => O.QuotaType == quotaType && O.QuotaRange == quotaRange);
|
|
DbContext.quotaSetting.RemoveRange(dbData);
|
|
}
|
|
|
|
/// <summary>
|
|
/// 查询操作记录
|
|
/// </summary>
|
|
/// <param name="req"></param>
|
|
/// <returns></returns>
|
|
public SearchListResult<ProcesslogViewModel> QueryQuotaSettingHistory(ProcesslogReq req)
|
|
{
|
|
var service = new ClientProcessLogService(OptUser);
|
|
req.ProcessType = "QuotaSetting";
|
|
req.sord = "desc";
|
|
var result = service.SearchList(req);
|
|
return result;
|
|
}
|
|
|
|
public bool QuotaCheck(ref TradeOpenResult res)
|
|
{
|
|
if (res == null && res.Trade == null)
|
|
{
|
|
throw new ArgumentNullException(nameof(res));
|
|
}
|
|
|
|
var tradeId = (res.Trade.ParentTradeId == 0 || res.Trade.IsGroup != 0) ? res.Trade.id : res.Trade.ParentTradeId;
|
|
var tradeNumber = DbContext.trade.Where(O => O.id == tradeId).Select(O => O.TradeNumber).FirstOrDefault();
|
|
var trialService = new QuotaMonitorService(this);
|
|
var quotaObj = trialService.QuotaCheck(tradeId, 1);
|
|
if (quotaObj.TrialStatus == QuotaTrialStatusEnum.Success)//这次计算结果是通过,直接通过
|
|
{
|
|
if (!string.IsNullOrWhiteSpace(quotaObj.AvailableForClient)) // 返回用户资金情况
|
|
{
|
|
res.RetCode = TradeOpenRetCode.FundStatus;
|
|
res.TrialDataId = quotaObj.id;
|
|
return false;
|
|
}
|
|
return true;
|
|
}
|
|
var lastTrial = trialService.QueryLastQuotaTrial(tradeId, true);
|
|
if (quotaObj.TrialStatus == QuotaTrialStatusEnum.Success)
|
|
{
|
|
new TradeRiskCheckLogService(UserInfo).AddLog(quotaObj);
|
|
}
|
|
//否则的情况是上次没算,这次是预警,或上次算了,结果是不通过\通过或预警,这次是预警或不通过,提示用户;
|
|
res.RetCode = TradeOpenRetCode.QuotaTrialError;
|
|
res.TrialDataId = quotaObj.id;
|
|
return false;
|
|
}
|
|
|
|
/// <summary>
|
|
/// 校验电子盘下单风控
|
|
/// </summary>
|
|
/// <param name="clientRiskCheckReq"></param>
|
|
/// <returns></returns>
|
|
public List<ClientRiskCheckItem> QuotaClientCheck(ClientRiskCheckItemParam clientRiskCheckReq)
|
|
{
|
|
List<ClientRiskCheckItem> clientRiskCheckResps = new List<ClientRiskCheckItem>();
|
|
var allList = QueryPrecheckQuotaSetting(false);
|
|
var precheckQuotaSettingList = allList.Where(O => O.Precheck).ToList();
|
|
List<string> quotaIndexs = new List<string>() { "名义本金", "轧差集中度", "轧差名义本金" };
|
|
if (clientRiskCheckReq.isClient)
|
|
{
|
|
precheckQuotaSettingList = precheckQuotaSettingList.Where(x => quotaIndexs.Contains(x.QuotaIndex)).ToList();
|
|
}
|
|
var client = DataCacheProvider.GetClientDataSource().GetData(clientRiskCheckReq.clientId);
|
|
if (client == null)
|
|
{
|
|
throw new ServiceException("未找到客户信息");
|
|
}
|
|
var dealDate = DateTime.Parse(clientRiskCheckReq.dealDate);
|
|
using var bondDb = new BondOmsDBContext();
|
|
var (clientPositions, clientOrderPositions) = GetClientPositionsAndOrders(bondDb, clientRiskCheckReq.orderId);
|
|
var position = clientPositions
|
|
.FirstOrDefault(x => x.security_id == clientRiskCheckReq.securityId && x.client_id == clientRiskCheckReq.clientId);
|
|
precheckQuotaSettingList = FilterPrecheckQuotaSettings(precheckQuotaSettingList, clientRiskCheckReq);
|
|
|
|
AddClientOrderPositions(clientOrderPositions, clientPositions);
|
|
AddClientRiskCheckPosition(clientRiskCheckReq, clientPositions);
|
|
var umCodes = GetUnderlyingCodes(clientPositions, clientRiskCheckReq.securityId);
|
|
var ums = GetUnderlyings(umCodes);
|
|
|
|
var checkPoisiList = BuildCheckQuotaMoitorModels(clientPositions, ums, dealDate);
|
|
var underlyingPositions = checkPoisiList.Where(x => x.UnderlyingCode == clientRiskCheckReq.securityId).ToList();
|
|
var sameQty = underlyingPositions.Where(x=>x.ClientId== clientRiskCheckReq.clientId && x.Side== clientRiskCheckReq.side).Sum(s => s.Qty);
|
|
var qty = underlyingPositions.Where(x => x.ClientId == clientRiskCheckReq.clientId && x.Side != clientRiskCheckReq.side).Sum(s => s.Qty);
|
|
if (sameQty > qty)
|
|
{
|
|
//开仓需要校验适当性评估
|
|
if (client.EvaluateExpireDate < dealDate)
|
|
{
|
|
throw new ServiceException("适当性评估已经过期,只有在适当性有效期内才可以新开仓");
|
|
}
|
|
}
|
|
else
|
|
{
|
|
return clientRiskCheckResps; //平仓不过风控
|
|
}
|
|
if (precheckQuotaSettingList.Count == 0)
|
|
{
|
|
return clientRiskCheckResps;
|
|
}
|
|
foreach (var settingItem in precheckQuotaSettingList)
|
|
{
|
|
var clientRiskCheckItem = CreateClientRiskCheckItem(settingItem);
|
|
if (clientRiskCheckItem == null) continue;
|
|
if (!settingItem.QuotaLowerLimit.HasValue && !settingItem.QuotaUpperLimit.HasValue && !settingItem.WarningUpperLimit.HasValue && !settingItem.WarningLowerLimit.HasValue)
|
|
{
|
|
continue;
|
|
}
|
|
clientRiskCheckItem = CheckQuota(checkPoisiList, settingItem.QuotaType, clientRiskCheckItem, settingItem.QuotaRange, allList);
|
|
if (clientRiskCheckItem != null)
|
|
{
|
|
clientRiskCheckItem.quotaType = $"{EnumHelper.GetDescriptionByName(settingItem.QuotaType)}({settingItem.QuotaIndex})";
|
|
clientRiskCheckResps.Add(clientRiskCheckItem);
|
|
}
|
|
}
|
|
return clientRiskCheckResps;
|
|
}
|
|
/// <summary>
|
|
/// 获取持仓标的
|
|
/// </summary>
|
|
/// <param name="clientPositions"></param>
|
|
/// <param name="securityId"></param>
|
|
/// <returns></returns>
|
|
private List<string> GetUnderlyingCodes(List<ClientPosition> clientPositions, string securityId)
|
|
{
|
|
var umCodes = clientPositions.Select(s => s.security_id).Distinct().ToList();
|
|
umCodes.Add(securityId);
|
|
return umCodes.Distinct().ToList();
|
|
}
|
|
/// <summary>
|
|
/// 获取标的信息
|
|
/// </summary>
|
|
/// <param name="umCodes"></param>
|
|
/// <returns></returns>
|
|
private List<underlying_manager> GetUnderlyings(List<string> umCodes)
|
|
{
|
|
return DataCacheProvider.GetUnderlyingDataSource().AsQueryable()
|
|
.Where(x => umCodes.Contains(x.UnderlyingCode))
|
|
.ToList();
|
|
}
|
|
private (List<ClientPosition>, List<ClientOrder>) 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);
|
|
}
|
|
/// <summary>
|
|
/// 过滤风控检查项
|
|
/// </summary>
|
|
/// <param name="precheckQuotaSettingList"></param>
|
|
/// <param name="clientRiskCheckReq"></param>
|
|
/// <param name="upClient"></param>
|
|
/// <returns></returns>
|
|
private List<QuotaSetting> FilterPrecheckQuotaSettings(List<QuotaSetting> 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();
|
|
}
|
|
/// <summary>
|
|
/// 构建风控指标类
|
|
/// </summary>
|
|
/// <param name="settingItem"></param>
|
|
/// <returns></returns>
|
|
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
|
|
};
|
|
}
|
|
/// <summary>
|
|
/// 将client_order构建 clientPosition
|
|
/// </summary>
|
|
/// <param name="clientOrderPositions"></param>
|
|
/// <param name="clientPositions"></param>
|
|
private void AddClientOrderPositions(List<ClientOrder> clientOrderPositions, List<ClientPosition> 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);
|
|
}
|
|
}
|
|
/// <summary>
|
|
/// 将下单风控校构建clientPosition
|
|
/// </summary>
|
|
/// <param name="clientRiskCheckReq"></param>
|
|
/// <param name="clientPositions"></param>
|
|
private void AddClientRiskCheckPosition(ClientRiskCheckItemParam clientRiskCheckReq, List<ClientPosition> 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);
|
|
}
|
|
/// <summary>
|
|
/// 给客户持仓标的信息赋值现价,构建风控校验类
|
|
/// </summary>
|
|
/// <param name="clientPositions"></param>
|
|
/// <param name="ums"></param>
|
|
/// <returns></returns>
|
|
private List<CheckQuotaMoitorModel> BuildCheckQuotaMoitorModels(List<ClientPosition> clientPositions, List<underlying_manager> 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<UnderlyingBond>(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();
|
|
}
|
|
/// <summary>
|
|
/// 获取实时持仓数据构建限额查询数据
|
|
/// </summary>
|
|
private List<CheckQuotaMoitorModel> GetCheckPosiList()
|
|
{
|
|
List<CheckQuotaMoitorModel> checkPoisiList = new List<CheckQuotaMoitorModel>();
|
|
var dealDate = valuedateBLL.ValueDate;
|
|
using var bondDb = new BondOmsDBContext();
|
|
var clientPositions = bondDb.client_position.AsNoTracking().ToList();//所有持仓
|
|
Dictionary<string, EodPrice> eodPriceDic = new Dictionary<string, EodPrice>();
|
|
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;
|
|
}
|
|
/// <summary>
|
|
/// 查询最新的试算结果
|
|
/// </summary>
|
|
/// <param name="tradeNumber">要查询的交易</param>
|
|
/// <param name="ignoreAuto">是否忽略自动试算的记录(确认成交,交易复核等操作时,会自动调用试算)</param>
|
|
/// <returns></returns>
|
|
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;
|
|
}
|
|
|
|
/// <summary>
|
|
/// 查询试算结果
|
|
/// </summary>
|
|
/// <returns></returns>
|
|
public QuotaTrial QueryQuotaTrial(int id)
|
|
{
|
|
var result = DbContext.quotaTrial.Where(O => O.id == id).FirstOrDefault();
|
|
return result;
|
|
}
|
|
|
|
/// <summary>
|
|
/// 查询试算历史
|
|
/// </summary>
|
|
/// <returns></returns>
|
|
public SearchListResult<QuotaTrial> QueryQuotaTrial(QuotaTrialReq req)
|
|
{
|
|
var query = DbContext.quotaTrial.Where(O => O.TradeId == req.TradeId).AsQueryable();
|
|
var result = query.ToSearchList(req);
|
|
return result;
|
|
}
|
|
|
|
/// <summary>
|
|
/// 交易试算
|
|
/// </summary>
|
|
/// <param name="tradeId">交易Id,结构化交易请传主交易Id</param>
|
|
/// <param name="trialSource">试算来源,0:手动点击试算按钮;1:确认成交时自动调用;</param>
|
|
/// <returns></returns>
|
|
public QuotaTrial QuotaCheck(int tradeId, int trialSource)
|
|
{
|
|
var result = new QuotaTrial();
|
|
var tradeObj = DbContext.trade.AsNoTracking().Where(O => O.id == tradeId).FirstOrDefault();
|
|
var tradeList = new List<trade>();
|
|
if (tradeObj.TradeType == "结构化交易")
|
|
{
|
|
tradeList.AddRange(DbContext.trade.AsNoTracking().Where(O => O.ParentTradeId == tradeId).ToArray());
|
|
}
|
|
else
|
|
{
|
|
tradeList.Add(tradeObj);
|
|
}
|
|
var fundStatus = CheckFund(tradeList, tradeObj.StockEqvNotional, tradeObj.ClientId, out var fundMsg, out var availableMsg);
|
|
//var riskWarningStatus = CheckRiskWarning(tradeList, out var riskWarningMsg);
|
|
string riskWarningMsg = string.Empty;
|
|
var riskWarningStatus = true;
|
|
tradeList = tradeList.Where(O => !string.IsNullOrEmpty(O.UnderlyingCode) && O.TradeType != "结构化交易" && O.TradeType != "现金流交易").ToList();
|
|
if (tradeList.Count == 0)
|
|
{
|
|
//("当前交易不需要试算");
|
|
return new QuotaTrial() { TrialStatus = QuotaTrialStatusEnum.Success };
|
|
}
|
|
string quotaWarningMsg = "", quotaMsg = "";
|
|
bool quotaWarningStatus = true, quotaStatus = true;
|
|
var positions = DbContext.swap_position.Where(x => x.SwapTradeId == tradeId && x.PosiQuantity > 0 && x.IsInitial && !x.Invalid).ToList();
|
|
quotaStatus = CheckUnderlyingWhiteList(tradeObj.ClientId, positions, out quotaMsg);
|
|
if (quotaStatus)
|
|
{
|
|
var allList = QueryPrecheckQuotaSetting(false);
|
|
var list = allList.Where(O => O.Precheck).ToList();
|
|
if (list.Count > 0)
|
|
{
|
|
var clientPrecheckQuotaSettingList = list.Where(x => x.QuotaRange == tradeObj.ClientId && x.QuotaType == QuotaTypeEnum.CLIENT && x.QuotaIndex == "名义本金").ToList();
|
|
var allClientPrecheckQuotaSettingList = list.Where(x => x.QuotaRange == 0 && x.QuotaType == QuotaTypeEnum.CLIENT && x.QuotaIndex == "名义本金").ToList();
|
|
if (clientPrecheckQuotaSettingList.Count > 0)
|
|
{
|
|
foreach (var item in allClientPrecheckQuotaSettingList)
|
|
{
|
|
list.Remove(item);
|
|
}
|
|
}
|
|
List<ClientRiskCheckItem> clientRiskCheckResps = new List<ClientRiskCheckItem>();
|
|
var floatPosi = DbContext.swap_position.Where(x => x.SwapTradeId == tradeId && x.PosiQuantity > 0 && x.IsInitial && !x.Invalid).FirstOrDefault();
|
|
if (floatPosi != null)
|
|
{
|
|
using var bondDb = new BondOmsDBContext();
|
|
var clientPositions = bondDb.client_position.AsNoTracking().ToList();//所有持仓
|
|
ClientPosition posi = new ClientPosition();
|
|
posi.commission = floatPosi.PosiTradingFee;
|
|
posi.side = floatPosi.PositionType == 1 ? 0 : 1;
|
|
posi.security_id = floatPosi.UnderlyingCode;
|
|
posi.full_price_now = floatPosi.PosiNetPrice * ConsGlobal.bondShowPriceMultiple;
|
|
posi.deal_full_price_avg = floatPosi.PosiNetPrice * ConsGlobal.bondShowPriceMultiple;
|
|
posi.client_id = tradeObj.ClientId;
|
|
posi.position_qty = floatPosi.PosiQuantity / 10000;
|
|
posi.position_notional_principal = floatPosi.PosiNotionalValue;
|
|
posi.direction = floatPosi.PosiDirection;
|
|
clientPositions.Add(posi);
|
|
var umCodes = clientPositions.Select(s => s.security_id).Distinct().ToList();
|
|
umCodes.Add(floatPosi.UnderlyingCode);
|
|
umCodes = umCodes.Distinct().ToList();
|
|
var ums = DataCacheProvider.GetUnderlyingDataSource().AsQueryable().Where(x => umCodes.Contains(x.UnderlyingCode));
|
|
List<CheckQuotaMoitorModel> checkPoisiList = new List<CheckQuotaMoitorModel>();
|
|
var posiList = GetPosiQuotaMoitors();
|
|
foreach (var item in clientPositions)
|
|
{
|
|
var dealDate = tradeObj.TradeDate.Value;
|
|
decimal vobp = 0;
|
|
double lastPrice = 0;
|
|
var um = ums.FirstOrDefault(x => x.UnderlyingCode == item.security_id);
|
|
CheckQuotaMoitorModel checkQuotaMoitorModel = new CheckQuotaMoitorModel();
|
|
checkQuotaMoitorModel.Qty = (item.position_qty ?? 0) * 10000;
|
|
checkQuotaMoitorModel.id = item.id;
|
|
checkQuotaMoitorModel.Price = (item.deal_full_price_avg ?? 0);
|
|
checkQuotaMoitorModel.NowPrice = (item.full_price_now ?? 0);
|
|
checkQuotaMoitorModel.ContractSize = um == null ? 1 : Convert.ToDecimal(um.ContractSize);
|
|
checkQuotaMoitorModel.Pv = item.position_notional_principal ?? 0;
|
|
checkQuotaMoitorModel.UnderlyingCode = item.security_id;
|
|
checkQuotaMoitorModel.Side = item.side;
|
|
checkQuotaMoitorModel.Commision = item.commission ?? 0;
|
|
checkQuotaMoitorModel.Direction = item.direction;
|
|
checkQuotaMoitorModel.ClientId = Convert.ToInt32(item.client_id ?? 0);
|
|
checkQuotaMoitorModel.Delta = (checkQuotaMoitorModel.Side == 0 ? checkQuotaMoitorModel.Price : -checkQuotaMoitorModel.Price) * checkQuotaMoitorModel.Qty;
|
|
if (um != null)
|
|
{
|
|
checkQuotaMoitorModel.UnderlyingId = um.id;
|
|
if (!um.IsBond())
|
|
{
|
|
checkQuotaMoitorModel.Pv = checkQuotaMoitorModel.Pv * checkQuotaMoitorModel.Price;
|
|
lastPrice = DataCacheProvider.GetUnderlyingDataSource().GetPrice(item.security_id);
|
|
}
|
|
else
|
|
{
|
|
checkQuotaMoitorModel.Price *= ConsGlobal.bondPriceMultiple;
|
|
checkQuotaMoitorModel.NowPrice *= ConsGlobal.bondPriceMultiple;
|
|
var bondPrice = EodPriceQueryService.GetBondPrice(dealDate, item.security_id);
|
|
lastPrice = bondPrice != null ? bondPrice.ClosePrice : (um.Price ?? 0) * Convert.ToDouble(ConsGlobal.bondPriceMultiple);
|
|
vobp = bondPrice != null ? bondPrice.Vobp ?? 0 : 0;
|
|
var bond = JsonHelper.Deserialize<UnderlyingBond>(um.ExJson);
|
|
checkQuotaMoitorModel.Circulation = (bond.IssueSize * 100000000m) ?? 0;
|
|
}
|
|
}
|
|
var ratio = checkQuotaMoitorModel.Side == 0 ? 1 : -1;
|
|
var directionRatio = checkQuotaMoitorModel.Direction == (int)SwapDirectionEnum.收取 ? 1 : -1;
|
|
vobp = vobp * ratio * directionRatio;
|
|
checkQuotaMoitorModel.LastPrice = Convert.ToDecimal(lastPrice);
|
|
checkQuotaMoitorModel.Pnl = (checkQuotaMoitorModel.NowPrice - checkQuotaMoitorModel.Price) * checkQuotaMoitorModel.Qty;
|
|
checkQuotaMoitorModel.Current = checkQuotaMoitorModel.id == 0;
|
|
checkPoisiList.Add(checkQuotaMoitorModel);
|
|
}
|
|
quotaWarningStatus = CheckQuota(checkPoisiList, posiList, list, allList, true, out quotaWarningMsg);
|
|
quotaStatus = CheckQuota(checkPoisiList, posiList, list, allList, false, out quotaMsg);
|
|
}
|
|
}
|
|
}
|
|
if (quotaWarningStatus && riskWarningStatus && fundStatus && quotaStatus)
|
|
{
|
|
result.TrialStatus = QuotaTrialStatusEnum.Success;
|
|
}
|
|
else
|
|
{
|
|
if (fundStatus && quotaStatus && (!quotaWarningStatus || !riskWarningStatus))
|
|
{
|
|
result.TrialStatus = QuotaTrialStatusEnum.Warning;
|
|
}
|
|
else
|
|
{
|
|
result.TrialStatus = QuotaTrialStatusEnum.Error;
|
|
}
|
|
}
|
|
result.TradeId = tradeObj.id;
|
|
result.TradeNumber = tradeObj.TradeNumber;
|
|
result.ClientName = tradeObj.ClientName;
|
|
result.FundCheckDetails = fundMsg;
|
|
result.QuotaCheckDetails = quotaMsg;
|
|
result.RiskWarningDetails = riskWarningMsg;
|
|
result.QuotaWarningDetails = quotaWarningMsg;
|
|
result.AvailableForClient = availableMsg;
|
|
result.TrialSource = trialSource;
|
|
SaveQuotaTrial(result);
|
|
return result;
|
|
}
|
|
/// <summary>
|
|
/// 校验标的白名单
|
|
/// </summary>
|
|
/// <param name="clientId"></param>
|
|
/// <param name="positions"></param>
|
|
/// <param name="quotaMsg"></param>
|
|
/// <returns></returns>
|
|
private bool CheckUnderlyingWhiteList(int clientId, List<swap_position> positions, out string quotaMsg)
|
|
{
|
|
quotaMsg = "";
|
|
if (positions.Count == 0)
|
|
{
|
|
return true;
|
|
}
|
|
var baseUrl = Environment.GetEnvironmentVariable("BondOmsInterface_BaseUrl");
|
|
var checkUrl = "/clientOrderAdmin/checkOrderBonds";
|
|
var clientOrderParams = new List<CheckUnderlyingRequest>();
|
|
foreach (var item in positions)
|
|
{
|
|
CheckUnderlyingRequest clientOrderParam = new CheckUnderlyingRequest() { clientId = clientId, securityId = item.UnderlyingCode, orderQty = item.PosiQuantity * 0.0001m, side = item.PositionType - 1 };
|
|
clientOrderParams.Add(clientOrderParam);
|
|
}
|
|
if (!string.IsNullOrEmpty(baseUrl) && !string.IsNullOrEmpty(checkUrl))
|
|
{
|
|
var httpHelper = new HttpHelper(baseUrl, null);
|
|
// http 请求 Web项目接口
|
|
var result = httpHelper.PostRequestNoAuth<List<CheckUnderlyingRequest>, ApiResponse>(checkUrl, clientOrderParams).Result;
|
|
if (result != null && !result.success)
|
|
{
|
|
quotaMsg = result.message;
|
|
return false;
|
|
}
|
|
}
|
|
return true;
|
|
}
|
|
|
|
private bool CheckRiskWarning(List<trade> trades, out string msg)
|
|
{
|
|
msg = "";
|
|
var obj = new SubstandardTradeRuleService(OptUser).QueryCurrentRule();
|
|
var msgList = new HashSet<string>();
|
|
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<string>(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<trade> trades, double stockEqvNotional, int clientId, out string msg, out string avmsg)
|
|
{
|
|
|
|
msg = "";
|
|
avmsg = "";
|
|
var totalCredit = 0.0;
|
|
var availableStockEqvNotional = 0.0;
|
|
var client = DataCacheProvider.GetClientDataSource().GetData(clientId);
|
|
if (client == null)
|
|
{
|
|
msg = "获取客户信息失败!";
|
|
return false;
|
|
}
|
|
if (client.SwapTradeType == 0)// 非dma不做资金校验
|
|
{
|
|
msg = "";
|
|
return true;
|
|
}
|
|
var clientBalances = RealtimePnlCalc.GetRealTimeClientBanlance(new List<int> { clientId });
|
|
if (clientBalances == null || clientBalances.Count <= 0)
|
|
{
|
|
msg = "客户实时资金错误!";
|
|
return false;
|
|
}
|
|
var subClientIds = getSubclientId(clientId);
|
|
var positionStockEqvNotional = DbContext.trade
|
|
.Where(O => ConsTrade.PositionTradeStatusList.Contains(O.TradeStatus) && O.ValidState != "InValid" && subClientIds.Contains(O.ClientId))
|
|
.Select(O => (double?)(O.ParentTradeId > 0 ? 0 : O.StockEqvNotional))?.Sum() ?? 0;
|
|
totalCredit = DbContext.credit.Where(t =>
|
|
t.ClientId == clientId &&
|
|
t.ProcessStatus == "已审批" &&
|
|
(!t.CreditDeadLine.HasValue ||
|
|
t.CreditDeadLine >= SystemValueDate) &&
|
|
(!t.CreditStartDate.HasValue ||
|
|
t.CreditStartDate <= SystemValueDate))
|
|
.Select(O => O.StockEqvNotional).Sum() ?? double.NaN;
|
|
|
|
if (PS.Config.ErpElement.UseClientStockEqvNotional)
|
|
{
|
|
availableStockEqvNotional = totalCredit - positionStockEqvNotional;
|
|
}
|
|
else
|
|
{
|
|
totalCredit = double.MaxValue;
|
|
availableStockEqvNotional = totalCredit - positionStockEqvNotional;
|
|
}
|
|
|
|
foreach (var t in trades)
|
|
{
|
|
if (t.Warning)
|
|
{
|
|
new TradeDalService(OptUserInfo.SystemUser).RollbackToBeforeSettle(t, valuedateBLL.ValueDate);
|
|
}
|
|
}
|
|
var msgList = new List<string>();
|
|
if (trades.Any(O => O.IsOption() || O.TradeType == "结构化交易" || O.TradeType == "自定义交易" || O.TradeType == "现金流交易"))
|
|
{
|
|
var margin = trades.Sum(O => O.InitialMargin) ?? 0;
|
|
var tradePrice = trades.Sum(O => O.TradePrice * (-TradeCalcHelper.GetSign(O.BuySell))) ?? 0;
|
|
var clientBalance = clientBalances[0];
|
|
if (tradePrice <= 0)
|
|
{
|
|
if (trades[0].ExerciseDate.Value.Date >= valuedateBLL.ValueDate.Date)
|
|
{
|
|
var margins = margin + tradePrice;
|
|
var AvailableAmount = Math.Max(clientBalance.AvailableAmount + clientBalance.TotalCredit, 0);
|
|
if (AvailableAmount < margins)
|
|
{
|
|
if (PS.Config.Is申万)
|
|
{
|
|
msgList.Add($"当前账户可用资金{AvailableAmount.ToString("#,##0.000")}元,不足以支付预付金{margin.ToString("#,##0.000")}元");
|
|
}
|
|
else
|
|
{
|
|
msgList.Add($"当前交易应付预付金:{margin.ToString("#,##0.000")},应付成交金额:{tradePrice.ToString("#,##0.000")},总应付资金:{margins.ToString("#,##0.000")}");
|
|
msgList.Add($"当前剩余资金:{clientBalance.AmountFund.ToString("#,##0.000")},冻结资金:{clientBalance.AllFreezeBalance().ToString("#,##0.000")},抵押品价值:{clientBalance.GuaranteesTotalAmount.ToString("#,##0.000")},授信额度:{clientBalance.TotalCredit.ToString("#,##0.000")},可用总额度:{AvailableAmount.ToString("#,##0.000")}。不足以支付上述金额。");
|
|
}
|
|
}
|
|
if ((availableStockEqvNotional - stockEqvNotional) < 0)
|
|
{
|
|
msgList.Add($"当前名义本金可用规模:{availableStockEqvNotional.ToString("#,##0.000")}。不足抵充该交易名义本金:{stockEqvNotional.ToString("#,##0.000")}");
|
|
}
|
|
var amountFund = AvailableAmount - margin;
|
|
avmsg = $"当前可用总额度:{AvailableAmount.ToString("#,##0.000")},计入当前交易后,可用总额度为:{amountFund.ToString("#,##0.000")}";
|
|
}
|
|
}
|
|
else
|
|
{
|
|
var AvailableAmount = clientBalance.AvailablePremium();
|
|
|
|
if (AvailableAmount < tradePrice)
|
|
{
|
|
if (PS.Config.Is申万)
|
|
{
|
|
msgList.Add($"当前账户可用资金{AvailableAmount.ToString("#,##0.000")}元,不足以支付期权费{tradePrice.ToString("#,##0.000")}元");
|
|
}
|
|
else
|
|
{
|
|
msgList.Add($"当前交易应付预付金:{0.ToString("#,##0.000")},应付成交金额:{tradePrice.ToString("#,##0.000")},总应付资金:{tradePrice.ToString("#,##0.000")}");
|
|
msgList.Add($"当前剩余资金:{clientBalance.AmountFund.ToString("#,##0.000")},冻结资金:{clientBalance.AllFreezeBalance().ToString("#,##0.000")},抵押品价值:{clientBalance.GuaranteesTotalAmount.ToString("#,##0.000")},授信额度:{clientBalance.TotalCredit.ToString("#,##0.000")},可用总额度:{AvailableAmount.ToString("#,##0.000")}。不足以支付上述金额。");
|
|
|
|
}
|
|
}
|
|
if ((availableStockEqvNotional - stockEqvNotional) < 0)
|
|
{
|
|
msgList.Add($"当前名义本金可用规模:{availableStockEqvNotional.ToString("#,##0.000")}。不足抵充该交易名义本金:{stockEqvNotional.ToString("#,##0.000")}");
|
|
}
|
|
var amountFund = AvailableAmount - tradePrice;
|
|
avmsg = $"当前可用总额度:{AvailableAmount.ToString("#,##0.000")},计入当前交易后,可用总额度为:{amountFund.ToString("#,##0.000")}";
|
|
}
|
|
}
|
|
else if (trades[0].TradeType == "收益互换")
|
|
{
|
|
|
|
var clientBalance = clientBalances[0];
|
|
using var db = new YLContext();
|
|
var id = trades[0].id;
|
|
var tradePrice = (trades[0].TradePrice ?? 0) * (-TradeCalcHelper.GetSign(trades[0].BuySell));
|
|
var margin = trades[0].InitialMargin ?? 0;
|
|
|
|
var AvailableAmount = clientBalance.AvailablePremium();
|
|
|
|
if (AvailableAmount < tradePrice && tradePrice > 0)
|
|
{
|
|
if (PS.Config.Is申万)
|
|
{
|
|
msgList.Add($"当前账户可用资金{AvailableAmount.ToString("#,##0.000")}元,不足以支付交易费用{tradePrice.ToString("#,##0.000")}元");
|
|
}
|
|
else
|
|
{
|
|
msgList.Add($"当前交易应付预付金:{0.ToString("#,##0.000")},应付成交金额:{tradePrice.ToString("#,##0.000")},总应付资金:{tradePrice.ToString("#,##0.000")}");
|
|
msgList.Add($"当前剩余资金:{clientBalance.AmountFund.ToString("#,##0.000")},冻结资金:{clientBalance.AllFreezeBalance().ToString("#,##0.000")},抵押品价值:{clientBalance.GuaranteesTotalAmount.ToString("#,##0.000")},授信额度:{clientBalance.TotalCredit.ToString("#,##0.000")},可用总额度:{AvailableAmount.ToString("#,##0.000")}。不足以支付上述金额。");
|
|
}
|
|
var amountFund = AvailableAmount - tradePrice;
|
|
avmsg = $"当前可用总额度:{AvailableAmount.ToString("#,##0.000")},计入当前交易后,可用总额度为:{amountFund.ToString("#,##0.000")}";
|
|
}
|
|
else
|
|
{
|
|
var margins = margin + tradePrice;
|
|
if (trades[0].ExerciseDate.Value.Date >= valuedateBLL.ValueDate.Date)
|
|
{
|
|
AvailableAmount = Math.Max(clientBalance.AvailableAmount + clientBalance.TotalCredit, 0);
|
|
if (AvailableAmount < margins)
|
|
{
|
|
if (PS.Config.Is申万)
|
|
{
|
|
msgList.Add($"当前账户可用资金{AvailableAmount.ToString("#,##0.000")}元,不足以支付预付金{margin.ToString("#,##0.000")}元和交易费用{tradePrice.ToString("#,##0.000")}元的加和");
|
|
}
|
|
else
|
|
{
|
|
msgList.Add($"当前交易应付预付金:{margin.ToString("#,##0.000")},应付成交金额:{tradePrice.ToString("#,##0.000")},总应付资金:{margins.ToString("#,##0.000")}");
|
|
msgList.Add($"当前剩余资金:{clientBalance.AmountFund.ToString("#,##0.000")},冻结资金:{clientBalance.AllFreezeBalance().ToString("#,##0.000")},抵押品价值:{clientBalance.GuaranteesTotalAmount.ToString("#,##0.000")},授信额度:{clientBalance.TotalCredit.ToString("#,##0.000")},可用总额度:{AvailableAmount.ToString("#,##0.000")}。不足以支付上述金额。");
|
|
}
|
|
}
|
|
var amountFund = AvailableAmount - tradePrice - margin;
|
|
avmsg = $"当前可用总额度:{AvailableAmount.ToString("#,##0.000")},计入当前交易后,可用总额度为:{amountFund.ToString("#,##0.000")}";
|
|
}
|
|
}
|
|
if ((availableStockEqvNotional - trades[0].StockEqvNotional) < 0)
|
|
{
|
|
msgList.Add($"当前名义本金可用规模:{availableStockEqvNotional.ToString("#,##0.000")}。不足抵充该交易名义本金:{trades[0].StockEqvNotional.ToString("#,##0.000")}");
|
|
}
|
|
}
|
|
else if (trades[0].TradeType == "远期")
|
|
{
|
|
using var db = new YLContext();
|
|
var margin = trades[0].InitialMargin ?? 0;
|
|
var tradePrice = trades[0].TradePrice ?? 0;
|
|
var margins = margin + tradePrice;
|
|
var id = trades[0].id;
|
|
var forwardTrade = db.trade_forward.Where(x => x.TradeId == id).FirstOrDefault();
|
|
if (forwardTrade != null)
|
|
{
|
|
//远期验资,验证客户余额>= 开仓总费用
|
|
var clientBalance = clientBalances[0];
|
|
var AvailableAmount = clientBalance.AvailablePremium();
|
|
if (AvailableAmount < margins)
|
|
{
|
|
msgList.Add($"当前交易应付预付金:{margin.ToString("#,##0.000")},应付成交金额:{tradePrice.ToString("#,##0.000")},总应付资金:{margins.ToString("#,##0.000")}");
|
|
msgList.Add($"当前剩余资金:{clientBalance.AmountFund.ToString("#,##0.000")},冻结资金:{clientBalance.AllFreezeBalance().ToString("#,##0.000")},抵押品价值:{clientBalance.GuaranteesTotalAmount.ToString("#,##0.000")},授信额度:{clientBalance.TotalCredit.ToString("#,##0.000")},可用总额度:{AvailableAmount.ToString("#,##0.000")}。不足以支付上述金额。");
|
|
}
|
|
if ((availableStockEqvNotional - trades[0].StockEqvNotional) < 0)
|
|
{
|
|
msgList.Add($"当前名义本金可用规模:{availableStockEqvNotional:#,##0.000}。不足抵充该交易名义本金:{trades[0].StockEqvNotional:#,##0.000}");
|
|
msgList.Add($"当前剩余资金:{clientBalance.AmountFund.ToString("#,##0.000")},冻结资金:{clientBalance.AllFreezeBalance().ToString("#,##0.000")},抵押品价值:{clientBalance.GuaranteesTotalAmount.ToString("#,##0.000")},授信额度:{clientBalance.TotalCredit.ToString("#,##0.000")},可用总额度:{AvailableAmount.ToString("#,##0.000")}。不足以支付上述金额。");
|
|
}
|
|
var amountFund = AvailableAmount - margins;
|
|
avmsg = $"当前可用总额度:{AvailableAmount:#,##0.000},计入当前交易后,可用总额度为:{amountFund.ToString("#,##0.000")}";
|
|
}
|
|
else
|
|
{
|
|
msgList.Add("客户远期交易数据未找到!");
|
|
}
|
|
}
|
|
msg = string.Join("\r\n", msgList);
|
|
if (!PS.Config.IsAvailableForClient)
|
|
{
|
|
avmsg = "";
|
|
}
|
|
|
|
return msgList.Count == 0;
|
|
}
|
|
|
|
/// <summary>
|
|
/// 执行试算检查
|
|
/// </summary>
|
|
/// <param name="current">当前试算的交易和盈亏值</param>
|
|
/// <param name="positionList">当前持仓的所有场外(不包含远期)交易+当前试算的交易和盈亏值</param>
|
|
/// <param name="positionList">当前持仓的所有远期交易+当前试算的交易和盈亏值;//之所以加这个字段,是因为只有整体业务试算时要包含远期交易</param>
|
|
/// <param name="hedgeList">场内持仓</param>
|
|
/// <param name="stockEqvNotionalDict">名义本金(包含远期)</param>
|
|
/// <param name="positionList">远期交易名义本金;//之所以加这个字段,是因为只有整体业务试算时要包含远期交易</param>
|
|
/// <param name="setting">试算配置</param>
|
|
/// <param name="warning">试算类型是预警还是警告</param>
|
|
/// <param name="msg">试算结果描述</param>
|
|
/// <returns></returns>
|
|
private bool CheckQuota(List<CheckQuotaMoitorModel> positionList, List<CheckQuotaMoitorModel> posiList, List<QuotaSetting> setting, List<QuotaSetting> settingAll, bool warning, out string msg)
|
|
{
|
|
msg = "";
|
|
var msgList = new List<string>();
|
|
var tag_prefix = "";
|
|
var dict = setting.GroupBy(O => new { O.QuotaType, O.QuotaRange }).ToDictionary(K => K.Key, V => V.ToArray());
|
|
foreach (var item in dict)
|
|
{
|
|
switch (item.Key.QuotaType)
|
|
{
|
|
case QuotaTypeEnum.GLOBAL_SWAP:
|
|
msgList.AddRange(checkGlobal(positionList, posiList, "场外业务-互换", item.Value, warning));
|
|
break;
|
|
case QuotaTypeEnum.TRADE:
|
|
msgList.AddRange(checkTrade(positionList, posiList, "单笔交易", item.Value, warning));
|
|
break;
|
|
case QuotaTypeEnum.UNDERLYING:
|
|
msgList.AddRange(checkUnderlying(positionList, posiList, "标的资产", item.Value, settingAll, warning));
|
|
break;
|
|
case QuotaTypeEnum.CLIENT:
|
|
CheckQuotaMoitorModel current = positionList.FirstOrDefault(x => x.Current);
|
|
var clientRange = item.Value.Where(O => O.QuotaRange == current.ClientId || O.QuotaRange == 0).ToArray();
|
|
if (!clientRange.Any())
|
|
{
|
|
continue;
|
|
}
|
|
else
|
|
{
|
|
foreach (var quota in clientRange)
|
|
{
|
|
tag_prefix = quota.QuotaRange == 0 ? "客户合计" : "当前客户";
|
|
msgList.AddRange(checkClient(positionList, posiList, tag_prefix, new QuotaSetting[] { quota }, settingAll, warning));
|
|
}
|
|
|
|
}
|
|
|
|
break;
|
|
default:
|
|
continue;
|
|
}
|
|
}
|
|
for (var i = 0; i < msgList.Count; i++)
|
|
{
|
|
if (string.IsNullOrWhiteSpace(msgList[i]))
|
|
{
|
|
msgList.RemoveAt(i);
|
|
i = --i;
|
|
continue;
|
|
}
|
|
msgList[i] = $"{i + 1}.{msgList[i]}";
|
|
}
|
|
msg = string.Join("\r\n", msgList);
|
|
return msgList.Count == 0;
|
|
}
|
|
|
|
/// <summary>
|
|
/// 限额事前检查校验
|
|
/// </summary>
|
|
/// <param name="current"></param>
|
|
/// <param name="positionList"></param>
|
|
/// <param name="positionList_forward"></param>
|
|
/// <param name="hedgeList"></param>
|
|
/// <param name="stockEqvNotionalDict"></param>
|
|
/// <param name="quotaType"></param>
|
|
/// <param name="checkItem"></param>
|
|
/// <param name="quotaRange"></param>
|
|
/// <param name="stockEqvNotional">单笔名义本金</param>
|
|
/// <returns></returns>
|
|
private ClientRiskCheckItem CheckQuota(List<CheckQuotaMoitorModel> positionList, QuotaTypeEnum quotaType, ClientRiskCheckItem checkItem, int quotaRange, List<QuotaSetting> settingAll)
|
|
{
|
|
var tag_prefix = "";
|
|
var positionListAll = positionList;
|
|
switch (quotaType)
|
|
{
|
|
case QuotaTypeEnum.GLOBAL_SWAP:
|
|
checkItem = checkGlobal(positionList, "场外业务-互换", checkItem, quotaType);
|
|
break;
|
|
case QuotaTypeEnum.TRADE:
|
|
checkItem = checkTrade(positionList, "单笔交易", checkItem);
|
|
break;
|
|
case QuotaTypeEnum.UNDERLYING:
|
|
checkItem = checkUnderlying(positionList, "标的资产", checkItem, quotaRange, settingAll);
|
|
break;
|
|
case QuotaTypeEnum.CLIENT:
|
|
CheckQuotaMoitorModel current = positionList.FirstOrDefault(x => x.Current);
|
|
if (current.ClientId == quotaRange || quotaRange == 0)
|
|
{
|
|
tag_prefix = quotaRange == 0 ? "客户合计" : "当前客户";
|
|
checkItem = checkClient(positionList, tag_prefix, checkItem, quotaRange, settingAll);
|
|
}
|
|
else
|
|
{
|
|
return null;
|
|
}
|
|
break;
|
|
default:
|
|
return null;
|
|
}
|
|
return checkItem;
|
|
}
|
|
|
|
/// <summary>
|
|
/// 算标的交易盈亏
|
|
/// </summary>
|
|
/// <returns></returns>
|
|
private QuotaMonitor_Global GetTradePositionPnl()
|
|
{
|
|
QuotaMonitor_Global quotaMonitor_Global = new QuotaMonitor_Global()
|
|
{
|
|
ParentKey= "场外",
|
|
BusinessType = "标的交易",
|
|
PositionPnl=0,
|
|
DeltaExposure=0,
|
|
StockEqvNotional=0
|
|
};
|
|
List<string> tradetypes = new List<string> { "利率债", "信用债", "其它债券" };
|
|
var tposis = DbContext.TradePosition.Where(x => tradetypes.Contains(x.TradeType)).AsNoTracking().ToList();
|
|
var umCodes = tposis.Select(x => x.UnderlyingCode).Distinct().ToList();
|
|
var ums = DataCacheProvider.GetUnderlyingDataSource().AsQueryable().Where(x => umCodes.Contains(x.UnderlyingCode));
|
|
foreach (var item in tposis)
|
|
{
|
|
double lastPrice = 0;
|
|
var um = ums.FirstOrDefault(x => x.UnderlyingCode == item.UnderlyingCode);
|
|
double contractSize = 0;
|
|
if (um != null)
|
|
{
|
|
contractSize = um.ContractSize;
|
|
if (!um.IsBond())
|
|
{
|
|
lastPrice = EodPriceQueryService.GetClosePrice(valuedateBLL.ValueDate, item.UnderlyingCode);
|
|
}
|
|
else
|
|
{
|
|
var bondPrice = EodPriceQueryService.GetBondPrice(valuedateBLL.ValueDate, item.UnderlyingCode);
|
|
lastPrice = bondPrice != null ? bondPrice.ClosePrice : (um.Price ?? 0) * Convert.ToDouble(ConsGlobal.bondPriceMultiple);
|
|
}
|
|
}
|
|
quotaMonitor_Global.PositionPnl += lastPrice * item.Position - item.PositionCost;
|
|
quotaMonitor_Global.StockEqvNotional += item.PositionCost;
|
|
quotaMonitor_Global.DeltaExposure += (item.PositionType == PositionTypeFlag.Long ? 1 : -1) * item.Position;
|
|
}
|
|
|
|
return quotaMonitor_Global;
|
|
}
|
|
/// <summary>
|
|
/// 计算日终标的交易盈亏
|
|
/// </summary>
|
|
/// <param name="valueDate"></param>
|
|
/// <returns></returns>
|
|
private QuotaMonitor_Global GetEodTradePositionPnl(DateTime valueDate)
|
|
{
|
|
QuotaMonitor_Global quotaMonitor_Global = new QuotaMonitor_Global()
|
|
{
|
|
ParentKey = "场外",
|
|
BusinessType = "标的交易",
|
|
PositionPnl = 0,
|
|
DeltaExposure = 0,
|
|
StockEqvNotional = 0
|
|
};
|
|
List<string> tradetypes = new List<string> { "利率债", "信用债", "其它债券" };
|
|
var tposis = DbContext.eod_trade_position.Where(x => tradetypes.Contains(x.TradeType)&&x.ValueDate==valueDate).AsNoTracking().ToList();
|
|
var umCodes = tposis.Select(x => x.UnderlyingCode).Distinct().ToList();
|
|
var ums = DataCacheProvider.GetUnderlyingDataSource().AsQueryable().Where(x => umCodes.Contains(x.UnderlyingCode));
|
|
foreach (var item in tposis)
|
|
{
|
|
double lastPrice = 0;
|
|
var um = ums.FirstOrDefault(x => x.UnderlyingCode == item.UnderlyingCode);
|
|
double contractSize = 0;
|
|
if (um != null)
|
|
{
|
|
contractSize = um.ContractSize;
|
|
if (!um.IsBond())
|
|
{
|
|
lastPrice = EodPriceQueryService.GetClosePrice(valuedateBLL.ValueDate, item.UnderlyingCode);
|
|
}
|
|
else
|
|
{
|
|
var bondPrice = EodPriceQueryService.GetBondPrice(valuedateBLL.ValueDate, item.UnderlyingCode);
|
|
lastPrice = bondPrice != null ? bondPrice.ClosePrice : (um.Price ?? 0) * Convert.ToDouble(ConsGlobal.bondPriceMultiple);
|
|
}
|
|
}
|
|
quotaMonitor_Global.PositionPnl += item.PositionPnL;
|
|
quotaMonitor_Global.StockEqvNotional += item.Cost;
|
|
quotaMonitor_Global.DeltaExposure += item.Amount;
|
|
}
|
|
quotaMonitor_Global.StockEqvNotional = Math.Abs(quotaMonitor_Global.StockEqvNotional??0);
|
|
return quotaMonitor_Global;
|
|
}
|
|
private List<string> checkGlobal(List<CheckQuotaMoitorModel> positionList, List<CheckQuotaMoitorModel> posiList, string tag_prefix, QuotaSetting[] settings, bool warning)
|
|
{
|
|
var messageList = new List<string>();
|
|
double? upperLimit, lowerLimit, currentValue = null;
|
|
foreach (var settingItem in settings)
|
|
{
|
|
if (warning)
|
|
{
|
|
upperLimit = settingItem.WarningUpperLimit;
|
|
lowerLimit = settingItem.WarningLowerLimit;
|
|
}
|
|
else
|
|
{
|
|
upperLimit = settingItem.QuotaUpperLimit;
|
|
lowerLimit = settingItem.QuotaLowerLimit;
|
|
}
|
|
|
|
if (upperLimit == null && lowerLimit == null)
|
|
{
|
|
continue;
|
|
}
|
|
currentValue = null;
|
|
double posiVal = 0;
|
|
decimal noPosiVal = 0;
|
|
decimal tradePosiVal = 0;
|
|
var tag = $"{tag_prefix}({settingItem.QuotaIndex})";
|
|
CheckQuotaMoitorModel current = positionList.FirstOrDefault(x => x.Current);
|
|
switch (settingItem.QuotaIndex)
|
|
{
|
|
case "名义本金":
|
|
var clientPv = positionList.Where(s => s.ClientId == current.ClientId && s.UnderlyingCode == current.UnderlyingCode).Sum(s => s.Pv * (s.Side == 0 ? 1 : -1));
|
|
var otherPv = positionList.Where(s => s.ClientId != current.ClientId || s.UnderlyingCode != current.UnderlyingCode).Sum(s => s.Pv);
|
|
currentValue = Convert.ToDouble(otherPv + Math.Abs(clientPv));
|
|
var posiPv = positionList.Where(s => !s.Current).Sum(s => s.Pv);
|
|
posiVal = Convert.ToDouble(posiPv);
|
|
tradePosiVal = posiList.Sum(s => s.Pv);
|
|
noPosiVal = posiPv - tradePosiVal;
|
|
messageList.Add(SetQuotaMsg(tag, settingItem.QuotaIndex, currentValue, Convert.ToDouble(tradePosiVal), posiVal, Convert.ToDouble(current.Pv), Convert.ToDouble(noPosiVal), null, upperLimit, lowerLimit, settingItem.Percent, warning));
|
|
break;
|
|
case "Delta金额":
|
|
currentValue = Convert.ToDouble(positionList.Sum(s => s.Delta));
|
|
messageList.Add(SetMsg(tag, currentValue, upperLimit, lowerLimit, settingItem.Percent, warning));
|
|
break;
|
|
case "浮动盈亏":
|
|
currentValue = Convert.ToDouble(positionList.Sum(s => s.Pnl));
|
|
messageList.Add(SetMsg(tag, currentValue, upperLimit, lowerLimit, settingItem.Percent, warning));
|
|
break;
|
|
default:
|
|
continue;
|
|
}
|
|
|
|
}
|
|
return messageList;
|
|
}
|
|
|
|
private ClientRiskCheckItem checkGlobal(List<CheckQuotaMoitorModel> positionList, string tag_prefix, ClientRiskCheckItem checkItem, QuotaTypeEnum quotaType)
|
|
{
|
|
double? currentValue = null;
|
|
currentValue = null;
|
|
var tag = $"{tag_prefix}({checkItem.quotaType})";
|
|
CheckQuotaMoitorModel current = positionList.FirstOrDefault(x => x.Current);
|
|
double posiVal = 0;
|
|
switch (checkItem.quotaType)
|
|
{
|
|
case "名义本金":
|
|
var _posiList = DataHelper.DeepCopyObject(positionList);
|
|
var clientPv = CalcPv(_posiList, false, false);
|
|
currentValue = Convert.ToDouble(clientPv);
|
|
var posiPv = positionList.Where(s => !s.Current).Sum(s => s.Pv);
|
|
posiVal = Convert.ToDouble(posiPv);
|
|
if (!ValidateQuoteResult(checkItem, currentValue, posiVal))
|
|
{
|
|
return checkItem;
|
|
}
|
|
return null;
|
|
case "浮动盈亏":
|
|
currentValue = Convert.ToDouble(positionList.Sum(O => O.Pnl));
|
|
break;
|
|
default:
|
|
return null;
|
|
}
|
|
checkItem.currentValue = Math.Round(currentValue ?? 0, 2);
|
|
if (!ValidateQuoteResult(checkItem))
|
|
{
|
|
return checkItem;
|
|
}
|
|
return null;
|
|
}
|
|
private List<string> checkTrade(List<CheckQuotaMoitorModel> positionList, List<CheckQuotaMoitorModel> posiList, string tag_prefix, QuotaSetting[] settings, bool warning)
|
|
{
|
|
if (string.IsNullOrEmpty(tag_prefix))
|
|
{
|
|
throw new ArgumentException($"“{nameof(tag_prefix)}”不能是 Null 或为空", nameof(tag_prefix));
|
|
}
|
|
|
|
if (settings is null)
|
|
{
|
|
throw new ArgumentNullException(nameof(settings));
|
|
}
|
|
|
|
var messageList = new List<string>();
|
|
double? upperLimit, lowerLimit, currentValue = null;
|
|
|
|
CheckQuotaMoitorModel current = positionList.FirstOrDefault(x => x.Current);
|
|
foreach (var settingItem in settings)
|
|
{
|
|
if (warning)
|
|
{
|
|
upperLimit = settingItem.WarningUpperLimit;
|
|
lowerLimit = settingItem.WarningLowerLimit;
|
|
}
|
|
else
|
|
{
|
|
upperLimit = settingItem.QuotaUpperLimit;
|
|
lowerLimit = settingItem.QuotaLowerLimit;
|
|
}
|
|
|
|
if (upperLimit == null && lowerLimit == null)
|
|
{
|
|
continue;
|
|
}
|
|
currentValue = null;
|
|
var tag = $"{tag_prefix}({settingItem.QuotaIndex})";
|
|
double posiVal = 0;
|
|
decimal noPosiVal = 0;
|
|
decimal tradePosiVal = 0;
|
|
switch (settingItem.QuotaIndex)
|
|
{
|
|
case "名义本金":
|
|
var clientPv = positionList.Where(s => s.ClientId == current.ClientId && s.UnderlyingCode == current.UnderlyingCode && s.Side != current.Side).Sum(s => s.Pv * (s.Side == 0 ? 1 : -1));
|
|
var currentPv = current.Pv * (current.Side == 0 ? 1 : -1);
|
|
currentValue = Convert.ToDouble(Math.Abs(clientPv + currentPv));
|
|
var posiPv = positionList.Where(s => s.ClientId == current.ClientId && s.UnderlyingCode == current.UnderlyingCode && !s.Current).Sum(s => s.Pv);
|
|
posiVal = Convert.ToDouble(posiPv);
|
|
tradePosiVal = posiList.Where(s => s.ClientId == current.ClientId && s.UnderlyingCode == current.UnderlyingCode).Sum(s => s.Pv);
|
|
noPosiVal = posiPv - tradePosiVal;
|
|
messageList.Add(SetQuotaMsg(tag, settingItem.QuotaIndex, currentValue, Convert.ToDouble(tradePosiVal), posiVal, Convert.ToDouble(current.Pv), Convert.ToDouble(noPosiVal), null, upperLimit, lowerLimit, settingItem.Percent, warning));
|
|
break;
|
|
case "止损金额":
|
|
currentValue = Convert.ToDouble(current.Pnl);
|
|
messageList.Add(SetMsg(tag, currentValue, upperLimit, lowerLimit, settingItem.Percent, warning));
|
|
break;
|
|
case "Delta金额":
|
|
currentValue = Convert.ToDouble(current.Delta);
|
|
messageList.Add(SetMsg(tag, currentValue, upperLimit, lowerLimit, settingItem.Percent, warning));
|
|
break;
|
|
default:
|
|
continue;
|
|
}
|
|
|
|
}
|
|
return messageList;
|
|
}
|
|
private ClientRiskCheckItem checkTrade(List<CheckQuotaMoitorModel> positionList, string tag_prefix, ClientRiskCheckItem checkItem)
|
|
{
|
|
if (string.IsNullOrEmpty(tag_prefix))
|
|
{
|
|
throw new ArgumentException($"“{nameof(tag_prefix)}”不能是 Null 或为空", nameof(tag_prefix));
|
|
}
|
|
double? currentValue = null;
|
|
|
|
currentValue = null;
|
|
double posiVal = 0;
|
|
CheckQuotaMoitorModel current = positionList.FirstOrDefault(x => x.Current);
|
|
var tag = $"{tag_prefix}({checkItem.quotaType})";
|
|
switch (checkItem.quotaType)
|
|
{
|
|
case "互换价格偏离比例":
|
|
var sportPrice = Convert.ToDouble(current.Price);
|
|
var basePrice = Convert.ToDouble(current.LastPrice);
|
|
if (checkItem.isPercent)
|
|
{
|
|
currentValue = basePrice == 0 ? 0 : Math.Abs((sportPrice / basePrice) - 1);
|
|
}
|
|
else
|
|
{
|
|
currentValue = Math.Abs(sportPrice - basePrice);
|
|
}
|
|
checkItem.currentValue = currentValue;
|
|
if (!ValidateQuoteResult(checkItem))
|
|
{
|
|
|
|
return checkItem;
|
|
}
|
|
return null;
|
|
default:
|
|
return null;
|
|
}
|
|
|
|
}
|
|
|
|
private List<string> checkUnderlying(List<CheckQuotaMoitorModel> positionList, List<CheckQuotaMoitorModel> posiList, string tag_prefix, QuotaSetting[] settings, List<QuotaSetting> settingAll, bool warning)
|
|
{
|
|
if (string.IsNullOrEmpty(tag_prefix))
|
|
{
|
|
throw new ArgumentException($"“{nameof(tag_prefix)}”不能是 Null 或为空", nameof(tag_prefix));
|
|
}
|
|
|
|
if (settings is null)
|
|
{
|
|
throw new ArgumentNullException(nameof(settings));
|
|
}
|
|
|
|
var messageList = new List<string>();
|
|
double? upperLimit, lowerLimit, currentValue = null;
|
|
|
|
CheckQuotaMoitorModel current = positionList.FirstOrDefault(x => x.Current);
|
|
//settings 可能包含全部
|
|
var setting = settings.OrderByDescending(o => o.QuotaRange).First();
|
|
var noQuoteSetting = settingAll.FirstOrDefault(x => x.QuotaType == setting.QuotaType && x.QuotaRange == setting.QuotaRange && x.QuotaIndex == setting.QuotaIndex && !x.Precheck);//是否设置了不事前检查
|
|
if (noQuoteSetting != null)
|
|
{
|
|
return messageList;
|
|
}
|
|
if (warning)
|
|
{
|
|
upperLimit = setting.WarningUpperLimit;
|
|
lowerLimit = setting.WarningLowerLimit;
|
|
}
|
|
else
|
|
{
|
|
upperLimit = setting.QuotaUpperLimit;
|
|
lowerLimit = setting.QuotaLowerLimit;
|
|
}
|
|
|
|
if (upperLimit == null && lowerLimit == null)
|
|
{
|
|
return messageList;
|
|
}
|
|
if (current.UnderlyingId != setting.QuotaRange && setting.QuotaRange != 0)
|
|
{
|
|
return messageList;
|
|
}
|
|
currentValue = null;
|
|
var tag = $"{tag_prefix}({setting.QuotaIndex})";
|
|
double posiVal = 0;
|
|
decimal noPosiVal = 0;
|
|
decimal tradePosiVal = 0;
|
|
decimal currentPv = 0;
|
|
decimal posiPv = 0;
|
|
tradePosiVal = posiList.Where(s => s.UnderlyingCode == current.UnderlyingCode).Sum(s => s.Pv * (s.Side == 0 ? 1 : -1));
|
|
tradePosiVal = Math.Abs(tradePosiVal);
|
|
currentPv = positionList.Where(s => s.UnderlyingCode == current.UnderlyingCode).Sum(s => s.Pv * (s.Side == 0 ? 1 : -1));
|
|
currentValue = Convert.ToDouble(currentPv);
|
|
currentValue = Math.Abs(currentValue ?? 0);
|
|
posiPv = positionList.Where(s => s.UnderlyingCode == current.UnderlyingCode && !s.Current).Sum(s => s.Pv * (s.Side == 0 ? 1 : -1));
|
|
posiPv = Math.Abs(posiPv);
|
|
posiVal = Convert.ToDouble(posiPv);
|
|
noPosiVal = posiPv - tradePosiVal;
|
|
noPosiVal = Math.Abs(noPosiVal);
|
|
switch (setting.QuotaIndex)
|
|
{
|
|
case "轧差名义本金":
|
|
messageList.Add(SetQuotaMsg(tag, setting.QuotaIndex, currentValue, Convert.ToDouble(tradePosiVal), posiVal, Convert.ToDouble(current.Pv), Convert.ToDouble(noPosiVal), null, upperLimit, lowerLimit, setting.Percent, warning));
|
|
break;
|
|
case "轧差集中度":
|
|
currentValue = current.Circulation == 0 ? 0 : currentValue / Convert.ToDouble(current.Circulation);
|
|
tradePosiVal = current.Circulation == 0 ? 0 : tradePosiVal / current.Circulation;
|
|
posiVal = current.Circulation == 0 ? 0 : posiVal / Convert.ToDouble(current.Circulation);
|
|
var curentPv = current.Circulation == 0 ? 0 : current.Pv / current.Circulation;
|
|
noPosiVal = current.Circulation == 0 ? 0 : noPosiVal / current.Circulation;
|
|
messageList.Add(SetQuotaMsg(tag, setting.QuotaIndex, currentValue, Convert.ToDouble(tradePosiVal), posiVal, posiVal, Convert.ToDouble(noPosiVal), null, upperLimit, lowerLimit, setting.Percent, warning));
|
|
break;
|
|
default:
|
|
break;
|
|
}
|
|
return messageList;
|
|
}
|
|
private ClientRiskCheckItem checkUnderlying(List<CheckQuotaMoitorModel> positionList, string tag_prefix, ClientRiskCheckItem checkItem, int quotaRange, List<QuotaSetting> settingAll)
|
|
{
|
|
if (string.IsNullOrEmpty(tag_prefix))
|
|
{
|
|
throw new ArgumentException($"“{nameof(tag_prefix)}”不能是 Null 或为空", nameof(tag_prefix));
|
|
}
|
|
double? currentValue = null;
|
|
|
|
currentValue = null;
|
|
double posiVal = 0;
|
|
decimal currentPv = 0;
|
|
decimal posiPv = 0;
|
|
CheckQuotaMoitorModel current = positionList.FirstOrDefault(x => x.Current);
|
|
if (current.UnderlyingId != quotaRange && quotaRange != 0)
|
|
{
|
|
return null;
|
|
}
|
|
var noQuoteSetting = settingAll.FirstOrDefault(x => x.QuotaType == QuotaTypeEnum.UNDERLYING && x.QuotaRange == quotaRange && x.QuotaIndex == checkItem.quotaType && !x.Precheck);//是否设置了不事前检查
|
|
if (noQuoteSetting != null)
|
|
{
|
|
return null;
|
|
}
|
|
positionList = positionList.Where(s => s.UnderlyingCode == current.UnderlyingCode).ToList();
|
|
var _posiList = DataHelper.DeepCopyObject(positionList);
|
|
currentPv = _posiList.Where(s => s.UnderlyingCode == current.UnderlyingCode).Sum(s => s.Pv * (s.Side == 0 ? 1 : -1));
|
|
posiPv = _posiList.Where(s => s.UnderlyingCode == current.UnderlyingCode && !s.Current).Sum(s => s.Pv * (s.Side == 0 ? 1 : -1));
|
|
posiVal = Math.Abs(Convert.ToDouble(posiPv));
|
|
var tag = $"{tag_prefix}({checkItem.quotaType})";
|
|
switch (checkItem.quotaType)
|
|
{
|
|
case "轧差名义本金":
|
|
currentValue = Math.Abs(Convert.ToDouble(currentPv));
|
|
if (!ValidateQuoteResult(checkItem, currentValue, posiVal))
|
|
{
|
|
return checkItem;
|
|
}
|
|
return null;
|
|
case "轧差集中度":
|
|
currentValue = current.Circulation == 0 ? 0 : currentValue / Convert.ToDouble(current.Circulation);
|
|
posiPv = _posiList.Where(s => s.UnderlyingCode == current.UnderlyingCode && !s.Current).Sum(s => s.Pv);
|
|
posiVal = current.Circulation == 0 ? 0 : Convert.ToDouble(posiPv / current.Circulation);
|
|
if (!ValidateQuoteResult(checkItem, currentValue, posiVal))
|
|
{
|
|
return checkItem;
|
|
}
|
|
return null;
|
|
default:
|
|
return null;
|
|
}
|
|
}
|
|
private List<string> checkClient(List<CheckQuotaMoitorModel> positionList, List<CheckQuotaMoitorModel> posiList, string tag_prefix, QuotaSetting[] settings, List<QuotaSetting> settingAll, bool warning)
|
|
{
|
|
if (string.IsNullOrEmpty(tag_prefix))
|
|
{
|
|
throw new ArgumentException($"“{nameof(tag_prefix)}”不能是 Null 或为空", nameof(tag_prefix));
|
|
}
|
|
|
|
if (settings is null)
|
|
{
|
|
throw new ArgumentNullException(nameof(settings));
|
|
}
|
|
|
|
var messageList = new List<string>();
|
|
double? upperLimit, lowerLimit, currentValue = null;
|
|
CheckQuotaMoitorModel current = positionList.FirstOrDefault(x => x.Current);
|
|
//settings 可能包含全部
|
|
var settingItem = settings.First();
|
|
var noQuoteSetting = settingAll.FirstOrDefault(x => x.QuotaType == settingItem.QuotaType && x.QuotaRange == settingItem.QuotaRange && x.QuotaIndex == settingItem.QuotaIndex && !x.Precheck);//是否设置了不事前检查
|
|
if (noQuoteSetting != null)
|
|
{
|
|
return messageList;
|
|
}
|
|
if (warning)
|
|
{
|
|
upperLimit = settingItem.WarningUpperLimit;
|
|
lowerLimit = settingItem.WarningLowerLimit;
|
|
}
|
|
else
|
|
{
|
|
upperLimit = settingItem.QuotaUpperLimit;
|
|
lowerLimit = settingItem.QuotaLowerLimit;
|
|
}
|
|
|
|
if (upperLimit == null && lowerLimit == null)
|
|
{
|
|
return messageList;
|
|
}
|
|
if (current.ClientId != settingItem.QuotaRange && settingItem.QuotaRange != 0)//不是某一客户的不走限额
|
|
{
|
|
return messageList;
|
|
}
|
|
currentValue = null;
|
|
double posiVal = 0;
|
|
decimal noPosiVal = 0;
|
|
decimal tradePosiVal = 0;
|
|
var tag = $"{tag_prefix}({settingItem.QuotaIndex})";
|
|
var clientPositionList = positionList.Where(O => O.ClientId == current.ClientId).ToList();
|
|
var tradeIds = clientPositionList.Select(O => O.id);
|
|
switch (settingItem.QuotaIndex)
|
|
{
|
|
case "名义本金":
|
|
var clientPv = positionList.Where(s => s.ClientId == current.ClientId && s.UnderlyingCode == current.UnderlyingCode).Sum(s => s.Pv * (s.Side == 0 ? 1 : -1));
|
|
var otherPv = positionList.Where(s => s.ClientId == current.ClientId && s.UnderlyingCode != current.UnderlyingCode).Sum(s => s.Pv);
|
|
currentValue = Convert.ToDouble(otherPv + Math.Abs(clientPv));
|
|
var posiPv = positionList.Where(s => s.ClientId == current.ClientId && !s.Current).Sum(s => s.Pv);
|
|
posiVal = Convert.ToDouble(posiPv);
|
|
tradePosiVal = posiList.Where(s => s.ClientId == current.ClientId).Sum(s => s.Pv);
|
|
noPosiVal = posiPv - tradePosiVal;
|
|
messageList.Add(SetQuotaMsg(tag, settingItem.QuotaIndex, currentValue, Convert.ToDouble(tradePosiVal), posiVal, Convert.ToDouble(current.Pv), Convert.ToDouble(noPosiVal), null, upperLimit, lowerLimit, settingItem.Percent, warning));
|
|
break;
|
|
default:
|
|
break;
|
|
}
|
|
return messageList;
|
|
}
|
|
|
|
private ClientRiskCheckItem checkClient(List<CheckQuotaMoitorModel> positionList, string tag_prefix, ClientRiskCheckItem checkItem, int quotaRange, List<QuotaSetting> settingAll)
|
|
{
|
|
if (string.IsNullOrEmpty(tag_prefix))
|
|
{
|
|
throw new ArgumentException($"“{nameof(tag_prefix)}”不能是 Null 或为空", nameof(tag_prefix));
|
|
}
|
|
|
|
double? currentValue = null;
|
|
double posiVal = 0;
|
|
var tag = $"{tag_prefix}({checkItem.quotaType})";
|
|
CheckQuotaMoitorModel current = positionList.FirstOrDefault(x => x.Current);
|
|
var clientPositionList = positionList.Where(O => O.ClientId == current.ClientId).ToList();
|
|
var _posiList = DataHelper.DeepCopyObject(clientPositionList);
|
|
var _tradePosiList = clientPositionList.Where(s => !s.Current).ToList();
|
|
var clientPv = CalcPv(_posiList, true, true);
|
|
var clientTradePv = CalcPv(_tradePosiList, true);
|
|
clientPv = Math.Abs(clientPv);
|
|
clientTradePv = Math.Abs(clientTradePv);
|
|
switch (checkItem.quotaType)
|
|
{
|
|
case "名义本金":
|
|
currentValue = Convert.ToDouble(Math.Abs(clientPv));
|
|
var posiPv = clientTradePv;
|
|
posiVal = Convert.ToDouble(posiPv);
|
|
if (!ValidateQuoteResult(checkItem, currentValue, posiVal))
|
|
{
|
|
return checkItem;
|
|
}
|
|
return null;
|
|
default:
|
|
return null;
|
|
}
|
|
}
|
|
|
|
/// <summary>
|
|
/// 计算client名义本金,最新持仓市值,返回(名义本金,持仓市值)
|
|
/// </summary>
|
|
/// <param name="positionList"></param>
|
|
/// <param name="current"></param>
|
|
/// <param name="isclient">是否单客户户</param>
|
|
/// <param name="roll">是否轧差</param>
|
|
/// <returns></returns>
|
|
private decimal CalcPv(List<CheckQuotaMoitorModel> positionList, bool isclient, bool roll)
|
|
{
|
|
decimal clientPv = 0;
|
|
CheckQuotaMoitorModel current = positionList.FirstOrDefault(s => s.Current);
|
|
if (current == null)
|
|
{
|
|
return 0;
|
|
}
|
|
var clientUnderlyList = positionList.Where(s => s.ClientId == current.ClientId && s.UnderlyingCode == current.UnderlyingCode && s.Side != current.Side).ToList();
|
|
foreach (var item in clientUnderlyList)
|
|
{
|
|
if (item.Qty == 0)
|
|
{
|
|
continue;
|
|
}
|
|
if (item.Qty >= current.Qty)
|
|
{
|
|
item.Qty -= current.Qty;
|
|
item.Pv = item.Qty * item.Price;
|
|
current.Qty = 0;
|
|
current.Pv = 0;
|
|
}
|
|
else if (item.Qty < current.Qty)
|
|
{
|
|
current.Qty -= item.Qty;
|
|
current.Pv = current.Qty * current.Price;
|
|
item.Qty = 0;
|
|
item.Pv = 0;
|
|
CalcPv(positionList, isclient, roll);
|
|
}
|
|
}
|
|
if (isclient)
|
|
{
|
|
clientPv = positionList.Where(s => s.ClientId == current.ClientId).Sum(s => s.Pv);
|
|
}
|
|
else
|
|
{
|
|
if (roll)
|
|
{
|
|
clientPv = positionList.Sum(s => s.Pv * (s.Side == 0 ? 1 : -1));
|
|
}
|
|
else
|
|
{
|
|
clientPv = positionList.Sum(s => s.Pv);
|
|
}
|
|
}
|
|
return clientPv;
|
|
}
|
|
/// <summary>
|
|
/// 计算现有持仓pv
|
|
/// </summary>
|
|
/// <param name="positionList"></param>
|
|
/// <param name="roll"></param>
|
|
/// <returns></returns>
|
|
private decimal CalcPv(List<CheckQuotaMoitorModel> positionList, bool roll)
|
|
{
|
|
// 按ClientId和UnderlyingCode分组
|
|
var groupedPositions = positionList.GroupBy(p => new { p.ClientId, p.UnderlyingCode });
|
|
|
|
decimal totalPv = 0;
|
|
|
|
foreach (var group in groupedPositions)
|
|
{
|
|
// 计算每个分组的PV总和
|
|
decimal groupPv = 0;
|
|
foreach (var position in group)
|
|
{
|
|
// 根据roll参数决定是否考虑方向
|
|
decimal pv = position.Qty * position.Price;
|
|
if (roll)
|
|
{
|
|
pv *= (position.Side == 0 ? 1 : -1); // 根据Side调整符号
|
|
}
|
|
groupPv += pv;
|
|
}
|
|
totalPv += Math.Abs(groupPv); // 累加各分组的PV
|
|
}
|
|
|
|
return totalPv;
|
|
}
|
|
private List<string> checkStockPercent(IEnumerable<KeyValuePair<trade, realtime_trade_risk>> positionList, Dictionary<int, double> stockEqvNotionalDict, string tag_prefix, QuotaSetting[] settings, bool warning)
|
|
{
|
|
if (string.IsNullOrEmpty(tag_prefix))
|
|
{
|
|
throw new ArgumentException($"“{nameof(tag_prefix)}”不能是 Null 或为空", nameof(tag_prefix));
|
|
}
|
|
|
|
if (settings is null)
|
|
{
|
|
throw new ArgumentNullException(nameof(settings));
|
|
}
|
|
|
|
var messageList = new List<string>();
|
|
double? upperLimit, lowerLimit, currentValue = null;
|
|
var count = settings[0].QuotaRange;
|
|
var code = positionList.First().Key.UnderlyingCode;
|
|
var list = DbContext.Eod_Market_Sum.Where(O => O.ValueDate < SystemValueDate && O.UnderlyingCode == code).OrderByDescending(O => O.ValueDate).Take(count);
|
|
if (!list.Any())
|
|
{
|
|
messageList.Add($"{tag_prefix}-{count}天:试算失败,历史成交量不存在;");
|
|
return messageList;
|
|
}
|
|
var totalVolume = list.Sum(O => O.TotalValue) / list.Count();
|
|
|
|
foreach (var settingItem in settings)
|
|
{
|
|
if (warning)
|
|
{
|
|
upperLimit = settingItem.WarningUpperLimit;
|
|
lowerLimit = settingItem.WarningLowerLimit;
|
|
}
|
|
else
|
|
{
|
|
upperLimit = settingItem.QuotaUpperLimit;
|
|
lowerLimit = settingItem.QuotaLowerLimit;
|
|
}
|
|
|
|
if (upperLimit == null && lowerLimit == null)
|
|
{
|
|
continue;
|
|
}
|
|
currentValue = null;
|
|
var tag = $"{tag_prefix}-{count}天({settingItem.QuotaIndex})";
|
|
switch (settingItem.QuotaIndex)
|
|
{
|
|
case "名义本金":
|
|
var currentRangeTradeIds = positionList.Select(O => O.Key.ParentTradeId == 0 ? O.Key.id : O.Key.ParentTradeId);
|
|
currentValue = stockEqvNotionalDict.Where(O => currentRangeTradeIds.Contains(O.Key)).Sum(O => O.Value) / totalVolume;
|
|
break;
|
|
case "Delta金额":
|
|
currentValue = positionList.Sum(O => O.Value.DeltaCash) / totalVolume;
|
|
break;
|
|
default:
|
|
continue;
|
|
}
|
|
messageList.Add(SetMsg(tag, currentValue, upperLimit, lowerLimit, settingItem.Percent, warning));
|
|
}
|
|
return messageList;
|
|
}
|
|
|
|
private ClientRiskCheckItem checkStockPercent(IEnumerable<KeyValuePair<trade, realtime_trade_risk>> positionList, Dictionary<int, double> stockEqvNotionalDict, string tag_prefix, ClientRiskCheckItem checkItem, int quotaRange)
|
|
{
|
|
if (string.IsNullOrEmpty(tag_prefix))
|
|
{
|
|
throw new ArgumentException($"“{nameof(tag_prefix)}”不能是 Null 或为空", nameof(tag_prefix));
|
|
}
|
|
|
|
double? currentValue = null;
|
|
var count = quotaRange;
|
|
var code = positionList.First().Key.UnderlyingCode;
|
|
var list = DbContext.Eod_Market_Sum.Where(O => O.ValueDate < SystemValueDate && O.UnderlyingCode == code).OrderByDescending(O => O.ValueDate).Take(count);
|
|
if (!list.Any())
|
|
{
|
|
throw new ServiceException($"{tag_prefix}-{count}天:试算失败,历史成交量不存在;");
|
|
}
|
|
var totalVolume = list.Sum(O => O.TotalValue) / list.Count();
|
|
|
|
currentValue = null;
|
|
var tag = $"{tag_prefix}-{count}天({checkItem.quotaType})";
|
|
switch (checkItem.quotaType)
|
|
{
|
|
case "名义本金":
|
|
var currentRangeTradeIds = positionList.Select(O => O.Key.ParentTradeId == 0 ? O.Key.id : O.Key.ParentTradeId);
|
|
currentValue = stockEqvNotionalDict.Where(O => currentRangeTradeIds.Contains(O.Key)).Sum(O => O.Value) / totalVolume;
|
|
break;
|
|
case "Delta金额":
|
|
currentValue = positionList.Sum(O => O.Value.DeltaCash) / totalVolume;
|
|
break;
|
|
default:
|
|
return null;
|
|
}
|
|
checkItem.currentValue = currentValue;
|
|
if (!ValidateQuoteResult(checkItem))
|
|
{
|
|
return checkItem;
|
|
}
|
|
return null;
|
|
}
|
|
|
|
/// <summary>
|
|
/// 已簿记持仓
|
|
/// </summary>
|
|
/// <returns></returns>
|
|
private List<CheckQuotaMoitorModel> GetPosiQuotaMoitors()
|
|
{
|
|
List<CheckQuotaMoitorModel> list = new List<CheckQuotaMoitorModel>();
|
|
var flows = DbContext.realtime_trade_risk.Where(x => x.Pv != 0).AsNoTracking().ToList();
|
|
var dealDate = QdpCalendarHelper.GetNonHolidayDefore(valuedateBLL.ValueDate.AddDays(-1));
|
|
Dictionary<string, EodPrice> eodPriceDic = new Dictionary<string, EodPrice>();
|
|
foreach (var item in flows)
|
|
{
|
|
CheckQuotaMoitorModel quotaMoitorModel = new CheckQuotaMoitorModel();
|
|
quotaMoitorModel.ClientId = item.ClientId ?? 0;
|
|
quotaMoitorModel.Pv = Math.Abs(Convert.ToDecimal(item.Pv ?? 0));
|
|
quotaMoitorModel.UnderlyingCode = item.Exposure;
|
|
list.Add(quotaMoitorModel);
|
|
}
|
|
return list;
|
|
}
|
|
/// <summary>
|
|
/// 组织试算消息
|
|
/// </summary>
|
|
/// <param name="tag">维度说明</param>
|
|
/// <param name="value">当前值</param>
|
|
/// <param name="upperNumber">上限</param>
|
|
/// <param name="lowerNumber">下限</param>
|
|
/// <param name="percent">限额是否为百分比</param>
|
|
/// <param name="warning">是否为预警</param>
|
|
/// <returns></returns>
|
|
private string SetMsg(string tag, double? value, double? upperNumber, double? lowerNumber, bool percent, bool warning)
|
|
{
|
|
var result = "";
|
|
if (value == null)
|
|
{
|
|
result = $"{tag}:试算失败,未能计算出当前持仓值;";
|
|
}
|
|
else if ((upperNumber.HasValue && value > upperNumber) || (lowerNumber.HasValue && value < lowerNumber))
|
|
{
|
|
result = $"{tag}:当前:{value.Value.ToString(percent ? "#,##0.00%" : "#,##0.00")},{(warning ? "预警" : "限额")}:{lowerNumber?.ToString(percent ? "#,##0.00%" : "#,##0.00")}~{upperNumber?.ToString(percent ? "#,##0.00%" : "#,##0.00")}";
|
|
}
|
|
return result;
|
|
}
|
|
private string SetMsg(string tag, string quoteIndex, double? value, double posiedVal, double currVal, double noposi, double? uDv, double? upperNumber, double? lowerNumber, bool percent, bool warning)
|
|
{
|
|
var result = "";
|
|
if (value == null)
|
|
{
|
|
result = $"{tag}:试算失败,未能计算出当前持仓值;";
|
|
}
|
|
else if ((upperNumber.HasValue && value > upperNumber) || (lowerNumber.HasValue && value < lowerNumber))
|
|
{
|
|
result = $"{tag}:当前:{value.Value.ToString(percent ? "#,##0.00%" : "#,##0.00")},{"预警"}:{lowerNumber?.ToString(percent ? "#,##0.00%" : "#,##0.00")}~{upperNumber?.ToString(percent ? "#,##0.00%" : "#,##0.00")}";
|
|
if (!warning)
|
|
{
|
|
result = $"{tag}:累积:{(value == 0 ? "0.00" : value?.ToString(percent ? "#,##0.00%" : "#,##0.00"))},{"限额"}:{lowerNumber?.ToString(percent ? "#,##0.00%" : "#,##0.00")}~{upperNumber?.ToString(percent ? "#,##0.00%" : "#,##0.00")}";
|
|
result += $"\r\n本交易({quoteIndex}):{(currVal == 0 ? "0.00" : currVal.ToString(percent ? "#,##0.00%" : "#,##0.00"))};";
|
|
result += $"\r\n已簿记交易({quoteIndex}):{(posiedVal == 0 ? "0.00" : posiedVal.ToString(percent ? "#,##0.00%" : "#,##0.00"))};";
|
|
result += $"\r\n未簿记交易({quoteIndex}):{(noposi == 0 ? "0.00" : noposi.ToString(percent ? "#,##0.00%" : "#,##0.00"))};";
|
|
}
|
|
}
|
|
return result;
|
|
}
|
|
/// <summary>
|
|
/// 限额检查消息
|
|
/// </summary>
|
|
/// <param name="tag">维度</param>
|
|
/// <param name="quoteIndex">限额检查项</param>
|
|
/// <param name="value">假设合成持仓后的值</param>
|
|
/// <param name="posiedVal">已簿记的值</param>
|
|
/// <param name="posiVal">持仓值+未簿记的流水值</param>
|
|
/// <param name="currVal">当前交易</param>
|
|
/// <param name="noposi">未簿记的流水</param>
|
|
/// <param name="upperNumber"></param>
|
|
/// <param name="lowerNumber"></param>
|
|
/// <param name="percent"></param>
|
|
/// <param name="warning"></param>
|
|
/// <returns></returns>
|
|
private string SetQuotaMsg(string tag, string quoteIndex, double? value, double? posiedVal, double? posiVal, double? currVal, double noposi, double? uDv, double? upperNumber, double? lowerNumber, bool percent, bool warning)
|
|
{
|
|
var result = SetMsg(tag, quoteIndex, value, posiedVal ?? 0, currVal ?? 0, noposi, uDv, upperNumber, lowerNumber, percent, warning);
|
|
var resultLast = $"{tag}:当前:{value?.ToString(percent ? "#,##0.00%" : "#,##0.00")},{"预警"}:{lowerNumber?.ToString(percent ? "#,##0.00%" : "#,##0.00")}~{upperNumber?.ToString(percent ? "#,##0.00%" : "#,##0.00")}";
|
|
if (!warning)
|
|
{
|
|
resultLast = $"{tag}:累积:{(value == 0 ? "0.00" : value?.ToString(percent ? "#,##0.00%" : "#,##0.00"))},{"限额"}:{lowerNumber?.ToString(percent ? "#,##0.00%" : "#,##0.00")}~{upperNumber?.ToString(percent ? "#,##0.00%" : "#,##0.00")}";
|
|
resultLast += $"\r\n本交易({quoteIndex}):{(currVal == 0 ? "0.00" : currVal?.ToString(percent ? "#,##0.00%" : "#,##0.00"))};";
|
|
resultLast += $"\r\n已簿记交易({quoteIndex}):{(posiedVal == 0 ? "0.00" : posiedVal?.ToString(percent ? "#,##0.00%" : "#,##0.00"))};";
|
|
resultLast += $"\r\n未簿记交易({quoteIndex}):{(noposi == 0 ? "0.00" : noposi.ToString(percent ? "#,##0.00%" : "#,##0.00"))};";
|
|
}
|
|
if (!string.IsNullOrEmpty(result) && posiVal != 0)
|
|
{
|
|
var resultPosi = SetMsg(tag, posiVal, upperNumber, lowerNumber, percent, warning);
|
|
if (string.IsNullOrEmpty(resultPosi))
|
|
{
|
|
return resultLast;
|
|
}
|
|
|
|
if (upperNumber.HasValue && value > upperNumber)
|
|
{
|
|
|
|
if (posiVal > upperNumber)
|
|
{
|
|
if (value <= posiVal)
|
|
{
|
|
return "";
|
|
}
|
|
}
|
|
if (lowerNumber.HasValue && posiVal < lowerNumber)
|
|
{
|
|
if (upperNumber - posiVal >= value - upperNumber)
|
|
{
|
|
return "";
|
|
}
|
|
}
|
|
return resultLast;
|
|
}
|
|
if (lowerNumber.HasValue && value < lowerNumber)
|
|
{
|
|
if (upperNumber.HasValue && posiVal > upperNumber)
|
|
{
|
|
if (posiVal - lowerNumber >= lowerNumber - value)
|
|
{
|
|
return "";
|
|
}
|
|
}
|
|
if (posiVal < lowerNumber)
|
|
{
|
|
if (value >= posiVal)
|
|
{
|
|
return "";
|
|
}
|
|
|
|
}
|
|
return resultLast;
|
|
}
|
|
}
|
|
|
|
return result;
|
|
}
|
|
/// <summary>
|
|
/// 限额指标校验
|
|
/// </summary>
|
|
/// <param name="clientRiskCheckItem"></param>
|
|
/// <returns></returns>
|
|
private bool ValidateQuoteResult(ClientRiskCheckItem clientRiskCheckItem)
|
|
{
|
|
var value = clientRiskCheckItem.currentValue;
|
|
if (value == null)
|
|
{
|
|
return false;
|
|
}
|
|
else if ((clientRiskCheckItem.quotaUpperLimit.HasValue && value > clientRiskCheckItem.quotaUpperLimit) || (clientRiskCheckItem.quotaLowerLimit.HasValue && value < clientRiskCheckItem.quotaLowerLimit))
|
|
{
|
|
return false;
|
|
}
|
|
else if ((clientRiskCheckItem.warningUpperLimit.HasValue && value > clientRiskCheckItem.warningUpperLimit) || (clientRiskCheckItem.warningLowerLimit.HasValue && value < clientRiskCheckItem.warningLowerLimit))
|
|
{
|
|
return false;
|
|
}
|
|
return true;
|
|
}
|
|
/// <summary>
|
|
/// 校验风控结果是否好于当前持仓结果
|
|
/// </summary>
|
|
/// <param name="clientRiskCheckItem"></param>
|
|
/// <param name="curruentVal"></param>
|
|
/// <param name="posiVal"></param>
|
|
/// <returns></returns>
|
|
private bool ValidateQuoteResult(ClientRiskCheckItem clientRiskCheckItem, double? curruentVal, double posiVal)
|
|
{
|
|
clientRiskCheckItem.currentValue = curruentVal;
|
|
if (!ValidateQuoteResult(clientRiskCheckItem))//假设合成持仓成立,校验不通过
|
|
{
|
|
if (posiVal == 0)
|
|
{
|
|
return false;
|
|
}
|
|
clientRiskCheckItem.currentValue = posiVal;
|
|
if (ValidateQuoteResult(clientRiskCheckItem))//当前持仓通过,拦截
|
|
{
|
|
clientRiskCheckItem.currentValue = curruentVal;
|
|
return false;
|
|
}
|
|
clientRiskCheckItem.currentValue = curruentVal;
|
|
var quoteResult = ValidateQuote(curruentVal ?? 0, posiVal, clientRiskCheckItem.quotaUpperLimit, clientRiskCheckItem.quotaLowerLimit);
|
|
var warningResult = ValidateQuote(curruentVal ?? 0, posiVal, clientRiskCheckItem.warningUpperLimit, clientRiskCheckItem.warningLowerLimit);
|
|
if (!quoteResult || !warningResult)
|
|
{
|
|
return false;
|
|
}
|
|
}
|
|
return true;
|
|
}
|
|
/// <summary>
|
|
/// 校验风控结果
|
|
/// </summary>
|
|
/// <param name="curruentVal"></param>
|
|
/// <param name="posiVal"></param>
|
|
/// <param name="upperLimit"></param>
|
|
/// <param name="lowerLimit"></param>
|
|
/// <returns></returns>
|
|
private bool ValidateQuote(double curruentVal, double posiVal, double? upperLimit, double? lowerLimit)
|
|
{
|
|
if (upperLimit.HasValue && curruentVal > upperLimit)//超过上限情况
|
|
{
|
|
if (posiVal > upperLimit)
|
|
{
|
|
if (curruentVal <= posiVal)
|
|
{
|
|
return true;
|
|
}
|
|
}
|
|
if (lowerLimit.HasValue && posiVal < lowerLimit)
|
|
{
|
|
if (upperLimit - posiVal >= curruentVal - upperLimit)
|
|
{
|
|
return true;
|
|
}
|
|
}
|
|
return false;
|
|
}
|
|
if (lowerLimit.HasValue && curruentVal < lowerLimit)//超过下限情况
|
|
{
|
|
if (upperLimit.HasValue && posiVal > upperLimit)
|
|
{
|
|
if (lowerLimit.HasValue && posiVal - lowerLimit >= lowerLimit - curruentVal)
|
|
{
|
|
return true;
|
|
}
|
|
}
|
|
if (posiVal < lowerLimit)
|
|
{
|
|
if (curruentVal >= posiVal)
|
|
{
|
|
return true;
|
|
}
|
|
|
|
}
|
|
return false;
|
|
}
|
|
|
|
return true;
|
|
}
|
|
/// <summary>
|
|
/// 保存试算结果
|
|
/// </summary>
|
|
/// <param name="obj">试算结果</param>
|
|
public void SaveQuotaTrial(QuotaTrial obj)
|
|
{
|
|
if (obj is null)
|
|
{
|
|
throw new ArgumentNullException(nameof(obj));
|
|
}
|
|
|
|
var dbObj = DbContext.quotaTrial.Where(O => O.id == obj.id).FirstOrDefault();
|
|
if (dbObj == null)
|
|
{
|
|
dbObj = obj;
|
|
DbContext.quotaTrial.Add(obj);
|
|
}
|
|
|
|
dbObj.FundCheckDetails ??= "";
|
|
dbObj.QuotaCheckDetails ??= "";
|
|
dbObj.QuotaWarningDetails ??= "";
|
|
dbObj.RiskWarningDetails ??= "";
|
|
dbObj.Remark = obj.Remark ?? "";
|
|
dbObj.TrialSource = obj.TrialSource;
|
|
dbObj.OptId = UserId;
|
|
dbObj.OptName = UserName;
|
|
dbObj.OptDate = DateTime.Now;
|
|
|
|
DbContext.SaveChanges();
|
|
}
|
|
|
|
|
|
}
|
|
}
|