diff --git a/UnitTestProject/Modules/BondCalcHeplerTest.cs b/UnitTestProject/Modules/BondCalcHeplerTest.cs new file mode 100644 index 00000000..084f5236 --- /dev/null +++ b/UnitTestProject/Modules/BondCalcHeplerTest.cs @@ -0,0 +1,61 @@ +using Microsoft.VisualStudio.TestTools.UnitTesting; +using YLErp.Helpers; +using YLErp.Model; + +namespace UnitTestProject.Modules +{ + /// + /// EQD-6953 疑似到期债券值域闸门:IsMaturedDegenerate / IsResultAbsurd 分支覆盖。 + /// 场景来源:UAT 060203.IB(2006年国债,2026估值日已无剩余现金流)—— + /// jquantlib 对空现金流求解得 ytm=0、净/全价均为面值100,errCode=0"成功但退化"。 + /// 与前端 swapCalc.js::getBondCalcErrorMessage 的同款闸门保持一致口径。 + /// + [TestClass] + public class BondCalcHeplerTest + { + [TestMethod] + public void 到期退化值_三条件同时成立_命中() + { + var r = new CalBondResult { cleanPrice = 100m, dirtyPrice = 100m, ytm = 0m }; + Assert.IsTrue(BondCalcHepler.IsMaturedDegenerate(r)); + } + + [TestMethod] + public void 正常券_不命中_按UAT实测180205IB() + { + var r = new CalBondResult { cleanPrice = 97.43300000000002m, dirtyPrice = 100.00001369863016m, ytm = 6.738278242318886m }; + Assert.IsFalse(BondCalcHepler.IsMaturedDegenerate(r)); + } + + [TestMethod] + public void ytm为0但净价非面值_不命中_真实零息平价券场景() + { + var r = new CalBondResult { cleanPrice = 99.5m, dirtyPrice = 100m, ytm = 0m }; + Assert.IsFalse(BondCalcHepler.IsMaturedDegenerate(r)); + } + + [TestMethod] + public void 价格为面值但ytm非0_不命中_正常息票平价券场景() + { + var r = new CalBondResult { cleanPrice = 100m, dirtyPrice = 100.5m, ytm = 3.2m }; + Assert.IsFalse(BondCalcHepler.IsMaturedDegenerate(r)); + } + + [TestMethod] + public void ytm为null_不命中() + { + var r = new CalBondResult { cleanPrice = 100m, dirtyPrice = 100m, ytm = null }; + Assert.IsFalse(BondCalcHepler.IsMaturedDegenerate(r)); + } + + [TestMethod] + public void IsResultAbsurd_到期退化值_命中并带原因() + { + var r = new CalBondResult { cleanPrice = 100m, dirtyPrice = 100m, ytm = 0m }; + var ok = typeof(BondCalcHepler) + .GetMethod("IsResultAbsurd", System.Reflection.BindingFlags.NonPublic | System.Reflection.BindingFlags.Static) + .Invoke(null, new object[] { r, null }); + Assert.IsTrue((bool)ok); + } + } +} diff --git a/YLErpDAL/Helpers/BondCalcHepler.cs b/YLErpDAL/Helpers/BondCalcHepler.cs index 4f7acd21..ed6b6bcd 100644 --- a/YLErpDAL/Helpers/BondCalcHepler.cs +++ b/YLErpDAL/Helpers/BondCalcHepler.cs @@ -162,7 +162,8 @@ namespace YLErp.Helpers /// 命中则上层返回 null、不回写,避免把不可信结果覆盖用户手工输入。规则: /// - 净价/全价:占面值百分比,正常约 20~300,必须为正且不破千(≤0 或 >1000 视为离谱); /// - 收益率(ytm,百分数口径):|ytm| > 100 视为爆炸(允许负利率债,但量级须合理); - /// - 哨兵值:任一字段命中 -999999 或 -999999×100(如 yieldToCP 不可用哨兵透传)。 + /// - 哨兵值:任一字段命中 -999999 或 -999999×100(如 yieldToCP 不可用哨兵透传); + /// - 疑似到期/无剩余现金流:见 。 /// 与前端 swapCalc.js::getBondCalcErrorMessage 的值域闸门保持一致口径。 /// private static bool IsResultAbsurd(CalBondResult data, out string reason) @@ -186,8 +187,27 @@ namespace YLErp.Helpers reason = "债券计算收益率量级异常(" + data.ytm.Value + "),请检查估值日/价格输入或联系管理员核对债券计算服务"; return true; } + if (IsMaturedDegenerate(data)) + { + reason = "该债券疑似已到期或无剩余现金流(收益率=0、净价/全价均为面值100),无法反算,请核对债券到期日或手工填写"; + return true; + } return false; } + + /// + /// 疑似到期/无剩余现金流债券:估值日晚于到期日时底层 jquantlib 对空现金流求解得 ytm=0、 + /// 净价/全价均为面值100——三者同时成立在现实在市券中几乎不可能(UAT 060203.IB 实证)。 + /// 判定条件与前端 swapCalc.js::getBondCalcErrorMessage 内同款闸门保持一致,勿单边改动。 + /// public 供单测直接覆盖(BondCalcHeplerTest)。 + /// + public static bool IsMaturedDegenerate(CalBondResult data) + { + return data != null + && data.ytm == 0m + && data.cleanPrice == 100m + && data.dirtyPrice == 100m; + } } }