using Newtonsoft.Json; using System; using System.Collections.Generic; using System.Globalization; using System.Linq; using YLErp.BLL; namespace YLErp.Modules.RiskEngine { /// /// 风控规则专用日历辅助类。 /// 当前主要用于债券类规则按银行间日历确认“上一收盘日”,避免简单按估值表倒序取最近日期导致口径偏差。 /// public static class RiskCalendarHelper { /// /// 获取指定日期的上一银行间交易日。 /// /// 当前风控执行使用的数据库上下文。 /// 当前交易日或业务基准日。 /// 严格早于入参日期的上一银行间交易日。 /// 数据库上下文为空。 /// 缺少银行间日历、日历内容异常或在保护范围内找不到上一交易日。 public static DateTime GetPreviousInterbankTradingDay(YLContext dbContext, DateTime date) { if (dbContext == null) throw new ArgumentNullException(nameof(dbContext)); var holidayCache = new Dictionary>(); var currentDate = date.Date.AddDays(-1); // 最多向前查 370 天,既覆盖跨年和长假场景,也避免日历配置异常时出现无限循环。 for (var i = 0; i < 370; i++) { var holidays = GetInterbankHolidays(dbContext, currentDate.Year, holidayCache); var currentDateText = currentDate.ToString("yyyy,MM,dd", CultureInfo.InvariantCulture); // calendar.HolidayJson 存的是非交易日;不在非交易日集合内,即认为是银行间交易日。 if (!holidays.Contains(currentDateText)) return currentDate; currentDate = currentDate.AddDays(-1); } throw new Exception($"未找到{date:yyyy-MM-dd}的上一银行间交易日"); } /// /// 获取指定年份的银行间非交易日集合。 /// /// 当前风控执行使用的数据库上下文。 /// 日历年份。 /// 单次查询过程内的年份级缓存,跨年查找时避免重复读取同一年日历。 /// 格式为 yyyy,MM,dd 的非交易日集合。 private static HashSet GetInterbankHolidays(YLContext dbContext, int year, Dictionary> holidayCache) { if (holidayCache.TryGetValue(year, out var holidays)) return holidays; // 同一年可能存在多种市场日历;规则 12 明确使用 Country=IB 的银行间日历。 var calendar = dbContext.calendar .Where(c => c.Year == year && (c.ValidState == null || c.ValidState != ConsGlobal.InValid)) .ToList() .FirstOrDefault(c => string.Equals(c.Country, "IB", StringComparison.OrdinalIgnoreCase)); if (calendar == null) throw new Exception($"未找到{year}年银行间日历"); if (string.IsNullOrWhiteSpace(calendar.HolidayJson)) throw new Exception($"{year}年银行间日历HolidayJson为空"); List holidayList; try { holidayList = JsonConvert.DeserializeObject>(calendar.HolidayJson); } catch (Exception ex) { throw new Exception($"{year}年银行间日历HolidayJson解析失败", ex); } holidays = new HashSet(holidayList ?? new List()); holidayCache[year] = holidays; return holidays; } } }