namespace UnitTestProject.Modules.SwapModule.Accrual { /// /// 契约参考实现(确认书公式,TEST-MATRIX §8a)——全矩阵统一 oracle 供给。 /// /// 【独立性约束·勿破坏】本类只实现确认书公式原文,禁止引用任何生产计息引擎类 /// (YLErp.Modules.SwapModule.Accrual.* / SwapDealService),否则 oracle 与被测对象同源, /// 失去"独立参考"资格(oracle 分级第一级,见 TEST-MATRIX §7.4)。 /// /// 确认书公式(国联民生收益互换确认书-现券/ETF 四份一致): /// 参考利率(绝对) = ∏[i=1..k] ( 1 + (FR007i + 利差) × di / 365 ) − 1 /// 结息额(平仓部分) = 实际平掉额 × 参考利率(绝对) /// - k = 计息期包含的重置期个数;完整重置期 di = 重置频率(生产 7 天),末段不足按实际日历日 /// - 重置期自计息期首日按重置频率依次推算;首个重置期始于计息期首日;末段收口到计息期最后一日 /// - 利率确定日 = 每个重置期首日(重置日)的上一个营业日,取该日 FR007 /// - 计息期 = 自起始日(含)至到期日(不含)——即算头不算尾 "10"(生产主力条款) /// - 计息基准 A/365 /// /// 营业日准则:本参考实现按周末近似(周六/周日非营业日);法定节假日历由调用方通过 /// 取价委托自行吸收(如按确定日提供同一利率)。测试与生产参数对齐(§8):重置 7 天 / 365。 /// public static class ContractReferenceCalc { /// /// 参考利率(绝对) = ∏(1 + (FR007i+利差)×di/annualDays) − 1。 /// /// 计息期首日(含) /// 计息期末日("10"不含/"11"含,由 calcLast 决定) /// 重置频率天数(生产 7) /// 利差(InterestRateDefault,如 +0.25% = 0.0025) /// 取价委托:入参=利率确定日(重置日上一营业日),返回该日 FR007 /// 算头(生产 "10"/"11" 为 true) /// 算尾(生产 "10" 为 false) /// 计息基准(生产 365) public static decimal ReferenceRateAbsolute( DateTime startDate, DateTime endDate, int resetDays, decimal spread, Func fixing, bool calcFirst = true, bool calcLast = false, int annualDays = 365) { var totalDays = (endDate - startDate).Days + (calcFirst ? 0 : -1) + (calcLast ? 1 : 0); if (totalDays <= 0) return 0m; decimal factor = 1m; var resetDate = startDate; // 首个重置期始于计息期首日 var remaining = totalDays; while (remaining > 0) { var di = Math.Min(resetDays, remaining); // 完整期 di=resetDays,末段按实际日历日 var fixingDate = PreviousBusinessDay(resetDate); var allIn = fixing(fixingDate) + spread; factor *= 1m + allIn * di / annualDays; remaining -= di; resetDate = resetDate.AddDays(di); } return factor - 1m; } /// 结息额(平仓部分)= 实际平掉额 × 参考利率(绝对)。 public static decimal ClosedInterest(decimal closedNotional, decimal referenceRate) => closedNotional * referenceRate; /// 利率确定日 = 重置日的上一营业日(周末近似)。 public static DateTime PreviousBusinessDay(DateTime date) { do { date = date.AddDays(-1); } while (date.DayOfWeek is DayOfWeek.Saturday or DayOfWeek.Sunday); return date; } } }