using BaseOUDAL; using System; using System.Data; using System.Linq; using System.Text; using YLErp.Commons; using YLErp.Configuration.Enums; using YLErp.Helpers; using YLErp.Model; namespace YLErp.Modules.UnderlyingModule { /// /// 个股黑白名单信息管理服务 /// public class StockBlackWhiteService : YLBaseService { public StockBlackWhiteService(YLBaseService baseService) : base(baseService) { } public StockBlackWhiteService(OptUserInfo userInfo) : base(userInfo) { } private string getUnderlyingType(string code) { string assetVariety = ""; if (code.StartsWith("60") || code.StartsWith("000")) { //主板 assetVariety = "ZB"; } else if (code.StartsWith("002")) {//中小板 assetVariety = "ZXB"; } else if (code.StartsWith("300")) {//创业板 assetVariety = "CYB"; } else if (code.StartsWith("688")) {//科创版 assetVariety = "KCB"; } else { assetVariety = "JW"; } return assetVariety; } public void StockBlackWhiteAddJson(List data, bool checkStatus) { if (data.Any(O => string.IsNullOrWhiteSpace(O.UnderlyingCode))) { throw new ServiceException("代码不应为空"); } var query = data.Where(O => !O.UnderlyingCode.Contains(".")); if (query.Any()) { var errCode = query.Select(O => O.UnderlyingCode); throw new ServiceException($"{string.Join(",", errCode)}代码应包含市场后缀"); } query = data.Where(O => O.BlackWhiteState < 0 || O.BlackWhiteState > 1); if (query.Any()) { var errCode = query.Select(O => O.UnderlyingCode); throw new ServiceException($"{string.Join(",", errCode)}记录应指定所属黑名单或白名单"); } for (int i = 0; i < data.Count; i++) { data[i].UnderlyingCode = data[i].UnderlyingCode.ToUpper(); if (data[i].LimitRange == LimitRangeEnum.All) { data[i].LimitRange = LimitRangeEnum.Option; data.Add(new StockBlackWhite() { UnderlyingCode = data[i].UnderlyingCode, UnderlyingName = data[i].UnderlyingName, BlackWhiteState = data[i].BlackWhiteState, LimitRange = LimitRangeEnum.Swap }); } } if (checkStatus) { var newCodeDict = data.GroupBy(O => O.BlackWhiteState).ToDictionary(K => K.Key, V => V.Select(O => O)); var predicate = PredicateBuilder.False(); if (newCodeDict.ContainsKey(0)) { var blackList = newCodeDict[0].Select(O => O.UnderlyingCode).ToHashSet(); var limits = newCodeDict[0].Select(O => O.LimitRange).ToHashSet(); predicate = predicate.Or(O => O.BlackWhiteState != 0 && limits.Contains(O.LimitRange) && blackList.Contains(O.UnderlyingCode)); } if (newCodeDict.ContainsKey(1)) { var whiteList = newCodeDict[1].Select(O => O.UnderlyingCode).ToHashSet(); var limits = newCodeDict[1].Select(O => O.LimitRange).ToHashSet(); predicate = predicate.Or(O => O.BlackWhiteState != 1 && limits.Contains(O.LimitRange) && whiteList.Contains(O.UnderlyingCode)); } var checkData = DbContext.Stock_BlackWhite.Where(predicate) .AsEnumerable() .GroupBy(O => O.BlackWhiteState).ToDictionary(K => K.Key, V => V.Select(O => O.UnderlyingCode)); StringBuilder sb = new StringBuilder(); if (checkData.Count > 0) { if (checkData.ContainsKey(0)) { sb.Append($"{string.Join(",", checkData[0])}代码存在于黑名单中,"); } if (checkData.ContainsKey(1)) { sb.Append($"{string.Join(",", checkData[1])}代码存在于白名单中,"); } if (sb.Length > 0) { sb.Append("是否覆盖?"); } } if (sb.Length > 0) { throw new ServiceException(sb.ToString()); } } var codes = data.Select(O => O.UnderlyingCode); var codeDict = DbContext.underlying_manager.Where(O => O.UnderlyingInstrumentType == "Stock" && codes.Contains(O.UnderlyingCode)).ToDictionary(K => K.UnderlyingCode, V => V); var dbDict = DbContext.Stock_BlackWhite.AsEnumerable().GroupBy(O => O.UnderlyingCode).ToDictionary(K => K.Key, V => V.ToArray()); var tempList = new HashSet(); foreach (var item in data) { if (!codeDict.ContainsKey(item.UnderlyingCode)) { if (item.BlackWhiteState != 0) { item.UnderlyingCode = item.UnderlyingCode.ToUpper(); //如果标的不存在,则自动创建;如果标的资产类型不在已知范围内,则创建失败; var varietyCode = getUnderlyingType(item.UnderlyingCode); var marketCode = item.UnderlyingCode.Split('.')[1]; var tempName = $"A股股票{(marketCode == "SZ" ? "深圳" : "上海")}"; var variety = DataCacheProvider.GetVarietyDataSource().AsQueryable().Where(O => O.VarietyCode == varietyCode).FirstOrDefault() ?? DataCacheProvider.GetVarietyDataSource().AsQueryable().Where(O => O.VarietyName == tempName).FirstOrDefault(); var um = new underlying_manager(); um.UnderlyingCode = item.UnderlyingCode; um.UnderlyingName = item.UnderlyingName; um.UnderlyingInstrumentType = "Stock"; um.UnderlyingType = variety?.VarietyCode; um.UnderlyingTypeId = variety?.id ?? 0; um.LaunchState = item.BlackWhiteState.ToString(); um.MarketCode = marketCode; um.UnderlyingState = "Live"; um.UnderlyingStatus = "正常运行"; um.TradeCode = item.UnderlyingCode; um.UnderlyingDesc = "黑白名单同步"; var underlyingService = new UnderlyingDalService(OptUser).SaveUnderlyingData(um); codeDict[item.UnderlyingCode] = um; } } if (!tempList.Add($"{item.UnderlyingCode}_{item.LimitRange}")) { continue; } StockBlackWhite stockBlackWhite = dbDict.ContainsKey(item.UnderlyingCode) ? dbDict[item.UnderlyingCode].FirstOrDefault(c => c.UnderlyingCode == item.UnderlyingCode && (c.LimitRange == LimitRangeEnum.All || c.LimitRange == item.LimitRange)) : null; if (stockBlackWhite == null) { stockBlackWhite = new StockBlackWhite { UnderlyingCode = item.UnderlyingCode, }; DbContext.Stock_BlackWhite.Add(stockBlackWhite); } stockBlackWhite.UnderlyingName = codeDict.ContainsKey(item.UnderlyingCode) ? codeDict[item.UnderlyingCode].UnderlyingName : item.UnderlyingName ?? ""; stockBlackWhite.BlackWhiteState = item.BlackWhiteState; stockBlackWhite.LimitRange = item.LimitRange; stockBlackWhite.OptId = UserId; stockBlackWhite.OptName = UserName; stockBlackWhite.OptDate = DateTime.Now; } DbContext.SaveChanges(); } /// /// 查询股票黑白名单列表 /// /// /// public SearchListResult SearchList(StockBlackWhiteQuery req, bool needSign = false) { var query = from source in DbContext.Stock_BlackWhite select source; if (!string.IsNullOrEmpty(req.UnderlyingCode)) { query = query.Where(d => d.UnderlyingCode.Contains(req.UnderlyingCode)); } if (!string.IsNullOrEmpty(req.UnderlyingName)) { query = query.Where(d => d.UnderlyingName.Contains(req.UnderlyingName)); } //if (req.tabIndex == (int)StockBlackWhiteEnum.黑名单) //{ // query = query.Where(d => d.BlackWhiteState == 0); //} //if (req.tabIndex == (int)StockBlackWhiteEnum.白名单) //{ // query = query.Where(d => d.BlackWhiteState == 1); //} if (req.OptId != null) { query = query.Where(d => d.OptId == req.OptId); } if (!string.IsNullOrEmpty(req.OptName)) { query = query.Where(d => d.OptName.Contains(req.OptName)); } if (req.BlackWhiteState >= 0) { query = query.Where(d => d.BlackWhiteState == req.BlackWhiteState); } if (req.LimitRange != LimitRangeEnum.All) { query = query.Where(O => O.LimitRange == LimitRangeEnum.All || O.LimitRange == req.LimitRange); } if (string.IsNullOrEmpty(req.sidx)) { req.sidx = "id"; req.sord = "asc"; } SearchListResult retListResult = query.ToSearchList(req); if (needSign) { var list = retListResult.rows.Select(O => O.UnderlyingCode); if (list.Count() > 0) { var codes = DbContext.Stock_BlackWhite .Where(O => O.LimitRange == req.LimitRange && O.BlackWhiteState != req.BlackWhiteState && list.Contains(O.UnderlyingCode)) .Select(O => O.UnderlyingCode); foreach (var item in retListResult.rows) { item.NeedSign = codes.Contains(item.UnderlyingCode); } } } return retListResult; } public bool ImportStockBlackWhitePublic(Stream stream, int blackWhiteState, bool checkStatus) { try { List dc = new List(); dc.Add(new ExcelHelper.DataColumnModel("标的资产代码", nameof(StockBlackWhite.UnderlyingCode), (cv, obj) => { return ((string)cv ?? "").ToUpper(); })); dc.Add(new ExcelHelper.DataColumnModel("标的名称", nameof(StockBlackWhite.UnderlyingName))); dc.Add(new ExcelHelper.DataColumnModel("名单类别", nameof(StockBlackWhite.BlackWhiteState), (cv, obj) => { int result = -1; switch (cv) { case "黑名单": result = 0; break; case "白名单": result = 1; break; } return result; })); dc.Add(new ExcelHelper.DataColumnModel("业务类别", nameof(StockBlackWhite.LimitRange), (cv, obj) => { LimitRangeEnum result = ((string)cv).GetEnumByDescription(); return result; })); var dict = new ExcelHelper().ExcelToListT(dc.ToArray(), stream, new[] { "场外期权", "互换" }); var stockBlackWhite = new List(); foreach (var item in dict) { foreach (var O in item.Value) { O.LimitRange = item.Key == "场外期权" ? LimitRangeEnum.Option : LimitRangeEnum.Swap; O.BlackWhiteState = blackWhiteState; O.OptId = UserId; O.OptName = UserName; O.OptDate = DateTime.Now; } stockBlackWhite.AddRange(item.Value); } StockBlackWhiteAddJson(stockBlackWhite, checkStatus); return true; } catch (ServiceException) { throw; } catch (Exception ex) { LogFactory.GetLogger("StockBlackWhiteStarvice").Error(ex, "导入黑白名单数据,操作出错"); throw; } } public bool DeleteStockBlackWhite(IEnumerable ids, LimitRangeEnum limitRange, int blackWhiteState) { var targetLimit = limitRange == LimitRangeEnum.Option ? LimitRangeEnum.Swap : LimitRangeEnum.Option; List delList = new List(); var query = DbContext.Stock_BlackWhite.Where(O => ids.Contains(O.id) && O.BlackWhiteState == blackWhiteState); var data = query.Where(c => c.LimitRange == limitRange); DbContext.Stock_BlackWhite.RemoveRange(data); data = query.Where(O => O.LimitRange == LimitRangeEnum.All); foreach (var item in data) { item.LimitRange = targetLimit; item.OptId = UserId; item.OptName = UserName; item.OptDate = DateTime.Now; } DbContext.SaveChanges(); return true; } /// /// 获取标的黑白名单列表 /// /// 获取黑白名单的维度 /// 黑白名单都不存在时,该值为Null,只存在黑名单时返回黑名单列表,否则返回包含在白名单且不包含在黑名单中的列表 /// 黑白名单都不存在或黑白名单都存在或只存在白名单时,返回false;只存在黑名单时,返回true; public bool GetStockBlackWhiteList(LimitRangeEnum limitRange, out List Codes) { var useWhiteCode = false; System.Diagnostics.Debug.WriteLine($"{"".PadLeft(50, '=')}"); var blackWhiteQuery = DataCacheProvider.GetStockBlackWhiteDataSource().AsQueryable() .Where(O => O.LimitRange == Configuration.Enums.LimitRangeEnum.All || O.LimitRange == limitRange); var existBlackLimit = blackWhiteQuery.Any(O => O.BlackWhiteState == 0); var existWhiteLimit = blackWhiteQuery.Any(O => O.BlackWhiteState == 1); if (existBlackLimit || existWhiteLimit)//如果黑白名单都不存在,则不限制; { var blackCodes = new List(); var whiteCodes = new List(); if (existBlackLimit) { System.Diagnostics.Debug.WriteLine($"存在黑名单"); blackCodes = blackWhiteQuery .Where(O => O.BlackWhiteState == 0) .Select(O => O.UnderlyingCode).ToList(); } if (existWhiteLimit) { System.Diagnostics.Debug.WriteLine($"存在白名单"); useWhiteCode = false; whiteCodes = blackWhiteQuery .Where(O => O.BlackWhiteState == 1 && !blackCodes.Contains(O.UnderlyingCode)) .Select(O => O.UnderlyingCode).ToList(); } else { System.Diagnostics.Debug.WriteLine($"不存在白名单"); useWhiteCode = true; whiteCodes.AddRange(blackCodes); blackCodes.Clear(); } whiteCodes.RemoveAll(O => blackCodes.Contains(O)); Codes = new List(whiteCodes.ToHashSet()); } else { Codes = null; } return useWhiteCode; } /// /// 2021-05-21:天风证券已经修改了逻辑,股票启用与否和黑白名单断开了关系 /// 这段代码也没有经过测试,暂时先留着,如果没用,3.14版本就删除掉 /// public string CanSetLaunchState(int[] uids, string launchState) { if (uids == null || uids.Any(n => n > 0)) { return "请求参数为空"; } //如果禁用标的则检查是否有交易正在使用要禁用的标的 if (launchState != "1") { var query1 = from a in DbContext.underlying_manager join t in DbContext.trade on a.UnderlyingCode equals t.UnderlyingCode where uids.Contains(a.id) && !ConsTrade.TradeCompleteStatus.Contains(t.TradeStatus) && t.ValidState != ConsGlobal.InValid select t.UnderlyingCode; var arr = query1.ToArray(); if (arr.Length > 0) { var codeStr = string.Join(",", arr); return $"不能禁用标的,有{arr.Length }笔交易还在使用标的:{codeStr}"; } return null; } var query = from a in DbContext.underlying_manager join b in DbContext.Stock_BlackWhite on a.UnderlyingCode equals b.UnderlyingCode into bs from b in bs.DefaultIfEmpty() where uids.Contains(a.id) && a.UnderlyingInstrumentType == ConsGlobal.InstrumentType.Stock select new { a.UnderlyingCode, state = b == null ? 100 : b.BlackWhiteState }; var datas = query.ToArray(); if (!datas.Any()) { return null; } if (datas.Any(n => n.state == 1) || DbContext.Stock_BlackWhite.Any(n => n.BlackWhiteState == 1)) { if (!datas.All(n => n.state == 1)) { var codeStr = string.Join(",", datas.Where(n => n.state != 1).Select(n => n.UnderlyingCode).ToArray()); return "禁止不在白名单中的标的启用:" + codeStr; } } if (datas.Any(n => n.state == 0)) { var codeStr = string.Join(",", datas.Where(n => n.state == 0).Select(n => n.UnderlyingCode).ToArray()); return "禁止在黑名单中的标的启用:" + codeStr; } return null; } } }