diff --git a/YLErpWeb/fe-tests/bondCalc.test.js b/YLErpWeb/fe-tests/bondCalc.test.js index 6595bdab..5fc69d0f 100644 --- a/YLErpWeb/fe-tests/bondCalc.test.js +++ b/YLErpWeb/fe-tests/bondCalc.test.js @@ -120,6 +120,24 @@ describe('边界:计算器未返回的值不覆盖、无变化不写', () => { }); }); +describe('疑似到期债券值域闸门:ytm=0 且 净/全价均为面值100(UAT 060203.IB 实证的退化形态)', () => { + test('三条件同时成立 → 命中闸门返回真实原因(不回写)', () => { + const resp = { cleanPrice: 100, dirtyPrice: 100, ytm: 0 }; + const msg = SwapCalc.getBondCalcErrorMessage(resp); + expect(msg).toContain('疑似已到期'); + expect(msg).toContain('手工填写'); + }); + test('正常券不命中 → 返回 null 放行回写', () => { + expect(SwapCalc.getBondCalcErrorMessage({ cleanPrice: 97.43, dirtyPrice: 100.00001369863016, ytm: 6.738278242318886 })).toBeNull(); + }); + test('ytm=0 但价格非面值(真实零息平价券)→ 不命中', () => { + expect(SwapCalc.getBondCalcErrorMessage({ cleanPrice: 99.5, dirtyPrice: 100, ytm: 0 })).toBeNull(); + }); + test('价格=面值 但 ytm 非 0(正常息票平价券)→ 不命中', () => { + expect(SwapCalc.getBondCalcErrorMessage({ cleanPrice: 100, dirtyPrice: 100.5, ytm: 3.2 })).toBeNull(); + }); +}); + describe('错误反馈:getBondCalcErrorMessage(对齐 C# BondCalcHepler 的 errCode 守卫)', () => { test('空响应 → 提示"无响应",且绝不回写', () => { expect(SwapCalc.getBondCalcErrorMessage(null)).toBe("债券计算器无响应,已保留手工输入"); diff --git a/YLErpWeb/wwwroot/Scripts/app/swaptrade/swapCalc.js b/YLErpWeb/wwwroot/Scripts/app/swaptrade/swapCalc.js index 90106724..d912b401 100644 --- a/YLErpWeb/wwwroot/Scripts/app/swaptrade/swapCalc.js +++ b/YLErpWeb/wwwroot/Scripts/app/swaptrade/swapCalc.js @@ -352,6 +352,13 @@ return "债券计算收益率量级异常(" + yd + "),已保留手工输入;" + "请检查估值日/价格输入或联系管理员核对债券计算服务"; } + // 疑似到期/无剩余现金流:估值日过到期日后 jquantlib 对空现金流求解得 ytm=0、净/全价均为面值100 + // (三者同时成立在现实在市券中几乎不可能,UAT 060203.IB 实证)。宁可不回写并提示真实原因, + // 也不用退化值覆盖手工输入。与后端 BondCalcHepler.IsMaturedDegenerate 同口径,勿单边改动判定条件。 + if (yd === 0 && cp === 100 && dp === 100) { + return "该债券疑似已到期或无剩余现金流(收益率=0、净价/全价均为面值100),无法反算," + + "请核对债券到期日或手工填写净价/全价/收益率"; + } return null; }