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
@@ -90,35 +90,6 @@ def test_pmt_decimal(self):
9090 assert_equal (res [1 ][0 ], tgt [1 ][0 ])
9191 assert_equal (res [1 ][1 ], tgt [1 ][1 ])
9292
93- def test_ppmt (self ):
94- assert_equal (numpy .round (npf .ppmt (0.1 / 12 , 1 , 60 , 55000 ), 2 ), - 710.25 )
95-
96- def test_ppmt_decimal (self ):
97- assert_equal (npf .ppmt (Decimal ('0.1' ) / Decimal ('12' ), Decimal ('1' ),
98- Decimal ('60' ), Decimal ('55000' )),
99- Decimal ('-710.2541257864217612489830917' ))
100-
101- # Two tests showing how Decimal is actually getting at a more exact result
102- # .23 / 12 does not come out nicely as a float but does as a decimal
103- def test_ppmt_special_rate (self ):
104- assert_equal (numpy .round (npf .ppmt (0.23 / 12 , 1 , 60 , 10000000000 ), 8 ),
105- - 90238044.232277036 )
106-
107- def test_ppmt_special_rate_decimal (self ):
108- # When rounded out to 8 decimal places like the float based test,
109- # this should not equal the same value as the float, substituted
110- # for the decimal
111- def raise_error_because_not_equal ():
112- assert_equal (
113- round (npf .ppmt (Decimal ('0.23' ) / Decimal ('12' ), 1 , 60 ,
114- Decimal ('10000000000' )), 8 ),
115- Decimal ('-90238044.232277036' ))
116-
117- assert_raises (AssertionError , raise_error_because_not_equal )
118- assert_equal (npf .ppmt (Decimal ('0.23' ) / Decimal ('12' ), 1 , 60 ,
119- Decimal ('10000000000' )),
120- Decimal ('-90238044.2322778884413969909' ))
121-
12293 def test_npv (self ):
12394 assert_almost_equal (
12495 npf .npv (0.05 , [- 15000 , 1500 , 2500 , 3500 , 4500 , 6000 ]),
@@ -191,15 +162,6 @@ def test_when(self):
191162 assert_equal (npf .pmt (0.08 / 12 , 5 * 12 , 15000. , 0 , 0 ),
192163 npf .pmt (0.08 / 12 , 5 * 12 , 15000. , 0 , 'end' ))
193164
194- # begin
195- assert_equal (npf .ppmt (0.1 / 12 , 1 , 60 , 55000 , 0 , 1 ),
196- npf .ppmt (0.1 / 12 , 1 , 60 , 55000 , 0 , 'begin' ))
197- # end
198- assert_equal (npf .ppmt (0.1 / 12 , 1 , 60 , 55000 , 0 ),
199- npf .ppmt (0.1 / 12 , 1 , 60 , 55000 , 0 , 'end' ))
200- assert_equal (npf .ppmt (0.1 / 12 , 1 , 60 , 55000 , 0 , 0 ),
201- npf .ppmt (0.1 / 12 , 1 , 60 , 55000 , 0 , 'end' ))
202-
203165 # begin
204166 assert_equal (npf .nper (0.075 , - 2000 , 0 , 100000. , 1 ),
205167 npf .nper (0.075 , - 2000 , 0 , 100000. , 'begin' ))
@@ -243,86 +205,10 @@ def test_decimal_with_when(self):
243205 npf .pv (Decimal ('0.07' ), Decimal ('20' ), Decimal ('12000' ),
244206 Decimal ('0' ), 'end' ))
245207
246- # begin
247- assert_equal (npf .pmt (Decimal ('0.08' ) / Decimal ('12' ),
248- Decimal ('5' ) * Decimal ('12' ), Decimal ('15000.' ),
249- Decimal ('0' ), Decimal ('1' )),
250- npf .pmt (Decimal ('0.08' ) / Decimal ('12' ),
251- Decimal ('5' ) * Decimal ('12' ), Decimal ('15000.' ),
252- Decimal ('0' ), 'begin' ))
253- # end
254- assert_equal (npf .pmt (Decimal ('0.08' ) / Decimal ('12' ),
255- Decimal ('5' ) * Decimal ('12' ), Decimal ('15000.' ),
256- Decimal ('0' )),
257- npf .pmt (Decimal ('0.08' ) / Decimal ('12' ),
258- Decimal ('5' ) * Decimal ('12' ), Decimal ('15000.' ),
259- Decimal ('0' ), 'end' ))
260- assert_equal (npf .pmt (Decimal ('0.08' ) / Decimal ('12' ),
261- Decimal ('5' ) * Decimal ('12' ), Decimal ('15000.' ),
262- Decimal ('0' ), Decimal ('0' )),
263- npf .pmt (Decimal ('0.08' ) / Decimal ('12' ),
264- Decimal ('5' ) * Decimal ('12' ), Decimal ('15000.' ),
265- Decimal ('0' ), 'end' ))
266-
267- # begin
268- assert_equal (npf .ppmt (Decimal ('0.1' ) / Decimal ('12' ),
269- Decimal ('1' ), Decimal ('60' ), Decimal ('55000' ),
270- Decimal ('0' ), Decimal ('1' )),
271- npf .ppmt (Decimal ('0.1' ) / Decimal ('12' ), Decimal ('1' ),
272- Decimal ('60' ), Decimal ('55000' ),
273- Decimal ('0' ), 'begin' ))
274- # end
275- assert_equal (npf .ppmt (Decimal ('0.1' ) / Decimal ('12' ), Decimal ('1' ),
276- Decimal ('60' ), Decimal ('55000' ), Decimal ('0' )),
277- npf .ppmt (Decimal ('0.1' ) / Decimal ('12' ), Decimal ('1' ),
278- Decimal ('60' ), Decimal ('55000' ), Decimal ('0' ),
279- 'end' ))
280- assert_equal (npf .ppmt (Decimal ('0.1' ) / Decimal ('12' ), Decimal ('1' ),
281- Decimal ('60' ), Decimal ('55000' ), Decimal ('0' ),
282- Decimal ('0' )),
283- npf .ppmt (Decimal ('0.1' ) / Decimal ('12' ), Decimal ('1' ),
284- Decimal ('60' ), Decimal ('55000' ), Decimal ('0' ),
285- 'end' ))
286-
287208 def test_broadcast (self ):
288209 assert_almost_equal (npf .nper (0.075 , - 2000 , 0 , 100000. , [0 , 1 ]),
289210 [21.5449442 , 20.76156441 ], 4 )
290211
291- assert_almost_equal (npf .ppmt (0.1 / 12 , list (range (5 )), 24 , 2000 ),
292- [numpy .nan , - 75.62318601 , - 76.25337923 ,
293- - 76.88882405 , - 77.52956425 ], 4 )
294-
295- assert_almost_equal (npf .ppmt (0.1 / 12 , list (range (5 )), 24 , 2000 , 0 ,
296- [0 , 0 , 1 , 'end' , 'begin' ]),
297- [numpy .nan , - 75.62318601 , - 75.62318601 ,
298- - 76.88882405 , - 76.88882405 ], 4 )
299-
300- def test_broadcast_decimal (self ):
301- # Use almost equal because precision is tested in the explicit tests,
302- # this test is to ensure broadcast with Decimal is not broken.
303- assert_almost_equal (npf .ppmt (Decimal ('0.1' ) / Decimal ('12' ),
304- list (range (1 , 5 )), Decimal ('24' ),
305- Decimal ('2000' )),
306- [Decimal ('-75.62318601' ),
307- Decimal ('-76.25337923' ), Decimal ('-76.88882405' ),
308- Decimal ('-77.52956425' )], 4 )
309-
310- result = npf .ppmt (
311- Decimal ('0.1' ) / Decimal ('12' ),
312- list (range (1 , 5 )),
313- Decimal ('24' ),
314- Decimal ('2000' ),
315- Decimal ('0' ),
316- [Decimal ('0' ), Decimal ('1' ), 'end' , 'begin' ]
317- )
318- desired = [
319- Decimal ('-75.62318601' ),
320- Decimal ('-75.62318601' ),
321- Decimal ('-76.88882405' ),
322- Decimal ('-76.88882405' )
323- ]
324- assert_almost_equal (result , desired , decimal = 4 )
325-
326212
327213class TestNper :
328214 def test_basic_values (self ):
@@ -348,6 +234,129 @@ def test_no_interest(self):
348234 assert_ (npf .nper (0 , - 100 , 1000 ) == 10 )
349235
350236
237+ class TestPpmt :
238+ def test_float (self ):
239+ assert_allclose (
240+ npf .ppmt (0.1 / 12 , 1 , 60 , 55000 ),
241+ - 710.25 ,
242+ rtol = 1e-4
243+ )
244+
245+ def test_decimal (self ):
246+ result = npf .ppmt (
247+ Decimal ('0.1' ) / Decimal ('12' ),
248+ Decimal ('1' ),
249+ Decimal ('60' ),
250+ Decimal ('55000' )
251+ )
252+ assert_equal (
253+ result ,
254+ Decimal ('-710.2541257864217612489830917' ),
255+ )
256+
257+ @pytest .mark .parametrize ('when' , [1 , 'begin' ])
258+ def test_when_is_begin (self , when ):
259+ assert_allclose (
260+ npf .ppmt (0.1 / 12 , 1 , 60 , 55000 , 0 , when ),
261+ - 1158.929712 , # Computed using Google Sheet's PPMT
262+ rtol = 1e-9 ,
263+ )
264+
265+ @pytest .mark .parametrize ('when' , [None , 0 , 'end' ])
266+ def test_when_is_end (self , when ):
267+ args = (0.1 / 12 , 1 , 60 , 55000 , 0 )
268+ result = npf .ppmt (* args ) if when is None else npf .ppmt (* args , when )
269+ assert_allclose (
270+ result ,
271+ - 710.254126 , # Computed using Google Sheet's PPMT
272+ rtol = 1e-9 ,
273+ )
274+
275+ @pytest .mark .parametrize ('when' , [Decimal ('1' ), 'begin' ])
276+ def test_when_is_begin_decimal (self , when ):
277+ result = npf .ppmt (
278+ Decimal ('0.08' ) / Decimal ('12' ),
279+ Decimal ('1' ),
280+ Decimal ('60' ),
281+ Decimal ('15000.' ),
282+ Decimal ('0' ),
283+ when
284+ )
285+ assert_almost_equal (
286+ result ,
287+ Decimal ('-302.131703' ), # Computed using Google Sheet's PPMT
288+ decimal = 5 ,
289+ )
290+
291+ @pytest .mark .parametrize ('when' , [None , Decimal ('0' ), 'end' ])
292+ def test_when_is_end_decimal (self , when ):
293+ args = (
294+ Decimal ('0.08' ) / Decimal ('12' ),
295+ Decimal ('1' ),
296+ Decimal ('60' ),
297+ Decimal ('15000.' ),
298+ Decimal ('0' )
299+ )
300+ result = npf .ppmt (* args ) if when is None else npf .ppmt (* args , when )
301+ assert_almost_equal (
302+ result ,
303+ Decimal ('-204.145914' ), # Computed using Google Sheet's PPMT
304+ decimal = 5 ,
305+ )
306+
307+ @pytest .mark .parametrize ('args' , [
308+ (0.1 / 12 , 0 , 60 , 15000 ),
309+ (Decimal ('0.012' ), Decimal ('0' ), Decimal ('60' ), Decimal ('15000' ))
310+ ])
311+ def test_invalid_per (self , args ):
312+ # Note that math.isnan() handles Decimal NaN correctly.
313+ assert math .isnan (npf .ppmt (* args ))
314+
315+ @pytest .mark .parametrize ('when, desired' , [
316+ (
317+ None ,
318+ [- 75.62318601 , - 76.25337923 , - 76.88882405 , - 77.52956425 ],
319+ ), (
320+ [0 , 1 , 'end' , 'begin' ],
321+ [- 75.62318601 , - 75.62318601 , - 76.88882405 , - 76.88882405 ],
322+ )
323+ ])
324+ def test_broadcast (self , when , desired ):
325+ args = (0.1 / 12 , numpy .arange (1 , 5 ), 24 , 2000 , 0 )
326+ result = npf .ppmt (* args ) if when is None else npf .ppmt (* args , when )
327+ assert_allclose (result , desired , rtol = 1e-5 )
328+
329+ @pytest .mark .parametrize ('when, desired' , [
330+ (
331+ None ,
332+ [
333+ Decimal ('-75.62318601' ),
334+ Decimal ('-76.25337923' ),
335+ Decimal ('-76.88882405' ),
336+ Decimal ('-77.52956425' )
337+ ],
338+ ), (
339+ [Decimal ('0' ), Decimal ('1' ), 'end' , 'begin' ],
340+ [
341+ Decimal ('-75.62318601' ),
342+ Decimal ('-75.62318601' ),
343+ Decimal ('-76.88882405' ),
344+ Decimal ('-76.88882405' )
345+ ]
346+ )
347+ ])
348+ def test_broadcast_decimal (self , when , desired ):
349+ args = (
350+ Decimal ('0.1' ) / Decimal ('12' ),
351+ numpy .arange (1 , 5 ),
352+ Decimal ('24' ),
353+ Decimal ('2000' ),
354+ Decimal ('0' )
355+ )
356+ result = npf .ppmt (* args ) if when is None else npf .ppmt (* args , when )
357+ assert_almost_equal (result , desired , decimal = 8 )
358+
359+
351360class TestIpmt :
352361 def test_float (self ):
353362 assert_allclose (
@@ -372,7 +381,6 @@ def test_when_is_end(self, when):
372381 result = npf .ipmt (0.1 / 12 , 1 , 24 , 2000 , 0 , when )
373382 assert_allclose (result , - 16.666667 , rtol = 1e-6 )
374383
375-
376384 @pytest .mark .parametrize ('when' , [Decimal ('1' ), 'begin' ])
377385 def test_when_is_begin_decimal (self , when ):
378386 result = npf .ipmt (
0 commit comments