1-
21from decimal import Decimal
2+ import math
33
44# Don't use 'import numpy as np', to avoid accidentally testing
55# the versions in numpy instead of numpy_financial.
66import numpy
77from numpy .testing import (
8- assert_ , assert_almost_equal , assert_allclose , assert_equal , assert_raises
8+ assert_ , assert_almost_equal , assert_allclose , assert_equal
99 )
1010import pytest
1111
@@ -71,35 +71,6 @@ def test_pmt_decimal(self):
7171 assert_equal (res [1 ][0 ], tgt [1 ][0 ])
7272 assert_equal (res [1 ][1 ], tgt [1 ][1 ])
7373
74- def test_ppmt (self ):
75- assert_equal (numpy .round (npf .ppmt (0.1 / 12 , 1 , 60 , 55000 ), 2 ), - 710.25 )
76-
77- def test_ppmt_decimal (self ):
78- assert_equal (npf .ppmt (Decimal ('0.1' ) / Decimal ('12' ), Decimal ('1' ),
79- Decimal ('60' ), Decimal ('55000' )),
80- Decimal ('-710.2541257864217612489830917' ))
81-
82- # Two tests showing how Decimal is actually getting at a more exact result
83- # .23 / 12 does not come out nicely as a float but does as a decimal
84- def test_ppmt_special_rate (self ):
85- assert_equal (numpy .round (npf .ppmt (0.23 / 12 , 1 , 60 , 10000000000 ), 8 ),
86- - 90238044.232277036 )
87-
88- def test_ppmt_special_rate_decimal (self ):
89- # When rounded out to 8 decimal places like the float based test,
90- # this should not equal the same value as the float, substituted
91- # for the decimal
92- def raise_error_because_not_equal ():
93- assert_equal (
94- round (npf .ppmt (Decimal ('0.23' ) / Decimal ('12' ), 1 , 60 ,
95- Decimal ('10000000000' )), 8 ),
96- Decimal ('-90238044.232277036' ))
97-
98- assert_raises (AssertionError , raise_error_because_not_equal )
99- assert_equal (npf .ppmt (Decimal ('0.23' ) / Decimal ('12' ), 1 , 60 ,
100- Decimal ('10000000000' )),
101- Decimal ('-90238044.2322778884413969909' ))
102-
10374 def test_npv (self ):
10475 assert_almost_equal (
10576 npf .npv (0.05 , [- 15000 , 1500 , 2500 , 3500 , 4500 , 6000 ]),
@@ -172,15 +143,6 @@ def test_when(self):
172143 assert_equal (npf .pmt (0.08 / 12 , 5 * 12 , 15000. , 0 , 0 ),
173144 npf .pmt (0.08 / 12 , 5 * 12 , 15000. , 0 , 'end' ))
174145
175- # begin
176- assert_equal (npf .ppmt (0.1 / 12 , 1 , 60 , 55000 , 0 , 1 ),
177- npf .ppmt (0.1 / 12 , 1 , 60 , 55000 , 0 , 'begin' ))
178- # end
179- assert_equal (npf .ppmt (0.1 / 12 , 1 , 60 , 55000 , 0 ),
180- npf .ppmt (0.1 / 12 , 1 , 60 , 55000 , 0 , 'end' ))
181- assert_equal (npf .ppmt (0.1 / 12 , 1 , 60 , 55000 , 0 , 0 ),
182- npf .ppmt (0.1 / 12 , 1 , 60 , 55000 , 0 , 'end' ))
183-
184146 # begin
185147 assert_equal (npf .nper (0.075 , - 2000 , 0 , 100000. , 1 ),
186148 npf .nper (0.075 , - 2000 , 0 , 100000. , 'begin' ))
@@ -224,86 +186,10 @@ def test_decimal_with_when(self):
224186 npf .pv (Decimal ('0.07' ), Decimal ('20' ), Decimal ('12000' ),
225187 Decimal ('0' ), 'end' ))
226188
227- # begin
228- assert_equal (npf .pmt (Decimal ('0.08' ) / Decimal ('12' ),
229- Decimal ('5' ) * Decimal ('12' ), Decimal ('15000.' ),
230- Decimal ('0' ), Decimal ('1' )),
231- npf .pmt (Decimal ('0.08' ) / Decimal ('12' ),
232- Decimal ('5' ) * Decimal ('12' ), Decimal ('15000.' ),
233- Decimal ('0' ), 'begin' ))
234- # end
235- assert_equal (npf .pmt (Decimal ('0.08' ) / Decimal ('12' ),
236- Decimal ('5' ) * Decimal ('12' ), Decimal ('15000.' ),
237- Decimal ('0' )),
238- npf .pmt (Decimal ('0.08' ) / Decimal ('12' ),
239- Decimal ('5' ) * Decimal ('12' ), Decimal ('15000.' ),
240- Decimal ('0' ), 'end' ))
241- assert_equal (npf .pmt (Decimal ('0.08' ) / Decimal ('12' ),
242- Decimal ('5' ) * Decimal ('12' ), Decimal ('15000.' ),
243- Decimal ('0' ), Decimal ('0' )),
244- npf .pmt (Decimal ('0.08' ) / Decimal ('12' ),
245- Decimal ('5' ) * Decimal ('12' ), Decimal ('15000.' ),
246- Decimal ('0' ), 'end' ))
247-
248- # begin
249- assert_equal (npf .ppmt (Decimal ('0.1' ) / Decimal ('12' ),
250- Decimal ('1' ), Decimal ('60' ), Decimal ('55000' ),
251- Decimal ('0' ), Decimal ('1' )),
252- npf .ppmt (Decimal ('0.1' ) / Decimal ('12' ), Decimal ('1' ),
253- Decimal ('60' ), Decimal ('55000' ),
254- Decimal ('0' ), 'begin' ))
255- # end
256- assert_equal (npf .ppmt (Decimal ('0.1' ) / Decimal ('12' ), Decimal ('1' ),
257- Decimal ('60' ), Decimal ('55000' ), Decimal ('0' )),
258- npf .ppmt (Decimal ('0.1' ) / Decimal ('12' ), Decimal ('1' ),
259- Decimal ('60' ), Decimal ('55000' ), Decimal ('0' ),
260- 'end' ))
261- assert_equal (npf .ppmt (Decimal ('0.1' ) / Decimal ('12' ), Decimal ('1' ),
262- Decimal ('60' ), Decimal ('55000' ), Decimal ('0' ),
263- Decimal ('0' )),
264- npf .ppmt (Decimal ('0.1' ) / Decimal ('12' ), Decimal ('1' ),
265- Decimal ('60' ), Decimal ('55000' ), Decimal ('0' ),
266- 'end' ))
267-
268189 def test_broadcast (self ):
269190 assert_almost_equal (npf .nper (0.075 , - 2000 , 0 , 100000. , [0 , 1 ]),
270191 [21.5449442 , 20.76156441 ], 4 )
271192
272- assert_almost_equal (npf .ppmt (0.1 / 12 , list (range (5 )), 24 , 2000 ),
273- [numpy .nan , - 75.62318601 , - 76.25337923 ,
274- - 76.88882405 , - 77.52956425 ], 4 )
275-
276- assert_almost_equal (npf .ppmt (0.1 / 12 , list (range (5 )), 24 , 2000 , 0 ,
277- [0 , 0 , 1 , 'end' , 'begin' ]),
278- [numpy .nan , - 75.62318601 , - 75.62318601 ,
279- - 76.88882405 , - 76.88882405 ], 4 )
280-
281- def test_broadcast_decimal (self ):
282- # Use almost equal because precision is tested in the explicit tests,
283- # this test is to ensure broadcast with Decimal is not broken.
284- assert_almost_equal (npf .ppmt (Decimal ('0.1' ) / Decimal ('12' ),
285- list (range (1 , 5 )), Decimal ('24' ),
286- Decimal ('2000' )),
287- [Decimal ('-75.62318601' ),
288- Decimal ('-76.25337923' ), Decimal ('-76.88882405' ),
289- Decimal ('-77.52956425' )], 4 )
290-
291- result = npf .ppmt (
292- Decimal ('0.1' ) / Decimal ('12' ),
293- list (range (1 , 5 )),
294- Decimal ('24' ),
295- Decimal ('2000' ),
296- Decimal ('0' ),
297- [Decimal ('0' ), Decimal ('1' ), 'end' , 'begin' ]
298- )
299- desired = [
300- Decimal ('-75.62318601' ),
301- Decimal ('-75.62318601' ),
302- Decimal ('-76.88882405' ),
303- Decimal ('-76.88882405' )
304- ]
305- assert_almost_equal (result , desired , decimal = 4 )
306-
307193
308194class TestNper :
309195 def test_basic_values (self ):
@@ -329,6 +215,129 @@ def test_no_interest(self):
329215 assert_ (npf .nper (0 , - 100 , 1000 ) == 10 )
330216
331217
218+ class TestPpmt :
219+ def test_float (self ):
220+ assert_allclose (
221+ npf .ppmt (0.1 / 12 , 1 , 60 , 55000 ),
222+ - 710.25 ,
223+ rtol = 1e-4
224+ )
225+
226+ def test_decimal (self ):
227+ result = npf .ppmt (
228+ Decimal ('0.1' ) / Decimal ('12' ),
229+ Decimal ('1' ),
230+ Decimal ('60' ),
231+ Decimal ('55000' )
232+ )
233+ assert_equal (
234+ result ,
235+ Decimal ('-710.2541257864217612489830917' ),
236+ )
237+
238+ @pytest .mark .parametrize ('when' , [1 , 'begin' ])
239+ def test_when_is_begin (self , when ):
240+ assert_allclose (
241+ npf .ppmt (0.1 / 12 , 1 , 60 , 55000 , 0 , when ),
242+ - 1158.929712 , # Computed using Google Sheet's PPMT
243+ rtol = 1e-9 ,
244+ )
245+
246+ @pytest .mark .parametrize ('when' , [None , 0 , 'end' ])
247+ def test_when_is_end (self , when ):
248+ args = (0.1 / 12 , 1 , 60 , 55000 , 0 )
249+ result = npf .ppmt (* args ) if when is None else npf .ppmt (* args , when )
250+ assert_allclose (
251+ result ,
252+ - 710.254126 , # Computed using Google Sheet's PPMT
253+ rtol = 1e-9 ,
254+ )
255+
256+ @pytest .mark .parametrize ('when' , [Decimal ('1' ), 'begin' ])
257+ def test_when_is_begin_decimal (self , when ):
258+ result = npf .ppmt (
259+ Decimal ('0.08' ) / Decimal ('12' ),
260+ Decimal ('1' ),
261+ Decimal ('60' ),
262+ Decimal ('15000.' ),
263+ Decimal ('0' ),
264+ when
265+ )
266+ assert_almost_equal (
267+ result ,
268+ Decimal ('-302.131703' ), # Computed using Google Sheet's PPMT
269+ decimal = 5 ,
270+ )
271+
272+ @pytest .mark .parametrize ('when' , [None , Decimal ('0' ), 'end' ])
273+ def test_when_is_end_decimal (self , when ):
274+ args = (
275+ Decimal ('0.08' ) / Decimal ('12' ),
276+ Decimal ('1' ),
277+ Decimal ('60' ),
278+ Decimal ('15000.' ),
279+ Decimal ('0' )
280+ )
281+ result = npf .ppmt (* args ) if when is None else npf .ppmt (* args , when )
282+ assert_almost_equal (
283+ result ,
284+ Decimal ('-204.145914' ), # Computed using Google Sheet's PPMT
285+ decimal = 5 ,
286+ )
287+
288+ @pytest .mark .parametrize ('args' , [
289+ (0.1 / 12 , 0 , 60 , 15000 ),
290+ (Decimal ('0.012' ), Decimal ('0' ), Decimal ('60' ), Decimal ('15000' ))
291+ ])
292+ def test_invalid_per (self , args ):
293+ # Note that math.isnan() handles Decimal NaN correctly.
294+ assert math .isnan (npf .ppmt (* args ))
295+
296+ @pytest .mark .parametrize ('when, desired' , [
297+ (
298+ None ,
299+ [- 75.62318601 , - 76.25337923 , - 76.88882405 , - 77.52956425 ],
300+ ), (
301+ [0 , 1 , 'end' , 'begin' ],
302+ [- 75.62318601 , - 75.62318601 , - 76.88882405 , - 76.88882405 ],
303+ )
304+ ])
305+ def test_broadcast (self , when , desired ):
306+ args = (0.1 / 12 , numpy .arange (1 , 5 ), 24 , 2000 , 0 )
307+ result = npf .ppmt (* args ) if when is None else npf .ppmt (* args , when )
308+ assert_allclose (result , desired , rtol = 1e-5 )
309+
310+ @pytest .mark .parametrize ('when, desired' , [
311+ (
312+ None ,
313+ [
314+ Decimal ('-75.62318601' ),
315+ Decimal ('-76.25337923' ),
316+ Decimal ('-76.88882405' ),
317+ Decimal ('-77.52956425' )
318+ ],
319+ ), (
320+ [Decimal ('0' ), Decimal ('1' ), 'end' , 'begin' ],
321+ [
322+ Decimal ('-75.62318601' ),
323+ Decimal ('-75.62318601' ),
324+ Decimal ('-76.88882405' ),
325+ Decimal ('-76.88882405' )
326+ ]
327+ )
328+ ])
329+ def test_broadcast_decimal (self , when , desired ):
330+ args = (
331+ Decimal ('0.1' ) / Decimal ('12' ),
332+ numpy .arange (1 , 5 ),
333+ Decimal ('24' ),
334+ Decimal ('2000' ),
335+ Decimal ('0' )
336+ )
337+ result = npf .ppmt (* args ) if when is None else npf .ppmt (* args , when )
338+ assert_almost_equal (result , desired , decimal = 8 )
339+
340+
332341class TestIpmt :
333342 def test_float (self ):
334343 assert_allclose (
@@ -353,7 +362,6 @@ def test_when_is_end(self, when):
353362 result = npf .ipmt (0.1 / 12 , 1 , 24 , 2000 , 0 , when )
354363 assert_allclose (result , - 16.666667 , rtol = 1e-6 )
355364
356-
357365 @pytest .mark .parametrize ('when' , [Decimal ('1' ), 'begin' ])
358366 def test_when_is_begin_decimal (self , when ):
359367 result = npf .ipmt (
0 commit comments