From cd981527ab7e1c86ed4dd5555b0c72c163877c36 Mon Sep 17 00:00:00 2001 From: hjhan Date: Fri, 21 Aug 2026 15:26:46 +0800 Subject: [PATCH] =?UTF-8?q?feat(web):=20EQD-6953=20=E5=89=8D=E7=AB=AF?= =?UTF-8?q?=E5=80=BC=E5=9F=9F=E9=97=B8=E9=97=A8=E8=A1=A5=E7=96=91=E4=BC=BC?= =?UTF-8?q?=E5=88=B0=E6=9C=9F=E5=80=BA=E5=88=B8=E5=88=A4=E5=AE=9A=E2=80=94?= =?UTF-8?q?=E2=80=94ytm=3D0=E4=B8=94=E5=87=80/=E5=85=A8=E4=BB=B7=E5=9D=87?= =?UTF-8?q?=E4=B8=BA=E9=9D=A2=E5=80=BC100=E4=B8=89=E8=80=85=E5=90=8C?= =?UTF-8?q?=E6=97=B6=E6=88=90=E7=AB=8B(=E7=8E=B0=E5=AE=9E=E5=9C=A8?= =?UTF-8?q?=E5=B8=82=E5=88=B8=E5=87=A0=E4=B9=8E=E4=B8=8D=E5=8F=AF=E8=83=BD?= =?UTF-8?q?,UAT=20060203.IB=E5=AE=9E=E8=AF=81jquantlib=E7=A9=BA=E7=8E=B0?= =?UTF-8?q?=E9=87=91=E6=B5=81=E9=80=80=E5=8C=96=E5=BD=A2=E6=80=81)?= =?UTF-8?q?=E6=97=B6=E6=8C=89=E6=97=A2=E6=9C=89'=E4=B8=8D=E5=9B=9E?= =?UTF-8?q?=E5=86=99+toast=E7=9C=9F=E5=AE=9E=E5=8E=9F=E5=9B=A0'=E5=A4=84?= =?UTF-8?q?=E7=90=86=EF=BC=8C=E6=8F=90=E7=A4=BA=E6=A0=B8=E5=AF=B9=E5=88=B0?= =?UTF-8?q?=E6=9C=9F=E6=97=A5=E6=88=96=E6=89=8B=E5=B7=A5=E5=A1=AB=E5=86=99?= =?UTF-8?q?=EF=BC=9B=E5=BD=95=E5=85=A5=E9=A1=B5/=E5=B9=B3=E4=BB=93?= =?UTF-8?q?=E9=A1=B5=E5=85=B1=E7=94=A8=E6=AD=A4=E9=97=B8=E9=97=A8=E5=90=8C?= =?UTF-8?q?=E6=AD=A5=E7=94=9F=E6=95=88=EF=BC=9B=E5=88=A4=E5=AE=9A=E6=9D=A1?= =?UTF-8?q?=E4=BB=B6=E4=B8=8E=E5=90=8E=E7=AB=AFBondCalcHepler.IsMaturedDeg?= =?UTF-8?q?enerate=E5=90=8C=E5=8F=A3=E5=BE=84(=E6=B3=A8=E9=87=8A=E5=B7=B2?= =?UTF-8?q?=E4=BA=92=E6=8C=87)=EF=BC=9B=E6=96=B0=E5=A2=9E4=E4=B8=AAjest?= =?UTF-8?q?=E7=94=A8=E4=BE=8B(=E5=91=BD=E4=B8=AD/=E6=AD=A3=E5=B8=B8?= =?UTF-8?q?=E5=88=B8/ytm=3D0=E4=BD=86=E4=BB=B7=E6=A0=BC=E9=9D=9E=E9=9D=A2?= =?UTF-8?q?=E5=80=BC/=E4=BB=B7=E6=A0=BC=E9=9D=A2=E5=80=BC=E4=BD=86ytm?= =?UTF-8?q?=E9=9D=9E0)61/61=E9=80=9A=E8=BF=87?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- YLErpWeb/fe-tests/bondCalc.test.js | 18 ++++++++++++++++++ .../wwwroot/Scripts/app/swaptrade/swapCalc.js | 7 +++++++ 2 files changed, 25 insertions(+) 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; }