419 lines
16 KiB
C#
419 lines
16 KiB
C#
using YLErp.Abstract.DataProviders;
|
||
using YLErp.Helpers;
|
||
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 = (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;
|
||
}
|
||
|
||
/// <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;
|
||
}
|
||
}
|
||
}
|