using YLErp.DBModels; using YLErp.DBModels.Enums; namespace YLErp.Modules.SwapModule; /// /// EQD-7084 新“框架合约”Tab 的口径测试。 /// 纯计算测试不依赖数据库,直接锁定 EodPnlCalculator 的新口径。 /// [TestClass] public class SwapEodPositionRiskNewTabTest { [TestMethod] public void 新口径_普通利息排除保证金_浮动收益剔除费用并保留估值总额() { var floating = new[] { // EOD 归一后,费用已经按我方收益视角落库;支付费用为负数。 FloatingLeg("600000", 1, 100m, 0m, -12m, "普通收益互换") }; var interests = new[] { InterestLeg(1, (int)InterestModeEnum.固定值, 80m, 0.02m, 0.02m), InterestLeg(1, (int)InterestModeEnum.初始预付金, 20m, 0.01m, 0.01m) }; var fields = InvokeCalculation( floating, interests, structureType: "普通收益互换", notionalValue: 1_000m, startDate: new DateTime(2026, 1, 1), maturityDate: new DateTime(2026, 12, 31), periodAmount: 5m, dividendPayDate: 0); Assert.AreEqual(100m, GetDecimal(fields, "FloatingUnrealizedPnl"), 0.0001m, "新浮动端待实现收益应排除 PosiFeePending:PosiProfitSum(88) - PosiFeePending(-12) = 100"); Assert.AreEqual(-12m, GetDecimal(fields, "OpeningClosingFee"), 0.0001m, "开平仓费用直接使用 EOD 已归一的 PosiFeePending"); Assert.AreEqual(80m, GetDecimal(fields, "OrdinaryInterestPnl"), 0.0001m, "利息端待实现收益应排除初始/维持保证金腿"); Assert.AreEqual(-20m, GetDecimal(fields, "MarginInterestAmount"), 0.0001m, "保证金利息仍应按保证金腿方向计入估值"); Assert.AreEqual(153m, GetDecimal(fields, "MaturityNettingValuation"), 0.0001m, "估值应保持旧口径:100 - 12 + 80 - 20 + 5 = 153;费用只计一次"); } [TestMethod] public void 新口径_当前利率合计使用普通利息腿TdInterestRate_并识别FR007() { var fr007Leg = InterestLeg(2, (int)InterestModeEnum.合约名义本金规模, 20m, 0.03m, 0.018m); fr007Leg.FloatRateUnderlyingCode = "FR007"; fr007Leg.FloatRate = 0.015m; var fields = InvokeCalculation( new[] { FloatingLeg("600001", 2, 100m, 0m, 0m, "普通收益互换") }, new[] { InterestLeg(1, (int)InterestModeEnum.固定值, 10m, 0.02m, 0.0125m), fr007Leg }, structureType: "普通收益互换", notionalValue: 100m, startDate: new DateTime(2026, 2, 1), maturityDate: new DateTime(2026, 8, 1), periodAmount: 0m, dividendPayDate: 1); Assert.AreEqual(0.0305m, GetDecimal(fields, "InterestRatePrice"), 0.0000001m, "利率端价格应为普通利息腿当前 TdInterestRate 合计,而非默认利差合计"); Assert.AreEqual("FR007", GetString(fields, "InterestBenchmark")); } [TestMethod] public void 新口径_普通利息腿无FR007时基准为固定利率() { var fields = InvokeCalculation( new[] { FloatingLeg("600002", 1, 100m, 0m, 0m, "普通收益互换") }, new[] { InterestLeg(1, (int)InterestModeEnum.固定值, 10m, 0.02m, 0.0125m) }, structureType: "普通收益互换", notionalValue: 100m, startDate: new DateTime(2026, 2, 1), maturityDate: new DateTime(2026, 8, 1), periodAmount: 0m, dividendPayDate: 1); Assert.AreEqual("固定利率", GetString(fields, "InterestBenchmark")); } [TestMethod] public void 新口径_债券期初价格按风险页约定放大百分价格_并保留合同字段() { var fields = InvokeCalculation( new[] { FloatingLeg("110000", 1, 99.12m, 0m, 0m, "普通债券类收益互换", "Bond") }, new[] { InterestLeg(1, (int)InterestModeEnum.固定值, 1m, 0.01m, 0.01m) }, structureType: "普通债券类收益互换", notionalValue: 9_900m, startDate: new DateTime(2026, 3, 1), maturityDate: new DateTime(2027, 3, 1), periodAmount: 0m, dividendPayDate: 1); Assert.AreEqual(99.12m, GetDecimal(fields, "InitialPrice"), 0.0001m, "债券日终 PosiGrossPrice 已由 SetPosiPrice 按风险页口径缩放,新接口不能再次乘 100"); Assert.AreEqual(9_900m, GetDecimal(fields, "NotionalQuantity"), 0.0001m); Assert.AreEqual("多头", GetString(fields, "UnderlyingDirection")); Assert.AreEqual("110000", GetString(fields, "UnderlyingCode")); Assert.AreEqual("Bond", GetString(fields, "UnderlyingInstrumentType")); Assert.AreEqual(new DateTime(2026, 3, 1), GetDate(fields, "ContractStartDate")); Assert.AreEqual(new DateTime(2027, 3, 1), GetDate(fields, "ContractMaturityDate")); } private static object InvokeCalculation( IEnumerable floating, IEnumerable interests, string structureType, decimal notionalValue, DateTime startDate, DateTime maturityDate, decimal periodAmount, int dividendPayDate) { return EodPnlCalculator.CalculateEodSwapRiskNewFields( floating, interests, structureType, notionalValue, startDate, maturityDate, periodAmount, dividendPayDate); } private static decimal GetDecimal(object fields, string name) => Convert.ToDecimal(fields.GetType().GetProperty(name)!.GetValue(fields)); private static string GetString(object fields, string name) => (string)fields.GetType().GetProperty(name)!.GetValue(fields)!; private static DateTime GetDate(object fields, string name) => (DateTime)fields.GetType().GetProperty(name)!.GetValue(fields)!; private static eod_swap_position FloatingLeg( string code, int positionType, decimal mtm, decimal dividend, decimal fee, string structureType, string instrumentType = null) => new() { UnderlyingCode = code, UnderlyingInstrumentType = instrumentType ?? structureType, PositionType = positionType, PosiGrossPrice = mtm, PosiMtmPnL = mtm, PosiDividendSum = dividend, PosiFeePending = fee, PosiProfitSum = mtm + dividend + fee, PosiNotionalValue = 100m }; private static eod_swap_position InterestLeg( int direction, int mode, decimal profit, decimal defaultRate, decimal currentRate) => new() { InterestDirection = direction, InterestMode = mode, InterestProfitSum = profit, InterestRateDefault = defaultRate, TdInterestRate = currentRate }; }