Files
zszq-trs/YLErpDAL/Modules/RiskModule/QuotaMonitorService.cs
T

6907 lines
370 KiB
C#
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
using BaseOUDAL;
using ClosedXML.Report.Utils;
using Confluent.Kafka;
using CsvHelper;
using Dapper;
using DocumentFormat.OpenXml.Office.CustomUI;
using DocumentFormat.OpenXml.Office2010.Excel;
using DocumentFormat.OpenXml.Spreadsheet;
using Org.BouncyCastle.Asn1.Ocsp;
using Qdp.Foundation.Utilities;
using Qdp.Pricing.Ecosystem.Trade.FixedIncome;
using Qdp.Pricing.Library.Base.Utilities;
using Qdp.Pricing.Library.Common.Products.Rates;
using Snowflake.Core;
using System.Collections.Generic;
using System.Data;
using System.Linq;
using System.Runtime.Intrinsics.Arm;
using System.Text.RegularExpressions;
using YieldChain.Helpers;
using YLErp.Abstract.DataProviders;
using YLErp.BLL;
using YLErp.BLL.Calculation;
using YLErp.BLL.Eod;
using YLErp.BLL.EodSettlement;
using YLErp.BLL.Hedge;
using YLErp.Cache;
using YLErp.Commons;
using YLErp.DataBase;
using YLErp.DBModels;
using YLErp.DBModels.Consts;
using YLErp.DBModels.Enums;
using YLErp.Enums;
using YLErp.Helpers;
using YLErp.Model;
using YLErp.Model.Enum;
using YLErp.Model.HengTaiModel;
using YLErp.Models;
using YLErp.Modules.ApiModule;
using YLErp.Modules.CalculationModule;
using YLErp.Modules.ClientModule;
using YLErp.Modules.DataProviderModule;
using YLErp.Modules.EodModule;
using YLErp.Modules.EodModule.QueryModule;
using YLErp.Modules.EodModule.SettlementModule;
using YLErp.Modules.MarketRiskMoudule;
using YLErp.Modules.MarketRiskMoudule.Dto;
using YLErp.Modules.RiskExposure;
using YLErp.Modules.SwapModule;
using YLErp.Modules.TradeDalModule;
using YLErp.Modules.TradeModule;
using YLErp.Modules.TradeModule.DealModule;
using YLErp.QdpModule;
using YLErp.Modules.RiskEngine;
using static YLErp.ConsGlobal;
namespace YLErp.Modules.RiskModule
{
public class QuotaMonitorService : YLBaseService
{
private const string RiskCheckTriggerRemark = "触发风控";
IYcLogger _logger = LogFactory.GetLogger("QuotaMonitorService");
static QuotaMonitorService()
{
initSetting();
}
public QuotaMonitorService(OptUserInfo userInfo) : base(userInfo)
{
}
public QuotaMonitorService(YLBaseService baseService) : base(baseService)
{
}
public QuotaMonitorService(OptUserInfo optUser, YLContext dbContext) : base(optUser, dbContext)
{
}
/// <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;
// 计算互换DV汇总(按标的缓存bondPrice
var swapPositions = posiQuery.ToList();
var swapDV = 0d;
var swapBPCache = swapPositions.Select(p => p.UnderlyingCode).Distinct()
.ToDictionary(code => code, code => EodPriceQueryService.GetBondPrice(valuedateBLL.ValueDate, code));
foreach (var pos in swapPositions)
{
if (!swapBPCache.TryGetValue(pos.UnderlyingCode, out var bondPrice) || bondPrice == null) continue;
var vobp = bondPrice.Vobp ?? 0;
if (vobp == 0) continue;
swapDV += Convert.ToDouble(Dv01Helper.CalcDv01(pos.UnderlyingCode, pos.PosiQuantity, pos.PosiDirection, pos.PositionType, vobp));
}
var setValue = new Action<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 globalDvSetting = allSetting.Where(O => O.IsValid && O.Status == QuotaSettingApprovalStatus.Valid && O.QuotaRange == 0 && O.QuotaIndex == "DV").FirstOrDefault()?.Clone();
var tradeSetting = QueryCurrentQuotaSetting(QuotaTypeEnum.TRADE);
tradeSetting = MargeQuotaSetting(tradeSetting, 0, 0);
var tradeDvSetting = tradeSetting.Where(O => O.IsValid && O.Status == QuotaSettingApprovalStatus.Valid && O.QuotaRange == 0 && O.QuotaIndex == "DV").FirstOrDefault()?.Clone();
var swapSetting = QueryCurrentQuotaSetting(QuotaTypeEnum.GLOBAL_SWAP);
swapSetting = MargeQuotaSetting(swapSetting, 0, 0);
var swap = new QuotaMonitor_Global()
{
ParentKey = "场外",
BusinessType = "互换"
};
setValue(swap, swapSetting);
swap.DV = swapDV;
//swap.Quota_DV_Upper = tradeDvSetting?.QuotaUpperLimit ?? double.NaN;
//swap.Quota_DV_Lower = tradeDvSetting?.QuotaLowerLimit ?? double.NaN;
//swap.Quota_DV_wUpper = tradeDvSetting?.WarningUpperLimit ?? double.NaN;
//swap.Quota_DV_wLower = tradeDvSetting?.WarningLowerLimit ?? double.NaN;
var positionList = new List<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();
// 计算checkPosiList中所有持仓的DV合计,用于算未簿记DV(按标的缓存bondPrice
var totalCheckPosiDV = 0d;
if (checkPosiList.Any())
{
var ckBPCache = checkPosiList.Select(p => p.UnderlyingCode).Distinct()
.ToDictionary(code => code, code => EodPriceQueryService.GetBondPrice(valuedateBLL.ValueDate, code));
foreach (var pos in checkPosiList)
{
if (!ckBPCache.TryGetValue(pos.UnderlyingCode, out var bp) || bp == null) continue;
var vobp = bp.Vobp ?? 0;
if (vobp == 0) continue;
int positionType = pos.Side == 0 ? (int)PositionTypeFlag.Long : (int)PositionTypeFlag.Short;
totalCheckPosiDV += Convert.ToDouble(Dv01Helper.CalcDv01(pos.UnderlyingCode, pos.Qty, pos.Direction, positionType, vobp));
}
}
var posiStockEqvNotional = checkPosiList.Sum(s => s.Pv);
var delta = checkPosiList.Sum(s => s.Delta);
var unTrade = new QuotaMonitor_Global()
{
ParentKey = "场外",
BusinessType = "未簿记合约",
StockEqvNotional = Convert.ToDouble(posiStockEqvNotional) - swap.StockEqvNotional.Normalize(),
DeltaExposure=(double)delta- (swap.DeltaExposure??0),
DV = totalCheckPosiDV - (swap.DV ?? 0),
};
var all = new QuotaMonitor_Global()
{
BusinessType = "全局",
StockEqvNotional = Convert.ToDouble(posiStockEqvNotional),
PositionPnl = underly.PositionPnl + swap.PositionPnl,
DeltaExposure=(double)delta+ underly.DeltaExposure,
DV = (swap.DV ?? 0) + (underly.DV ?? 0) + (unTrade.DV ?? 0),
Quota_DV_Upper = globalDvSetting?.QuotaUpperLimit ?? double.NaN,
Quota_DV_Lower = globalDvSetting?.QuotaLowerLimit ?? double.NaN,
Quota_DV_wUpper = globalDvSetting?.WarningUpperLimit ?? double.NaN,
Quota_DV_wLower = globalDvSetting?.WarningLowerLimit ?? double.NaN,
};
var list = new List<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 maxDate = DbContext.eod_swap_position.Where(O => O.PosiQuantity > 0).Max(O => (DateTime?)O.ValueDate) ?? req.ValueDate;
var posiQuery = DbContext.eod_swap_position.Where(O =>O.ValueDate==maxDate&&O.PosiQuantity>0);
req.PvPercent = 1 - req.PvPercent;
// 互换DV汇总:直接取eod_swap_position中已计算好的dv01
var eodSwapDV = Convert.ToDouble(posiQuery.Sum(p => p.dv01));
var setValue = new Action<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 globalDvSetting = allSetting.Where(O => O.IsValid && O.Status == QuotaSettingApprovalStatus.Valid && O.QuotaRange == 0 && O.QuotaIndex == "DV").FirstOrDefault()?.Clone();
var tradeSetting = QueryCurrentQuotaSetting(QuotaTypeEnum.TRADE);
tradeSetting = MargeQuotaSetting(tradeSetting, 0, 0);
var tradeDvSetting = tradeSetting.Where(O => O.IsValid && O.Status == QuotaSettingApprovalStatus.Valid && O.QuotaRange == 0 && O.QuotaIndex == "DV").FirstOrDefault()?.Clone();
var swapSetting = QueryCurrentQuotaSetting(QuotaTypeEnum.GLOBAL_SWAP);
swapSetting = MargeQuotaSetting(swapSetting, 0, 0);
var swap = new QuotaMonitor_Global()
{
ParentKey = "场外",
BusinessType = "互换",
};
setValue(swap, swapSetting);
swap.DV = eodSwapDV;
swap.Quota_DV_Upper = tradeDvSetting?.QuotaUpperLimit ?? double.NaN;
swap.Quota_DV_Lower = tradeDvSetting?.QuotaLowerLimit ?? double.NaN;
swap.Quota_DV_wUpper = tradeDvSetting?.WarningUpperLimit ?? double.NaN;
swap.Quota_DV_wLower = tradeDvSetting?.WarningLowerLimit ?? double.NaN;
var underly = GetEodTradePositionPnl(req.ValueDate);
var unTrade = new QuotaMonitor_Global()
{
ParentKey = "场外",
BusinessType = "未簿记合约",
StockEqvNotional = 0,
DeltaExposure = 0
};
var all = new QuotaMonitor_Global()
{
BusinessType = "全局",
StockEqvNotional = swap.StockEqvNotional,
PositionPnl = underly.PositionPnl + swap.PositionPnl,
DeltaExposure = swap.DeltaExposure+ underly.DeltaExposure,
DV = (swap.DV ?? 0) + (underly.DV ?? 0) + (unTrade.DV ?? 0),
Quota_DV_Upper = globalDvSetting?.QuotaUpperLimit ?? double.NaN,
Quota_DV_Lower = globalDvSetting?.QuotaLowerLimit ?? double.NaN,
Quota_DV_wUpper = globalDvSetting?.WarningUpperLimit ?? double.NaN,
Quota_DV_wLower = globalDvSetting?.WarningLowerLimit ?? double.NaN,
};
var list = new List<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 dvSetting = temp.Where(O => O.QuotaIndex == "DV").FirstOrDefault()?.Clone();
obj.Quota_DV_Upper = dvSetting?.QuotaUpperLimit ?? double.NaN;
obj.Quota_DV_Lower = dvSetting?.QuotaLowerLimit ?? double.NaN;
obj.Quota_DV_wUpper = dvSetting?.WarningUpperLimit ?? double.NaN;
obj.Quota_DV_wLower = dvSetting?.WarningLowerLimit ?? double.NaN;
});
var setting = QueryCurrentQuotaSetting(QuotaTypeEnum.TRADE);
setting = MargeQuotaSetting(setting, 0, 0);
var ccrDict = new Dictionary<int, double>();
// 批量获取swap_position方向/多空/数量,用于DV计算
var tradeIds = list.Select(O => O.trade.id).Distinct().ToList();
var swapPositionDict = DbContext.swap_position
.Where(p => tradeIds.Contains(p.SwapTradeId) && !p.IsInitial && p.PosiDirection > 0 && !p.Invalid)
.ToDictionary(p => p.SwapTradeId);
foreach (var item in list)
{
setValue(item, setting);
var sportPrice = item.trade.SpotPrice ?? 0;
var bondPrice = EodPriceQueryService.GetBondPrice(valuedateBLL.ValueDate, item.trade.UnderlyingCode);
var basePrice = bondPrice == null ? 0 : bondPrice.ClosePrice;
var vobp = bondPrice == null ? 0 : Convert.ToDouble(bondPrice.Vobp);
var pricePercent = basePrice == 0 ? 0 : Math.Abs((sportPrice / basePrice) - 1);
item.Quota_SwapPercent = pricePercent;
// 计算DV
if (swapPositionDict.TryGetValue(item.trade.id, out var swapPos) && bondPrice != null && vobp != 0)
{
item.DV = Convert.ToDouble(Dv01Helper.CalcDv01(item.trade.UnderlyingCode, swapPos.PosiQuantity, swapPos.PosiDirection, swapPos.PositionType, Convert.ToDecimal(vobp)));
}
var obj = new QuotaMonitor_Trade();
ObjectHelper.MapValues(obj, item);
result.Add(obj);
}
var total = new QuotaMonitor_Trade()
{
TradeNumber = "合计",
StockEqvNotional = result.Sum(O => O.StockEqvNotional),
DeltaCash = result.Sum(O => O.DeltaCash),
GammaCash = result.Sum(O => O.GammaCash),
Vega = result.Sum(O => O.Vega),
VegaCash = result.Sum(O => O.VegaCash),
Quota_CCR = result.Sum(O => O.Quota_CCR),
PnL = result.Sum(O => O.PnL),
DV = result.Sum(O => O.DV),
};
result.Add(total);
return result;
}
/// <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 dvSetting = temp.Where(O => O.QuotaIndex == "DV").FirstOrDefault()?.Clone();
obj.Quota_DV_Upper = dvSetting?.QuotaUpperLimit ?? double.NaN;
obj.Quota_DV_Lower = dvSetting?.QuotaLowerLimit ?? double.NaN;
obj.Quota_DV_wUpper = dvSetting?.WarningUpperLimit ?? double.NaN;
obj.Quota_DV_wLower = dvSetting?.WarningLowerLimit ?? double.NaN;
});
var setting = QueryCurrentQuotaSetting(QuotaTypeEnum.TRADE);
setting = MargeQuotaSetting(setting, 0, 0);
var ccrDict = new Dictionary<int, double>();
// 批量获取eod_swap_position方向/多空/数量,用于DV计算和dv01回写
var tradeIds = list.Select(O => O.trade.id).Distinct().ToList();
var eodSwapPosDict = DbContext.eod_swap_position
.Where(p => p.ValueDate == req.ValueDate && tradeIds.Contains(p.SwapTradeId) && p.PosiQuantity > 0)
.ToDictionary(p => p.SwapTradeId);
var eodSwapDict = DbContext.eod_swap
.Where(p => p.ValueDate == req.ValueDate && tradeIds.Contains(p.SwapTradeId))
.ToDictionary(p => p.SwapTradeId);
foreach (var item in list)
{
setValue(item, setting);
var sportPrice = item.trade.SpotPrice ?? 0;
var bondPrice = EodPriceQueryService.GetBondPrice(valuedateBLL.ValueDate, item.trade.UnderlyingCode);
var basePrice = bondPrice == null ? 0 : bondPrice.ClosePrice;
var vobp = bondPrice == null ? 0 : Convert.ToDouble(bondPrice.Vobp);
var pricePercent = basePrice == 0 ? 0 : Math.Abs((sportPrice / basePrice) - 1);
item.Quota_SwapPercent = pricePercent;
// 计算DV,并回写dv01
if (eodSwapPosDict.TryGetValue(item.trade.id, out var eodSwapPos))
{
item.DV = Convert.ToDouble(eodSwapPos.dv01);
}
var obj = new QuotaMonitor_Trade();
ObjectHelper.MapValues(obj, item);
result.Add(obj);
}
var total = new QuotaMonitor_Trade()
{
TradeNumber = "合计",
StockEqvNotional = result.Sum(O => O.StockEqvNotional),
DeltaCash = result.Sum(O => O.DeltaCash),
GammaCash = result.Sum(O => O.GammaCash),
Vega = result.Sum(O => O.Vega),
VegaCash = result.Sum(O => O.VegaCash),
Quota_CCR = result.Sum(O => O.Quota_CCR),
PnL = result.Sum(O => O.PnL),
DV = result.Sum(O => O.DV),
};
result.Add(total);
return result;
}
/// <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:
if (O.QuotaRange == 0)
{
O.QuotaRangeStr = "全部";
}
else
{
var underlying = DataCacheProvider.GetUnderlyingDataSource().GetData(O.QuotaRange);
if (underlying == null)
{
_logger.Info($"未找到ID为 {O.QuotaRange} 的标的,quotaSetting ID: {O.id}");
O.QuotaRangeStr = "未知标的";
}
else
{
O.QuotaRangeStr = underlying.UnderlyingCode;
}
}
break;
case QuotaTypeEnum.STOCK_PERCENT:
O.QuotaRangeStr = O.QuotaRange + "天";
break;
case QuotaTypeEnum.CLIENT:
if (O.QuotaRange == 0)
{
O.QuotaRangeStr = "全部";
}
else
{
var client = DataCacheProvider.GetClientDataSource().GetData(O.QuotaRange);
if (client == null)
{
_logger.Info($"未找到ID为 {O.QuotaRange} 的客户,quotaSetting ID: {O.id}");
O.QuotaRangeStr = "未知客户";
}
else
{
O.QuotaRangeStr = client.Name;
}
}
break;
case QuotaTypeEnum.Assetunit:
if (O.QuotaRange == 0)
{
O.QuotaRangeStr = "全部";
}
else
{
var assetUnit = DataCacheProvider.GetAssetUnitDataSource().GetData(O.QuotaRange);
if (assetUnit == null)
{
_logger.Info($"未找到ID为 {O.QuotaRange} 的簿记,quotaSetting ID: {O.id}");
O.QuotaRangeStr = "未知簿记";
}
else
{
O.QuotaRangeStr = assetUnit.Name;
}
}
break;
case QuotaTypeEnum.GLOBAL_OFF_EXCHANGE:
case QuotaTypeEnum.GLOBAL_OPTION:
case QuotaTypeEnum.GLOBAL_SWAP:
case QuotaTypeEnum.GLOBAL_FORWARD:
case QuotaTypeEnum.GLOBAL_ON_EXCHANGE:
case QuotaTypeEnum.ASSET_EQUITY:
case QuotaTypeEnum.ASSET_COMMODITY:
case QuotaTypeEnum.ASSET_PRECIOUSMETALS:
case QuotaTypeEnum.VARIETY:
case QuotaTypeEnum.TRADE:
default:
break;
}
if (O.Status == QuotaSettingApprovalStatus.Pending_Approval)
{
logReq.TypeId = O.id;
var log = logService.SearchList(logReq);
remarkDict[O.id] = log.rows.FirstOrDefault()?.Remark;
}
});
temp.Sort((x, y) =>
{
var t = DateTime.Compare(y.OptDate.Value, x.OptDate.Value);
if (t == 0)
{
t = x.QuotaType - y.QuotaType;
}
if (t == 0)
{
t = string.Compare(x.QuotaIndex, y.QuotaIndex);
}
if (t == 0)
{
t = x.QuotaRange - y.QuotaRange;
}
return t;
});
var result = new SearchListResult<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 整体业务
//全局-DV
_quotaSettings.Add(new QuotaSetting()
{
QuotaType = QuotaTypeEnum.GLOBAL_ALL,
QuotaRange = 0,
QuotaIndex = "DV",
QuotaLowerLimit = null,
QuotaUpperLimit = null,
WarningLowerLimit = null,
WarningUpperLimit = null,
Percent = false,
IsValid = true,
Status = QuotaSettingApprovalStatus.Valid,
});
#endregion
#region 互换
//互换-名义本金
_quotaSettings.Add(new QuotaSetting()
{
QuotaType = QuotaTypeEnum.GLOBAL_SWAP,
QuotaRange = 0,
QuotaIndex = "名义本金",
QuotaLowerLimit = null,
QuotaUpperLimit = null,
WarningLowerLimit = null,
WarningUpperLimit = null,
Percent = false,
IsValid = true,
Status = QuotaSettingApprovalStatus.Valid,
});
////互换-浮动盈亏
//_quotaSettings.Add(new QuotaSetting()
//{
// QuotaType = QuotaTypeEnum.GLOBAL_SWAP,
// QuotaRange = 0,
// QuotaIndex = "浮动盈亏",
// QuotaLowerLimit = null,
// QuotaUpperLimit = null,
// WarningLowerLimit = null,
// WarningUpperLimit = null,
// Percent = false,
// IsValid = true,
// Status = QuotaSettingApprovalStatus.Valid,
//});
#endregion
#region 标的资产
//标的资产-名义本金
_quotaSettings.Add(new QuotaSetting()
{
QuotaType = QuotaTypeEnum.UNDERLYING,
QuotaRange = 0,
QuotaIndex = "轧差名义本金",
QuotaLowerLimit = null,
QuotaUpperLimit = null,
WarningLowerLimit = null,
WarningUpperLimit = null,
Percent = false,
IsValid = true,
Status = QuotaSettingApprovalStatus.Valid,
});
//标的资产-集中度
_quotaSettings.Add(new QuotaSetting()
{
QuotaType = QuotaTypeEnum.UNDERLYING,
QuotaRange = 0,
QuotaIndex = "轧差集中度",
QuotaLowerLimit = null,
QuotaUpperLimit = null,
WarningLowerLimit = null,
WarningUpperLimit = null,
Percent = true,
IsValid = true,
Status = QuotaSettingApprovalStatus.Valid,
});
#endregion
#region 单笔交易
////交易-名义本金
//_quotaSettings.Add(new QuotaSetting()
//{
// QuotaType = QuotaTypeEnum.TRADE,
// QuotaRange = 0,
// QuotaIndex = "名义本金",
// QuotaLowerLimit = null,
// QuotaUpperLimit = null,
// WarningLowerLimit = null,
// WarningUpperLimit = null,
// Percent = false,
// IsValid = true,
// Status = QuotaSettingApprovalStatus.Valid,
//});
//交易-互换价格偏离比例
_quotaSettings.Add(new QuotaSetting()
{
QuotaType = QuotaTypeEnum.TRADE,
QuotaRange = 0,
QuotaIndex = "互换价格偏离比例",
QuotaLowerLimit = null,
QuotaUpperLimit = null,
WarningLowerLimit = null,
WarningUpperLimit = null,
Percent = true,
IsValid = true,
Status = QuotaSettingApprovalStatus.Valid,
});
//交易-DV
_quotaSettings.Add(new QuotaSetting()
{
QuotaType = QuotaTypeEnum.TRADE,
QuotaRange = 0,
QuotaIndex = "DV",
QuotaLowerLimit = null,
QuotaUpperLimit = null,
WarningLowerLimit = null,
WarningUpperLimit = null,
Percent = false,
IsValid = true,
Status = QuotaSettingApprovalStatus.Valid,
});
#endregion
#region 客户
//客户-名义本金
_quotaSettings.Add(new QuotaSetting()
{
QuotaType = QuotaTypeEnum.CLIENT,
QuotaRange = 0,
QuotaIndex = "名义本金",
QuotaLowerLimit = null,
QuotaUpperLimit = null,
WarningLowerLimit = null,
WarningUpperLimit = null,
Percent = false,
IsValid = true,
Status = QuotaSettingApprovalStatus.Valid,
});
#endregion
if (PS.Config.Company != Configuration.CompanyEnum.天风)
{
_quotaSettings = _quotaSettings.Where(O => !(O.QuotaIndex.StartsWith("Delta敞口") || O.QuotaIndex.StartsWith("Delta净敞口"))).ToList();
}
for (var i = 0; i < _quotaSettings.Count; i++)
{
_quotaSettings[i].id = 0 - i - 1;
}
}
}
private List<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;
}
/// <summary>
/// 交易确认风控检查入口:执行完整风控试算(老风控 + 新风控),并把试算结果转换为交易确认结果。
/// 返回 true 表示确认流程可以继续;返回 false 表示需要阻断、提示或审批。
/// </summary>
public bool CheckTradeConfirmRisk(ref TradeOpenResult res)
{
return CheckTradeConfirmRisk(ref res, false);
}
/// <summary>
/// 交易确认风控检查入口:执行完整风控试算(老风控 + 新风控)。
/// ignoreRiskWarning/ignoreRiskRuleIds 只用于新风控“需审批”规则的二次确认,不影响老风控结果。
/// </summary>
public bool CheckTradeConfirmRisk(ref TradeOpenResult res, bool ignoreRiskWarning, IEnumerable<string> ignoreRiskRuleIds = null)
{
if (res == null)
{
throw new ArgumentNullException(nameof(res));
}
if (res.Trade == null)
{
throw new ArgumentNullException(nameof(res.Trade));
}
var tradeId = (res.Trade.ParentTradeId == 0 || res.Trade.IsGroup != 0) ? res.Trade.id : res.Trade.ParentTradeId;
var trialService = new QuotaMonitorService(this);
var quotaObj = trialService.RunQuotaTrial(tradeId, 1);
return ApplyQuotaCheckResult(ref res, quotaObj, ignoreRiskWarning, ignoreRiskRuleIds);
}
/// <summary>
/// 交易确认新风控检查入口:只执行新风控试算。
/// 用于老风控已被特批放行(ignoreMoneyCheck = true)的场景,避免老风控特批误跳过新风控。
/// </summary>
public bool CheckTradeConfirmNewRisk(ref TradeOpenResult res, bool ignoreRiskWarning, IEnumerable<string> ignoreRiskRuleIds = null)
{
if (res == null)
{
throw new ArgumentNullException(nameof(res));
}
if (res.Trade == null)
{
throw new ArgumentNullException(nameof(res.Trade));
}
var tradeId = (res.Trade.ParentTradeId == 0 || res.Trade.IsGroup != 0) ? res.Trade.id : res.Trade.ParentTradeId;
var trialService = new QuotaMonitorService(this);
var quotaObj = trialService.RunNewRiskTrial(tradeId, 1);
return ApplyQuotaCheckResult(ref res, quotaObj, ignoreRiskWarning, ignoreRiskRuleIds);
}
/// <summary>
/// 将风控试算结果转换为交易确认结果。
/// 这里不重新执行风控,只负责设置 RetCode、ErrorMsg、TipMsg、TrialDataId 等返回给确认流程的字段。
/// </summary>
private bool ApplyQuotaCheckResult(ref TradeOpenResult res, QuotaTrial quotaObj, bool ignoreRiskWarning, IEnumerable<string> ignoreRiskRuleIds = null)
{
if (quotaObj.TrialStatus == QuotaTrialStatusEnum.Success)//这次计算结果是通过,直接通过
{
if (!string.IsNullOrWhiteSpace(quotaObj.AvailableForClient)) // 返回用户资金情况
{
res.RetCode = TradeOpenRetCode.FundStatus;
res.TrialDataId = quotaObj.id;
return false;
}
//成功状态下,如果触发TIp检查,提示用户
if (!string.IsNullOrWhiteSpace(quotaObj.RiskWarningDetails))
{
res.TrialDataId = quotaObj.id;
res.TipMsg = quotaObj.RiskWarningDetails;
}
return true;
}
//否则的情况是上次没算,这次是预警,或上次算了,结果是不通过\通过或预警,这次是预警或不通过,提示用户;
res.RetCode = TradeOpenRetCode.QuotaTrialError;
res.TrialDataId = quotaObj.id;
//汇总各类检查详情,避免只展示RiskWarningDetails而漏掉其它老风控检查
var detailParts = new List<string>();
if (!string.IsNullOrWhiteSpace(quotaObj.FundCheckDetails))
detailParts.Add($"应付资金检查:{quotaObj.FundCheckDetails}");
if (!string.IsNullOrWhiteSpace(quotaObj.QuotaCheckDetails))
detailParts.Add($"限额检查:{quotaObj.QuotaCheckDetails}");
if (!string.IsNullOrWhiteSpace(quotaObj.RiskWarningDetails))
detailParts.Add($"风险预警:{quotaObj.RiskWarningDetails}");
if (!string.IsNullOrWhiteSpace(quotaObj.QuotaWarningDetails))
detailParts.Add($"限额预警:{quotaObj.QuotaWarningDetails}");
res.ErrorMsg = string.Join("\n", detailParts);
//确认本次为需审批后,二次特批。因为只靠点击“交易特批”的ignoreRiskWarning,不能保证本次校验通过。
var isRiskApprovalWarning = quotaObj.TrialStatus == QuotaTrialStatusEnum.RiskWarning
&& !string.IsNullOrWhiteSpace(quotaObj.RiskWarningDetails)
&& (quotaObj.ApprovalRuleIds?.Any() ?? false);
//老风控试算不通过时,延用master逻辑进入交易特批/二次处理;新风控需审批由RiskWarning单独处理
res.OldRiskNeedSpecialApproval = quotaObj.OldRiskTrialStatus.HasValue
&& quotaObj.OldRiskTrialStatus.Value != QuotaTrialStatusEnum.Success;
if (isRiskApprovalWarning)
{
var currentApprovalRuleIds = quotaObj.ApprovalRuleIds ?? new List<string>();
res.IgnoredRiskRuleIds = currentApprovalRuleIds;
//需清除ErrorMsg,不然外部调用会认为失败
//触发需审批,交易特批逻辑
if (ignoreRiskWarning)
{
var ignoredRuleIdSet = new HashSet<string>((ignoreRiskRuleIds ?? Enumerable.Empty<string>()).Where(o => !string.IsNullOrWhiteSpace(o)));
var newApprovalRuleIds = currentApprovalRuleIds.Where(o => !ignoredRuleIdSet.Contains(o)).ToList();
if (!newApprovalRuleIds.Any())
{
res.ErrorMsg = string.Empty;
return true;
}
}
}
res.RetCode = isRiskApprovalWarning ? TradeOpenRetCode.RiskWarning : TradeOpenRetCode.QuotaTrialError;
var isOldRiskErrorSpecialApproval = !isRiskApprovalWarning
&& quotaObj.OldRiskTrialStatus == QuotaTrialStatusEnum.Error
&& res.OldRiskNeedSpecialApproval;
AddRiskCheckLog(quotaObj, isOldRiskErrorSpecialApproval);
return false;
}
/// <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);
// 仅在限额设置中有DV指标时才计算DV
//var (totalDv, currentDv) = precheckQuotaSettingList.Any(s => s.QuotaIndex == "DV")
// ? CalcRiskCheckDv(checkPoisiList, dealDate) : (0d, 0d);
var underlyingPositions = checkPoisiList.Where(x => x.UnderlyingCode == clientRiskCheckReq.securityId).ToList();
var sameQty = underlyingPositions.Where(x=>x.ClientId== clientRiskCheckReq.clientId && x.Side== clientRiskCheckReq.side).Sum(s => s.Qty);
var qty = underlyingPositions.Where(x => x.ClientId == clientRiskCheckReq.clientId && x.Side != clientRiskCheckReq.side).Sum(s => s.Qty);
if (sameQty > qty)
{
//开仓需要校验适当性评估
if (client.EvaluateExpireDate < dealDate)
{
throw new ServiceException("适当性评估已经过期,只有在适当性有效期内才可以新开仓");
}
}
else
{
return clientRiskCheckResps; //平仓不过风控
}
if (precheckQuotaSettingList.Count == 0)
{
return clientRiskCheckResps;
}
foreach (var settingItem in precheckQuotaSettingList)
{
var clientRiskCheckItem = CreateClientRiskCheckItem(settingItem);
if (clientRiskCheckItem == null) continue;
if (!settingItem.QuotaLowerLimit.HasValue && !settingItem.QuotaUpperLimit.HasValue && !settingItem.WarningUpperLimit.HasValue && !settingItem.WarningLowerLimit.HasValue)
{
continue;
}
clientRiskCheckItem = CheckQuota(checkPoisiList, settingItem.QuotaType, clientRiskCheckItem, settingItem.QuotaRange, allList, 0d, 0d);
if (clientRiskCheckItem != null)
{
clientRiskCheckItem.quotaType = $"{EnumHelper.GetDescriptionByName(settingItem.QuotaType)}({settingItem.QuotaIndex})";
clientRiskCheckResps.Add(clientRiskCheckItem);
}
}
return clientRiskCheckResps;
}
/// <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();
}
/// <summary>
/// 计算风控检查用的DV(全局DV + 当前单笔DV)
/// </summary>
private (double totalDv, double currentDv) CalcRiskCheckDv(List<CheckQuotaMoitorModel> checkPoisiList, DateTime dealDate)
{
var totalDv = 0d;
var currentDv = 0d;
// 按标的缓存bondPrice
var bpCache = checkPoisiList.Select(p => p.UnderlyingCode).Distinct()
.ToDictionary(code => code, code => EodPriceQueryService.GetBondPrice(dealDate, code));
// 全部持仓DV
totalDv = checkPoisiList.Sum(pos =>
{
if (!bpCache.TryGetValue(pos.UnderlyingCode, out var bp) || bp == null) return 0d;
var vobp = bp.Vobp ?? 0;
if (vobp == 0) return 0d;
int posType = pos.Side == 0 ? (int)PositionTypeFlag.Long : (int)PositionTypeFlag.Short;
return Convert.ToDouble(Dv01Helper.CalcDv01(pos.UnderlyingCode, pos.Qty, pos.Direction, posType, vobp));
});
// 当前单笔DV
var cur = checkPoisiList.FirstOrDefault(x => x.Current);
if (cur != null && bpCache.TryGetValue(cur.UnderlyingCode, out var curBp) && curBp != null)
{
var vobp = curBp.Vobp ?? 0;
if (vobp != 0)
{
int pt = cur.Side == 0 ? (int)PositionTypeFlag.Long : (int)PositionTypeFlag.Short;
currentDv = Convert.ToDouble(Dv01Helper.CalcDv01(cur.UnderlyingCode, cur.Qty, cur.Direction, pt, vobp));
}
}
// 标的交易DV
List<string> tradeTypes = new List<string> { "利率债", "信用债", "其它债券" };
var tposis = DbContext.TradePosition.Where(x => tradeTypes.Contains(x.TradeType)).AsNoTracking().ToList();
foreach (var code in tposis.Select(x => x.UnderlyingCode).Distinct().Where(c => !bpCache.ContainsKey(c)))
bpCache[code] = EodPriceQueryService.GetBondPrice(dealDate, code);
foreach (var item in tposis)
{
if (!bpCache.TryGetValue(item.UnderlyingCode, out var tpBp) || tpBp == null) continue;
var vobp = tpBp.Vobp ?? 0;
if (vobp == 0) continue;
totalDv += Convert.ToDouble(Dv01Helper.CalcDv01(item.UnderlyingCode, Convert.ToDecimal(Math.Abs(item.Position)), (int)SwapDirectionEnum.收取, (int)item.PositionType, vobp));
}
return (totalDv, currentDv);
}
private (List<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;
}
private DateTime? QueryLatestRiskCheckTriggerTime(int tradeId)
{
var tradeNumber = DbContext.trade.Where(x => x.id == tradeId).Select(x => x.TradeNumber).FirstOrDefault();
if (string.IsNullOrWhiteSpace(tradeNumber))
{
return null;
}
return DbContext.trade_risk_check_log
.Where(x => x.trade_number == tradeNumber && x.remark.StartsWith(RiskCheckTriggerRemark))
.OrderByDescending(x => x.create_time)
.Select(x => (DateTime?)x.create_time)
.FirstOrDefault();
}
private void AddRiskCheckLog(QuotaTrial quotaTrial, bool isOldRiskErrorSpecialApproval = false)
{
var td = DbContext.trade.FirstOrDefault(x => x.id == quotaTrial.TradeId);
var log = new trade_risk_check_log();
if (td != null)
{
var client = DataCacheProvider.GetClientDataSource().GetData(td.ClientId);
log.client_number = client?.Number;
}
var worker = new IdWorker(1, 1);
log.id = worker.NextId();
log.trial_result = (int)quotaTrial.TrialStatus;
log.client_name = quotaTrial.ClientName;
log.trader = td?.TraderName;
log.trade_number = quotaTrial.TradeNumber;
var riskWarning = string.Empty;
if (!string.IsNullOrWhiteSpace(quotaTrial.QuotaWarningDetails))
{
riskWarning = "限额预警: " + quotaTrial.QuotaWarningDetails;
}
if (!string.IsNullOrWhiteSpace(quotaTrial.RiskWarningDetails))
{
if (!string.IsNullOrWhiteSpace(riskWarning))
{
riskWarning += Environment.NewLine;
}
riskWarning += "风险预警: " + quotaTrial.RiskWarningDetails;
}
log.risk_warning = riskWarning;
log.limit_warning = quotaTrial.QuotaCheckDetails;
log.remark = RiskCheckTriggerRemark;
if (isOldRiskErrorSpecialApproval)
{
log.remark += ";老风控Error允许交易特批";
}
log.create_user = UserId;
log.create_time = DateTime.Now;
DbContext.trade_risk_check_log.Add(log);
DbContext.SaveChanges();
}
/// <summary>
/// 按 quotaTrial.id 校验最近一次触发风控的记录是否已超过配置时效
/// </summary>
/// <param name="trialDataId">quotaTrial.id</param>
/// <param name="expireSeconds">有效时长(秒)</param>
/// <returns>查不到记录或已超时返回 true,否则返回 false</returns>
public bool IsQuotaTrialExpired(int trialDataId, int expireSeconds)
{
var quotaTrial = QueryQuotaTrial(trialDataId);
if (quotaTrial == null)
{
return true;
}
var latestRiskCheckTriggerTime = QueryLatestRiskCheckTriggerTime(quotaTrial.TradeId);
var riskCheckTime = latestRiskCheckTriggerTime ?? quotaTrial.OptDate;
if (!riskCheckTime.HasValue)
{
return true;
}
return DateTime.Now - riskCheckTime.Value > TimeSpan.FromSeconds(expireSeconds);
}
/// <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>
/// 完整风控试算:先执行老风控资金/限额试算,再叠加新风控试算结果。
/// 后续老风控下线时,调用方可以保留该入口,内部改为直接调用 RunNewRiskTrial。
/// </summary>
/// <param name="tradeId">交易Id,结构化交易请传主交易Id</param>
/// <param name="trialSource">试算来源,0:手动点击试算按钮;1:确认成交时自动调用;</param>
/// <returns></returns>
public QuotaTrial RunQuotaTrial(int tradeId, int trialSource)
{
_logger.Info($"[限额试算] 开始执行 - tradeId: {tradeId}, trialSource: {trialSource}");
var result = new QuotaTrial();
trade tradeObj = DbContext.trade.AsNoTracking().Where(O => O.id == tradeId).FirstOrDefault();
// 检查交易对象是否存在
if (tradeObj == null)
{
_logger.Info($"[限额试算] 找不到交易记录 - tradeId: {tradeId}");
throw new ArgumentNullException(nameof(tradeObj), $"找不到ID为 {tradeId} 的交易记录");
}
_logger.Info($"[限额试算] 交易信息 - tradeId: {tradeObj.id}, TradeNumber: {tradeObj.TradeNumber}, TradeType: {tradeObj.TradeType}, ClientId: {tradeObj.ClientId}");
//// 检查关键字段是否为null
if (string.IsNullOrEmpty(tradeObj.TradeType))
{
_logger.Info($"[限额试算] TradeType 为 null 或空 - tradeId: {tradeId}");
throw new ArgumentNullException(nameof(tradeObj.TradeType), "交易类型不能为空");
}
var tradeList = new List<trade>();
if (tradeObj.TradeType == "结构化交易")
{
tradeList.AddRange(DbContext.trade.AsNoTracking().Where(O => O.ParentTradeId == tradeId).ToArray());
}
else
{
tradeList.Add(tradeObj);
}
// 检查关键字段
if (!tradeObj.TradeDate.HasValue)
{
_logger.Info($"[限额试算] TradeDate 为 null - tradeId: {tradeId}");
throw new ArgumentNullException(nameof(tradeObj.TradeDate), "交易日期不能为空");
}
var fundStatus = CheckFund(tradeList, tradeObj.StockEqvNotional, tradeObj.ClientId, out var fundMsg, out var availableMsg);
//var riskWarningStatus = CheckRiskWarning(tradeList, out var riskWarningMsg);
string riskWarningMsg = string.Empty;
var riskWarningStatus = true;
tradeList = tradeList.Where(O => !string.IsNullOrEmpty(O.UnderlyingCode) && O.TradeType != "结构化交易" && O.TradeType != "现金流交易").ToList();
if (tradeList.Count == 0)
{
// 老风控没有可试算的标的时,老逻辑认为试算通过;这里仍需继续执行新风控,避免普通确认漏掉新风控检查。
//("当前交易不需要试算");
result.TrialStatus = QuotaTrialStatusEnum.Success;
result.TradeId = tradeObj.id;
result.TradeNumber = tradeObj.TradeNumber ?? "";
result.ClientName = tradeObj.ClientName ?? "";
result.FundCheckDetails = fundMsg ?? "";
result.RiskWarningDetails = riskWarningMsg ?? "";
result.AvailableForClient = availableMsg ?? "";
result.TrialSource = trialSource;
result.OldRiskTrialStatus = result.TrialStatus;
result = ApplyNewRiskTrial(tradeId, result);
SaveQuotaTrial(result);
return result;
}
string quotaWarningMsg = "", quotaMsg = "";
bool quotaWarningStatus = true, quotaStatus = true;
var positions = DbContext.swap_position.Where(x => x.SwapTradeId == tradeId && x.PosiQuantity > 0 && x.IsInitial && !x.Invalid).ToList();
_logger.Info($"[限额试算] 检查持仓 - positions.Count: {positions?.Count ?? 0}");
quotaStatus = CheckUnderlyingWhiteList(tradeObj.ClientId, positions, out quotaMsg);
if (quotaStatus)
{
var allList = QueryPrecheckQuotaSetting(false);
var list = allList.Where(O => O.Precheck).ToList();
if (list.Count > 0)
{
var clientPrecheckQuotaSettingList = list.Where(x => x.QuotaRange == tradeObj.ClientId && x.QuotaType == QuotaTypeEnum.CLIENT && x.QuotaIndex == "名义本金").ToList();
var allClientPrecheckQuotaSettingList = list.Where(x => x.QuotaRange == 0 && x.QuotaType == QuotaTypeEnum.CLIENT && x.QuotaIndex == "名义本金").ToList();
if (clientPrecheckQuotaSettingList.Count > 0)
{
foreach (var item in allClientPrecheckQuotaSettingList)
{
list.Remove(item);
}
}
List<ClientRiskCheckItem> clientRiskCheckResps = new List<ClientRiskCheckItem>();
var floatPosi = DbContext.swap_position.Where(x => x.SwapTradeId == tradeId && x.PosiQuantity > 0 && x.IsInitial && !x.Invalid).FirstOrDefault();
_logger.Info($"[限额试算] floatPosi: {(floatPosi == null ? "null" : $"id={floatPosi.id}, UnderlyingCode={floatPosi.UnderlyingCode}")}");
if (floatPosi != null)
{
using var bondDb = new BondOmsDBContext();
var clientPositions = bondDb.client_position.AsNoTracking().ToList();//所有持仓
ClientPosition posi = new ClientPosition();
posi.commission = floatPosi.PosiTradingFee;
posi.side = floatPosi.PositionType == 1 ? 0 : 1;
posi.security_id = floatPosi.UnderlyingCode;
posi.full_price_now = floatPosi.PosiNetPrice * ConsGlobal.bondShowPriceMultiple;
posi.deal_full_price_avg = floatPosi.PosiNetPrice * ConsGlobal.bondShowPriceMultiple;
posi.client_id = tradeObj.ClientId;
posi.position_qty = floatPosi.PosiQuantity / 10000;
posi.position_notional_principal = floatPosi.PosiNotionalValue;
posi.direction = floatPosi.PosiDirection;
clientPositions.Add(posi);
var umCodes = clientPositions.Select(s => s.security_id).Distinct().ToList();
umCodes.Add(floatPosi.UnderlyingCode);
umCodes = umCodes.Distinct().ToList();
_logger.Info($"[限额试算] umCodes.Count: {umCodes?.Count ?? 0}");
var ums = DataCacheProvider.GetUnderlyingDataSource().AsQueryable().Where(x => umCodes.Contains(x.UnderlyingCode));
_logger.Info($"[限额试算] ums.Count: {ums?.Count() ?? 0}");
List<CheckQuotaMoitorModel> checkPoisiList = new List<CheckQuotaMoitorModel>();
var posiList = GetPosiQuotaMoitors();
_logger.Info($"[限额试算] posiList.Count: {posiList?.Count ?? 0}");
foreach (var item in clientPositions)
{
var dealDate = tradeObj.TradeDate.Value;
decimal vobp = 0;
double lastPrice = 0;
var um = ums.FirstOrDefault(x => x.UnderlyingCode == item.security_id);
_logger.Info($"[限额试算] 处理持仓 - security_id: {item.security_id}, um: {(um == null ? "null" : $"id={um.id}, UnderlyingCode={um.UnderlyingCode}")}");
if (um == null)
{
_logger.Info($"[限额试算] 找不到标的 - security_id: {item.security_id}");
continue;
}
CheckQuotaMoitorModel checkQuotaMoitorModel = new CheckQuotaMoitorModel();
checkQuotaMoitorModel.Qty = (item.position_qty ?? 0) * 10000;
checkQuotaMoitorModel.id = item.id;
checkQuotaMoitorModel.Price = (item.deal_full_price_avg ?? 0);
checkQuotaMoitorModel.NowPrice = (item.full_price_now ?? 0);
checkQuotaMoitorModel.ContractSize = um == null ? 1 : Convert.ToDecimal(um.ContractSize);
checkQuotaMoitorModel.Pv = item.position_notional_principal ?? 0;
checkQuotaMoitorModel.UnderlyingCode = item.security_id;
checkQuotaMoitorModel.Side = item.side;
checkQuotaMoitorModel.Commision = item.commission ?? 0;
checkQuotaMoitorModel.Direction = item.direction;
checkQuotaMoitorModel.ClientId = Convert.ToInt32(item.client_id ?? 0);
checkQuotaMoitorModel.Delta = (checkQuotaMoitorModel.Side == 0 ? checkQuotaMoitorModel.Price : -checkQuotaMoitorModel.Price) * checkQuotaMoitorModel.Qty;
if (um != null)
{
checkQuotaMoitorModel.UnderlyingId = um.id;
if (!um.IsBond())
{
checkQuotaMoitorModel.Pv = checkQuotaMoitorModel.Pv * checkQuotaMoitorModel.Price;
lastPrice = DataCacheProvider.GetUnderlyingDataSource().GetPrice(item.security_id);
}
else
{
checkQuotaMoitorModel.Price *= ConsGlobal.bondPriceMultiple;
checkQuotaMoitorModel.NowPrice *= ConsGlobal.bondPriceMultiple;
var bondPrice = EodPriceQueryService.GetBondPrice(dealDate, item.security_id);
lastPrice = bondPrice != null ? bondPrice.ClosePrice : (um.Price ?? 0) * Convert.ToDouble(ConsGlobal.bondPriceMultiple);
vobp = bondPrice != null ? bondPrice.Vobp ?? 0 : 0;
// 检查 ExJson 是否为 null 或空
if (string.IsNullOrEmpty(um.ExJson))
{
_logger.Info($"[限额试算] um.ExJson 为 null 或空 - um.id: {um.id}, UnderlyingCode: {um.UnderlyingCode}");
checkQuotaMoitorModel.Circulation = 0;
}
else
{
try
{
var bond = JsonHelper.Deserialize<UnderlyingBond>(um.ExJson);
if (bond == null)
{
_logger.Info($"[限额试算] 反序列化 bond 失败 - um.id: {um.id}, ExJson: {um.ExJson.Substring(0, Math.Min(100, um.ExJson.Length))}");
checkQuotaMoitorModel.Circulation = 0;
}
else
{
checkQuotaMoitorModel.Circulation = (bond.IssueSize * 100000000m) ?? 0;
}
}
catch (Exception ex)
{
_logger.Error($"[限额试算] 错误: 反序列化 bond 异常 - um.id: {um.id}", ex);
checkQuotaMoitorModel.Circulation = 0;
}
}
}
}
var ratio = checkQuotaMoitorModel.Side == 0 ? 1 : -1;
var directionRatio = checkQuotaMoitorModel.Direction == (int)SwapDirectionEnum.收取 ? 1 : -1;
vobp = vobp * ratio * directionRatio;
checkQuotaMoitorModel.LastPrice = Convert.ToDecimal(lastPrice);
checkQuotaMoitorModel.Pnl = (checkQuotaMoitorModel.NowPrice - checkQuotaMoitorModel.Price) * checkQuotaMoitorModel.Qty;
checkQuotaMoitorModel.Current = checkQuotaMoitorModel.id == 0;
checkPoisiList.Add(checkQuotaMoitorModel);
}
// 预计算DV,避免warning/error两次检查各算一遍
var (quotaTotalDv, quotaCurrentDv) = list.Any(s => s.QuotaIndex == "DV") ? CalcRiskCheckDv(checkPoisiList, tradeObj.TradeDate.Value) : (0d, 0d);
quotaWarningStatus = CheckQuota(checkPoisiList, posiList, list, allList, true, out quotaWarningMsg, quotaTotalDv, quotaCurrentDv);
quotaStatus = CheckQuota(checkPoisiList, posiList, list, allList, false, out quotaMsg, quotaTotalDv, quotaCurrentDv);
}
}
}
if (quotaWarningStatus && riskWarningStatus && fundStatus && quotaStatus)
{
result.TrialStatus = QuotaTrialStatusEnum.Success;
}
else
{
if (fundStatus && quotaStatus && (!quotaWarningStatus || !riskWarningStatus))
{
result.TrialStatus = QuotaTrialStatusEnum.Warning;
}
else
{
result.TrialStatus = QuotaTrialStatusEnum.Error;
}
}
result.TradeId = tradeObj.id;
result.TradeNumber = tradeObj.TradeNumber ?? "";
result.ClientName = tradeObj.ClientName ?? "";
result.FundCheckDetails = fundMsg ?? "";
result.QuotaCheckDetails = quotaMsg ?? "";
result.RiskWarningDetails = riskWarningMsg ?? "";
result.QuotaWarningDetails = quotaWarningMsg ?? "";
result.AvailableForClient = availableMsg ?? "";
result.TrialSource = trialSource;
result.OldRiskTrialStatus = result.TrialStatus;
// 保留老风控原始状态,随后叠加新风控结果;这样二次特批时可以区分是老风控失败还是新风控需审批。
result = ApplyNewRiskTrial(tradeId, result);
SaveQuotaTrial(result);
return result;
}
/// <summary>
/// 新风控独立试算:不执行老风控资金/限额检查,只调用新风控引擎。
/// 用于老风控特批后的确认流程,也可作为后续老风控下线后的主试算入口。
/// </summary>
public QuotaTrial RunNewRiskTrial(int tradeId, int trialSource)
{
var result = new QuotaTrial
{
TradeId = tradeId,
TrialSource = trialSource,
TrialStatus = QuotaTrialStatusEnum.Success,
OldRiskTrialStatus = QuotaTrialStatusEnum.Success
};
var tradeObj = DbContext.trade.AsNoTracking().Where(O => O.id == tradeId).FirstOrDefault();
if (tradeObj != null)
{
result.TradeNumber = tradeObj.TradeNumber ?? "";
result.ClientName = tradeObj.ClientName ?? "";
}
result = ApplyNewRiskTrial(tradeId, result);
SaveQuotaTrial(result);
return result;
}
/// <summary>
/// 将新风控引擎结果合并到 QuotaTrial。
/// 如果传入的是完整试算结果,则是在老风控结果基础上叠加新风控;如果传入的是新建结果,则表示只跑新风控。
/// </summary>
private QuotaTrial ApplyNewRiskTrial(int tradeId, QuotaTrial result)
{
// ===== 新风控引擎接入点(设计文档 4.6.1) =====
try
{
var riskEngine = new RiskEngineService(this);
var riskContext = new RiskContext
{
TradeId = tradeId,
TriggerPoint = "BOOK_CONFIRM"
};
var riskResult = riskEngine.EvaluateRisk(riskContext, "BOOK_CONFIRM");
_logger.Info($"[风控引擎] 簿记交易确认 - TradeId: {tradeId}, Passed: {riskResult.Passed}, Blocked: {riskResult.Blocked}, NeedApproval: {riskResult.NeedApproval}, ShowTip: {riskResult.ShowTip}");
// 按策略映射回 QuotaTrialStatusEnum
if (riskResult.Blocked)
{
result.TrialStatus = QuotaTrialStatusEnum.Error;
result.RiskWarningDetails += "[风控引擎] 规则触发:禁止\n";
foreach (var triggeredRule in riskResult.TriggeredRules.Where(r => r.ControlStrategy == RiskControlStrategy.Block))
{
result.RiskWarningDetails += $"规则ID:{triggeredRule.RuleId};规则名称:{triggeredRule.RuleName};规则说明:{triggeredRule.RuleText}";
if (!string.IsNullOrWhiteSpace(triggeredRule.Message))
{
result.RiskWarningDetails += $";信息:{triggeredRule.Message}";
}
result.RiskWarningDetails += "\n";
}
}
if (riskResult.NeedApproval)
{
if (result.TrialStatus != QuotaTrialStatusEnum.Error && result.TrialStatus != QuotaTrialStatusEnum.Warning)
{
result.TrialStatus = QuotaTrialStatusEnum.RiskWarning;
}
result.RiskWarningDetails += "[风控引擎] 规则触发:需审批\n";
var approvalTriggeredRules = riskResult.TriggeredRules.Where(r => r.ControlStrategy == RiskControlStrategy.Approval).ToList();
var approvalTriggeredRuleIds = approvalTriggeredRules.Select(r => r.RuleId)
.Where(r => !string.IsNullOrWhiteSpace(r))
.ToList();
var showTipTriggeredRules = riskResult.TriggeredRules.Where(r => r.ControlStrategy == RiskControlStrategy.ShowTip).ToList();
var showTipTriggeredRuleIds = showTipTriggeredRules.Select(r => r.RuleId)
.Where(r => !string.IsNullOrWhiteSpace(r))
.ToList();
// 二次确认时,需审批规则允许按首次命中结果忽略;同时已展示过的提示规则也不再重复展示
var processedRiskRuleIds = approvalTriggeredRuleIds.Concat(showTipTriggeredRuleIds)
.Distinct()
.ToList();
result.ApprovalRuleIds = processedRiskRuleIds;
foreach (var triggeredRule in approvalTriggeredRules)
{
result.RiskWarningDetails += $"规则ID:{triggeredRule.RuleId};规则名称:{triggeredRule.RuleName};规则说明:{triggeredRule.RuleText}\n";
}
}
if (riskResult.ShowTip)
{
var warningMessages = riskResult.TriggeredRules
.Where(r => r.ControlStrategy == RiskControlStrategy.ShowTip)
.Select(r => r.Message)
.Where(r => !string.IsNullOrWhiteSpace(r))
.ToList();
if (warningMessages.Count > 0)
{
result.RiskWarningDetails += "[风控引擎] 提示:" + string.Join("", warningMessages) + "\n";
}
}
}
catch (Exception ex)
{
_logger.Error($"[风控引擎] 执行异常 - TradeId: {tradeId}, Error: {ex.Message}");
// 第一版:异常不阻断原有流程,只记录日志
}
// ===== 新风控引擎接入结束 =====
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;
}
// 检查 positions 中是否有 null 元素或 UnderlyingCode 为 null
for (int i = 0; i < positions.Count; i++)
{
var item = positions[i];
if (item == null)
{
_logger.Info($"[CheckUnderlyingWhiteList] positions[{i}] 为 null");
quotaMsg = "持仓数据异常:存在空记录";
return false;
}
if (string.IsNullOrEmpty(item.UnderlyingCode))
{
_logger.Info($"[CheckUnderlyingWhiteList] positions[{i}].UnderlyingCode 为 null 或空 - id: {item.id}, SwapTradeId: {item.SwapTradeId}");
quotaMsg = "持仓数据异常:标的代码为空";
return false;
}
}
var baseUrl = Environment.GetEnvironmentVariable("BondOmsInterface_BaseUrl");
var checkUrl = "/clientOrderAdmin/checkOrderBonds";
var clientOrderParams = new List<CheckUnderlyingRequest>();
foreach (var item in positions)
{
CheckUnderlyingRequest clientOrderParam = new CheckUnderlyingRequest() { clientId = clientId, securityId = item.UnderlyingCode, orderQty = item.PosiQuantity * 0.0001m, side = item.PositionType - 1 };
_logger.Info($"[CheckUnderlyingWhiteList] 构建请求参数 - clientId: {clientId}, securityId: {item.UnderlyingCode}, orderQty: {item.PosiQuantity * 0.0001m}, side: {item.PositionType - 1}");
clientOrderParams.Add(clientOrderParam);
}
if (!string.IsNullOrEmpty(baseUrl) && !string.IsNullOrEmpty(checkUrl))
{
try
{
_logger.Info($"[CheckUnderlyingWhiteList] 开始调用外部接口 - baseUrl: {baseUrl}, checkUrl: {checkUrl}, clientOrderParams.Count: {clientOrderParams?.Count ?? 0}");
var httpHelper = new HttpHelper(baseUrl, null);
// http 请求 Web项目接口
var result = httpHelper.PostRequestNoAuth<List<CheckUnderlyingRequest>, ApiResponse>(checkUrl, clientOrderParams).Result;
_logger.Info($"[CheckUnderlyingWhiteList] 接口调用完成 - result: {(result == null ? "null" : $"success={result.success}")}");
if (result != null && !result.success)
{
quotaMsg = result.message;
return false;
}
}
catch (Exception ex)
{
_logger.Error($"[CheckUnderlyingWhiteList] 调用外部接口异常 - baseUrl: {baseUrl}, checkUrl: {checkUrl}", ex);
quotaMsg = "调用外部接口失败:" + ex.Message;
return false;
}
}
else
{
_logger.Info($"[CheckUnderlyingWhiteList] 跳过外部接口调用 - baseUrl: {(string.IsNullOrEmpty(baseUrl) ? "" : baseUrl)}, checkUrl: {(string.IsNullOrEmpty(checkUrl) ? "" : checkUrl)}");
}
return true;
}
private bool CheckRiskWarning(List<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)
{
_logger.Info($"[CheckFund] 开始资金检查 - clientId: {clientId}, stockEqvNotional: {stockEqvNotional}, trades.Count: {trades?.Count ?? 0}");
msg = "";
avmsg = "";
var totalCredit = 0.0;
var availableStockEqvNotional = 0.0;
if (trades == null || trades.Count == 0)
{
_logger.Info("[CheckFund] trades 为 null 或空列表");
msg = "交易列表为空";
return false;
}
var client = DataCacheProvider.GetClientDataSource().GetData(clientId);
if (client == null)
{
msg = "获取客户信息失败!";
return false;
}
if (client.SwapTradeType == 0)// 非dma不做资金校验
{
msg = "";
return true;
}
var clientBalances = RealtimePnlCalc.GetRealTimeClientBanlance(new List<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;
_logger.Info($"[CheckFund] 资金计算 - totalCredit: {totalCredit}, positionStockEqvNotional: {positionStockEqvNotional}, UseClientStockEqvNotional: {PS.Config.ErpElement.UseClientStockEqvNotional}");
if (PS.Config.ErpElement.UseClientStockEqvNotional)
{
availableStockEqvNotional = totalCredit - positionStockEqvNotional;
_logger.Info($"[CheckFund] 可用名义本金 - availableStockEqvNotional: {availableStockEqvNotional}");
}
else
{
totalCredit = double.MaxValue;
availableStockEqvNotional = totalCredit - positionStockEqvNotional;
}
foreach (var t in trades)
{
if (t.Warning)
{
new TradeDalService(OptUserInfo.SystemUser).RollbackToBeforeSettle(t, valuedateBLL.ValueDate);
}
}
var msgList = new List<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)
{
// 检查 ExerciseDate 是否为 null
if (!trades[0].ExerciseDate.HasValue)
{
_logger.Info($"[CheckFund] trades[0].ExerciseDate 为 null - tradeId: {trades[0].id}");
msg = "交易行权日期不能为空";
return false;
}
if (trades[0].ExerciseDate.Value.Date >= valuedateBLL.ValueDate.Date)
{
var margins = margin + tradePrice;
var AvailableAmount = Math.Max(clientBalance.AvailableAmount + clientBalance.TotalCredit, 0);
if (AvailableAmount < margins)
{
if (PS.Config.Is申万)
{
msgList.Add($"当前账户可用资金{AvailableAmount.ToString("#,##0.000")}元,不足以支付预付金{margin.ToString("#,##0.000")}元");
}
else
{
msgList.Add($"当前交易应付预付金:{margin.ToString("#,##0.000")},应付成交金额:{tradePrice.ToString("#,##0.000")},总应付资金:{margins.ToString("#,##0.000")}");
msgList.Add($"当前剩余资金:{clientBalance.AmountFund.ToString("#,##0.000")},冻结资金:{clientBalance.AllFreezeBalance().ToString("#,##0.000")},抵押品价值:{clientBalance.GuaranteesTotalAmount.ToString("#,##0.000")},授信额度:{clientBalance.TotalCredit.ToString("#,##0.000")},可用总额度:{AvailableAmount.ToString("#,##0.000")}。不足以支付上述金额。");
}
}
if ((availableStockEqvNotional - stockEqvNotional) < 0)
{
msgList.Add($"当前名义本金可用规模:{availableStockEqvNotional.ToString("#,##0.000")}。不足抵充该交易名义本金:{stockEqvNotional.ToString("#,##0.000")}");
}
var amountFund = AvailableAmount - margin;
avmsg = $"当前可用总额度:{AvailableAmount.ToString("#,##0.000")},计入当前交易后,可用总额度为:{amountFund.ToString("#,##0.000")}";
}
}
else
{
var AvailableAmount = clientBalance.AvailablePremium();
if (AvailableAmount < tradePrice)
{
if (PS.Config.Is申万)
{
msgList.Add($"当前账户可用资金{AvailableAmount.ToString("#,##0.000")}元,不足以支付期权费{tradePrice.ToString("#,##0.000")}元");
}
else
{
msgList.Add($"当前交易应付预付金:{0.ToString("#,##0.000")},应付成交金额:{tradePrice.ToString("#,##0.000")},总应付资金:{tradePrice.ToString("#,##0.000")}");
msgList.Add($"当前剩余资金:{clientBalance.AmountFund.ToString("#,##0.000")},冻结资金:{clientBalance.AllFreezeBalance().ToString("#,##0.000")},抵押品价值:{clientBalance.GuaranteesTotalAmount.ToString("#,##0.000")},授信额度:{clientBalance.TotalCredit.ToString("#,##0.000")},可用总额度:{AvailableAmount.ToString("#,##0.000")}。不足以支付上述金额。");
}
}
if ((availableStockEqvNotional - stockEqvNotional) < 0)
{
msgList.Add($"当前名义本金可用规模:{availableStockEqvNotional.ToString("#,##0.000")}。不足抵充该交易名义本金:{stockEqvNotional.ToString("#,##0.000")}");
}
var amountFund = AvailableAmount - tradePrice;
avmsg = $"当前可用总额度:{AvailableAmount.ToString("#,##0.000")},计入当前交易后,可用总额度为:{amountFund.ToString("#,##0.000")}";
}
}
else if (trades[0].TradeType == "收益互换")
{
var clientBalance = clientBalances[0];
using var db = new YLContext();
var id = trades[0].id;
var tradePrice = (trades[0].TradePrice ?? 0) * (-TradeCalcHelper.GetSign(trades[0].BuySell));
var margin = trades[0].InitialMargin ?? 0;
var AvailableAmount = clientBalance.AvailablePremium();
if (AvailableAmount < tradePrice && tradePrice > 0)
{
if (PS.Config.Is申万)
{
msgList.Add($"当前账户可用资金{AvailableAmount.ToString("#,##0.000")}元,不足以支付交易费用{tradePrice.ToString("#,##0.000")}元");
}
else
{
msgList.Add($"当前交易应付预付金:{0.ToString("#,##0.000")},应付成交金额:{tradePrice.ToString("#,##0.000")},总应付资金:{tradePrice.ToString("#,##0.000")}");
msgList.Add($"当前剩余资金:{clientBalance.AmountFund.ToString("#,##0.000")},冻结资金:{clientBalance.AllFreezeBalance().ToString("#,##0.000")},抵押品价值:{clientBalance.GuaranteesTotalAmount.ToString("#,##0.000")},授信额度:{clientBalance.TotalCredit.ToString("#,##0.000")},可用总额度:{AvailableAmount.ToString("#,##0.000")}。不足以支付上述金额。");
}
var amountFund = AvailableAmount - tradePrice;
avmsg = $"当前可用总额度:{AvailableAmount.ToString("#,##0.000")},计入当前交易后,可用总额度为:{amountFund.ToString("#,##0.000")}";
}
else
{
var margins = margin + tradePrice;
if (trades[0].ExerciseDate.Value.Date >= valuedateBLL.ValueDate.Date)
{
AvailableAmount = Math.Max(clientBalance.AvailableAmount + clientBalance.TotalCredit, 0);
if (AvailableAmount < margins)
{
if (PS.Config.Is申万)
{
msgList.Add($"当前账户可用资金{AvailableAmount.ToString("#,##0.000")}元,不足以支付预付金{margin.ToString("#,##0.000")}元和交易费用{tradePrice.ToString("#,##0.000")}元的加和");
}
else
{
msgList.Add($"当前交易应付预付金:{margin.ToString("#,##0.000")},应付成交金额:{tradePrice.ToString("#,##0.000")},总应付资金:{margins.ToString("#,##0.000")}");
msgList.Add($"当前剩余资金:{clientBalance.AmountFund.ToString("#,##0.000")},冻结资金:{clientBalance.AllFreezeBalance().ToString("#,##0.000")},抵押品价值:{clientBalance.GuaranteesTotalAmount.ToString("#,##0.000")},授信额度:{clientBalance.TotalCredit.ToString("#,##0.000")},可用总额度:{AvailableAmount.ToString("#,##0.000")}。不足以支付上述金额。");
}
}
var amountFund = AvailableAmount - tradePrice - margin;
avmsg = $"当前可用总额度:{AvailableAmount.ToString("#,##0.000")},计入当前交易后,可用总额度为:{amountFund.ToString("#,##0.000")}";
}
}
if ((availableStockEqvNotional - trades[0].StockEqvNotional) < 0)
{
msgList.Add($"当前名义本金可用规模:{availableStockEqvNotional.ToString("#,##0.000")}。不足抵充该交易名义本金:{trades[0].StockEqvNotional.ToString("#,##0.000")}");
}
}
else if (trades[0].TradeType == "远期")
{
using var db = new YLContext();
var margin = trades[0].InitialMargin ?? 0;
var tradePrice = trades[0].TradePrice ?? 0;
var margins = margin + tradePrice;
var id = trades[0].id;
var forwardTrade = db.trade_forward.Where(x => x.TradeId == id).FirstOrDefault();
if (forwardTrade != null)
{
//远期验资,验证客户余额>= 开仓总费用
var clientBalance = clientBalances[0];
var AvailableAmount = clientBalance.AvailablePremium();
if (AvailableAmount < margins)
{
msgList.Add($"当前交易应付预付金:{margin.ToString("#,##0.000")},应付成交金额:{tradePrice.ToString("#,##0.000")},总应付资金:{margins.ToString("#,##0.000")}");
msgList.Add($"当前剩余资金:{clientBalance.AmountFund.ToString("#,##0.000")},冻结资金:{clientBalance.AllFreezeBalance().ToString("#,##0.000")},抵押品价值:{clientBalance.GuaranteesTotalAmount.ToString("#,##0.000")},授信额度:{clientBalance.TotalCredit.ToString("#,##0.000")},可用总额度:{AvailableAmount.ToString("#,##0.000")}。不足以支付上述金额。");
}
if ((availableStockEqvNotional - trades[0].StockEqvNotional) < 0)
{
msgList.Add($"当前名义本金可用规模:{availableStockEqvNotional:#,##0.000}。不足抵充该交易名义本金:{trades[0].StockEqvNotional:#,##0.000}");
msgList.Add($"当前剩余资金:{clientBalance.AmountFund.ToString("#,##0.000")},冻结资金:{clientBalance.AllFreezeBalance().ToString("#,##0.000")},抵押品价值:{clientBalance.GuaranteesTotalAmount.ToString("#,##0.000")},授信额度:{clientBalance.TotalCredit.ToString("#,##0.000")},可用总额度:{AvailableAmount.ToString("#,##0.000")}。不足以支付上述金额。");
}
var amountFund = AvailableAmount - margins;
avmsg = $"当前可用总额度:{AvailableAmount:#,##0.000},计入当前交易后,可用总额度为:{amountFund.ToString("#,##0.000")}";
}
else
{
msgList.Add("客户远期交易数据未找到!");
}
}
msg = string.Join("\r\n", msgList);
if (!PS.Config.IsAvailableForClient)
{
avmsg = "";
}
return msgList.Count == 0;
}
/// <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, double totalDv = 0, double currentDv = 0)
{
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_ALL:
msgList.AddRange(checkGlobalDv(positionList, item.Value, warning, totalDv));
break;
case QuotaTypeEnum.GLOBAL_SWAP:
msgList.AddRange(checkGlobal(positionList, posiList, "场外业务-互换", item.Value, warning));
break;
case QuotaTypeEnum.TRADE:
msgList.AddRange(checkTrade(positionList, posiList, "单笔交易", item.Value, warning, currentDv));
break;
case QuotaTypeEnum.UNDERLYING:
msgList.AddRange(checkUnderlying(positionList, posiList, "标的资产", item.Value, settingAll, warning));
break;
case QuotaTypeEnum.CLIENT:
CheckQuotaMoitorModel current = positionList.FirstOrDefault(x => x.Current);
var clientRange = item.Value.Where(O => O.QuotaRange == current.ClientId || O.QuotaRange == 0).ToArray();
if (!clientRange.Any())
{
continue;
}
else
{
foreach (var quota in clientRange)
{
tag_prefix = quota.QuotaRange == 0 ? "客户合计" : "当前客户";
msgList.AddRange(checkClient(positionList, posiList, tag_prefix, new QuotaSetting[] { quota }, settingAll, warning));
}
}
break;
default:
continue;
}
}
for (var i = 0; i < msgList.Count; i++)
{
if (string.IsNullOrWhiteSpace(msgList[i]))
{
msgList.RemoveAt(i);
i = --i;
continue;
}
msgList[i] = $"{i + 1}.{msgList[i]}";
}
msg = string.Join("\r\n", msgList);
return msgList.Count == 0;
}
/// <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, double totalDv = 0, double currentDv = 0)
{
var tag_prefix = "";
var positionListAll = positionList;
switch (quotaType)
{
//case QuotaTypeEnum.GLOBAL_ALL:
// if (checkItem.quotaType == "DV")
// {
// checkItem.currentValue = Math.Round(totalDv, 2);
// if (!ValidateQuoteResult(checkItem)) return checkItem;
// return null;
// }
// break;
case QuotaTypeEnum.GLOBAL_SWAP:
checkItem = checkGlobal(positionList, "场外业务-互换", checkItem, quotaType);
break;
case QuotaTypeEnum.TRADE:
checkItem = checkTrade(positionList, "单笔交易", checkItem, currentDv);
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,
DV=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);
// 计算标的交易DV
var vobp = bondPrice != null ? bondPrice.Vobp ?? 0 : 0;
if (vobp != 0)
{
quotaMonitor_Global.DV += Convert.ToDouble(Dv01Helper.CalcDv01(item.UnderlyingCode,Math.Abs(Convert.ToDecimal(item.Position)), (int)SwapDirectionEnum.收取, (int)item.PositionType, vobp));
}
}
}
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,
DV = 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);
// 计算标的交易DV: Amount有符号(正=多,负=空)
var vobp = bondPrice != null ? bondPrice.Vobp ?? 0 : 0;
if (vobp != 0)
{
int positionType = item.Amount > 0 ? (int)PositionTypeFlag.Long : (int)PositionTypeFlag.Short;
quotaMonitor_Global.DV += Convert.ToDouble(Dv01Helper.CalcDv01(item.UnderlyingCode, Convert.ToDecimal(Math.Abs(item.Amount)), (int)SwapDirectionEnum.收取, positionType, vobp));
}
}
}
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;
}
/// <summary>
/// 检验全局DV限额
/// </summary>
private List<string> checkGlobalDv(List<CheckQuotaMoitorModel> positionList, QuotaSetting[] settings, bool warning, double totalDv = -1)
{
var messageList = new List<string>();
var dvSettings = settings.Where(s => s.QuotaIndex == "DV").ToArray();
if (!dvSettings.Any()) return messageList;
// totalDv未预计算时(其他调用方),才在这里算
if (totalDv < 0)
totalDv = CalcRiskCheckDv(positionList, valuedateBLL.ValueDate).totalDv;
foreach (var settingItem in dvSettings)
{
double? upperLimit = warning ? settingItem.WarningUpperLimit : settingItem.QuotaUpperLimit;
double? lowerLimit = warning ? settingItem.WarningLowerLimit : settingItem.QuotaLowerLimit;
if (upperLimit == null && lowerLimit == null) continue;
var tag = $"全局(DV)";
messageList.Add(SetMsg(tag, totalDv, upperLimit, lowerLimit, settingItem.Percent, warning));
}
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, double currentDv = -1)
{
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);
// currentDv未预计算时(其他调用方),才在这里算
if (currentDv < 0 && settings.Any(s => s.QuotaIndex == "DV"))
currentDv = CalcRiskCheckDv(positionList, valuedateBLL.ValueDate).currentDv;
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;
case "DV":
currentValue = currentDv;
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, double currentDv = 0)
{
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;
//case "DV":
// currentValue = currentDv;
// break;
default:
return null;
}
checkItem.currentValue = Math.Round(currentValue ?? 0, 2);
if (!ValidateQuoteResult(checkItem))
{
return checkItem;
}
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();
}
}
}