448 lines
20 KiB
C#
448 lines
20 KiB
C#
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
|
|
{
|
|
/// <summary>
|
|
/// 个股黑白名单信息管理服务
|
|
/// </summary>
|
|
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<StockBlackWhite> 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<StockBlackWhite>();
|
|
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<string>();
|
|
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();
|
|
}
|
|
/// <summary>
|
|
/// 查询股票黑白名单列表
|
|
/// </summary>
|
|
/// <param name="req"></param>
|
|
/// <returns></returns>
|
|
public SearchListResult<StockBlackWhite> 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<StockBlackWhite> 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<ExcelHelper.DataColumnModel> dc = new List<ExcelHelper.DataColumnModel>();
|
|
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<LimitRangeEnum>();
|
|
return result;
|
|
}));
|
|
var dict = new ExcelHelper().ExcelToListT<StockBlackWhite>(dc.ToArray(), stream, new[] { "场外期权", "互换" });
|
|
var stockBlackWhite = new List<StockBlackWhite>();
|
|
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<int> ids, LimitRangeEnum limitRange, int blackWhiteState)
|
|
{
|
|
var targetLimit = limitRange == LimitRangeEnum.Option ? LimitRangeEnum.Swap : LimitRangeEnum.Option;
|
|
List<StockBlackWhite> delList = new List<StockBlackWhite>();
|
|
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;
|
|
}
|
|
|
|
/// <summary>
|
|
/// 获取标的黑白名单列表
|
|
/// </summary>
|
|
/// <param name="limitRange">获取黑白名单的维度</param>
|
|
/// <param name="Codes">黑白名单都不存在时,该值为Null,只存在黑名单时返回黑名单列表,否则返回包含在白名单且不包含在黑名单中的列表</param>
|
|
/// <returns>黑白名单都不存在或黑白名单都存在或只存在白名单时,返回false;只存在黑名单时,返回true;</returns>
|
|
public bool GetStockBlackWhiteList(LimitRangeEnum limitRange, out List<string> 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<string>();
|
|
var whiteCodes = new List<string>();
|
|
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<string>(whiteCodes.ToHashSet());
|
|
}
|
|
else
|
|
{
|
|
Codes = null;
|
|
}
|
|
|
|
return useWhiteCode;
|
|
}
|
|
|
|
/// <summary>
|
|
/// 2021-05-21:天风证券已经修改了逻辑,股票启用与否和黑白名单断开了关系
|
|
/// 这段代码也没有经过测试,暂时先留着,如果没用,3.14版本就删除掉
|
|
/// </summary>
|
|
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;
|
|
}
|
|
}
|
|
}
|