using Qdp.Pricing.Base.Implementations; using YLErp.BLL; using YLErp.Models; using YLErp.Modules.TradeModule.DealModule; using YLErp.QdpModule; namespace YLErp.Modules.CalculationModule { /// /// 获取fixing的服务 /// public static class FixingService { /// /// 获取fixing数据 /// public static string GetFixingString(FixingRequest request) { if (request is null) { return null; } if (request.ValueDate == default) { request.ValueDate = valuedateBLL.ValueDate; } if (request.StartDate == default) { request.StartDate = request.ValueDate; } else if (request.ValueDate < request.StartDate) { return string.Empty; } //估值日期大于到期日期则估值日期变为到期日期 if (request.ValueDate > request.ExerciseDate) { request.ValueDate = request.ExerciseDate; } Dictionary priceDic = null; HashSet observationDates = null; //仅选出观察日列表中的价格作为fixing if (!string.IsNullOrWhiteSpace(request.ObservationDates)) { observationDates = QdpHelper.ParseObservationDate(request.ObservationDates).Select(x => x.DateTime).ToHashSet(); } else { observationDates = CalendarImpl.Get("chn").BizDaysBetweenDatesExcluStartDay(request.StartDate, request.ValueDate).Select(x => x.DateTime).ToHashSet(); observationDates.Add(request.StartDate); observationDates.Add(request.ValueDate); } var useReferencePrice = request.SettlementType == SettlementTypeEnum.ReferencePrice; using (var db = DbContextFactory.GetYLDbContext()) { //日终股票价格 if (ConsGlobal.InstrumentType.IsStock(request.InstrumentType)) { var query = from e in db.eod_stock_price where e.UnderlyingCode == request.UnderlyingCode && e.ValueDate >= request.StartDate && e.ValueDate <= request.ValueDate orderby e.ValueDate select new { e.ValueDate, Price = useReferencePrice ? (e.ReferencePrice ?? e.ClosePrice) : e.ClosePrice }; priceDic = query.ToDictionary(n => n.ValueDate, n => n.Price); ExdividenProcess(request, priceDic); } else { var query = from e in db.eod_commodity_future_price where e.UnderlyingCode == request.UnderlyingCode && e.ValueDate >= request.StartDate && e.ValueDate <= request.ValueDate orderby e.ValueDate select new EodPrice { ValueDate = e.ValueDate, ClosePrice = e.ClosePrice, ReferencePrice = e.ReferencePrice, SettlePrice = e.SettlePrice }; priceDic = query.ToDictionary(e => e.ValueDate, e => e.GetPrice(request.SettlementType)); } //厦门象屿使用参考价的交易在上午10点15分进入下一交易日,所以会出现导入参考价而没有导入收盘价和结算价的情况 //如果取到的收盘价或结算价为0则可能是异常价格,受制于数据库结构不好修改,所以只能简单处理下 if (priceDic != null && !useReferencePrice && PS.Config.Company == Configuration.CompanyEnum.厦门象屿 && request.ValueDate == valuedateBLL.ValueDate && DateTime.Now.TimeOfDay < GlobalConfig.EodStartTime && priceDic.TryGetValue(request.ValueDate, out var price) && Math.Abs(price) < 1e-6) { priceDic.Remove(request.ValueDate); } if (request.ValueDate <= request.ExerciseDate && request.ValueDate >= request.StartDate) { if (priceDic == null) { priceDic = new Dictionary(); } var valueDatePrice = DataCacheProvider.GetUnderlyingDataSource().GetData(request.UnderlyingCode).Price ?? 0; if (priceDic.Count < 1 && !QdpCalendarHelper.IsHoliday(request.ValueDate)) { foreach (var observationDate in observationDates.OrderBy(x => x)) { if(request.StartDate <= observationDate && observationDate <= request.ValueDate) { priceDic.Add(observationDate, valueDatePrice); } } } if (priceDic.Any() && priceDic.Last().Key != request.ValueDate && !QdpCalendarHelper.IsHoliday(request.ValueDate)) { priceDic.Add(request.ValueDate, valueDatePrice); } } } if (priceDic != null && priceDic.Count > 0) { var em = priceDic.Where(n => observationDates.Contains(n.Key)).Select(p => $"{p.Key:yyyy-MM-dd},{p.Value}"); return string.Join(";", em); } return string.Empty; } /// /// 获取fixing数据 /// /// 估值日期 /// 场外交易 /// 开始日期 /// 观察日 public static string GetFixingString(DateTime valueDate, OtcTradeBase otcTrade, DateTime startDate, string observationDates) { if (otcTrade is null) { return null; } return GetFixingString(new FixingRequest( tradeId: otcTrade.id, valueDate: valueDate, startDate: startDate, exerciseDate: otcTrade.ExerciseDate ?? valueDate, observationDates: observationDates, instrumentType: otcTrade.UnderlyingInstrumentType, underlyingCode: otcTrade.UnderlyingCode, settlementType: otcTrade.SettlementType)); } /// /// 日终价格进行除权处理 /// private static void ExdividenProcess(FixingRequest request, Dictionary priceDic) { if (priceDic == null || priceDic.Count < 1) { return; } var service = new DividendService(OptUserInfo.SystemUser); //获取按照除权除息日期正序排列的数组,估值日不需要除权处理(O.ExDividendDate < request.ValueDate) var exDividendInfos = service.GetExDividendInfos(request.UnderlyingCode) .Where(O => O.ExDividendDate >= request.StartDate && O.ExDividendDate < request.ValueDate) .OrderBy(n => n.ExDividendDate).ToArray(); if (!exDividendInfos.Any()) { return; } var ratioDict = new DbRecordChangesService(OptUserInfo.SystemUser).GetValue( changeType: DBModels.Consts.ConsInfoChangeType.Dividend, recordId: request.TradeId, fieldName: nameof(trade.DividendRatio), optDateStart: request.StartDate, optDateEnd: request.ValueDate) .ToDictionary(K => K.OptDate, V => { return double.TryParse(V.NewValue, out var tempValue) ? (double?)tempValue : null; }); //日终价格和除权除息信息都按照正序排列 //获取除权价格则使用大于日终价格日期的除权信息除权 //循环日终价格,如果除权日期小于价格日期则被排除掉 var startIndex = 0; foreach (var kv in priceDic) { (var date, var price) = (kv.Key, kv.Value); for (var i = startIndex; i < exDividendInfos.Length; i++) { var dividenInfo = exDividendInfos[i]; //除权日当天的收盘价也需要处理 if (date <= dividenInfo.ExDividendDate.Value) { ratioDict.TryGetValue(date, out var ratio); price = service.GetPrice(price, dividenInfo, ratio); } else { startIndex = i; } } priceDic[date] = price; } } /// /// 获取fixing请求参数 /// /// /// /// public static FixingRequestBase GetRequestBase(DateTime valueDate, OtcTradeBase otcTrade) { if (otcTrade is null) { return null; } return new FixingRequestBase( tradeId: otcTrade.id, valueDate: valueDate, exerciseDate: otcTrade.ExerciseDate ?? valueDate, instrumentType: otcTrade.UnderlyingInstrumentType, underlyingCode: otcTrade.UnderlyingCode, settlementType: otcTrade.SettlementType ); } /// /// 添加或替换最后一天的fixing价格,仅fixing有值的情况下进行处理 /// public static string AddOrReplaceLastDateSpotPrice(string fixing, DateTime lastDate, double spotPrice) { if (!string.IsNullOrEmpty(fixing)) { var span = fixing.AsSpan().Trim(';'); var lastIndex = span.LastIndexOf(';'); if (span.Slice(lastIndex + 1).StartsWith(lastDate.ToString("yyyy-MM-dd").AsSpan())) { fixing = span.Slice(0, lastIndex + 1).ToString(); } else { fixing += ";"; } fixing += $"{lastDate:yyyy-MM-dd},{spotPrice}"; } return fixing; } } /// /// 亚式期权fixing数据服务 /// public static class AsianOptionFixingService { private static trade_asian_option GetAsianOption(int tradeId) { if (tradeId > 0) { using (var db = DbContextFactory.GetYLDbContext()) { return db.trade_asian_option.AsNoTracking().FirstOrDefault(n => n.TradeId == tradeId); } } return null; } /// /// 为亚式期权交易获取fixing数据 /// public static string GetFixingString(DateTime valueDate, trade trade, bool setFixingBeforeAPdStartDate = false) { if (trade.trade_asian_option == null) { trade.trade_asian_option = GetAsianOption(trade.id); if (trade.trade_asian_option == null) { return string.Empty; } } var fixingReq = GetRequest(valueDate, trade, setFixingBeforeAPdStartDate); return GetFixingString(fixingReq, trade.trade_asian_option); } /// /// 为亚式期权交易获取fixing数据 /// public static string GetFixingString(DateTime valueDate, OtcTradeBase trade, trade_asian_option asianOption) { if (trade is null) { return null; } if (asianOption == null || asianOption.TradeId != trade.id) { asianOption = GetAsianOption(trade.id); if (asianOption == null) { return string.Empty; } } return FixingService.GetFixingString(valueDate, trade , asianOption.AveragingPeriodStartDate ?? trade.TradeDate ?? valueDate, asianOption.ObservationDates); } /// /// 为亚式期权交易获取fixing数据 /// public static string GetFixingString(AsianFixingRequest request, trade_asian_option asianOption) { if (request is null) { return null; } if (asianOption == null) { asianOption = GetAsianOption(request.TradeId); if (asianOption == null) { return string.Empty; } } var AveragingPeriodStartDate = asianOption.AveragingPeriodStartDate ?? request.ValueDate; //这段逻辑从方顿Logic中抽取,但应该是通用逻辑 if (request.ValueDate < AveragingPeriodStartDate) { if (request.SetFixingBeforeAPdStartDate) { var umPrice = DataCacheProvider.GetUnderlyingDataSource().GetData(request.UnderlyingCode).Price; asianOption.Fixings = $"{request.ValueDate:yyyy-MM-dd},{umPrice}"; } else { asianOption.Fixings = string.Empty; } return asianOption.Fixings; } return FixingService.GetFixingString(new FixingRequest(request, AveragingPeriodStartDate, asianOption.ObservationDates)); } /// /// 获取亚式期权fixing请求参数 /// /// 估值日期 /// 场外交易对象 /// 是否只在固定行权价时获取 /// public static AsianFixingRequest GetRequest(DateTime valueDate, OtcTradeBase otcTrade, bool setFixingBeforeAPdStartDate = false) { if (otcTrade is null) { return null; } return new AsianFixingRequest( tradeId: otcTrade.id, valueDate: valueDate, exerciseDate: otcTrade.ExerciseDate ?? valueDate, instrumentType: otcTrade.UnderlyingInstrumentType, underlyingCode: otcTrade.UnderlyingCode, settlementType: otcTrade.SettlementType) { SetFixingBeforeAPdStartDate = setFixingBeforeAPdStartDate }; } /// /// 检查fixings是否需要填充 /// public static string CheckAsiaFixings(OtcTradeBase tr, trade_asian_option asianOption, string fixings, double spotPrice) { if (string.IsNullOrWhiteSpace(fixings) && asianOption.PayoffType == "EnhancedArithmeticAverage" && asianOption.StrikeType != "Segmented") { return $"{tr.StartDate.Value:yyyy-MM-dd},{spotPrice}"; } return fixings; } } /// /// 亚式期权fixing请求model /// public class AsianFixingRequest : FixingRequestBase { public AsianFixingRequest(FixingRequestBase baseReq) : base(baseReq) { } public AsianFixingRequest(DateTime valueDate, int tradeId, string instrumentType, string underlyingCode, DateTime exerciseDate, SettlementTypeEnum settlementType) : base(valueDate, tradeId, instrumentType, underlyingCode, exerciseDate, settlementType) { } /// /// 来源于以前的方顿逻辑暂时保留 /// public bool SetFixingBeforeAPdStartDate { get; set; } } }