@@ -727,7 +727,7 @@ def rate(
727
727
return rn
728
728
729
729
730
- def irr (values , * , guess = None , tol = 1e-12 , maxiter = 100 , raise_exceptions = False ):
730
+ def irr (values ):
731
731
r"""Return the Internal Rate of Return (IRR).
732
732
733
733
This is the "average" periodically compounded rate of return
@@ -743,20 +743,6 @@ def irr(values, *, guess=None, tol=1e-12, maxiter=100, raise_exceptions=False):
743
743
are negative and net "withdrawals" are positive. Thus, for
744
744
example, at least the first element of `values`, which represents
745
745
the initial investment, will typically be negative.
746
- guess : float, optional
747
- Initial guess of the IRR for the iterative solver. If no guess is
748
- given an heuristic is used to estimate the guess through the ratio of
749
- positive to negative cash lows
750
- tol : float, optional
751
- Required tolerance to accept solution. Default is 1e-12.
752
- maxiter : int, optional
753
- Maximum iterations to perform in finding a solution. Default is 100.
754
- raise_exceptions: bool, optional
755
- Flag to raise an exception when the irr cannot be computed due to
756
- either having all cashflows of the same sign (NoRealSolutionException) or
757
- having reached the maximum number of iterations (IterationsExceededException).
758
- Set to False as default, thus returning NaNs in the two previous
759
- cases.
760
746
761
747
Returns
762
748
-------
@@ -816,13 +802,6 @@ def irr(values, *, guess=None, tol=1e-12, maxiter=100, raise_exceptions=False):
816
802
'cashflows are of the same sign.' )
817
803
return np .nan
818
804
819
- # If no value is passed for `guess`, then make a heuristic estimate
820
- if guess is None :
821
- positive_cashflow = values > 0
822
- inflow = values .sum (where = positive_cashflow )
823
- outflow = - values .sum (where = ~ positive_cashflow )
824
- guess = inflow / outflow - 1
825
-
826
805
# We aim to solve eirr such that NPV is exactly zero. This can be framed as
827
806
# simply finding the closest root of a polynomial to a given initial guess
828
807
# as follows:
@@ -840,20 +819,38 @@ def irr(values, *, guess=None, tol=1e-12, maxiter=100, raise_exceptions=False):
840
819
#
841
820
# which we solve using Newton-Raphson and then reverse out the solution
842
821
# as eirr = g - 1 (if we are close enough to a solution)
843
- npv_ = np .polynomial .Polynomial (values [::- 1 ])
844
- d_npv = npv_ .deriv ()
845
- g = 1 + guess
846
-
847
- for _ in range (maxiter ):
848
- delta = npv_ (g ) / d_npv (g )
849
- if abs (delta ) < tol :
850
- return g - 1
851
- g -= delta
852
-
853
- if raise_exceptions :
854
- raise IterationsExceededError ('Maximum number of iterations exceeded.' )
855
-
856
- return np .nan
822
+
823
+ g = np .roots (values )
824
+ IRR = np .real (g [np .isreal (g )]) - 1
825
+
826
+ # realistic IRR
827
+ IRR = IRR [IRR >= - 1 ]
828
+
829
+ # if no real solution
830
+ if len (IRR ) == 0 :
831
+ raise NoRealSolutionError ("No real solution is found for IRR." )
832
+
833
+ # if only one real solution
834
+ if len (IRR ) == 1 :
835
+ return IRR [0 ]
836
+
837
+ # below is for the situation when there are more than 2 real solutions.
838
+ # check sign of all IRR solutions
839
+ same_sign = np .all (IRR > 0 ) if IRR [0 ] > 0 else np .all (IRR < 0 )
840
+
841
+ # if the signs of IRR solutions are not the same, first filter potential IRR
842
+ # by comparing the total positive and negative cash flows.
843
+ if not same_sign :
844
+ pos = sum (cash_flow [cash_flow > 0 ])
845
+ neg = sum (cash_flow [cash_flow < 0 ])
846
+ if pos > neg :
847
+ IRR = IRR [IRR > 0 ]
848
+ else :
849
+ IRR = IRR [IRR < 0 ]
850
+
851
+ # pick the smallest one in magnitude and return
852
+ abs_IRR = np .abs (IRR )
853
+ return IRR [np .argmin (abs_IRR )]
857
854
858
855
859
856
@nb .njit (parallel = True )
0 commit comments