340 lines
17 KiB
C#
340 lines
17 KiB
C#
using BaseOUDAL;
|
|
using YLErp.BLL;
|
|
using YLErp.BLL.Calculation;
|
|
using YLErp.Enums;
|
|
using YLErp.Modules.DataProviderModule;
|
|
using YLErp.QdpModule;
|
|
|
|
namespace YLErp.Modules.TradeModule
|
|
{
|
|
/// <summary>
|
|
/// 交易确认书规则服务
|
|
/// </summary>
|
|
public class TradeConfirmRuleService : YLBaseService
|
|
{
|
|
public TradeConfirmRuleService(OptUserInfo userInfo) : base(userInfo)
|
|
{
|
|
}
|
|
|
|
public TradeConfirmRuleService(YLBaseService baseService) : base(baseService)
|
|
{
|
|
}
|
|
|
|
public trade_confirm_rule saveTradeConfirmRule(trade_confirm_rule req, int optId, string optName)
|
|
{
|
|
trade_confirm_rule tradeConfirmRule;
|
|
var isAddNew = req.id == 0;
|
|
|
|
if (isAddNew)
|
|
{
|
|
var tradeExists = DbContext.trade_confirm_rule.Where(x => x.Name == req.Name);
|
|
if (tradeExists.Any())
|
|
{
|
|
return null;
|
|
}
|
|
DbContext.trade_confirm_rule.Add(tradeConfirmRule = new trade_confirm_rule());
|
|
}
|
|
else
|
|
{
|
|
tradeConfirmRule = DbContext.trade_confirm_rule.Find(req.id);
|
|
}
|
|
|
|
DbContext.Entry(tradeConfirmRule).CurrentValues.SetValues(req);
|
|
tradeConfirmRule.OptId = optId;
|
|
tradeConfirmRule.OptName = optName;
|
|
tradeConfirmRule.OptDate = DateTime.Now;
|
|
|
|
DbContext.SaveChanges();
|
|
|
|
return tradeConfirmRule;
|
|
}
|
|
|
|
public SearchListResult<trade_confirm_rule> SearchTradeConfirmRuleList(trade_confirm_ruleReq req)
|
|
{
|
|
var query = from source in DbContext.trade_confirm_rule select source;
|
|
|
|
if (!string.IsNullOrEmpty(req.Name))
|
|
{
|
|
query = query.Where(x => x.Name.Contains(req.Name));
|
|
}
|
|
|
|
if (!string.IsNullOrEmpty(req.TargetType))
|
|
{
|
|
query = query.Where(x => x.TargetType == req.TargetType);
|
|
}
|
|
|
|
if (!string.IsNullOrEmpty(req.ScopeType))
|
|
{
|
|
query = query.Where(x => x.ScopeType == req.ScopeType);
|
|
}
|
|
|
|
if (!string.IsNullOrEmpty(req.TimeType))
|
|
{
|
|
query = query.Where(x => x.TimeType == req.TimeType);
|
|
}
|
|
|
|
if (!string.IsNullOrEmpty(req.OptName))
|
|
{
|
|
query = query.Where(x => x.Name.Contains(req.OptName));
|
|
}
|
|
|
|
if (string.IsNullOrEmpty(req.sidx))
|
|
{
|
|
req.sidx = "id";
|
|
req.sord = "desc";
|
|
}
|
|
return query.ToSearchList(req);
|
|
}
|
|
|
|
/// <summary>
|
|
/// 交易确认时验证各交易确认规则是否满足,不满足时给出不满足的规则名
|
|
/// </summary>
|
|
public string checkTradeConfirmRule(trade trade, int userId)
|
|
{
|
|
var trades = new List<trade>();
|
|
//结构化交易确认时,需要把未确认的子交易delta/deltacash/gamma加和一起参与判断,名义本金则按照主交易进行判断
|
|
if (trade.TradeType == "结构化交易")
|
|
{
|
|
trades = DbContext.trade.Where(x => x.ParentTradeId == trade.id && ConsTrade.UnConfirmTradeStatusList.Contains(x.TradeStatus)).ToList();
|
|
}
|
|
else
|
|
{
|
|
trades.Add(trade);
|
|
}
|
|
var underlyingPrice = DataCacheProvider.GetUnderlyingDataSource();
|
|
|
|
var error = string.Empty;
|
|
if (DbContext.trade_confirm_rule.Any(x => x.ValidState != "InValid"))
|
|
{
|
|
var tradeRiskResult = CalculatorHelper.CalculateRisksForTrades(
|
|
valueDate: valuedateBLL.ValueDate,
|
|
tradeList: trades,
|
|
calcScenario: CalcScenarioEnum.Default,
|
|
priceProvider: underlyingPrice,
|
|
pricingRequest: QdpPricingRequest.BASIC_GREEKS,
|
|
isUseTradeVol: PS.Config.IsTradeVol,
|
|
canUseManual: true);
|
|
|
|
//统计范围为交易员的验证逻辑
|
|
var traderRules = DbContext.trade_confirm_rule.Where(x => x.ValidState != "InValid" && x.ScopeType == LimitScopeTypeEnum.交易员.ToString()).ToList();
|
|
foreach (var rule in traderRules)
|
|
{
|
|
//名义本金按照主交易计算
|
|
var tradesOtherStockEqvNotional = DbContext.trade.Where(x => x.ValidState != "InValid" && x.TraderId == trade.TraderId && (ConsTrade.NeedMarginTradeStatusList.Contains(x.TradeStatus) || x.TradeStatus == "审批中") && x.ParentTradeId == 0);
|
|
//其他指标按照子交易计算
|
|
var tradesOther = DbContext.trade.Where(x => x.ValidState != "InValid" && x.TraderId == trade.TraderId && (ConsTrade.NeedMarginTradeStatusList.Contains(x.TradeStatus) || x.TradeStatus == "审批中") && x.TradeType != "结构化交易");
|
|
if (rule.TimeType == LimitTimeTypeEnum.每交易日.ToString())
|
|
{
|
|
tradesOtherStockEqvNotional = tradesOtherStockEqvNotional.Where(x => x.TradeDate == trade.TradeDate);
|
|
tradesOther = tradesOther.Where(x => x.TradeDate == trade.TradeDate);
|
|
}
|
|
var tradeIds = tradesOther.Select(x => x.id).ToList();
|
|
var realTimeTradeRisks = DbContext.realtime_trade_risk.Where(x => tradeIds.Contains(x.TradeId ?? 0) && x.VolType == "持仓").ToList();
|
|
switch (rule.TargetType)
|
|
{
|
|
case "DeltaCash":
|
|
var allDeltaCash = realTimeTradeRisks.Sum(x => x.DeltaCash ?? 0) + tradeRiskResult.Results.Sum(x => x.ValueResult.DeltaCash);
|
|
if (allDeltaCash > rule.UpLimit || allDeltaCash < rule.DownLimit)
|
|
{
|
|
return string.Format(" {0}: {1}/{2}/{3}/下限{4},上限{5},实际值{6}", rule.Name, rule.TimeType, trade.TraderName, rule.TargetType, rule.DownLimit, rule.UpLimit, allDeltaCash.ToString("F2"));
|
|
}
|
|
break;
|
|
case "Delta":
|
|
var allDelta = realTimeTradeRisks.Sum(x => x.Delta ?? 0) + tradeRiskResult.Results.Sum(x => x.ValueResult.Delta);
|
|
if (allDelta > rule.UpLimit || allDelta < rule.DownLimit)
|
|
{
|
|
return string.Format(" {0}: {1}/{2}/{3}/下限{4},上限{5},实际值{6}", rule.Name, rule.TimeType, trade.TraderName, rule.TargetType, rule.DownLimit, rule.UpLimit, allDelta.ToString("F2"));
|
|
}
|
|
break;
|
|
case "Gamma":
|
|
var allGamma = realTimeTradeRisks.Sum(x => x.Gamma ?? 0) + tradeRiskResult.Results.Sum(x => x.ValueResult.Gamma);
|
|
if (allGamma > rule.UpLimit || allGamma < rule.DownLimit)
|
|
{
|
|
return string.Format(" {0}: {1}/{2}/{3}/下限{4},上限{5},实际值{6}", rule.Name, rule.TimeType, trade.TraderName, rule.TargetType, rule.DownLimit, rule.UpLimit, allGamma.ToString("F2"));
|
|
}
|
|
break;
|
|
case "名义本金":
|
|
var allStockEqvNotional = (tradesOtherStockEqvNotional.Any() ? tradesOtherStockEqvNotional.Sum(x => x.StockEqvNotional) : 0) + trade.StockEqvNotional;
|
|
if (allStockEqvNotional > rule.UpLimit || allStockEqvNotional < rule.DownLimit)
|
|
{
|
|
return string.Format(" {0}: {1}/{2}/{3}/下限{4},上限{5},实际值{6}", rule.Name, rule.TimeType, trade.TraderName, rule.TargetType, rule.DownLimit, rule.UpLimit, allStockEqvNotional.ToString("F2"));
|
|
}
|
|
break;
|
|
}
|
|
|
|
if (!string.IsNullOrEmpty(error))
|
|
{
|
|
return error;
|
|
}
|
|
}
|
|
|
|
//统计范围为客户的验证逻辑
|
|
var clientRules = DbContext.trade_confirm_rule.Where(x => x.ValidState != "InValid" && x.ScopeType == LimitScopeTypeEnum.客户.ToString()).ToList();
|
|
foreach (var rule in clientRules)
|
|
{
|
|
//名义本金按照主交易计算
|
|
var tradesOtherStockEqvNotional = DbContext.trade.Where(x => x.ValidState != "InValid" && x.ClientId == trade.ClientId && (ConsTrade.NeedMarginTradeStatusList.Contains(x.TradeStatus) || x.TradeStatus == "审批中") && x.ParentTradeId == 0);
|
|
//其他指标按照子交易计算
|
|
var tradesOther = DbContext.trade.Where(x => x.ValidState != "InValid" && x.ClientId == trade.ClientId && (ConsTrade.NeedMarginTradeStatusList.Contains(x.TradeStatus) || x.TradeStatus == "审批中"));
|
|
if (rule.TimeType == LimitTimeTypeEnum.每交易日.ToString())
|
|
{
|
|
tradesOtherStockEqvNotional = tradesOtherStockEqvNotional.Where(x => x.TradeDate == trade.TradeDate);
|
|
tradesOther = tradesOther.Where(x => x.TradeDate == trade.TradeDate);
|
|
}
|
|
var tradeIds = tradesOther.Select(x => x.id).ToList();
|
|
var realTimeTradeRisks = DbContext.realtime_trade_risk.Where(x => tradeIds.Contains(x.TradeId ?? 0) && x.VolType == "持仓").ToList();
|
|
switch (rule.TargetType)
|
|
{
|
|
case "DeltaCash":
|
|
var allDeltaCash = realTimeTradeRisks.Sum(x => x.DeltaCash ?? 0) + tradeRiskResult.Results.Sum(x => x.ValueResult.DeltaCash);
|
|
if (allDeltaCash > rule.UpLimit || allDeltaCash < rule.DownLimit)
|
|
{
|
|
return string.Format(" {0}: {1}/{2}/{3}/下限{4},上限{5},实际值{6}", rule.Name, rule.TimeType, trade.ClientName, rule.TargetType, rule.DownLimit, rule.UpLimit, allDeltaCash.ToString("F2"));
|
|
}
|
|
break;
|
|
case "Delta":
|
|
var allDelta = realTimeTradeRisks.Sum(x => x.Delta ?? 0) + tradeRiskResult.Results.Sum(x => x.ValueResult.Delta);
|
|
if (allDelta > rule.UpLimit || allDelta < rule.DownLimit)
|
|
{
|
|
return string.Format(" {0}: {1}/{2}/{3}/下限{4},上限{5},实际值{6}", rule.Name, rule.TimeType, trade.ClientName, rule.TargetType, rule.DownLimit, rule.UpLimit, allDelta.ToString("F2"));
|
|
}
|
|
break;
|
|
case "Gamma":
|
|
var allGamma = realTimeTradeRisks.Sum(x => x.Gamma ?? 0) + tradeRiskResult.Results.Sum(x => x.ValueResult.Gamma);
|
|
if (allGamma > rule.UpLimit || allGamma < rule.DownLimit)
|
|
{
|
|
return string.Format(" {0}: {1}/{2}/{3}/下限{4},上限{5},实际值{6}", rule.Name, rule.TimeType, trade.ClientName, rule.TargetType, rule.DownLimit, rule.UpLimit, allGamma.ToString("F2"));
|
|
}
|
|
break;
|
|
case "名义本金":
|
|
var allStockEqvNotional = (tradesOtherStockEqvNotional.Any() ? tradesOtherStockEqvNotional.Sum(x => x.StockEqvNotional) : 0) + trade.StockEqvNotional;
|
|
if (allStockEqvNotional > rule.UpLimit || allStockEqvNotional < rule.DownLimit)
|
|
{
|
|
return string.Format(" {0}: {1}/{2}/{3}/下限{4},上限{5},实际值{6}", rule.Name, rule.TimeType, trade.ClientName, rule.TargetType, rule.DownLimit, rule.UpLimit, allStockEqvNotional.ToString("F2"));
|
|
}
|
|
break;
|
|
}
|
|
|
|
if (!string.IsNullOrEmpty(error))
|
|
{
|
|
return error;
|
|
}
|
|
}
|
|
|
|
//统计范围为单笔交易的验证逻辑
|
|
var tradeRules = DbContext.trade_confirm_rule.Where(x => x.ValidState != "InValid" && x.ScopeType == LimitScopeTypeEnum.单笔交易.ToString()).ToList();
|
|
foreach (var rule in tradeRules)
|
|
{
|
|
switch (rule.TargetType)
|
|
{
|
|
case "DeltaCash":
|
|
var deltaCash = tradeRiskResult.Results.Sum(x => x.ValueResult.DeltaCash);
|
|
if (deltaCash > rule.UpLimit || deltaCash < rule.DownLimit)
|
|
{
|
|
return string.Format(" {0}: 每笔交易/{1}/下限{2},上限{3},实际值{4}", rule.Name, rule.TargetType, rule.DownLimit, rule.UpLimit, deltaCash.ToString("F2"));
|
|
}
|
|
break;
|
|
case "Delta":
|
|
var delta = tradeRiskResult.Results.Sum(x => x.ValueResult.Delta);
|
|
if (delta > rule.UpLimit || delta < rule.DownLimit)
|
|
{
|
|
return string.Format(" {0}: 每笔交易/{1}/下限{2},上限{3},实际值{4}", rule.Name, rule.TargetType, rule.DownLimit, rule.UpLimit, delta.ToString("F2"));
|
|
}
|
|
break;
|
|
case "Gamma":
|
|
var gamma = tradeRiskResult.Results.Sum(x => x.ValueResult.Gamma);
|
|
if (gamma > rule.UpLimit || gamma < rule.DownLimit)
|
|
{
|
|
return string.Format(" {0}: 每笔交易/{1}/下限{2},上限{3},实际值{4}", rule.Name, rule.TargetType, rule.DownLimit, rule.UpLimit, gamma.ToString("F2"));
|
|
}
|
|
break;
|
|
case "名义本金":
|
|
var stockEqvNotional = trade.StockEqvNotional;
|
|
if (stockEqvNotional > rule.UpLimit || stockEqvNotional < rule.DownLimit)
|
|
{
|
|
return string.Format(" {0}: 每笔交易/{1}/下限{2},上限{3},实际值{4}", rule.Name, rule.TargetType, rule.DownLimit, rule.UpLimit, stockEqvNotional.ToString("F2"));
|
|
}
|
|
break;
|
|
case "互换价格偏离比例":
|
|
var sportPrice = trade.SpotPrice??0;
|
|
var valueDate = valuedateBLL.ValueDate;
|
|
var basePrice = EodPriceQueryService.UnderlyingCodePrice(trade.UnderlyingCode, valueDate);
|
|
var pricePercent = Math.Abs((sportPrice / basePrice) - 1);
|
|
if (pricePercent < rule.DownLimit|| pricePercent > rule.UpLimit)
|
|
{
|
|
return string.Format(" {0}: 每笔交易/{1}/下限{2},上限{3},实际值{4}", rule.Name, rule.TargetType, rule.DownLimit, rule.UpLimit, pricePercent.ToString("F2"));
|
|
}
|
|
break;
|
|
}
|
|
|
|
if (!string.IsNullOrEmpty(error))
|
|
{
|
|
return error;
|
|
}
|
|
}
|
|
}
|
|
return error;
|
|
}
|
|
}
|
|
|
|
public class trade_confirm_ruleReq : BaseSearchReq
|
|
{
|
|
public int? id { get; set; }
|
|
|
|
/// <summary>
|
|
/// 限额名称
|
|
/// </summary>
|
|
public string Name { get; set; }
|
|
|
|
/// <summary>
|
|
/// 限额指标
|
|
/// </summary>
|
|
public string TargetType { get; set; }
|
|
|
|
/// <summary>
|
|
/// 统计范围
|
|
/// </summary>
|
|
public string ScopeType { get; set; }
|
|
|
|
/// <summary>
|
|
/// 标的资产
|
|
/// </summary>
|
|
public string UnderlyingIds { get; set; }
|
|
|
|
/// <summary>
|
|
/// 统计时间
|
|
/// </summary>
|
|
public string TimeType { get; set; }
|
|
|
|
/// <summary>
|
|
/// 限额上限
|
|
/// </summary>
|
|
public double? UpLimit { get; set; }
|
|
|
|
/// <summary>
|
|
/// 限额下限
|
|
/// </summary>
|
|
public double? DownLimit { get; set; }
|
|
|
|
/// <summary>
|
|
/// 状态
|
|
/// </summary>
|
|
public string ValidState { get; set; }
|
|
|
|
/// <summary>
|
|
/// 操作人
|
|
/// </summary>
|
|
public int? OptId { get; set; }
|
|
|
|
/// <summary>
|
|
/// 操作人
|
|
/// </summary>
|
|
public string OptName { get; set; }
|
|
|
|
/// <summary>
|
|
/// 操作时间
|
|
/// </summary>
|
|
public DateTime? OptDate { get; set; }
|
|
}
|
|
}
|