@@ -598,7 +598,7 @@ def _g_div_gp(r, n, p, x, y, w):
598
598
# where
599
599
# g(r) is the formula
600
600
# g'(r) is the derivative with respect to r.
601
- def rate (nper , pmt , pv , fv , when = 'end' , guess = None , tol = None , maxiter = 100 ):
601
+ def rate (nper , pmt , pv , fv , when = 'end' , guess = None , tol = None , maxiter = 100 , raise_exceptions = False ):
602
602
"""
603
603
Compute the rate of interest per period.
604
604
@@ -620,6 +620,11 @@ def rate(nper, pmt, pv, fv, when='end', guess=None, tol=None, maxiter=100):
620
620
Required tolerance for the solution, default 1e-6
621
621
maxiter : int, optional
622
622
Maximum iterations in finding the solution
623
+ raise_exceptions: bool, optional
624
+ Flag to raise an exception when the at least one of the rates
625
+ cannot be computed due to having reached the maximum number of
626
+ iterations (IterationsExceededException). Set to False as default,
627
+ thus returning NaNs for those rates.
623
628
624
629
Notes
625
630
-----
@@ -664,17 +669,29 @@ def rate(nper, pmt, pv, fv, when='end', guess=None, tol=None, maxiter=100):
664
669
iterator += 1
665
670
rn = rnp1
666
671
672
+ # Define the custom Exceptions in case the flag raise_exceptions
673
+ # is set to True
674
+ if raise_exceptions :
675
+ class IterationsExceededException (Exception ):
676
+ "Raised when the maximum number of iterations is reached"
677
+ pass
678
+
667
679
if not np .all (close ):
668
680
if np .isscalar (rn ):
681
+ if raise_exceptions :
682
+ raise IterationsExceededException ('\n Maximum number of iterations exceeded.' )
669
683
return default_type (np .nan )
670
684
else :
671
685
# Return nan's in array of the same shape as rn
672
686
# where the solution is not close to tol.
687
+ if raise_exceptions :
688
+ raise IterationsExceededException (f'\n Maximum number of iterations exceeded in '
689
+ f'{ len (close )- close .sum ()} rate(s).' )
673
690
rn [~ close ] = np .nan
674
691
return rn
675
692
676
693
677
- def irr (values , guess = None , tol = 1e-12 , maxiter = 100 ):
694
+ def irr (values , guess = None , tol = 1e-12 , maxiter = 100 , raise_exceptions = False ):
678
695
"""
679
696
Return the Internal Rate of Return (IRR).
680
697
@@ -699,6 +716,12 @@ def irr(values, guess=None, tol=1e-12, maxiter=100):
699
716
Required tolerance to accept solution. Default is 1e-12.
700
717
maxiter : int, optional
701
718
Maximum iterations to perform in finding a solution. Default is 100.
719
+ raise_exceptions: bool, optional
720
+ Flag to raise an exception when the irr cannot be computed due to
721
+ either having all cashflows of the same sign (NoRealSolutionException) or
722
+ having reached the maximum number of iterations (IterationsExceededException).
723
+ Set to False as default, thus returning NaNs in the two previous
724
+ cases.
702
725
703
726
Returns
704
727
-------
@@ -749,13 +772,23 @@ def irr(values, guess=None, tol=1e-12, maxiter=100):
749
772
if values .ndim != 1 :
750
773
raise ValueError ("Cashflows must be a rank-1 array" )
751
774
775
+ # Define the custom Exceptions in case the flag raise_exceptions
776
+ # is set to True
777
+ if raise_exceptions :
778
+ class NoRealSolutionException (Exception ):
779
+ "Raised when all the input cashflows are of the same sign"
780
+ pass
781
+ class IterationsExceededException (Exception ):
782
+ "Raised when the maximum number of iterations is reached"
783
+ pass
784
+
752
785
# If all values are of the same sign no solution exists
753
786
# we don't perform any further calculations and exit early
754
787
same_sign = np .all (values > 0 ) if values [0 ] > 0 else np .all (values < 0 )
755
788
if same_sign :
756
- print ( ' \n No solution exists for IRR since all '
757
- 'cashflows are of the same sign. Returning '
758
- 'np.nan \n ' )
789
+ if raise_exceptions :
790
+ raise NoRealSolutionException ( ' \n No real solution exists for IRR since all '
791
+ 'cashflows are of the same sign. \n ' )
759
792
return np .nan
760
793
761
794
# If no value is passed for `guess`, then make a heuristic estimate
@@ -792,6 +825,9 @@ def irr(values, guess=None, tol=1e-12, maxiter=100):
792
825
return g - 1
793
826
g -= delta
794
827
828
+ if raise_exceptions :
829
+ raise IterationsExceededException ('\n Maximum number of iterations exceeded.' )
830
+
795
831
return np .nan
796
832
797
833
@@ -874,7 +910,7 @@ def npv(rate, values):
874
910
return npv
875
911
876
912
877
- def mirr (values , finance_rate , reinvest_rate ):
913
+ def mirr (values , finance_rate , reinvest_rate , raise_exceptions = False ):
878
914
"""
879
915
Modified internal rate of return.
880
916
@@ -888,6 +924,11 @@ def mirr(values, finance_rate, reinvest_rate):
888
924
Interest rate paid on the cash flows
889
925
reinvest_rate : scalar
890
926
Interest rate received on the cash flows upon reinvestment
927
+ raise_exceptions: bool, optional
928
+ Flag to raise an exception when the mirr cannot be computed due to
929
+ having all cashflows of the same sign (NoRealSolutionException).
930
+ Set to False as default, thus returning NaNs in the previous
931
+ case.
891
932
892
933
Returns
893
934
-------
@@ -898,6 +939,13 @@ def mirr(values, finance_rate, reinvest_rate):
898
939
values = np .asarray (values )
899
940
n = values .size
900
941
942
+ # Define the custom Exception in case the flag raise_exceptions
943
+ # is set to True
944
+ if raise_exceptions :
945
+ class NoRealSolutionException (Exception ):
946
+ "Raised when all the input cashflows are of the same sign"
947
+ pass
948
+
901
949
# Without this explicit cast the 1/(n - 1) computation below
902
950
# becomes a float, which causes TypeError when using Decimal
903
951
# values.
@@ -907,6 +955,9 @@ def mirr(values, finance_rate, reinvest_rate):
907
955
pos = values > 0
908
956
neg = values < 0
909
957
if not (pos .any () and neg .any ()):
958
+ if raise_exceptions :
959
+ raise NoRealSolutionException ('\n No real solution exists for IRR since'
960
+ ' all cashflows are of the same sign.\n ' )
910
961
return np .nan
911
962
numer = np .abs (npv (reinvest_rate , values * pos ))
912
963
denom = np .abs (npv (finance_rate , values * neg ))
0 commit comments