消除9处价格转换字面量(*0.01m/*100),统一走BondPriceConverter.ToStorage/ToDisplay。 - 新增YLErpDAL/Helpers/BondPriceConverter.cs(含可空重载) - 批次1(危险字面量): BondPaymentService/RealtimePnlCalc/RealTimeClientBanlanceService/SwapTradeAutoService - 批次2-4(调用收敛): SwapFlowService/SwapFlowImportService/SwapEndConfirmService/EodPriceProvider/EodPriceQueryService - RealtimePnlCalc:601/602价格×数量维度交织处加注释,不机械合并 不改bondPriceMultiple常量值,纯调用方式收敛。SwapModule 155测试全绿,行为不变。
416 lines
15 KiB
C#
416 lines
15 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 = 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(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;
|
|
}
|
|
}
|
|
}
|