From ff3ee38d538d5471d56019177e44cb13af1c820c Mon Sep 17 00:00:00 2001 From: user799595 Date: Mon, 6 Mar 2023 21:38:46 +0000 Subject: [PATCH 1/2] Added better guess for IRR --- numpy_financial/_financial.py | 24 ++++++++++++++++++++---- 1 file changed, 20 insertions(+), 4 deletions(-) diff --git a/numpy_financial/_financial.py b/numpy_financial/_financial.py index 60fc02f..b85519a 100644 --- a/numpy_financial/_financial.py +++ b/numpy_financial/_financial.py @@ -673,6 +673,25 @@ def rate(nper, pmt, pv, fv, when='end', guess=None, tol=None, maxiter=100): rn[~close] = np.nan return rn +def irr_guess(values, axis=-1): + """ + Return a guess for an Internal Rate of Return (IRR). + + A continuously compounded IRR r is defined as a solution to the equation: + log(sum_{k=0}^{n-1} values[k]^+ exp(-r k)) - log(sum_{k=0}^{n-1} values[k]^- exp(-r k)) = 0 + Newton's method can be used to find and r to solve this equation. + + This function implements the first step of Newton's method, starting from r_0 = 0. + """ + negative_part = -np.minimum(values, 0.) + positive_part = np.maximum(values, 0.) + negative_npv = np.sum(negative_part, axis=axis) + positive_npv = np.sum(positive_part, axis=axis) + times = np.arange(values.shape[axis]) + negative_duration = np.sum(negative_part * times, axis=axis)/positive_npv + positive_duration = np.sum(positive_part * times, axis=axis)/negative_npv + delta_t = positive_duration - negative_duration + return (positive_npv/negative_npv)**(1/delta_t) - 1 def irr(values, guess=None, tol=1e-12, maxiter=100): """ @@ -757,10 +776,7 @@ def irr(values, guess=None, tol=1e-12, maxiter=100): # If no value is passed for `guess`, then make a heuristic estimate if guess is None: - positive_cashflow = values > 0 - inflow = values.sum(where=positive_cashflow) - outflow = -values.sum(where=~positive_cashflow) - guess = inflow / outflow - 1 + guess = irr_guess(values) # We aim to solve eirr such that NPV is exactly zero. This can be framed as # simply finding the closest root of a polynomial to a given initial guess From 26c3b763e05ed6b18c27f45e29b4e3c3b2137e1b Mon Sep 17 00:00:00 2001 From: user799595 Date: Wed, 8 Mar 2023 14:54:42 +0000 Subject: [PATCH 2/2] Fixed denominators --- numpy_financial/_financial.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/numpy_financial/_financial.py b/numpy_financial/_financial.py index b85519a..4b493c0 100644 --- a/numpy_financial/_financial.py +++ b/numpy_financial/_financial.py @@ -688,8 +688,8 @@ def irr_guess(values, axis=-1): negative_npv = np.sum(negative_part, axis=axis) positive_npv = np.sum(positive_part, axis=axis) times = np.arange(values.shape[axis]) - negative_duration = np.sum(negative_part * times, axis=axis)/positive_npv - positive_duration = np.sum(positive_part * times, axis=axis)/negative_npv + negative_duration = np.sum(negative_part * times, axis=axis)/negative_npv + positive_duration = np.sum(positive_part * times, axis=axis)/positive_npv delta_t = positive_duration - negative_duration return (positive_npv/negative_npv)**(1/delta_t) - 1