@@ -710,7 +710,7 @@ def rate(
710
710
return rn
711
711
712
712
713
- def irr (values , * , guess = None , tol = 1e-12 , maxiter = 100 , raise_exceptions = False ):
713
+ def irr (values , * , raise_exceptions = False ):
714
714
r"""Return the Internal Rate of Return (IRR).
715
715
716
716
This is the "average" periodically compounded rate of return
@@ -726,14 +726,6 @@ def irr(values, *, guess=None, tol=1e-12, maxiter=100, raise_exceptions=False):
726
726
are negative and net "withdrawals" are positive. Thus, for
727
727
example, at least the first element of `values`, which represents
728
728
the initial investment, will typically be negative.
729
- guess : float, optional
730
- Initial guess of the IRR for the iterative solver. If no guess is
731
- given an heuristic is used to estimate the guess through the ratio of
732
- positive to negative cash lows
733
- tol : float, optional
734
- Required tolerance to accept solution. Default is 1e-12.
735
- maxiter : int, optional
736
- Maximum iterations to perform in finding a solution. Default is 100.
737
729
raise_exceptions: bool, optional
738
730
Flag to raise an exception when the irr cannot be computed due to
739
731
either having all cashflows of the same sign (NoRealSolutionException) or
@@ -799,13 +791,6 @@ def irr(values, *, guess=None, tol=1e-12, maxiter=100, raise_exceptions=False):
799
791
'cashflows are of the same sign.' )
800
792
return np .nan
801
793
802
- # If no value is passed for `guess`, then make a heuristic estimate
803
- if guess is None :
804
- positive_cashflow = values > 0
805
- inflow = values .sum (where = positive_cashflow )
806
- outflow = - values .sum (where = ~ positive_cashflow )
807
- guess = inflow / outflow - 1
808
-
809
794
# We aim to solve eirr such that NPV is exactly zero. This can be framed as
810
795
# simply finding the closest root of a polynomial to a given initial guess
811
796
# as follows:
@@ -823,20 +808,40 @@ def irr(values, *, guess=None, tol=1e-12, maxiter=100, raise_exceptions=False):
823
808
#
824
809
# which we solve using Newton-Raphson and then reverse out the solution
825
810
# as eirr = g - 1 (if we are close enough to a solution)
826
- npv_ = np . polynomial . Polynomial ( values [:: - 1 ])
827
- d_npv = npv_ . deriv ( )
828
- g = 1 + guess
811
+
812
+ g = np . roots ( values )
813
+ eirr = np . real ( g [ np . isreal ( g )]) - 1
829
814
830
- for _ in range (maxiter ):
831
- delta = npv_ (g ) / d_npv (g )
832
- if abs (delta ) < tol :
833
- return g - 1
834
- g -= delta
815
+ # realistic IRR
816
+ eirr = eirr [eirr >= - 1 ]
835
817
836
- if raise_exceptions :
837
- raise IterationsExceededError ('Maximum number of iterations exceeded.' )
818
+ # if no real solution
819
+ if len (eirr ) == 0 :
820
+ if raise_exceptions :
821
+ raise NoRealSolutionError ("No real solution is found for IRR." )
822
+ return np .nan
838
823
839
- return np .nan
824
+ # if only one real solution
825
+ if len (eirr ) == 1 :
826
+ return eirr [0 ]
827
+
828
+ # below is for the situation when there are more than 2 real solutions.
829
+ # check sign of all IRR solutions
830
+ same_sign = np .all (eirr > 0 ) if eirr [0 ] > 0 else np .all (eirr < 0 )
831
+
832
+ # if the signs of IRR solutions are not the same, first filter potential IRR
833
+ # by comparing the total positive and negative cash flows.
834
+ if not same_sign :
835
+ pos = sum (values [values > 0 ])
836
+ neg = sum (values [values < 0 ])
837
+ if pos >= neg :
838
+ eirr = eirr [eirr >= 0 ]
839
+ else :
840
+ eirr = eirr [eirr < 0 ]
841
+
842
+ # pick the smallest one in magnitude and return
843
+ abs_eirr = np .abs (eirr )
844
+ return eirr [np .argmin (abs_eirr )]
840
845
841
846
842
847
@nb .njit
0 commit comments