Files
zszq-trs/YLErpDAL/Modules/DataProviderModule/EodPriceProvider.cs
T

415 lines
15 KiB
C#

using YLErp.Abstract.DataProviders;
using YLErp.Models;
using YLErp.Modules.TradeModule.DealModule;
using YLErp.QdpModule;
namespace YLErp.Modules.DataProviderModule
{
/// <summary>
/// 收盘结算价数据服务(不包括场内期权结算价)
/// </summary>
public class EodPriceProvider : IEodPriceProvider, IEodPriceProviderV2, IBasketPriceProvider
{
readonly Dictionary<string, EodPrice> _priceDic;
/// <summary>
/// 构造函数
/// </summary>
/// <param name="valueDate">结算价取值日</param>
/// <param name="isDiviendPrice">是否需要前复权价</param>
public EodPriceProvider(DateTime valueDate, bool isDiviendPrice = false)
{
ValueDate = valueDate.Date;
IsDiviendPrice = isDiviendPrice;
_priceDic = new Dictionary<string, EodPrice>(StringComparer.OrdinalIgnoreCase);
}
public void SetPreValueDate(DateTime date)
{
PreValueDate = date;
}
/// <summary>
/// 结算价取值日
/// </summary>
public DateTime ValueDate { get; }
/// <summary>
/// 上一个结算日
/// </summary>
public DateTime? PreValueDate { get; set; }
/// <summary>
/// 是否价格除权
/// </summary>
public bool IsDiviendPrice { get; }
/// <summary>
/// 当前缓存中的价格数量
/// </summary>
public int Count => _priceDic.Count;
#region----初始化----
/// <summary>
/// 初始化数据字典
/// (提前初始化可在大批量标的取结算价时提高一定性能)
/// </summary>
public EodPriceProvider Initialize(IEnumerable<string> underlyingCodes = null)
{
using var db = DbContextFactory.GetYLDbContext();
var predicate1 = PredicateBuilder.Create<eod_commodity_future_price>(eodprice => eodprice.ValueDate == ValueDate);
var predicate2 = PredicateBuilder.Create<eod_stock_price>(eodprice => eodprice.ValueDate == ValueDate);
var predicate3 = PredicateBuilder.Create<ChinaBondValuation>(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 = 0,
SettlePrice = 0,
HighPrice = 0,
LowPrice = 0,
UnderlyingStatus = "正常运行",
UnderlyingInstrumentType = "Bonds",
ReferencePrice = 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")
{
item.SettlePrice = Convert.ToDouble(item.DeciSettlePrice * ConsGlobal.bondPriceMultiple);
item.ClosePrice = Convert.ToDouble(item.DeciClosePrice * ConsGlobal.bondPriceMultiple);
item.ReferencePrice = Convert.ToDouble(item.DeciReferencePrice * ConsGlobal.bondPriceMultiple);
}
_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;
}
/// <summary>
/// 获取标的收盘价或结算价(如果标的在取值日之前已到期,则取标的到期日的收盘价或结算价)
/// </summary>
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<EodPrice> 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);
}
}
}
/// <summary>
///
/// </summary>
public IEodPriceProviderWrap GetPriceProvider(SettlementTypeEnum settlementType = SettlementTypeEnum.ClosePrice)
{
return new EodPriceProviderWrap(this, settlementType);
}
#endregion
/// <summary>
/// 获取当前已缓存的日终价格数据列表(返回数据中一定不存在重复的标的代码)
/// </summary>
public IEnumerable<EodPrice> GetEodPriceList()
{
lock (_priceDic)
{
return _priceDic.Values.ToArray();
}
}
#region----IBasketPriceProvider----
/// <summary>
/// 获取篮子的子标的价格
/// </summary>
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
/// <summary>
/// 检查给定的标的是否在当前缓存中有值
/// </summary>
public bool HasValue(string underlyingCode)
{
if (string.IsNullOrEmpty(underlyingCode))
{
return false;
}
lock (_priceDic)
{
return _priceDic.TryGetValue(underlyingCode, out var value) && value != null;
}
}
/// <summary>
/// 手动设置日终价格
/// </summary>
public void SetPrice(EodPrice eodPrice)
{
if (!string.IsNullOrWhiteSpace(eodPrice?.UnderlyingCode))
{
lock (_priceDic)
{
_priceDic[eodPrice.UnderlyingCode] = eodPrice;
}
}
}
}
/// <summary>
/// IEodPriceProvider接口对象包装成IPriceProvider接口
/// </summary>
public class EodPriceProviderWrap : IPriceProvider, IEodPriceProviderWrap
{
public IEodPriceProvider EodPriceProvider { get; }
public SettlementTypeEnum SettlementType { get; private set; }
/// <summary>
///
/// </summary>
/// <param name="eodPriceProvider"></param>
/// <param name="settlementType"></param>
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();
}
}
/// <summary>
/// 将IPriceProvider接口对象包装成IEodPriceProvider接口
/// </summary>
public class EodPriceProviderAs : IEodPriceProvider
{
public IPriceProvider PriceProvider { get; }
/// <summary>
///
/// </summary>
/// <param name="priceProvider"></param>
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);
}
}
/// <summary>
///
/// </summary>
public static class EodPriceProviderFactory
{
/// <summary>
/// 使用缓存(10分钟更新一次)
/// </summary>
public static EodPriceProvider Get(DateTime valueDate)
{
var key = "EodPriceProviderFactory" + valueDate.ToString("yyyyMMdd");
var priceProvider = Providers.MemoryCacheProvider.Default.Get<EodPriceProvider>(key);
if (priceProvider != null)
{
return priceProvider;
}
priceProvider = new EodPriceProvider(valueDate);
Providers.MemoryCacheProvider.Default.Set(key, priceProvider, DateTimeOffset.Now.AddMinutes(10));
return priceProvider;
}
}
}