Permalink
Browse files

Added ipmt (interest portion of payment) and ppmt (principal portion …

…of payment) functions. Added doctests and unit tests.
  • Loading branch information...
tim cera authored and charris committed Jan 29, 2012
1 parent ca258fc commit 015cada57ddd00d866e2a8ef396f83582c192884
Showing with 73 additions and 14 deletions.
  1. +67 −5 numpy/lib/financial.py
  2. +6 −9 numpy/lib/tests/test_financial.py
View
@@ -277,7 +277,7 @@ def nper(rate, pmt, pv, fv=0, when='end'):
def ipmt(rate, per, nper, pv, fv=0.0, when='end'):
"""
- Not implemented. Compute the payment portion for loan interest.
+ Compute the interest portion of a payment.
Parameters
----------
@@ -314,14 +314,71 @@ def ipmt(rate, per, nper, pv, fv=0.0, when='end'):
``pmt = ppmt + ipmt``
+ Examples
+ --------
+ What is the amortization schedule for a 1 year loan of $2500 at
+ 8.24% interest per year compounded monthly?
+
+ >>> principal = 2500.00
+
+ The 'per' variable represents the periods of the loan. Remember that
+ financial equations start the period count at 1!
+
+ >>> per = np.arange(1*12) + 1
+ >>> ipmt = np.ipmt(0.0824/12, per, 1*12, principal)
+ >>> ppmt = np.ppmt(0.0824/12, per, 1*12, principal)
+
+ Each element of the sum of the 'ipmt' and 'ppmt' arrays should equal
+ 'pmt'.
+
+ >>> pmt = np.pmt(0.0824/12, 1*12, principal)
+ >>> np.allclose(ipmt + ppmt, pmt)
+ True
+
+ >>> fmt = '{0:2d} {1:8.2f} {2:8.2f} {3:8.2f}'
+ >>> for payment in per:
+ ... index = payment - 1
+ ... principal = principal + ppmt[index]
+ ... print fmt.format(payment, ppmt[index], ipmt[index], principal)
+ 1 -200.58 -17.17 2299.42
+ 2 -201.96 -15.79 2097.46
+ 3 -203.35 -14.40 1894.11
+ 4 -204.74 -13.01 1689.37
+ 5 -206.15 -11.60 1483.22
+ 6 -207.56 -10.18 1275.66
+ 7 -208.99 -8.76 1066.67
+ 8 -210.42 -7.32 856.25
+ 9 -211.87 -5.88 644.38
+ 10 -213.32 -4.42 431.05
+ 11 -214.79 -2.96 216.26
+ 12 -216.26 -1.49 -0.00
+
+ >>> interestpd = np.sum(ipmt)
+ >>> interestpd
+ -112.98308424136215
+
"""
- total = pmt(rate, nper, pv, fv, when)
- # Now, compute the nth step in the amortization
- raise NotImplementedError
+ when = _convert_when(when)
+ if when == 1 and per == 1:
+ return 0.0
+ total_pmt = pmt(rate, nper, pv, fv, when)
+ ipmt = _rbl(rate, per, total_pmt, pv, when)*rate
+ if when == 1:
+ return ipmt/(1 + rate)
+ return ipmt
+
+def _rbl(rate, per, pmt, pv, when):
+ """
+ This function is here to simply have a different name for the 'fv'
+ function to not interfere with the 'fv' keyword argument within the 'ipmt'
+ function. It is the 'remaining balance on loan' which might be useful as
+ it's own function, but is easily calculated with the 'fv' function.
+ """
+ return fv(rate, (per - 1), pmt, pv, when)
def ppmt(rate, per, nper, pv, fv=0.0, when='end'):
"""
- Not implemented. Compute the payment against loan principal.
+ Compute the payment against loan principal.
Parameters
----------
@@ -655,3 +712,8 @@ def mirr(values, finance_rate, reinvest_rate):
numer = np.abs(npv(reinvest_rate, values*pos))*(1 + reinvest_rate)
denom = np.abs(npv(finance_rate, values*neg))*(1 + finance_rate)
return (numer/denom)**(1.0/(n - 1))*(1 + reinvest_rate) - 1
+
+if __name__ == '__main__':
+ import doctest
+ import numpy as np
+ doctest.testmod(verbose=True)
@@ -23,6 +23,12 @@ def test_pmt(self):
assert_almost_equal(np.pmt(0.08/12,5*12,15000),
-304.146, 3)
+ def test_ppmt(self):
+ np.round(np.ppmt(0.1/12,1,60,55000),2) == 710.25
+
+ def test_ipmt(self):
+ np.round(np.ipmt(0.1/12,1,24,2000),2) == 16.67
+
def test_nper(self):
assert_almost_equal(np.nper(0.075,-2000,0,100000.),
21.54, 2)
@@ -49,14 +55,5 @@ def test_mirr(self):
assert_(np.isnan(np.mirr(val, 0.10, 0.12)))
-
-def test_unimplemented():
- # np.round(np.ppmt(0.1/12,1,60,55000),2) == 710.25
- assert_raises(NotImplementedError, np.ppmt, 0.1/12, 1, 60, 55000)
-
- # np.round(np.ipmt(0.1/12,1,24,2000),2) == 16.67
- assert_raises(NotImplementedError, np.ipmt, 0.1/12, 1, 24, 2000)
-
-
if __name__ == "__main__":
run_module_suite()

0 comments on commit 015cada

Please sign in to comment.