I am new to numpy
and using numpy.rate()
to calculate APR for flat interest rate monthly payment loans.
no_of_month = 24
payment = 8584
loan_amount = 50000
apr = rate(no_of_month,-payment,loan_amount,0.0) *12
The apr
is calculated to be -22.816
but the actual value should be 2.0102
(calculated in LibreOffice Calc
)
For no_of_month = 23
the apr
is 2.00079
but for no_of_month = 24
the apr
is -22.816
Following are my current guess on why this happens (They may be wrong)
numpy.rate
for certain rangeI can't find any related resource for either.
What is the root cause and how to fix this?
Range for the values
no_of_month - 1 to 36
payment - 1000 to 1000000
loan_amount - 10000 to 10000000
All combination is possible
np.rate returns the interest rate that solves a polynomial equation with x**24. This has 24 solutions, some of which may be duplicated, some of which may be complex. In the case of this particular data:
pv = 50000
payment = 8584
mpr= np.rate(24, -payment, pv, 0.0)
np.pv(mpr, 24, payment)
# -49999.999999789325 This represents the 50000 pv
mpr
# -1.901406995298687 # mpr = -190.1% per month!
mpr1 = np.rate(24, -payment, pv, 0.0, guess = .15)
# guess lets you change the starting point for the search
mpr1
# 0.16750654293672343 # mar = 16.8% per month
np.pv(mpr1, 24, payment)
# -49999.99999999999
def apr(mpr, periods = 12):
""" apr is ( 1 + monthly_rate ) ** 12 - 1 """
return (1+mpr)**periods-1
apr(apr)
# -0.7122263079633477 apr = -71.2%
apr(mpr1)
# 5.4137477809069345 apr of 541.4%
i.e. Both 16.8% and -190.1% are mathematically correct solutions to the equations. -190.1% doesn't make much sense in a financial context.
It's perhaps easier to understand for two periods.
loan_amount = 10
payment = 6
n_periods = 2
Solve 10 - 6r -6r**2
r = (6 +-sqrt(36-4*(-6)*10))/(2*-6)
r = -1.8844373105 and 0.8844373105
r = 1/(1+i) where i is the interest rate
i = 1/r - 1
r = -1.8844373105
1/r-1
# -1.5306623862879625
r1 = 0.8844373105
1/r1-1
# 0.13066238627435212
mpr = np.rate(2, -payment, loan_amount, 0.0)
mpr
# 0.13066238629183413
mpr1 = np.rate(2, -payment, loan_amount, 0.0, guess = -1.5)
mpr1
# -1.5306623862918336
There are two rates that numpy will solve for in this case which come from the two roots to the quadratic equation.
This probably doesn't help but does explain why np.rate
(and np.irr
) can solve to unexpected answers.
Edit:
I realised that if r = 1/(1 + interest_rate) there will only be one real positive solution for r. This is the solution that generally has the most business meaning.
import numpy as np
"""
To simplify the analysis let
r = 1 / ( 1 + interest_rate )
p = periodic payments
n = number of periods
then:
pv = loan - p*r - p*r**2 - ... -p*r**n
dpv/dr = -p -2*p*r - ... -p*n*r**(n-1)
If r > 0 and p > 0 dpv/dr is negative
Therefore there is at most one solution to pv == 0 for r > 0.
pv == loan when r == 0
For large r -p*r**n will dominate and pv will be negative.
Therefore there will be one positive real solution to pv == 0
"""
def polynomial_from(nper, loan, pay):
""" Create numpy array to represent the polynomial """
return np.array([-pay]*nper+[loan])
# np.roots returns one root per nper. Filter to real roots only.
def real_roots(poly):
roots_ = np.roots(poly)
return roots_[np.isclose(roots_.imag, 0)].real
# return the real part of the roots- with a zero imaginary part
def feasible_rate(nper, loan, pay):
poly = polynomial_from(nper, loan, pay)
reals = real_roots(poly)
r = reals[reals>0][0] # r is the real root > 0
return 1/r - 1
def apr(int_rate, nperiods = 12):
return ( 1 + int_rate ) ** nperiods - 1
mpr = feasible_rate( 24, 50000, 8584 )
print( 'Monthly rate: {:%}, Annual Rate: {:%}'.format(mpr, apr(mpr)) )
# Monthly rate: 16.750654%, Annual Rate: 541.374778%