From d92bd5e1a067b2b83a78946dff2cb5c13b68e8d2 Mon Sep 17 00:00:00 2001 From: "Tianlang (Jackie) Chen" <305351710+jackiectl2@users.noreply.github.com> Date: Fri, 30 Oct 2026 13:56:48 -0400 Subject: [PATCH] Exclude zero polynomial roots from IRR solutions Trailing zero cash flows can introduce a zero polynomial root, which is converted to an IRR of -1 even though the NPV denominator is undefined there. Require rates to be greater than -1, returning the existing no-solution result when appropriate. Cover both signs, leading zeros, exception mode, valid negative IRRs and batched inputs. Signed-off-by: Tianlang (Jackie) Chen <305351710+jackiectl2@users.noreply.github.com> --- numpy_financial/_financial.py | 4 ++-- numpy_financial/tests/test_financial.py | 12 ++++++++++++ 2 files changed, 14 insertions(+), 2 deletions(-) diff --git a/numpy_financial/_financial.py b/numpy_financial/_financial.py index 1adbbea..6ae42cd 100644 --- a/numpy_financial/_financial.py +++ b/numpy_financial/_financial.py @@ -1153,8 +1153,8 @@ def irr(values, *, raise_exceptions=False, selection_logic=_irr_default_selectio g = np.roots(row) eirr = np.real(g[np.isreal(g)]) - 1 - # Realistic IRR - eirr = eirr[eirr >= -1] + # The discount factor must be nonzero in the NPV equation. + eirr = eirr[eirr > -1] # If no real solution if len(eirr) == 0: diff --git a/numpy_financial/tests/test_financial.py b/numpy_financial/tests/test_financial.py index 765e1b2..9a2a0a0 100644 --- a/numpy_financial/tests/test_financial.py +++ b/numpy_financial/tests/test_financial.py @@ -796,6 +796,18 @@ def test_numpy_gh_6744(self, v): # Test that if there is no solution then npf.irr returns nan. assert numpy.isnan(npf.irr(v)) + @pytest.mark.parametrize("cashflows", [ + [100, 50, 0], [-100, -50, 0], [0, 100, 0], [0, -100, 0], + ]) + def test_zero_polynomial_root_is_not_an_irr(self, cashflows): + assert numpy.isnan(npf.irr(cashflows)) + with pytest.raises(npf.NoRealSolutionError): + npf.irr(cashflows, raise_exceptions=True) + + def test_trailing_zeros_preserve_negative_irr(self): + assert_allclose(npf.irr([-100, 50, 0, 0]), -0.5) + assert_allclose(npf.irr([[-100, 50, 0], [-100, 150, 0]]), [-0.5, 0.5]) + def test_gh_15(self): v = [ -3000.0,