using BaseOUDAL; using YLErp.BLL; using YLErp.Model; using YLErp.QdpModule; namespace YLErp.Modules.TradeModule.SwapModule { public class SwapTradeFlowCheckService : TradeServiceBase { public SwapTradeFlowCheckService(OptUserInfo userInfo) : base(userInfo) { } public SwapTradeFlowCheckService(YLBaseService baseService) : base(baseService) { } public void CheckSwapTradeFlow(string tradeNumber) { var valueDateConst = valuedateBLL.ValueDate; var flowsAll = DbContext.trade_swap_flow.Where(x => x.TradeDate == valueDateConst).ToList(); var flows = flowsAll.Where(x => x.Status != "已完成").ToList(); var positionTrades = (from td in DbContext.trade.Where(x => x.TradeStatus == "确认成交" && x.TradeType == "收益互换" && x.ValidState != "InValid") join ts in DbContext.trade_swap.Where(x => x.SwapType == "多空组合") on td.id equals ts.TradeId select new { td, ts }).ToList(); var existList = DbContext.trade_swap_flow_check_result.ToList(); if (!string.IsNullOrWhiteSpace(tradeNumber)) { positionTrades = positionTrades.Where(x => x.td.TradeNumber == tradeNumber).ToList(); if (!positionTrades.Any()) { throw new Exception($"未找到交易编号{tradeNumber}对应的多空组合持仓交易"); } var tradeIds = positionTrades.Select(x => x.td.id).ToList(); existList = existList.Where(x => tradeIds.Contains(x.TradeId)).ToList(); } if (existList.Any()) { DbContext.trade_swap_flow_check_result.RemoveRange(existList); } positionTrades.ForEach(x => { //创建一笔默认为成功的result var result = new trade_swap_flow_check_result() { IsSuccess = true, TradeId = x.td.id, OptId = UserId, OptName = UserName, OptDate = DateTime.Now }; //当天开仓的交易 if (x.ts.OriginalTradeId == null && x.td.TradeDate == valueDateConst) { if (!flowsAll.Any(y => y.TradeNumber == x.td.TradeNumber)) { //验证失败 result.IsSuccess = false; result.Detail += "该新开仓的多空组合交易未找到对应的操作流水;
"; } else { var flowsThis = flows.Where(y => y.TradeNumber == x.td.TradeNumber).ToList(); //开始验证逻辑 DoCheck(x.td, flowsThis, result); } } //前一天持仓的交易 else { var originalTradeNumber = x.td.TradeNumber; if (x.ts.OriginalTradeId != null) { originalTradeNumber = DbContext.trade.Find(x.ts.OriginalTradeId).TradeNumber; } var flowsThis = flows.Where(y => y.TradeNumber == originalTradeNumber).ToList(); //开始验证逻辑 DoCheck(x.td, flowsThis, result); } SaveCheckResult(result); }); } public string CheckSwapTradeGroup(string tradeNumber) { var valueDateConst = valuedateBLL.ValueDate; var flowsAll = DbContext.trade_swap_flow.Where(x => x.TradeDate == valueDateConst).ToList(); var flows = flowsAll.Where(x => x.Status != "已完成").ToList(); var positionTrade = (from td in DbContext.trade.Where(x => x.TradeStatus == "确认成交" && x.TradeNumber == tradeNumber && x.TradeType == "收益互换" && x.ValidState != "InValid") join ts in DbContext.trade_swap.Where(x => x.SwapType == "多空组合") on td.id equals ts.TradeId select new { td, ts }).FirstOrDefault(); if (positionTrade == null) { throw new Exception($"未找到交易编号{tradeNumber}对应的多空组合持仓交易"); } else { var existList = DbContext.trade_swap_flow_check_result.Where(x => x.TradeId == positionTrade.td.id).ToList(); if (existList.Any()) { DbContext.trade_swap_flow_check_result.RemoveRange(existList); } } //创建一笔默认为成功的result var result = new trade_swap_flow_check_result() { IsSuccess = true, TradeId = positionTrade.td.id, OptId = UserId, OptName = UserName, OptDate = DateTime.Now }; //当天开仓的交易 if (positionTrade.ts.OriginalTradeId == null && positionTrade.td.TradeDate == valueDateConst) { if (!flowsAll.Any(y => y.TradeNumber == positionTrade.td.TradeNumber)) { //验证失败 result.IsSuccess = false; result.Detail += "该新开仓的多空组合交易未找到对应的操作流水;
"; } else { var flowsThis = flows.Where(y => y.TradeNumber == positionTrade.td.TradeNumber).ToList(); //开始验证逻辑 DoCheck(positionTrade.td, flowsThis, result); } } //前一天持仓的交易 else { var originalTradeNumber = positionTrade.td.TradeNumber; if (positionTrade.ts.OriginalTradeId != null) { originalTradeNumber = DbContext.trade.Find(positionTrade.ts.OriginalTradeId).TradeNumber; } var flowsThis = flows.Where(y => y.TradeNumber == originalTradeNumber).ToList(); //开始验证逻辑 DoCheck(positionTrade.td, flowsThis, result); } SaveCheckResult(result); return string.IsNullOrEmpty(result.Detail) ? "检测成功" : result.Detail; } public SearchListResult SearchFlowCheckResultList(TradeFlowCheckResultReq req) { var query = from result in DbContext.trade_swap_flow_check_result join trade in DbContext.trade on result.TradeId equals trade.id select new TradeFlowCheckResultResponse() { trade = trade, trade_swap_flow_check_result = result }; if (!string.IsNullOrEmpty(req.TradeNumber)) { query = query.Where(d => d.trade.TradeNumber == req.TradeNumber); } if (req.ClientIds != null && req.ClientIds.Any()) { query = query.Where(d => req.ClientIds.Contains(d.trade.ClientId)); } if (req.IsSuccessList != null && req.IsSuccessList.Any()) { query = query.Where(d => req.IsSuccessList.Contains(d.trade_swap_flow_check_result.IsSuccess)); } if (string.IsNullOrEmpty(req.sidx)) { req.sidx = "trade_swap_flow_check_result.id"; req.sord = "asc"; } var retListResult = query.ToSearchList(req); return retListResult; } private void DoCheck(trade trade, List flows, trade_swap_flow_check_result result) { var valueDateConst = valuedateBLL.ValueDate; List childTrades = new List(); var children = (from td in DbContext.trade.Where(x => x.ParentTradeId == trade.id && x.ValidState != "InValid") join ts in DbContext.trade_swap on td.id equals ts.TradeId join un in DbContext.underlying_manager on td.UnderlyingId equals un.id select new { td.UnderlyingCode, td.Notional, ts.IsGetFloatingProfit, ts.GetLongShort, ts.PayLongShort, un.Price }).ToList(); if (children.Any()) { children.ForEach(x => { var child = new trade() { UnderlyingCode = x.UnderlyingCode, SpotPrice = x.Price, Notional = x.Notional, BuySell = x.IsGetFloatingProfit ? x.GetLongShort : x.PayLongShort }; childTrades.Add(child); }); } childTrades.ForEach(x => { var um = DataCacheProvider.GetUnderlyingDataSource().GetData(x.UnderlyingCode); var unFlows = flows.Where(y => y.UnderlyingCode == x.UnderlyingCode); var notional = x.Notional * (x.BuySell == "多头" ? 1 : -1) + unFlows.Sum(y => y.Lots * um.ContractSize * (y.BuySell == "买入" || y.BuySell == "B" ? 1 : -1)); x.Notional = Math.Abs(notional); x.BuySell = notional > 0 ? "多头" : "空头"; }); var childTradesUnderlyingCode = childTrades.Select(x => x.UnderlyingCode).ToList(); var addFlows = flows.Where(x => !childTradesUnderlyingCode.Contains(x.UnderlyingCode)); if (addFlows.Any()) { var addUnderlyingCodes = addFlows.Select(x => x.UnderlyingCode).Distinct().ToList(); addUnderlyingCodes.ForEach(x => { var um = DataCacheProvider.GetUnderlyingDataSource().GetData(x); var unFlows = addFlows.Where(y => y.UnderlyingCode == x); var notional = unFlows.Sum(y => y.Lots * um.ContractSize * (y.BuySell == "买入" || y.BuySell == "B" ? 1 : -1)); if (notional != 0) { //var spotPrice = Math.Abs(unFlows.Sum(y => (y.SpotPrice ?? 0) * y.Lots * (y.BuySell == "买入" || y.BuySell == "B" ? 1 : -1)) / unFlows.Sum(y => y.Lots)); var child = new trade() { UnderlyingCode = x, SpotPrice = um.Price, Notional = Math.Abs(notional), BuySell = notional > 0 ? "多头" : "空头" }; childTrades.Add(child); } }); } //验证多头或空头挂钩标的数量不少于50只 var countLong = childTrades.Where(x => x.BuySell == "多头").Select(x => x.UnderlyingCode).Distinct().Count(); var countShort = childTrades.Where(x => x.BuySell == "空头").Select(x => x.UnderlyingCode).Distinct().Count(); if (countLong < 50 && countShort < 50) { //验证失败 result.IsSuccess = false; result.Detail += $"多头挂钩标的数量【{countLong}】和空头挂钩标的数量【{countShort}】均少于50只;
"; } var buyStockEqvNotional = childTrades.Sum(x => x.Notional * (x.SpotPrice ?? 0) * (x.BuySell == "多头" ? 1 : 0)); var sellStockEqvNotional = childTrades.Sum(x => x.Notional * (x.SpotPrice ?? 0) * (x.BuySell == "空头" ? 1 : 0)); if (buyStockEqvNotional == 0 || sellStockEqvNotional == 0) { //验证失败 result.IsSuccess = false; result.Detail += $"多头收益互换名义本金与空头收益互换名义本金的比例不低于80%且不高于120%,多头名义本金[{buyStockEqvNotional}],空头名义本金[{sellStockEqvNotional}];
"; } else { //验证 多头收益互换名义本金与空头收益互换名义本金的比例不低于80%且不高于120% var rateTotal = buyStockEqvNotional / sellStockEqvNotional; if (rateTotal > 1.2 || rateTotal < 0.8) { //验证失败 result.IsSuccess = false; result.Detail += $"多头收益互换名义本金与空头收益互换名义本金的比例不低于80%且不高于120%,多头名义本金[{buyStockEqvNotional}],空头名义本金[{sellStockEqvNotional}];
"; } } //验证单一股票对应的合约名义本金占多方或空方股票对应的合约名义本金的比例不高于5% childTrades.ForEach(x => { var stockEqvNotional = (x.SpotPrice ?? 0) * x.Notional; var totalStockEqvNotional = (x.BuySell == "多头" ? buyStockEqvNotional : sellStockEqvNotional); var rate = stockEqvNotional / totalStockEqvNotional; if (rate > 0.05) { //验证失败 result.IsSuccess = false; result.Detail += $"单一股票对应的合约[{x.UnderlyingCode}]名义本金[{stockEqvNotional}]占多方或空方股票对应的合约名义本金[{totalStockEqvNotional}]的比例不高于5%;
"; } }); var underlyingCodes = childTrades.Select(x => x.UnderlyingCode).ToList(); var startDate = valueDateConst.AddYears(-1); var eodStockPricesDic = DbContext.eod_stock_price.Where(x => x.ValueDate >= startDate && x.ValueDate < valueDateConst && underlyingCodes.Contains(x.UnderlyingCode)).ToDictionary(x => new { x.ValueDate, x.UnderlyingCode }, x => x.ClosePrice); var eodCommodityFuturePricesDic = DbContext.eod_commodity_future_price.Where(x => x.ValueDate >= startDate && x.ValueDate < valueDateConst && underlyingCodes.Contains(x.UnderlyingCode)).ToDictionary(x => new { x.ValueDate, x.UnderlyingCode }, x => x.ClosePrice); List list = new List(); for (var time = startDate; time < valueDateConst;) { if (!QdpCalendarHelper.IsHoliday(time)) { List timeChildTrades = new List(); childTrades.ForEach(x => { if (eodStockPricesDic.ContainsKey(new { ValueDate = time, x.UnderlyingCode })) { timeChildTrades.Add(new trade() { UnderlyingCode = x.UnderlyingCode, SpotPrice = eodStockPricesDic[new { ValueDate = time, x.UnderlyingCode }], Notional = x.Notional, BuySell = x.BuySell }); } else if (eodCommodityFuturePricesDic.ContainsKey(new { ValueDate = time, x.UnderlyingCode })) { timeChildTrades.Add(new trade() { UnderlyingCode = x.UnderlyingCode, SpotPrice = eodCommodityFuturePricesDic[new { ValueDate = time, x.UnderlyingCode }], Notional = x.Notional, BuySell = x.BuySell }); } }); var buyChildTrades = timeChildTrades.Where(x => x.BuySell == "多头").ToList(); var sellChildTrades = timeChildTrades.Where(x => x.BuySell == "空头").ToList(); var item = new TimeXY() { Time = time, Px = buyChildTrades.Sum(x => (x.SpotPrice ?? 0) * x.Notional) / buyChildTrades.Sum(x => x.Notional), Py = sellChildTrades.Sum(x => (x.SpotPrice ?? 0) * x.Notional) / sellChildTrades.Sum(x => x.Notional), }; list.Add(item); } time = time.AddDays(1); } list = list.OrderBy(x => x.Time).ToList(); var n = list.Count; var index = 0; //价格变化率只有n-1组 foreach (var item in list) { index++; if (index < n) { item.x = list[index].Px / item.Px - 1; item.y = list[index].Py / item.Py - 1; } } var averageX = list.Sum(x => x.x) / (n - 1); var averageY = list.Sum(x => x.y) / (n - 1); var varX = list.Sum(x => Math.Pow(x.x - averageX, 2)) / ((n - 1) - 1); var varY = list.Sum(x => Math.Pow(x.y - averageY, 2)) / ((n - 1) - 1); var covXY = list.Sum(x => (x.x - averageX) * (x.y - averageY)) / ((n - 1) - 1); var relation = covXY / Math.Sqrt(varX * varY); if (relation < 0.8) { //验证失败 result.IsSuccess = false; result.Detail += $"多头与空头收益互换挂钩标的的过去一年相关系数[{relation}]低于80%;
"; } } private void SaveCheckResult(trade_swap_flow_check_result result) { var existList = DbContext.trade_swap_flow_check_result.Where(x => x.TradeId == result.TradeId); if (existList.Any()) { DbContext.trade_swap_flow_check_result.RemoveRange(existList); } DbContext.trade_swap_flow_check_result.Add(result); DbContext.SaveChanges(); } } public class TimeXY { public DateTime Time { get; set; } public double Px { get; set; } public double x { get; set; } public double Py { get; set; } public double y { get; set; } } }