从山证v2.3.0拷贝
This commit is contained in:
+58
@@ -0,0 +1,58 @@
|
||||
using YLErp.Modules.EodModule;
|
||||
|
||||
namespace YLErp.Modules.TradeRiskCalcModule
|
||||
{
|
||||
/// <summary>
|
||||
/// 初始累计盈亏数据源
|
||||
/// </summary>
|
||||
class AccruedTotalPnlStartingDataSource<TEntity>
|
||||
where TEntity : EodTradePosition
|
||||
{
|
||||
IEnumerable<TradePnlStatics> _list;
|
||||
|
||||
private AccruedTotalPnlStartingDataSource()
|
||||
{
|
||||
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 取值日
|
||||
/// </summary>
|
||||
public DateTime ValueDate { get; private set; }
|
||||
|
||||
/// <summary>
|
||||
/// 起算日
|
||||
/// </summary>
|
||||
public DateTime StartDate { get; private set; }
|
||||
|
||||
public void Reset()
|
||||
{
|
||||
_list = null;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
///
|
||||
/// </summary>
|
||||
/// <param name="valueDate">取值日</param>
|
||||
/// <param name="startDate">起算日</param>
|
||||
public IEnumerable<TradePnlStatics> GetDatas(DateTime valueDate, DateTime startDate)
|
||||
{
|
||||
valueDate = valueDate.Date;
|
||||
startDate = startDate.Date;
|
||||
if (_list != null && ValueDate == valueDate && StartDate == startDate)
|
||||
{
|
||||
return _list;
|
||||
}
|
||||
ValueDate = valueDate;
|
||||
StartDate = startDate;
|
||||
return _list = new AccruedTotalPnlService<TEntity>(OptUserInfo.SystemUser).GetAccruedStartPnls(startDate, true);
|
||||
}
|
||||
|
||||
public static readonly AccruedTotalPnlStartingDataSource<TEntity> Default;
|
||||
|
||||
static AccruedTotalPnlStartingDataSource()
|
||||
{
|
||||
Default = new AccruedTotalPnlStartingDataSource<TEntity>();
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,245 @@
|
||||
using YLErp.Models;
|
||||
|
||||
namespace YLErp.Modules.TradeRiskCalcModule
|
||||
{
|
||||
public class TradingRiskReqModelBase
|
||||
{
|
||||
/// <summary>
|
||||
/// 波动率类型
|
||||
/// </summary>
|
||||
public string VolType { get; set; }
|
||||
/// <summary>
|
||||
/// 分红率类型 交易、0 --国元特有,默认为交易
|
||||
/// </summary>
|
||||
public string DividendRateType { get; set; }
|
||||
/// <summary>
|
||||
/// 簿记账户过滤列表
|
||||
/// </summary>
|
||||
public List<int> BookIds { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// 用户绑定的簿记账户
|
||||
/// </summary>
|
||||
public List<int> UserBookIds { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// 簿记账户组过滤列表
|
||||
/// </summary>
|
||||
public IEnumerable<int> AssetIdGroupList { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// 客户过滤列表
|
||||
/// </summary>
|
||||
public List<int> ClientIds { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// 结构类型过滤列表
|
||||
/// </summary>
|
||||
public IEnumerable<string> AssetTypes { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// 只显示当日有持仓的交易
|
||||
/// </summary>
|
||||
public bool OnlyPosition { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// 反选项
|
||||
/// </summary>
|
||||
public string[] InvertSelects { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// 是否反选项
|
||||
/// </summary>
|
||||
public bool IsInvertSelect(string input)
|
||||
{
|
||||
return InvertSelects != null && InvertSelects.Contains(input);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 标准化模型数据
|
||||
/// </summary>
|
||||
public virtual void Normalize()
|
||||
{
|
||||
if (string.IsNullOrWhiteSpace(VolType))
|
||||
{
|
||||
VolType = "对冲";
|
||||
}
|
||||
|
||||
if (BookIds != null && BookIds.All(n => n == 0))
|
||||
{
|
||||
BookIds = null;
|
||||
}
|
||||
|
||||
if (AssetIdGroupList != null && AssetIdGroupList.All(n => n == 0))
|
||||
{
|
||||
AssetIdGroupList = null;
|
||||
}
|
||||
|
||||
if (ClientIds != null && ClientIds.All(n => n == 0))
|
||||
{
|
||||
ClientIds = null;
|
||||
}
|
||||
|
||||
if (AssetTypes != null && AssetTypes.Any(n => !string.IsNullOrEmpty(n)))
|
||||
{
|
||||
AssetTypes = AssetTypes.Where(n => !string.IsNullOrEmpty(n)).ToArray();
|
||||
}
|
||||
else
|
||||
{
|
||||
AssetTypes = null;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 实时风险查询模型
|
||||
/// </summary>
|
||||
public class TradingRiskReqModel : TradingRiskReqModelBase
|
||||
{
|
||||
private string subListCode;
|
||||
|
||||
/// <summary>
|
||||
/// 品种过滤列表(和标的过滤列表取并集)
|
||||
/// </summary>
|
||||
public IEnumerable<int> VarietyIds { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// 品种分组过滤列表(和品种过滤列表取并集)
|
||||
/// </summary>
|
||||
public IEnumerable<string> VarietyGroups { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// 标的过滤列表(和品种过滤列表取并集)
|
||||
/// </summary>
|
||||
public IEnumerable<int> UnderlyingIds { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// 标的代码过滤(优先于VarietyIds和UnderlyingIds)
|
||||
/// 在定价试算时会从前端页面传入
|
||||
/// </summary>
|
||||
public IEnumerable<string> UnderlyingCodes { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// 品种代码或标的代码
|
||||
/// </summary>
|
||||
public string SubListCode
|
||||
{
|
||||
get => subListCode;
|
||||
set
|
||||
{
|
||||
subListCode = value?.TrimEnd('\t');
|
||||
IsMainList = string.IsNullOrEmpty(value);
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 是否主页面列表请求
|
||||
/// </summary>
|
||||
public bool IsMainList { get; private set; } = true;
|
||||
|
||||
public DateTime? ValueTime { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// 是否权益类
|
||||
/// </summary>
|
||||
public bool Stock { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// 是否包含权益类
|
||||
/// </summary>
|
||||
public bool IncludeStock { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// 支持Delta风险敞口
|
||||
/// </summary>
|
||||
public bool CalcDeltaRisk { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// 场内期权现价使用行情价(详情页使用,非光子并且SubListCode有值的情况下)
|
||||
/// </summary>
|
||||
public bool UseMarketForExOptions { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// 请求来源(非系统内部调用暂定为'a1')
|
||||
/// </summary>
|
||||
public string RequestFrom { get; set; }
|
||||
|
||||
//----以下用于优化性能----
|
||||
|
||||
/// <summary>
|
||||
/// 强制获取最新结果集
|
||||
/// 前端页面需根据查询条件是否变化来判断请求输入值
|
||||
/// </summary>
|
||||
public bool ForceUpdate { get; set; } = true;
|
||||
|
||||
/// <summary>
|
||||
/// 计算开始时间,ForceUpdate为false时适用
|
||||
/// 由页面传入已加载数据的计算时间,用于判断是否和当前结果集相同
|
||||
/// </summary>
|
||||
public DateTime CalcStartTime { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// 标准化模型数据
|
||||
/// </summary>
|
||||
public override void Normalize()
|
||||
{
|
||||
base.Normalize();
|
||||
|
||||
if (VarietyIds != null && VarietyIds.All(n => n < 1))
|
||||
{
|
||||
VarietyIds = null;
|
||||
}
|
||||
|
||||
if (UnderlyingIds != null && UnderlyingIds.All(n => n < 1))
|
||||
{
|
||||
UnderlyingIds = null;
|
||||
}
|
||||
|
||||
if (VarietyGroups != null && VarietyGroups.Any(n => !string.IsNullOrEmpty(n)))
|
||||
{
|
||||
VarietyGroups = VarietyGroups.Where(n => !string.IsNullOrEmpty(n)).ToArray();
|
||||
}
|
||||
else
|
||||
{
|
||||
VarietyGroups = null;
|
||||
}
|
||||
|
||||
if (UnderlyingCodes != null && UnderlyingCodes.Any(n => !string.IsNullOrEmpty(n)))
|
||||
{
|
||||
UnderlyingCodes = UnderlyingCodes.Where(n => !string.IsNullOrEmpty(n)).ToArray();
|
||||
}
|
||||
else
|
||||
{
|
||||
UnderlyingCodes = null;
|
||||
}
|
||||
|
||||
if (!string.IsNullOrWhiteSpace(SubListCode))
|
||||
{
|
||||
VarietyIds = null;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 价格试算查询模型
|
||||
/// </summary>
|
||||
public class TradingRiskPriceCalcReqModel : TradingRiskReqModelBase
|
||||
{
|
||||
/// <summary>
|
||||
/// [必需]标的价格
|
||||
/// </summary>
|
||||
public IEnumerable<TradingRiskPriceModel> Prices { get; set; }
|
||||
}
|
||||
|
||||
public class TradingRiskPriceModel
|
||||
{
|
||||
public string Code { get; set; }
|
||||
|
||||
public double Price { get; set; }
|
||||
|
||||
public PriceModel ToPriceModel()
|
||||
{
|
||||
return new PriceModel { InstrumentCode = Code, Price = Price };
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,865 @@
|
||||
using System.Data;
|
||||
using System.Linq.Expressions;
|
||||
using YLErp.BLL;
|
||||
using YLErp.Configuration;
|
||||
using YLErp.DBModels.Enums;
|
||||
using YLErp.Models;
|
||||
using YLErp.Modules.DataProviderModule;
|
||||
using YLErp.QdpModule;
|
||||
|
||||
namespace YLErp.Modules.TradeRiskCalcModule
|
||||
{
|
||||
/// <summary>
|
||||
///
|
||||
/// </summary>
|
||||
public class TradingRiskReqService : YLBaseService
|
||||
{
|
||||
public TradingRiskReqService(OptUserInfo userInfo) : base(userInfo)
|
||||
{
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 获取实时风险计算结果
|
||||
/// </summary>
|
||||
/// <param name="req">筛选条件</param>
|
||||
public TradingRiskResult GetResult(TradingRiskReqModel req)
|
||||
{
|
||||
req ??= new TradingRiskReqModel();
|
||||
|
||||
if (string.IsNullOrWhiteSpace(req.VolType))
|
||||
{
|
||||
req.VolType = "对冲";
|
||||
}
|
||||
if (PS.Config.Company == CompanyEnum.国元固收 && req.DividendRateType == "采集")
|
||||
{
|
||||
req.VolType = "交易曲面分红率0";
|
||||
}
|
||||
var calcResult = TradeRiskCalcTaskRunner.GetCalcResult(req.VolType);
|
||||
|
||||
if (calcResult == null)
|
||||
{
|
||||
return null;
|
||||
}
|
||||
|
||||
if (!req.ForceUpdate && req.CalcStartTime.Year > 2000 && (calcResult.CalcStartTime - req.CalcStartTime).TotalSeconds < 1)
|
||||
{
|
||||
return new TradingRiskResult
|
||||
{
|
||||
Message = "#same",
|
||||
CalcStartTime = calcResult.CalcStartTime,
|
||||
CalcEndTime = calcResult.CalcEndTime
|
||||
};
|
||||
}
|
||||
|
||||
//风险对冲累积总盈亏是否统计已过期的场内交易
|
||||
TradePnlStaticsDataProvider.Default.IncludeExchangeTrades = PS.Config.ErpElement.RiskAccPnl_SumExpiredExchangeTrades;
|
||||
|
||||
var result = GroupingResults(calcResult, req);
|
||||
|
||||
//场内期权使用市场行情价格
|
||||
if (!PS.Config.Is光大光子 && req.UseMarketForExOptions)
|
||||
{
|
||||
SetExOptionMarketValue(result.TradeRiskList);
|
||||
}
|
||||
|
||||
if (req.IsMainList)
|
||||
{
|
||||
foreach (var item in result.TradeRiskList)
|
||||
{
|
||||
if (item.viewList == null)
|
||||
{
|
||||
continue;
|
||||
}
|
||||
|
||||
if (req.CalcDeltaRisk)
|
||||
{
|
||||
item.Notionals = new double[2];
|
||||
|
||||
foreach (var n in item.viewList)
|
||||
{
|
||||
if (!ConsTrade.TradeTypesForHedge.Contains(n.TradeType))
|
||||
{
|
||||
item.Notionals[0] += n.Notional;
|
||||
item.Notionals[1] += n.BuySell == "买入" ? n.Notional : -n.Notional;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#region 计算gamm定制
|
||||
if (PS.Config.Company == CompanyEnum.国贸启润)
|
||||
{
|
||||
var gammaCustom = 0D;
|
||||
foreach (var n in item.viewList)
|
||||
{
|
||||
|
||||
var vol = 0D;
|
||||
switch (req.VolType)
|
||||
{
|
||||
case "对冲":
|
||||
vol = n.TradeSavedVol ?? 0;
|
||||
break;
|
||||
case "持仓":
|
||||
default:
|
||||
vol = n.Vol;
|
||||
break;
|
||||
}
|
||||
gammaCustom += (-0.5) * n.Gamma * Math.Pow((n.SpotPrice ?? 0), 2) * Math.Pow(vol, 2) / 252;
|
||||
}
|
||||
item.GammaCustom = gammaCustom;
|
||||
}
|
||||
#endregion
|
||||
|
||||
if (string.IsNullOrEmpty(req.RequestFrom) || !req.RequestFrom.StartsWith("a"))
|
||||
{
|
||||
item.viewList = null;
|
||||
}
|
||||
|
||||
if (PS.Config.Company == Configuration.CompanyEnum.润和)
|
||||
{
|
||||
item.DeltaCash2 = item.DeltaCash;
|
||||
}
|
||||
|
||||
if (PS.Config.Company == CompanyEnum.红塔众鑫)
|
||||
{
|
||||
item.ThetaNet = Math.Abs(item.ThetaNet);
|
||||
}
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
//为特定公司处理明细列表数据
|
||||
ProcessSubListForCompany(result.TradeRiskList);
|
||||
}
|
||||
|
||||
if (req.ValueTime != null)
|
||||
{
|
||||
new DongZhengDeltaT1Service().ReCalcDeltaT1(result, req.ValueTime.Value);
|
||||
}
|
||||
|
||||
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 敲出相关值处理
|
||||
/// </summary>
|
||||
/// <param name="result"></param>
|
||||
private static void KnockOutHandler(TradingRiskResult result)
|
||||
{
|
||||
if (result != null && result.TradeRiskList != null && result.TradeRiskList.Any())
|
||||
{
|
||||
foreach (var risk in result.TradeRiskList)
|
||||
{
|
||||
if (risk.viewList != null && risk.viewList.Count > 0)
|
||||
{
|
||||
risk.viewList.ForEach(p =>
|
||||
{
|
||||
CalcContainsKnockOutValue(p);
|
||||
});
|
||||
risk.PvContainsKnockOut = risk.viewList.Sum(p => p.PvContainsKnockOut);
|
||||
risk.GammaInLotsContainsKnockOut = risk.viewList.Sum(p => p.GammaInLotsContainsKnockOut);
|
||||
risk.DeltaInLotsContainsKnockOut = risk.viewList.Sum(p => p.DeltaInLotsContainsKnockOut);
|
||||
}
|
||||
else
|
||||
{
|
||||
CalcContainsKnockOutValue(risk);
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 敲出交易 判断
|
||||
/// </summary>
|
||||
/// <param name="risk"></param>
|
||||
private static void CalcContainsKnockOutValue(TradingRiskParameter risk)
|
||||
{
|
||||
if (risk.IsKnockOut)
|
||||
{
|
||||
risk.PvContainsKnockOut = risk.KnockOutPayoff;
|
||||
risk.GammaInLotsContainsKnockOut = 0;
|
||||
risk.DeltaInLotsContainsKnockOut = 0;
|
||||
}
|
||||
else
|
||||
{
|
||||
risk.PvContainsKnockOut = risk.Pv;
|
||||
risk.GammaInLotsContainsKnockOut = risk.GammaInLots;
|
||||
risk.DeltaInLotsContainsKnockOut = risk.DeltaInLots;
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 获取定价计算实时风险计算结果
|
||||
/// </summary>
|
||||
public TradingRiskResult GetPriceCalcResult(TradingRiskPriceCalcReqModel req)
|
||||
{
|
||||
if (req?.Prices == null)
|
||||
{
|
||||
throw new ArgumentNullException(nameof(req));
|
||||
}
|
||||
|
||||
if (string.IsNullOrWhiteSpace(req.VolType))
|
||||
{
|
||||
req.VolType = "对冲";
|
||||
}
|
||||
|
||||
var result = TradeRiskCalcTaskRunner.ExecuteFixedPriceCalc(req.VolType, req.Prices.Select(n => n.ToPriceModel()));
|
||||
|
||||
if (result == null)
|
||||
{
|
||||
return null;
|
||||
}
|
||||
var filterReq = new TradingRiskReqModel
|
||||
{
|
||||
VolType = req.VolType,
|
||||
BookIds = req.BookIds,
|
||||
ClientIds = req.ClientIds,
|
||||
AssetTypes = req.AssetTypes,
|
||||
IncludeStock = true,
|
||||
UnderlyingCodes = req.Prices.Select(n => n.Code),
|
||||
OnlyPosition = req.OnlyPosition
|
||||
};
|
||||
|
||||
result = GroupingResults(result, filterReq);
|
||||
|
||||
if (req.OnlyPosition)
|
||||
{
|
||||
foreach (var rl in result.TradeRiskList)
|
||||
{
|
||||
if (rl.viewList != null)
|
||||
{
|
||||
rl.viewList = rl.viewList.Where(n => n.HasPosition).ToList();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 从明细结果数据中汇总到标的级别
|
||||
/// </summary>
|
||||
public static TradingRiskResult GroupingResults(TradingRiskResult calcResult, TradingRiskReqModel req)
|
||||
{
|
||||
if (calcResult == null)
|
||||
{
|
||||
return null;
|
||||
}
|
||||
KnockOutHandler(calcResult);
|
||||
req ??= new TradingRiskReqModel();
|
||||
|
||||
if (string.IsNullOrWhiteSpace(req.VolType))
|
||||
{
|
||||
req.VolType = "对冲";
|
||||
}
|
||||
|
||||
var result = calcResult.Clone();
|
||||
|
||||
result.TradeRiskList = InnerGroupingResults(calcResult, req);
|
||||
|
||||
foreach (var item in result.TradeRiskList)
|
||||
{
|
||||
//加\t可以避免品种和标的的代码相同时出现的数据呈现问题
|
||||
item.VarietyCode = item.VarietyCode?.Trim() + "\t";
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
#region----静态处理----
|
||||
|
||||
/// <summary>
|
||||
/// 设置场内期权行情价值
|
||||
/// </summary>
|
||||
private static void SetExOptionMarketValue(IEnumerable<TradingRiskParameter> tradeRiskList)
|
||||
{
|
||||
foreach (var titem in tradeRiskList)
|
||||
{
|
||||
if (titem.viewList == null || !titem.viewList.Any(n => n.TradeType == "场内期权"))
|
||||
{
|
||||
continue;
|
||||
}
|
||||
var tpvGap = titem.Pv;
|
||||
titem.HedgePv = titem.Pv = titem.Tv = 0;
|
||||
|
||||
foreach (var vitem in titem.viewList)
|
||||
{
|
||||
if (ConsTrade.TradeTypesForHedge.Contains(vitem.TradeType))
|
||||
{
|
||||
if (vitem.TradeType == "场内期权")
|
||||
{
|
||||
var vpvGap = vitem.Pv;
|
||||
vitem.Pv = vitem.ExOptionPrice.HasValue ? vitem.Notional * vitem.ExOptionPrice.Value : 0;
|
||||
var payoff = (vitem.SpotPrice ?? 0) - (vitem.Strike ?? 0);
|
||||
var outOfMoney = vitem.CallPut == "Call" ? payoff < 0 : payoff > 0;
|
||||
if (outOfMoney)
|
||||
{
|
||||
payoff = 0;
|
||||
}
|
||||
vitem.Tv = vitem.Pv + (payoff * vitem.Notional);
|
||||
vpvGap -= vitem.Pv;
|
||||
vitem.PnlWithHedge -= vpvGap;
|
||||
vitem.AccruedTotalPnl -= vpvGap;
|
||||
vitem.DailyPnl -= vpvGap;
|
||||
vitem.TotalPnl -= vpvGap;
|
||||
vitem.TotalPnlWithHedge -= vpvGap;
|
||||
vitem.ExercisePnl -= vpvGap;
|
||||
}
|
||||
titem.HedgePv += vitem.HedgePv = vitem.Pv;
|
||||
}
|
||||
|
||||
titem.Pv += vitem.Pv;
|
||||
titem.Tv += vitem.Tv;
|
||||
}
|
||||
tpvGap -= titem.Pv;
|
||||
titem.PnlWithHedge -= tpvGap;
|
||||
titem.AccruedTotalPnl -= tpvGap;
|
||||
titem.DailyPnl -= tpvGap;
|
||||
titem.TotalPnl -= tpvGap;
|
||||
titem.TotalPnlWithHedge -= tpvGap;
|
||||
titem.ExercisePnl -= tpvGap;
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 为特定公司处理明细列表数据
|
||||
/// </summary>
|
||||
private static void ProcessSubListForCompany(IEnumerable<TradingRiskParameter> tradeRiskList)
|
||||
{
|
||||
if (PS.Config.Company == Configuration.CompanyEnum.光大光子 || PS.Config.Company == Configuration.CompanyEnum.广期资本)
|
||||
{
|
||||
//光子:场外期权的成交份额根据交易方向增加正负号
|
||||
foreach (var item in tradeRiskList)
|
||||
{
|
||||
if (item.viewList == null)
|
||||
{
|
||||
continue;
|
||||
}
|
||||
|
||||
foreach (var n in item.viewList)
|
||||
{
|
||||
if (n.BuySell == "卖出" && !ConsTrade.TradeTypesForHedge.Contains(n.TradeType))
|
||||
{
|
||||
n.OriginalNotionalV = n.OriginalNotional = -Math.Abs(n.OriginalNotional);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
else if (PS.Config.Company == Configuration.CompanyEnum.天示)
|
||||
{
|
||||
foreach (var item in tradeRiskList)
|
||||
{
|
||||
if (item.viewList != null)
|
||||
{
|
||||
foreach (var n in item.viewList)
|
||||
{
|
||||
if (EnumTradeTypeUtil.IsOption((EnumTradeType)n.TradeFlag) && n.UnderlyingCode != null)
|
||||
{
|
||||
var adjLots = item.viewList.Where(m => m.TradeType == "商品期货" && n.UnderlyingCode.Equals(m.UnderlyingCode, StringComparison.OrdinalIgnoreCase)).Sum(m => m.Lots) ?? 0;
|
||||
adjLots -= n.DeltaInLots;
|
||||
n.CompanyObj = new TradingRiskParameter_TianShi
|
||||
{
|
||||
PositionAdjustLots = adjLots,
|
||||
RiskDegree = n.DeltaInLots != 0 ? Math.Abs(adjLots / n.DeltaInLots) : 0
|
||||
};
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
#region----数据合并到组----
|
||||
|
||||
//Math.Sqrt(243)
|
||||
const double Sqrt243 = 15.588457268119896;
|
||||
|
||||
/// <summary>
|
||||
/// 过滤并且聚合结果
|
||||
/// </summary>
|
||||
private static IEnumerable<TradingRiskParameter> InnerGroupingResults(TradingRiskResult result, TradingRiskReqModel req)
|
||||
{
|
||||
if (!BuildPredicate(req, out var tradeRiskPredicate, out var pnlStaticsPredicate))
|
||||
{
|
||||
return Enumerable.Empty<TradingRiskParameter>();
|
||||
}
|
||||
|
||||
var tradeRiskList = result.TradeRiskList;
|
||||
|
||||
if (tradeRiskPredicate != null)
|
||||
{
|
||||
tradeRiskList = tradeRiskList.Where(tradeRiskPredicate.Compile()).ToArray();
|
||||
}
|
||||
|
||||
if (!tradeRiskList.Any())
|
||||
{
|
||||
return Enumerable.Empty<TradingRiskParameter>();
|
||||
}
|
||||
|
||||
if (!req.IsMainList)
|
||||
{
|
||||
pnlStaticsPredicate = null;
|
||||
}
|
||||
|
||||
var pnlStaticsPredicateFunc = pnlStaticsPredicate?.Compile();
|
||||
var accruedTotalPnlStartDate = valuedateBLL.SystemDate.AccruedTotalPnlStartDate ?? DateTime.MinValue;
|
||||
|
||||
Dictionary<string, double> underlyingStaticsDic = null;
|
||||
|
||||
if (req.IsMainList)
|
||||
{
|
||||
//获取历史OTC盈亏统计信息(lastSettleDate换成ValueDate.AddDays(-1))
|
||||
var accruedTotalPnlDate = valuedateBLL.GetNonHolidayDefore(result.ValueDate.AddDays(-1));
|
||||
var pnlStaticsDatas = TradePnlStaticsDataProvider.Default.GetDatas(accruedTotalPnlDate);
|
||||
if (pnlStaticsPredicateFunc != null)
|
||||
{
|
||||
pnlStaticsDatas = pnlStaticsDatas.Where(pnlStaticsPredicateFunc);
|
||||
}
|
||||
underlyingStaticsDic = pnlStaticsDatas.Where(x => x.UnderlyingCode != null).GroupBy(n => n.UnderlyingCode.ToUpperInvariant())
|
||||
.ToDictionary(g => g.Key, g => g.Sum(n => n.TotalPnl), StringComparer.OrdinalIgnoreCase);
|
||||
}
|
||||
|
||||
Dictionary<string, double> accruedTotalPnLStartingDic = null;
|
||||
|
||||
if (req.IsMainList && accruedTotalPnlStartDate.Year > 2000)
|
||||
{
|
||||
IEnumerable<TradePnlStatics> datas;
|
||||
if (req.VolType != "对冲")
|
||||
{
|
||||
datas = AccruedTotalPnlStartingDataSource<eod_trade_position>.Default
|
||||
.GetDatas(result.ValueDate, accruedTotalPnlStartDate);
|
||||
}
|
||||
else
|
||||
{
|
||||
datas = AccruedTotalPnlStartingDataSource<eod_trade_position_hedgevol>.Default
|
||||
.GetDatas(result.ValueDate, accruedTotalPnlStartDate);
|
||||
}
|
||||
if (pnlStaticsPredicateFunc != null)
|
||||
{
|
||||
datas = datas.Where(pnlStaticsPredicateFunc);
|
||||
}
|
||||
accruedTotalPnLStartingDic = datas.Where(x => x.UnderlyingCode != null).GroupBy(e => e.UnderlyingCode).ToDictionary(g => g.Key, g => g.Sum(e => e.TotalPnl));
|
||||
}
|
||||
|
||||
//汇总到标的级别
|
||||
var tradeRiskGroupDic = new Dictionary<string, TradingRiskParameter>(StringComparer.OrdinalIgnoreCase);
|
||||
|
||||
foreach (var trp in tradeRiskList)
|
||||
{
|
||||
if (string.IsNullOrEmpty(trp.UnderlyingCode))
|
||||
{
|
||||
if (trp.TradeType == ConsGlobal.TradeType.CashFlow)
|
||||
{
|
||||
trp.VarietyCode = trp.UnderlyingCode = "现金流";
|
||||
}
|
||||
else
|
||||
{
|
||||
trp.VarietyCode = trp.UnderlyingCode = "未知";
|
||||
}
|
||||
}
|
||||
|
||||
if (tradeRiskGroupDic.TryGetValue(trp.UnderlyingCode, out var tempTrp))
|
||||
{
|
||||
tempTrp.AddItem(trp);
|
||||
}
|
||||
else
|
||||
{
|
||||
tempTrp = new TradingRiskParameter() { IsOption = false };
|
||||
tempTrp.AddItem(trp);
|
||||
tradeRiskGroupDic.Add(trp.UnderlyingCode, tempTrp);
|
||||
|
||||
if (req.IsMainList && underlyingStaticsDic != null)
|
||||
{
|
||||
tempTrp.AccruedTotalPnl += underlyingStaticsDic.TryGetValue(trp.UnderlyingCode, out var pnl) ? pnl : 0;
|
||||
|
||||
if (accruedTotalPnLStartingDic != null && accruedTotalPnLStartingDic.TryGetValue(trp.UnderlyingCode, out var startingPnl))
|
||||
{
|
||||
tempTrp.AccruedTotalPnl -= startingPnl;
|
||||
}
|
||||
}
|
||||
|
||||
if (PS.Config.Company == CompanyEnum.广期资本)
|
||||
{
|
||||
tempTrp.CompanyObj = new TradingRiskParameter_GuangQiZiBen();
|
||||
}
|
||||
}
|
||||
|
||||
//广期资本定制
|
||||
if (tempTrp.CompanyObj is TradingRiskParameter_GuangQiZiBen gqzbSumObj)
|
||||
{
|
||||
var basePart = (trp.SpotPrice ?? 0) * trp.Vol / Sqrt243;
|
||||
var gqzbObj = new TradingRiskParameter_GuangQiZiBen
|
||||
{
|
||||
DeltaAdjust = trp.Delta * basePart,
|
||||
GammaAdjust = 0.5 * trp.Gamma * basePart
|
||||
};
|
||||
gqzbSumObj.DeltaAdjust += gqzbObj.DeltaAdjust;
|
||||
gqzbSumObj.GammaAdjust += gqzbObj.GammaAdjust;
|
||||
trp.CompanyObj = gqzbObj;
|
||||
}
|
||||
}
|
||||
|
||||
var values = tradeRiskGroupDic.Values.ToArray();
|
||||
|
||||
//第二层viewlist
|
||||
var subLists = values.SelectMany(n => n.viewList).ToArray();
|
||||
|
||||
//将第三层的viewlist清除掉
|
||||
foreach (var item in subLists)
|
||||
{
|
||||
item.viewList = null;
|
||||
}
|
||||
|
||||
//只查看持仓数据
|
||||
if (req.OnlyPosition)
|
||||
{
|
||||
if (req.IsMainList)
|
||||
{
|
||||
values = values.Where(n => n.HasPosition).ToArray();
|
||||
}
|
||||
else
|
||||
{
|
||||
foreach (var item in values)
|
||||
{
|
||||
item.viewList.RemoveAll(n => !n.HasPosition);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
//对冲delta手数 + deltaT1(手数)
|
||||
var calcDeltaT1 = TradeRiskHelper.IsCalcDeltaT1();
|
||||
var list = req.IsMainList ? values : values.SelectMany(n => n.viewList);
|
||||
foreach (var item in list)
|
||||
{
|
||||
var un = DataCacheProvider.GetUnderlyingDataSource().GetData(item.UnderlyingCode);
|
||||
item.HedgeDeltaLots = un == null || un.ContractSize < 1 ? item.HedgeDelta : item.HedgeDelta / un.ContractSize;
|
||||
if (calcDeltaT1)
|
||||
{
|
||||
item.DeltaT1Lots = un == null || un.ContractSize < 1 ? item.DeltaT1 : item.DeltaT1 / un.ContractSize;
|
||||
}
|
||||
}
|
||||
|
||||
//排序
|
||||
Array.Sort(values, TradingRiskParameterSortComparer.Default);
|
||||
|
||||
if (req.Stock || !req.IsMainList)
|
||||
{
|
||||
return values;
|
||||
}
|
||||
|
||||
//20200619:风险对冲页面品种行的累积总盈亏需要统计已过期标的的累积总盈亏
|
||||
|
||||
var umDataSource = DataCacheProvider.GetUnderlyingDataSource();
|
||||
|
||||
//如果在req.OnlyPosition逻辑处理过程中已经把子项全部清掉则需要排除掉此类品种
|
||||
var varietySet = values.Where(n => n.viewList != null && n.viewList.Any())
|
||||
.Select(n => n.VarietyCode).ToHashSet(StringComparer.OrdinalIgnoreCase);
|
||||
|
||||
var dicMaturated = new Dictionary<string, TradingRiskParameter>(StringComparer.OrdinalIgnoreCase);
|
||||
|
||||
foreach (var kv in underlyingStaticsDic)
|
||||
{
|
||||
//跳过持仓交易包含的标的
|
||||
if (tradeRiskGroupDic.ContainsKey(kv.Key))
|
||||
{
|
||||
continue;
|
||||
}
|
||||
var varietyCode = umDataSource.GetData(kv.Key)?.CommodityCode;
|
||||
if (!string.IsNullOrWhiteSpace(varietyCode) && varietySet.Contains(varietyCode))
|
||||
{
|
||||
varietyCode = varietyCode.ToUpperInvariant();
|
||||
if (dicMaturated.TryGetValue(varietyCode, out var parm))
|
||||
{
|
||||
parm.AccruedTotalPnl += NumberHelper.Normalize(kv.Value);
|
||||
}
|
||||
else
|
||||
{
|
||||
dicMaturated[varietyCode] = parm = new TradingRiskParameter
|
||||
{
|
||||
VarietyCode = varietyCode,
|
||||
UnderlyingCode = "$$" + varietyCode,
|
||||
AccruedTotalPnl = NumberHelper.Normalize(kv.Value)
|
||||
};
|
||||
}
|
||||
|
||||
if (accruedTotalPnLStartingDic != null && accruedTotalPnLStartingDic.TryGetValue(kv.Key, out var startingPnl))
|
||||
{
|
||||
parm.AccruedTotalPnl -= startingPnl;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return values.Concat(dicMaturated.Values).ToArray();
|
||||
}
|
||||
|
||||
//构建查询条件(返回false表明根据查询条件筛选后的结果集必然是空)
|
||||
private static bool BuildPredicate(TradingRiskReqModel req,
|
||||
out Expression<Func<TradingRiskParameter, bool>> tradeRiskPredicate,
|
||||
out Expression<Func<TradePnlStatics, bool>> pnlStaticsPredicate)
|
||||
{
|
||||
tradeRiskPredicate = null;
|
||||
pnlStaticsPredicate = null;
|
||||
|
||||
//用户没有任何绑定的簿记账户
|
||||
if (req.UserBookIds != null && !req.UserBookIds.Any())
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
//簿记账户
|
||||
if (!BuildAssetBookPredicate(req, out tradeRiskPredicate, out pnlStaticsPredicate))
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
if (req.IsMainList)
|
||||
{
|
||||
//标的筛选条件构建(pnlStaticsPredicate将根据tradeRiskPredicate已筛选出来的数据构建标的筛选条件)
|
||||
|
||||
if (!BuildUnderlyingPredicateForMainList(req, out var pre))
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
tradeRiskPredicate = CombinePredicate(tradeRiskPredicate, pre, false);
|
||||
}
|
||||
else
|
||||
{
|
||||
BuildUnderlyingPredicateForSubList(req, out var pre);
|
||||
|
||||
tradeRiskPredicate = CombinePredicate(tradeRiskPredicate, pre, false);
|
||||
}
|
||||
|
||||
//交易对手方
|
||||
if (req.ClientIds != null && req.ClientIds.Any())
|
||||
{
|
||||
var invert = req.IsInvertSelect("客户名称");
|
||||
|
||||
tradeRiskPredicate = CombinePredicate(tradeRiskPredicate, t => req.ClientIds.Contains(t.ClientId), invert);
|
||||
|
||||
if (req.IsMainList)
|
||||
{
|
||||
pnlStaticsPredicate = CombinePredicate(pnlStaticsPredicate, t => req.ClientIds.Contains(t.ClientId), invert);
|
||||
}
|
||||
}
|
||||
|
||||
//结构类型
|
||||
if (req.AssetTypes != null && req.AssetTypes.Any())
|
||||
{
|
||||
var invert = req.IsInvertSelect("结构类型");
|
||||
var set = req.AssetTypes.Select(n => (int)EnumTradeTypeUtil.GetTradeFlag(n, null)).ToHashSet();
|
||||
|
||||
tradeRiskPredicate = CombinePredicate(tradeRiskPredicate
|
||||
, n => set.Contains(n.TradeFlag) || (set.Contains((int)EnumTradeType.CustomGroup) && n.IsGroup != 0)
|
||||
, invert);
|
||||
|
||||
if (req.IsMainList)
|
||||
{
|
||||
pnlStaticsPredicate = CombinePredicate(pnlStaticsPredicate, t => req.AssetTypes.Contains(t.AssetType), invert);
|
||||
}
|
||||
}
|
||||
|
||||
//权益类风险对冲index3
|
||||
if (req.Stock)
|
||||
{
|
||||
tradeRiskPredicate = CombinePredicate(tradeRiskPredicate
|
||||
, t => t.InstrumentType == ConsGlobal.InstrumentType.Stock
|
||||
|| t.UnderlyingCode.StartsWith("IF") || t.UnderlyingCode.StartsWith("IH")
|
||||
|| t.UnderlyingCode.StartsWith("IC") || t.UnderlyingCode.StartsWith("IO"), false);
|
||||
}
|
||||
else if (!req.IncludeStock) //商品类风险对冲index2包含股票
|
||||
{
|
||||
tradeRiskPredicate = CombinePredicate(tradeRiskPredicate, t => t.InstrumentType != ConsGlobal.InstrumentType.Stock, false);
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 为主列表页面构建标的筛选条件
|
||||
/// </summary>
|
||||
private static bool BuildUnderlyingPredicateForMainList(TradingRiskReqModel req
|
||||
, out Expression<Func<TradingRiskParameter, bool>> tradeRiskPredicate)
|
||||
{
|
||||
//定价试算会传入UnderlyingCodes请求参数,否则传入UnderlyingIds参数
|
||||
if (req.UnderlyingCodes != null && req.UnderlyingCodes.Any())
|
||||
{
|
||||
tradeRiskPredicate = PredicateBuilder.Create<TradingRiskParameter>(t => req.UnderlyingCodes.Contains(t.UnderlyingCode, StringComparer.OrdinalIgnoreCase));
|
||||
return true;
|
||||
}
|
||||
|
||||
tradeRiskPredicate = null;
|
||||
|
||||
//标的品种
|
||||
if (req.VarietyIds != null && req.VarietyIds.Any())
|
||||
{
|
||||
var dataSource = DataCacheModule.DataCacheManager.GetVarietyDataSource();
|
||||
var varietyCodes = new HashSet<string>(StringComparer.OrdinalIgnoreCase);
|
||||
|
||||
foreach (var id in req.VarietyIds)
|
||||
{
|
||||
var variety = dataSource.GetData(id);
|
||||
|
||||
if (variety != null)
|
||||
{
|
||||
varietyCodes.Add(variety.VarietyCode);
|
||||
}
|
||||
}
|
||||
|
||||
if (varietyCodes.Any())
|
||||
{
|
||||
tradeRiskPredicate = CombinePredicate(tradeRiskPredicate, n => varietyCodes.Contains(n.VarietyCode), req.IsInvertSelect("标的品种"));
|
||||
}
|
||||
else if (!req.IsInvertSelect("标的品种"))
|
||||
{
|
||||
return false; //筛选条件要包含的品种不存在
|
||||
}
|
||||
}
|
||||
|
||||
//品种分类
|
||||
if (req.VarietyGroups != null && req.VarietyGroups.Any())
|
||||
{
|
||||
var varietyCodes = DataCacheModule.DataCacheManager.GetVarietyDataSource()
|
||||
.AsQueryable().Where(n => req.VarietyGroups.Contains(n.AssetType))
|
||||
.Select(n => n.VarietyCode).ToHashSet(StringComparer.OrdinalIgnoreCase);
|
||||
|
||||
if (varietyCodes.Any())
|
||||
{
|
||||
tradeRiskPredicate = CombinePredicate(tradeRiskPredicate, n => varietyCodes.Contains(n.VarietyCode), req.IsInvertSelect("品种分类"));
|
||||
}
|
||||
else if (!req.IsInvertSelect("品种分类"))
|
||||
{
|
||||
return false; //筛选条件要包含的品种分类不存在
|
||||
}
|
||||
}
|
||||
|
||||
//标的代码
|
||||
if (req.UnderlyingIds != null && req.UnderlyingIds.Any())
|
||||
{
|
||||
var underlyingCodes = DataCacheModule.DataCacheManager.GetUnderlyingDataSource()
|
||||
.AsQueryable().Where(n => req.UnderlyingIds.Contains(n.id))
|
||||
.Select(n => n.UnderlyingCode).ToArray();
|
||||
|
||||
if (underlyingCodes.Any())
|
||||
{
|
||||
tradeRiskPredicate = CombinePredicate(tradeRiskPredicate
|
||||
, t => underlyingCodes.Contains(t.UnderlyingCode, StringComparer.OrdinalIgnoreCase)
|
||||
, req.IsInvertSelect("标的代码"));
|
||||
}
|
||||
else if (!req.IsInvertSelect("标的代码"))
|
||||
{
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
//为明细列表子页面构建标的筛选条件
|
||||
private static void BuildUnderlyingPredicateForSubList(TradingRiskReqModel req
|
||||
, out Expression<Func<TradingRiskParameter, bool>> tradeRiskPredicate)
|
||||
{
|
||||
//取股票板块下的数据
|
||||
if (req.SubListCode.StartsWith("$,"))
|
||||
{
|
||||
var UnderlyingCodes = req.SubListCode.Substring(2).Split(',');
|
||||
tradeRiskPredicate = PredicateBuilder.Create<TradingRiskParameter>(t => UnderlyingCodes.Contains(t.UnderlyingCode, StringComparer.OrdinalIgnoreCase));
|
||||
}
|
||||
//取某品种下的数据
|
||||
else if (req.SubListCode.StartsWith("$$"))
|
||||
{
|
||||
var varietyCode = req.SubListCode.TrimStart('$');
|
||||
|
||||
tradeRiskPredicate = PredicateBuilder.Create<TradingRiskParameter>(t => varietyCode.Equals(t.VarietyCode, StringComparison.OrdinalIgnoreCase));
|
||||
|
||||
//标的代码过滤
|
||||
if (req.UnderlyingIds != null && req.UnderlyingIds.Any())
|
||||
{
|
||||
var underlyingCodes = DataCacheModule.DataCacheManager.GetUnderlyingDataSource()
|
||||
.AsQueryable().Where(n => req.UnderlyingIds.Contains(n.id))
|
||||
.Select(n => n.UnderlyingCode).ToArray();
|
||||
|
||||
tradeRiskPredicate = CombinePredicate(tradeRiskPredicate
|
||||
, t => underlyingCodes.Contains(t.UnderlyingCode, StringComparer.OrdinalIgnoreCase)
|
||||
, req.IsInvertSelect("标的代码"));
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
tradeRiskPredicate = PredicateBuilder.Create<TradingRiskParameter>(t => req.SubListCode.Equals(t.UnderlyingCode, StringComparison.OrdinalIgnoreCase));
|
||||
}
|
||||
}
|
||||
|
||||
//构建簿记账户筛选条件
|
||||
private static bool BuildAssetBookPredicate(TradingRiskReqModel req
|
||||
, out Expression<Func<TradingRiskParameter, bool>> tradeRiskPredicate
|
||||
, out Expression<Func<TradePnlStatics, bool>> pnlStaticsPredicate)
|
||||
{
|
||||
tradeRiskPredicate = null;
|
||||
pnlStaticsPredicate = null;
|
||||
|
||||
if (req.UserBookIds != null)
|
||||
{
|
||||
var bookIds = req.UserBookIds.ToHashSet();
|
||||
|
||||
if (!bookIds.Any())
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
tradeRiskPredicate = PredicateBuilder.Create<TradingRiskParameter>(t => bookIds.Contains(t.BookId));
|
||||
|
||||
if (req.IsMainList)
|
||||
{
|
||||
pnlStaticsPredicate = PredicateBuilder.Create<TradePnlStatics>(t => bookIds.Contains(t.BookId));
|
||||
}
|
||||
}
|
||||
|
||||
if (req.BookIds != null && req.BookIds.Any())
|
||||
{
|
||||
var invert = req.IsInvertSelect("簿记账户");
|
||||
|
||||
tradeRiskPredicate = CombinePredicate(tradeRiskPredicate, t => req.BookIds.Contains(t.BookId), invert);
|
||||
|
||||
if (req.IsMainList)
|
||||
{
|
||||
pnlStaticsPredicate = CombinePredicate(pnlStaticsPredicate, t => req.BookIds.Contains(t.BookId), invert);
|
||||
}
|
||||
}
|
||||
|
||||
if (req.AssetIdGroupList != null && req.AssetIdGroupList.Any())
|
||||
{
|
||||
var groupBookIds = DataCacheProvider.GetAssetUnitDataSource().AsQueryable()
|
||||
.Where(x => req.AssetIdGroupList.Contains(x.GroupId)).Select(x => x.id).ToArray();
|
||||
|
||||
if (!groupBookIds.Any())
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
var invert = req.IsInvertSelect("簿记账户组");
|
||||
|
||||
tradeRiskPredicate = CombinePredicate(tradeRiskPredicate, t => groupBookIds.Contains(t.BookId), invert);
|
||||
|
||||
if (req.IsMainList)
|
||||
{
|
||||
pnlStaticsPredicate = CombinePredicate(pnlStaticsPredicate, t => groupBookIds.Contains(t.BookId), invert);
|
||||
}
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
[System.Runtime.CompilerServices.MethodImpl(methodImplOptions: System.Runtime.CompilerServices.MethodImplOptions.AggressiveInlining)]
|
||||
static Expression<Func<T, bool>> CombinePredicate<T>(Expression<Func<T, bool>> basePre, Expression<Func<T, bool>> andPre, bool invertAndPre) where T : class
|
||||
{
|
||||
return basePre == null ? (invertAndPre ? andPre.Not() : andPre) : (andPre == null ? basePre : basePre.And(invertAndPre ? andPre.Not() : andPre));
|
||||
}
|
||||
|
||||
#endregion
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user