using System.Data; using YieldChain.Helpers; using YLErp.BLL; using YLErp.DBModels.Helpers; using YLErp.Model; using YLErp.Models; namespace YLErp.Modules.UnderlyingModule { /// /// 标的数据操作服务 /// public class UnderlyingDalService : YLBaseService { public UnderlyingDalService(OptUserInfo userInfo) : base(userInfo) { } /// /// 获取数据详情 /// public UnderlyingManagerDto GetDetail(int id) { var um = DbContext.underlying_manager.AsNoTracking().FirstOrDefault(n => n.id == id); if (um == null) { return null; } var dto = new UnderlyingManagerDto(); ObjectHelper.MapValues(dto, um); dto.HisDataList = DbContext.UnderlyingHisData.Where(n => n.UnderlyingCode == um.UnderlyingCode) .OrderBy(n => n.ValueDate) .Select(n => new HistoryData { Value = n.Value, ValueDate = n.ValueDate, ValueType = n.ValueType, ValueFlag = n.ValueFlag }).ToArray(); return dto; } /// /// 获取所有有效的商品期货标的 /// public IEnumerable GetAllValidCommodityFutureCodes(DateTime minMaturityDate) { var datas = DbContext.underlying_manager.Where(n => n.MaturityDate >= minMaturityDate && n.UnderlyingInstrumentType == "CommodityFutures" && n.UnderlyingCode != null && n.UnderlyingType != "组合标的") .Select(n => n.UnderlyingCode).ToArray(); Array.Sort(datas); return datas; } /// /// 保存组合标的数据 /// public int SaveSyntheticUnderlying(SyntheticUnderlyingDto reqModel, out SyntheticUnderlying dbModel) { if (reqModel is null) { throw new ArgumentNullException(nameof(reqModel)); } if (string.IsNullOrWhiteSpace(reqModel.Name)) { throw new ServiceException("合成标的代码 不能为空"); } if (DbContext.synthetic_underlying.Any(n => n.Name == reqModel.Name && n.id != reqModel.id)) { throw new ServiceException("合成标的代码 已经存在"); } var isAddNew = reqModel.id <= 0; dbModel = null; if (isAddNew) { DbContext.synthetic_underlying.Add(dbModel = new SyntheticUnderlying()); } else { dbModel = DbContext.synthetic_underlying.FirstOrDefault(n => n.Name == reqModel.UnderlyingTipsInfo); if (dbModel == null) { isAddNew = true; DbContext.synthetic_underlying.Add(dbModel = new SyntheticUnderlying()); reqModel.id = 0; } else if (DbContext.trade.Any(trade => trade.UnderlyingCode == reqModel.UnderlyingTipsInfo && trade.ValidState != ConsGlobal.InValid)) { throw new ServiceException("系统存在使用此组合标的的交易,不能修改"); } } if (isAddNew && DbContext.underlying_manager.Any(n => n.UnderlyingCode == reqModel.UnderlyingTipsInfo)) { throw new ServiceException("当前组合标的的标的代码在系统中已经存在,无法重复新增"); } //复制值 DbContext.Entry(dbModel).CurrentValues.SetValues(reqModel); dbModel.Name = dbModel.UnderlyingTipsInfo; //---------------------------------- // 获取最小的到期日 //---------------------------------- var underlyingCodes = new string[] { dbModel.UnderlyingCode1.TrimToNull(), dbModel.UnderlyingCode2.TrimToNull(), dbModel.UnderlyingCode3.TrimToNull(), dbModel.UnderlyingCode4.TrimToNull(), }; underlyingCodes = underlyingCodes.Where(n => !string.IsNullOrEmpty(n)).ToArray(); var underlyings = DbContext.underlying_manager .Where(u => underlyingCodes.Contains(u.UnderlyingCode)) .Select(u => new { u.UnderlyingCode, u.MaturityDate, u.TradeUnit, u.QuoteUnit, u.UnderlyingInstrumentType, u.MarketCode, u.MarketName }).ToArray(); if (underlyings.Length != underlyingCodes.Length) { var missingCode = underlyingCodes.Where(n => !underlyings.Any(m => n.Equals(m.UnderlyingCode, StringComparison.OrdinalIgnoreCase))).ToArray(); throw new ServiceException("标的数据未找到:" + string.Join(",", missingCode)); } DateTime? maturityDate = null; var underlying1 = underlyings.FirstOrDefault(n => ConsGlobal.InstrumentType.CalcTypeIsFutures(n.UnderlyingInstrumentType)); if (underlying1 == null) { underlying1 = underlyings.First(); } else { maturityDate = underlyings.Where(n => ConsGlobal.InstrumentType.CalcTypeIsFutures(n.UnderlyingInstrumentType) && n.MaturityDate.HasValue) .Min(n => n.MaturityDate); } underlying_manager underlying; if (isAddNew) { underlying = new underlying_manager { UnderlyingCode = dbModel.UnderlyingTipsInfo, MaturityDate = maturityDate, UnderlyingState = "Live", UnderlyingDesc = "组合标的", OptId = dbModel.OptId, OptName = dbModel.OptName, OptDate = dbModel.OptDate, CommodityCode = "组合标的", UnderlyingType = "组合标的", UnderlyingName = reqModel.Name, UnderlyingInstrumentType = underlying1.UnderlyingInstrumentType, Price = 0, LaunchState = "1", LastUpdateTime = dbModel.OptDate, UnderlyingStatus = "正常运行", VolatilityRate = reqModel.VolatilityRate, UpDownLimit = reqModel.UpDownLimit, MarginRate = reqModel.MarginRate, ContractSize = reqModel.ContractSize ?? 0, //---------------------------------- //2020/5/14 使用组合标的的第一个标的为新添加的字段赋值 //---------------------------------- TradeUnit = underlying1.TradeUnit, QuoteUnit = underlying1.QuoteUnit, MarketCode = underlying1.MarketCode, MarketName = underlying1.MarketName, OpenDate = DateTime.Today }; if (string.IsNullOrWhiteSpace(underlying.TradeUnit)) { underlying.TradeUnit = "份"; } DbContext.underlying_manager.Add(underlying); UpdateHisData(underlying, true); } else { var UnderlyingCodestr = dbModel.Name; underlying = DbContext.underlying_manager.FirstOrDefault(u => u.UnderlyingCode == UnderlyingCodestr); underlying.MaturityDate = maturityDate; underlying.UnderlyingState = "Live"; underlying.UnderlyingInstrumentType = underlying1.UnderlyingInstrumentType; underlying.Price = 0; underlying.LaunchState = "1"; underlying.LastUpdateTime = dbModel.OptDate; underlying.UnderlyingStatus = "正常运行"; underlying.VolatilityRate = reqModel.VolatilityRate; underlying.UpDownLimit = reqModel.UpDownLimit; underlying.MarginRate = reqModel.MarginRate; underlying.ContractSize = reqModel.ContractSize ?? 0; //---------------------------------- //2020/5/14 使用组合标的的第一个标的为新添加的字段赋值 //---------------------------------- underlying.TradeUnit = underlying1.TradeUnit; underlying.QuoteUnit = underlying1.QuoteUnit; underlying.MarketCode = underlying1.MarketCode; underlying.MarketName = underlying1.MarketName; underlying.UnderlyingName = reqModel.Name; if (string.IsNullOrWhiteSpace(underlying.TradeUnit)) { underlying.TradeUnit = "份"; } UpdateHisData(underlying, false); } underlying.UnderlyingTypeId = UnderlyingHelper.GetSyntheticVariety().id; DbContext.SaveChanges(); //---------------------------------- // 新增underlying_parameter //---------------------------------- var underlying_params = DbContext.underlying_parameter.Where(u => u.UnderlyingId == underlying.id).ToList(); //如果没有报价参数,则新增 if (underlying_params.Count == 0) { underlying_parameter.defaultQuoteTypes.ForEach(t => { var up = new underlying_parameter() { NoRiskRate = valuedateBLL.SystemDate.RiskFreeRate * 0.01, Price = 0, Gamma = 0, Rho = 0, Vega = 0, Theta = 0, Delta = 0, UnderlyingId = underlying.id, Type = t, OptDate = DateTime.Now, OptId = reqModel.OptId, OptName = reqModel.OptName }; DbContext.underlying_parameter.Add(up); underlying_params.Add(up); }); } //最后调用保存以保证事物完整性 return DbContext.SaveChanges(); } /// /// 获取所有有效的组合标的 /// public IEnumerable GetAllValidSyntheticUnderlyings() { var queryU = from u in DbContext.underlying_manager where u.UnderlyingState != "Matured" && u.LaunchState == "1" select new { u.id }; var date = DateTime.Today; var query = from u in DbContext.underlying_manager join su in DbContext.synthetic_underlying on u.UnderlyingCode equals su.Name where u.MaturityDate >= date && u.UnderlyingType == "组合标的" && (su.UnderlyingId1 == null || su.UnderlyingId1 == 0 || queryU.Any(n => n.id == su.UnderlyingId1)) && (su.UnderlyingId2 == null || su.UnderlyingId2 == 0 || queryU.Any(n => n.id == su.UnderlyingId2)) && (su.UnderlyingId3 == null || su.UnderlyingId3 == 0 || queryU.Any(n => n.id == su.UnderlyingId3)) && (su.UnderlyingId4 == null || su.UnderlyingId4 == 0 || queryU.Any(n => n.id == su.UnderlyingId4)) select su; return query.ToArray(); } /// /// /// public SyntheticUnderlyingDto GetSyntheticUnderlyingByName(string name) { var query = from u in DbContext.underlying_manager join su in DbContext.synthetic_underlying on u.UnderlyingCode equals su.Name where u.UnderlyingCode == name select new SyntheticUnderlyingDto { id = su.id, Coefficient1 = su.Coefficient1, Coefficient2 = su.Coefficient2, Coefficient3 = su.Coefficient3, Coefficient4 = su.Coefficient4, Constant = su.Constant, ContractSize = su.ContractSize, Name = su.Name, OptDate = su.OptDate, OptId = su.OptId, OptName = su.OptName, UnderlyingCode1 = su.UnderlyingCode1, UnderlyingCode2 = su.UnderlyingCode2, UnderlyingCode3 = su.UnderlyingCode3, UnderlyingCode4 = su.UnderlyingCode4, UnderlyingId1 = su.UnderlyingId1, UnderlyingId2 = su.UnderlyingId2, UnderlyingId3 = su.UnderlyingId3, UnderlyingId4 = su.UnderlyingId4, MarginRate = u.MarginRate, UpDownLimit = u.UpDownLimit, VolatilityRate = u.VolatilityRate, UnderlyingName = u.UnderlyingName }; return query.FirstOrDefault(); } /// /// 保存标的数据 /// public underlying_manager SaveUnderlyingData(underlying_manager req) { if (string.IsNullOrWhiteSpace(req.UnderlyingCode)) { throw new ServiceException("标的资产码 必须填写!"); } if (string.IsNullOrWhiteSpace(req.UnderlyingName)) { throw new ServiceException("标的名称 必须填写!"); } if (string.IsNullOrWhiteSpace(req.UnderlyingInstrumentType)) { throw new ServiceException("资产类型 必须填写!"); } if (req.UnderlyingTypeId < 1) { throw new ServiceException("资产品种类型 必须填写!"); } if (req.IsFutures()) { if (!req.MaturityDate.HasValue) { throw new ServiceException("期货标的到期日期 必须填写!"); } if (req.MaturityDate.Value.Year < 2000) { throw new ServiceException("期货标的到期日期 填写错误!"); } if (req.MaturityDate.Value < DateTime.Today) { req.UnderlyingState = "Matured"; } } //if (DataCacheProvider.GetStockBlackWhiteDataSource().AsQueryable().Any( // n => n.UnderlyingCode == req.UnderlyingCode && n.BlackWhiteState == 0)) //{ // throw new ServiceException("此标的存在于黑名单中,无法新增和修改"); //} //判断是否重复标的 if (DbContext.underlying_manager.Any(d => d.UnderlyingCode == req.UnderlyingCode && d.id != req.id)) { throw new ServiceException("标的资产码重复"); } underlying_manager dbModel; var isNew = req.id == 0; if (isNew) { dbModel = req; if (typeof(underlying_manager) != req.GetType()) { dbModel = new underlying_manager(); ObjectHelper.MapValues(dbModel, req); } dbModel.Price = underlying_manager.DefaultSpotPrice; // 新增标的默认启用 dbModel.LaunchState = "1"; DbContext.underlying_manager.Add(dbModel); } else { dbModel = DbContext.underlying_manager.Find(req.id); if (dbModel == null) { throw new ServiceException("保存失败,数据不存在"); } UpdateChanges(dbModel, req, new[] { nameof(underlying_manager.id) , //nameof(underlying_manager.Price) , nameof(underlying_manager.PrevClosePrice) , nameof(underlying_manager.LaunchState) , nameof(underlying_manager.LastUpdateTime) }); } dbModel.OptId = UserId; dbModel.OptName = UserName; dbModel.OptDate = DateTime.Now; //更新关联表 var variety = DbContext.variety.Find(dbModel.UnderlyingTypeId); if (variety != null) { dbModel.UnderlyingType = variety.VarietyName; dbModel.CommodityCode = variety.VarietyCode; dbModel.QuoteUnit = variety.QuoteUnitSingleOriginal; dbModel.TradeUnit = variety.TradeUnitSingle; if (string.IsNullOrWhiteSpace(req.MarketCode)) { dbModel.MarketName = variety.TradingMarket; dbModel.MarketCode = string.IsNullOrWhiteSpace(dbModel.MarketName) ? "" : DataCacheProvider.GetMarketDataSource().AsQueryable().FirstOrDefault(n => n.MarketName == dbModel.MarketName)?.ExchangeNo; } if (dbModel.ContractSize < 1) { dbModel.ContractSize = variety.TradeUnitValue > 0 ? variety.TradeUnitValue.Value : 1; } if (dbModel.PriceTick < 1e-5) { dbModel.PriceTick = VarietyHelper.ParseMinPriceChange(variety.MinPriceChange) ?? 0.01; } } if (!string.IsNullOrWhiteSpace(req.MarketCode)) { dbModel.MarketName = string.IsNullOrWhiteSpace(dbModel.MarketCode) ? "" : DataCacheProvider.GetMarketDataSource().AsQueryable().FirstOrDefault(n => n.ExchangeNo == dbModel.MarketCode)?.MarketName; } if (dbModel.CalcTypeIsStock()) { dbModel.MaturityDate = null; } UpdateHisData(req, isNew); DbContext.SaveChanges(); new DicForTranslationModule.DicForTranslationService(OptUser).SetWordDictionary(req); return dbModel; } /// /// 保存标的数据 /// public IEnumerable SaveUnderlyingData(IEnumerable reqs) { if (reqs.Any(O => string.IsNullOrWhiteSpace(O.UnderlyingCode))) { throw new ServiceException("标的资产码 必须填写!"); } if (reqs.Any(O => string.IsNullOrWhiteSpace(O.UnderlyingName))) { throw new ServiceException("标的名称 必须填写!"); } if (reqs.Any(O => string.IsNullOrWhiteSpace(O.UnderlyingInstrumentType))) { throw new ServiceException("资产类型 必须填写!"); } if (reqs.Any(O => O.UnderlyingTypeId < 1)) { throw new ServiceException("资产品种类型 必须填写!"); } if (reqs.Any(O => O.IsFutures() && !O.MaturityDate.HasValue)) { throw new ServiceException("期货标的到期日期 必须填写!"); } if (reqs.Any(O => O.IsFutures() && O.MaturityDate.Value.Year < 2000)) { throw new ServiceException("期货标的到期日期 填写错误!"); } if (reqs.Where(O => O.IsFutures() && O.MaturityDate.Value < DateTime.Today).ToList() is List um) { um.ForEach(O => O.UnderlyingState = "Matured"); } List underlyingCode_New = reqs.Where(O => O.id < 1).Select(O => O.UnderlyingCode).ToList(); if (DataCacheProvider.GetStockBlackWhiteDataSource().AsQueryable().Any( n => underlyingCode_New.Contains(n.UnderlyingCode) && n.BlackWhiteState == 0)) { throw new ServiceException("此标的存在于黑名单中,无法新增和修改"); } //判断是否重复标的 if (underlyingCode_New.Distinct().Count() != underlyingCode_New.Count || DbContext.underlying_manager.Any(d => underlyingCode_New.Contains(d.UnderlyingCode))) { throw new ServiceException("标的资产码重复"); } foreach (var req in reqs) { underlying_manager dbModel; var isNew = req.id == 0; if (isNew) { dbModel = req; dbModel.Price = underlying_manager.DefaultSpotPrice; DbContext.underlying_manager.Add(dbModel); } else { dbModel = DbContext.underlying_manager.Find(req.id); if (dbModel == null) { throw new ServiceException("保存失败,数据不存在"); } UpdateChanges(dbModel, req, new[] { nameof(underlying_manager.id) , nameof(underlying_manager.Price) , nameof(underlying_manager.PrevClosePrice) , nameof(underlying_manager.LaunchState) , nameof(underlying_manager.LastUpdateTime) }); } dbModel.OptId = UserId; dbModel.OptName = UserName; dbModel.OptDate = DateTime.Now; dbModel.MarketName = string.IsNullOrWhiteSpace(dbModel.MarketCode) ? "" : DataCacheProvider.GetMarketDataSource().AsQueryable().FirstOrDefault(n => n.ExchangeNo == dbModel.MarketCode)?.MarketName; //更新关联表 var variety = DataCacheProvider.GetVarietyDataSource().GetData(dbModel.UnderlyingTypeId); if (variety != null) { dbModel.UnderlyingType = variety.VarietyName; dbModel.CommodityCode = variety.VarietyCode; dbModel.QuoteUnit = variety.QuoteUnitSingleOriginal; dbModel.TradeUnit = variety.TradeUnitSingle; if (dbModel.ContractSize <= 1) { dbModel.ContractSize = variety.TradeUnitValue ?? 0; } if (dbModel.PriceTick < 1e-5) { dbModel.PriceTick = VarietyHelper.ParseMinPriceChange(variety.MinPriceChange) ?? 0.01; } } if (dbModel.CalcTypeIsStock()) { dbModel.MaturityDate = null; } UpdateHisData(req, isNew); } DbContext.SaveChanges(); return reqs; } /// /// 保存标的历史数据 /// private void UpdateHisData(underlying_manager newData, bool isNewUnderlying) { var newPara = MarginParamModel.Create(newData.MarginRate, newData.VolatilityRate, newData.UpDownLimit, true); if (!string.IsNullOrWhiteSpace(newData.VolatilityRate) && !newPara.VolatilityRate.HasValue) { throw new ServiceException("解析Span波动率变动失败:" + newData.VolatilityRate); } if (!string.IsNullOrWhiteSpace(newData.UpDownLimit) && !newPara.UpDownLimit.HasValue) { throw new ServiceException("解析Span涨跌幅度失败:" + newData.UpDownLimit); } if (isNewUnderlying) { DbContext.BulkDelete($"{nameof(UnderlyingHisData.UnderlyingCode)}='{newData.UnderlyingCode}'"); } var hisdataService = new UnderlyingHisDataService(this); var valdate = isNewUnderlying ? new DateTime(2000, 1, 1) : valuedateBLL.ValueDate; hisdataService.AddOrUpdateHisData(new UnderlyingHisDataAddOrUpdateRequest { UnderlyingCode = newData.UnderlyingCode, ValueType = nameof(MarginParamModel.MarginRate), Value = newPara.MarginRate, ValueDate = valdate, ValueFlag = "F" }, false); hisdataService.AddOrUpdateHisData(new UnderlyingHisDataAddOrUpdateRequest { UnderlyingCode = newData.UnderlyingCode, ValueType = nameof(MarginParamModel.VolatilityRate), Value = newPara.VolatilityRate, ValueDate = valdate, ValueFlag = "F" }, false); hisdataService.AddOrUpdateHisData(new UnderlyingHisDataAddOrUpdateRequest { UnderlyingCode = newData.UnderlyingCode, ValueType = nameof(MarginParamModel.UpDownLimit), Value = newPara.UpDownLimit, ValueDate = valdate, ValueFlag = newPara.IsUpDownLimitFixed ? "F" : "%" }, false); } /// /// 更新数据 /// public int UpdateData(string UnderlyingCode, double? MarginRate, string VolatilityRate, string UpDownLimit) { if (UnderlyingCode is null) { throw new ArgumentNullException(nameof(UnderlyingCode)); } var underlying = DbContext.underlying_manager.FirstOrDefault(u => u.UnderlyingCode == UnderlyingCode); if (underlying == null) { throw new ServiceException("系统中没有相关的标的:" + UnderlyingCode); } if (underlying.IsSynthetic()) { underlying.UnderlyingTypeId = UnderlyingHelper.GetSyntheticVariety().id; } underlying.MarginRate = MarginRate; underlying.VolatilityRate = VolatilityRate; underlying.UpDownLimit = UpDownLimit; UpdateHisData(underlying, false); return DbContext.SaveChanges(); } /// /// 删除合成标的 /// public int RemoveSyntheticUnderlyingByName(string syntheticUnderlyingName) { if (syntheticUnderlyingName is null) { throw new ArgumentNullException(nameof(syntheticUnderlyingName)); } if (DbContext.trade.Any(trade => trade.UnderlyingCode == syntheticUnderlyingName && trade.ValidState != ConsGlobal.InValid)) { throw new ServiceException("系统存在使用此组合标的的交易,不能修改"); } var synModel = DbContext.synthetic_underlying.FirstOrDefault(n => n.Name == syntheticUnderlyingName); var unModel = DbContext.underlying_manager.FirstOrDefault(n => n.UnderlyingCode == syntheticUnderlyingName); if (unModel != null) { DbContext.underlying_manager.Remove(unModel); } if (synModel != null) { DbContext.synthetic_underlying.Remove(synModel); } return DbContext.SaveChanges(); } } }