using YLErp.Abstract.DataProviders; using YLErp.Helpers; using YLErp.Models; using YLErp.Modules.TradeModule.DealModule; using YLErp.QdpModule; namespace YLErp.Modules.DataProviderModule { /// /// 收盘结算价数据服务(不包括场内期权结算价) /// public class EodPriceProvider : IEodPriceProvider, IEodPriceProviderV2, IBasketPriceProvider { readonly Dictionary _priceDic; /// /// 构造函数 /// /// 结算价取值日 /// 是否需要前复权价 public EodPriceProvider(DateTime valueDate, bool isDiviendPrice = false) { ValueDate = valueDate.Date; IsDiviendPrice = isDiviendPrice; _priceDic = new Dictionary(StringComparer.OrdinalIgnoreCase); } public void SetPreValueDate(DateTime date) { PreValueDate = date; } /// /// 结算价取值日 /// public DateTime ValueDate { get; } /// /// 上一个结算日 /// public DateTime? PreValueDate { get; set; } /// /// 是否价格除权 /// public bool IsDiviendPrice { get; } /// /// 当前缓存中的价格数量 /// public int Count => _priceDic.Count; #region----初始化---- /// /// 初始化数据字典 /// (提前初始化可在大批量标的取结算价时提高一定性能) /// public EodPriceProvider Initialize(IEnumerable underlyingCodes = null) { using var db = DbContextFactory.GetYLDbContext(); var predicate1 = PredicateBuilder.Create(eodprice => eodprice.ValueDate == ValueDate); var predicate2 = PredicateBuilder.Create(eodprice => eodprice.ValueDate == ValueDate); var predicate3 = PredicateBuilder.Create(eodprice => eodprice.valuation_date == ValueDate); if (underlyingCodes != null && underlyingCodes.Any(n => !string.IsNullOrEmpty(n))) { var set = underlyingCodes.Where(n => n != null && !_priceDic.ContainsKey(n)).ToHashSet(); if (set.Count < 1) { return this; } predicate1 = predicate1.And(n => set.Contains(n.UnderlyingCode)); predicate2 = predicate2.And(n => set.Contains(n.UnderlyingCode)); } var eodFutureQuery = from eodprice in db.eod_commodity_future_price.Where(predicate1) join um in db.underlying_manager on eodprice.UnderlyingCode equals um.UnderlyingCode select new EodPrice { IsStock = false, ValueDate = ValueDate, UnderlyingId = um.id, UnderlyingCode = um.UnderlyingCode, ClosePrice = eodprice.ClosePrice, SettlePrice = eodprice.SettlePrice, HighPrice = eodprice.HighPrice, LowPrice = eodprice.LowPrice, UnderlyingStatus = "正常运行", UnderlyingInstrumentType = "CommodityFutures", ReferencePrice = eodprice.ReferencePrice, DeciSettlePrice = 0, DeciClosePrice = 0, DeciReferencePrice = 0, }; var eodStockQuery = from eodprice in db.eod_stock_price.Where(predicate2) join um in db.underlying_manager on eodprice.UnderlyingCode equals um.UnderlyingCode select new EodPrice { IsStock = true, ValueDate = ValueDate, UnderlyingId = um.id, UnderlyingCode = um.UnderlyingCode, ClosePrice = eodprice.ClosePrice, SettlePrice = eodprice.ClosePrice, HighPrice = eodprice.HighPrice, LowPrice = eodprice.LowPrice, UnderlyingStatus = eodprice.UnderlyingStatus, UnderlyingInstrumentType = "Stock", ReferencePrice = eodprice.ReferencePrice, DeciSettlePrice = 0, DeciClosePrice = 0, DeciReferencePrice = 0, }; var eodBondQuery = from eodprice in db.china_bond_valuation.Where(predicate3) join um in db.underlying_manager on eodprice.bond_id equals um.UnderlyingCode select new EodPrice { IsStock = false, ValueDate = ValueDate, UnderlyingId = um.id, UnderlyingCode = um.UnderlyingCode, ClosePrice = (double)(eodprice.dirty_price_close ?? 0) / 100, SettlePrice = (double)(eodprice.net_price ?? 0) / 100, HighPrice = 0, LowPrice = 0, UnderlyingStatus = "正常运行", UnderlyingInstrumentType = "Bonds", ReferencePrice = (double)(eodprice.yield ?? 0), DeciSettlePrice = eodprice.dirty_price_close, DeciClosePrice = eodprice.net_price, DeciReferencePrice = eodprice.yield, }; //数据加载到字典中 var list = eodFutureQuery.Concat(eodStockQuery).Concat(eodBondQuery).ToArray(); lock (_priceDic) { foreach (var item in list) { if (item.UnderlyingCode != null) { //if (item.UnderlyingInstrumentType == "Bonds") //{ // // [Layer2-待统一] 债券映射口径:SettlePrice=全价(dirty_price_close),ClosePrice=净价(net_price)。 // // 注意:这与 EodPriceQueryService.GetBondPrice 的映射【完全相反】(GetBondPrice: ClosePrice=全价,SettlePrice=净价)。 // // 两处对"债券收盘价/结算价"的净全价定义不一致属历史遗留,请勿随意改动单侧,需业务先定调后统一(见 TryGetSettlementEodPrice 注释)。 // item.SettlePrice = Convert.ToDouble(BondPriceConverter.ToStorage(item.DeciSettlePrice)); // item.ClosePrice = Convert.ToDouble(BondPriceConverter.ToStorage(item.DeciClosePrice)); // item.ReferencePrice = Convert.ToDouble(BondPriceConverter.ToStorage(item.DeciReferencePrice)); //} _priceDic[item.UnderlyingCode] = item; } } if (underlyingCodes != null) { foreach (var code in underlyingCodes) { if (!string.IsNullOrWhiteSpace(code) && !_priceDic.ContainsKey(code)) { _priceDic[code] = null; } } } } if (IsDiviendPrice) { DividendPrice(ValueDate, _priceDic.Values); } return this; } #endregion #region----IPriceProvider+IEodPriceProvider----- public double GetPrice(string instrumentCode, SettlementTypeEnum settlementType) { return TryGetEodPrice(instrumentCode, out var ep) ? ep.GetPrice(settlementType) : 0; } public bool TryGetPrice(string instrumentCode, SettlementTypeEnum settlementType, out double price) { if (TryGetEodPrice(instrumentCode, out var ep)) { price = ep.GetPrice(settlementType); return true; } price = 0; return false; } /// /// 获取标的收盘价或结算价(如果标的在取值日之前已到期,则取标的到期日的收盘价或结算价) /// public bool TryGetEodPrice(string underlyingCode, out EodPrice eodPrice) { eodPrice = null; if (string.IsNullOrWhiteSpace(underlyingCode)) { return false; } lock (_priceDic) { if (_priceDic.TryGetValue(underlyingCode, out eodPrice)) { return eodPrice != null; } } if (EodPriceQueryService.TryGetEodPrice(ValueDate, underlyingCode, out eodPrice)) { if (eodPrice.IsStock && IsDiviendPrice) { //根据标的ID或标的代码从数据源中查出来以后进行除权并缓存 DividendPrice(ValueDate, new[] { eodPrice }); } } else { eodPrice = null; //使用标的代码查找结算价未找到数据时也缓存起来 } lock (_priceDic) { _priceDic[underlyingCode] = eodPrice; } return eodPrice != null; } private static void DividendPrice(DateTime valueDate, IEnumerable eodPrices) { int[] unids = null; var stockQuery = eodPrices.Where(n => n.IsStock); var stockCount = stockQuery.Count(); if (stockCount < 1) { return; } if (stockCount < 10) { unids = stockQuery.Select(n => n.UnderlyingId).ToArray(); } var dividendService = new DividendService(OptUserInfo.SystemUser); var dividends = dividendService.GetExDividends(valueDate, unids).ToArray(); foreach (var ep in stockQuery) { var dividend = dividends.FirstOrDefault(n => n.UnderlyingId == ep.UnderlyingId); if (dividend != null) { ep.ClosePrice = ep.SettlePrice = dividendService.GetPrice(ep.ClosePrice, dividend); } } } /// /// /// public IEodPriceProviderWrap GetPriceProvider(SettlementTypeEnum settlementType = SettlementTypeEnum.ClosePrice) { return new EodPriceProviderWrap(this, settlementType); } #endregion /// /// 获取当前已缓存的日终价格数据列表(返回数据中一定不存在重复的标的代码) /// public IEnumerable GetEodPriceList() { lock (_priceDic) { return _priceDic.Values.ToArray(); } } #region----IBasketPriceProvider---- /// /// 获取篮子的子标的价格 /// public bool TryGetSubPrice(string underlyingCode, out double price, out double settlePrice) { if (TryGetEodPrice(underlyingCode, out var eodPrice)) { price = eodPrice.ClosePrice; settlePrice = eodPrice.SettlePrice; return true; } price = settlePrice = 0; return false; } #endregion /// /// 检查给定的标的是否在当前缓存中有值 /// public bool HasValue(string underlyingCode) { if (string.IsNullOrEmpty(underlyingCode)) { return false; } lock (_priceDic) { return _priceDic.TryGetValue(underlyingCode, out var value) && value != null; } } /// /// 手动设置日终价格 /// public void SetPrice(EodPrice eodPrice) { if (!string.IsNullOrWhiteSpace(eodPrice?.UnderlyingCode)) { lock (_priceDic) { _priceDic[eodPrice.UnderlyingCode] = eodPrice; } } } } /// /// IEodPriceProvider接口对象包装成IPriceProvider接口 /// public class EodPriceProviderWrap : IPriceProvider, IEodPriceProviderWrap { public IEodPriceProvider EodPriceProvider { get; } public SettlementTypeEnum SettlementType { get; private set; } /// /// /// /// /// public EodPriceProviderWrap(IEodPriceProvider eodPriceProvider, SettlementTypeEnum settlementType) { SettlementType = settlementType; EodPriceProvider = eodPriceProvider ?? throw new ArgumentNullException(nameof(eodPriceProvider)); } public double GetPrice(string instrumentCode) { return EodPriceProvider.GetPrice(instrumentCode, SettlementType); } public bool TryGetPrice(string instrumentCode, out double price) { return EodPriceProvider.TryGetPrice(instrumentCode, SettlementType, out price); } public override string ToString() { return SettlementType.ToString(); } } /// /// 将IPriceProvider接口对象包装成IEodPriceProvider接口 /// public class EodPriceProviderAs : IEodPriceProvider { public IPriceProvider PriceProvider { get; } /// /// /// /// public EodPriceProviderAs(IPriceProvider priceProvider) { PriceProvider = priceProvider ?? throw new ArgumentNullException(nameof(priceProvider)); } public double GetPrice(string instrumentCode, SettlementTypeEnum settlementType) { return PriceProvider.GetPrice(instrumentCode); } public bool TryGetPrice(string instrumentCode, SettlementTypeEnum settlementType, out double price) { return PriceProvider.TryGetPrice(instrumentCode, out price); } } /// /// /// public static class EodPriceProviderFactory { /// /// 使用缓存(10分钟更新一次) /// public static EodPriceProvider Get(DateTime valueDate) { var key = "EodPriceProviderFactory" + valueDate.ToString("yyyyMMdd"); var priceProvider = Providers.MemoryCacheProvider.Default.Get(key); if (priceProvider != null) { return priceProvider; } priceProvider = new EodPriceProvider(valueDate); Providers.MemoryCacheProvider.Default.Set(key, priceProvider, DateTimeOffset.Now.AddMinutes(10)); return priceProvider; } } }