File Coverage

blib/lib/Finance/Amortization.pm
Criterion Covered Total %
statement 41 44 93.1
branch 8 14 57.1
condition 2 6 33.3
subroutine 9 9 100.0
pod 7 7 100.0
total 67 80 83.7


line stmt bran cond sub pod time code
1             package Finance::Amortization;
2              
3 2     2   44820 use strict;
  2         7  
  2         97  
4 2     2   13 use warnings;
  2         4  
  2         1251  
5              
6             our $VERSION = '0.5';
7              
8             =head1 NAME
9              
10             Finance::Amortization - Simple Amortization Schedules
11              
12             =head1 SYNOPSIS
13              
14             use Finance::Amortization
15              
16             # make a new schedule
17              
18             $amortization = new Finance::Amortization(principal => 100000, rate = 0.06/12,
19             periods = 360);
20              
21             # get the balance after a the twelveth period
22              
23             $balance = $amortization->balance(12)
24              
25             # get the interest paid during the twelfth period
26              
27             $interest = $amortization->interest(12);
28              
29             =head1 DESCRIPTION
30              
31             Finance::Amortization is a simple object oriented interface to an
32             amortization table. Pass in the principal to be amortized, the number
33             of payments to be made, and the interest rate per payment. It will
34             calculate the rest on demand, and provides a few methods to ask
35             for the state of the table after a given number of periods.
36              
37             Finance::Amortization is written in pure perl and does not depend
38             on any other modules. It exports no functions; all access is via
39             methods called on an amortization object. (Except for new(), of course.)
40              
41             =cut
42              
43             =head2 new()
44              
45             $am = Finance::Amortization->new(principal => 0, rate => 0, periods => 0,
46             compounding => 12, precision => 2);
47              
48             Creates a new amortization object. Calling interface is hash style.
49             The fields principal, rate, and periods are available, all defaulting
50             to zero.
51              
52             Compounding is a parameter which sets how many periods the rate is compounded
53             over. Thus, if each amortization period is one month, setting compounding
54             to 12 (the default), will make the rate an annual rate. That is, the
55             interest rate per period is the rate specified, divided by the compounding.
56              
57             So, to get an amortization for 30 years on 200000, with a 6% annual rate,
58             you would call new(principal => 200000, periods => 12*30, rate => 0.06),
59             the compounding will default to 12, and so the rate will work out right
60             for monthly payments.
61              
62             precision is used to specify the number of decimal places to round to
63             when returning answers. It defaults to 2, which is appropriate for
64             US currency and many others.
65              
66             =cut
67              
68             sub new {
69 1     1 1 10 my $pkg = shift;
70             # bless package variables
71 1         8 my %conf = (
72             principal => 0.00,
73             rate => 0.00,
74             compounding => 12,
75             precision => 2, # how many decimals to round
76             @_
77             );
78 1 50       4 if (!defined $conf{'periods'}) {
79 0         0 $conf{'periods'} = $conf{'length'} * $conf{'compounding'};
80             }
81 1 50       3 if (defined($conf{'compounding'})) {
82 1         3 $conf{'rate'} /= $conf{'compounding'};
83             }
84              
85             bless {
86 1         7 %conf
87             }, $pkg;
88             }
89              
90             =head2 rate()
91              
92             $rate_per_period = $am->rate()
93              
94             returns the interest rate per period. Ignores any arguments.
95              
96             =cut
97              
98             sub rate {
99 4     4 1 5 my $am = shift;
100 4         32 return $am->{'rate'};
101             }
102              
103             =head2 principal()
104              
105             $initial_value = $am->principal()
106              
107             returns the initial principal being amortized. Ignores any arguments.
108              
109             =cut
110              
111             sub principal {
112 4     4 1 5 my $am = shift;
113 4         41 return sprintf('%.*f', $am->{'precision'}, $am->{'principal'});
114             }
115              
116             =head2 periods()
117              
118             $number_of_periods = $am->periods()
119              
120             returns the number of periods in which the principal is being amortized.
121             Ignores any arguments.
122              
123             =cut
124              
125             sub periods {
126 5     5 1 647 my $am = shift;
127 5         21 return $am->{'periods'};
128             }
129              
130             #P = r*L*(1+r)^n/{(1+r)^n - 1}
131              
132             =head2 payment()
133              
134             $pmt = $am->payment()
135              
136             returns the payment per period. This method will cache the value the
137             first time it is called.
138              
139             =cut
140              
141             sub payment {
142 1     1 1 1 my $am = shift;
143              
144 1 50       3 if ($am->{'payment'}) {
145 0         0 return $am->{'payment'}
146             }
147              
148 1         2 my $r = $am->rate;
149 1         2 my $r1 = $r + 1;
150 1         3 my $n = $am->periods();
151 1         2 my $p = $am->principal;
152              
153 1 50       4 if ($r == 0) {
154 0         0 return $am->{'payment'} = $p / $n;
155             }
156              
157 1         26 $am->{'payment'} = sprintf('%.2f', $r * $p * $r1**$n / ($r1**$n-1));
158             }
159              
160             =head2 balance(n)
161              
162             $balance = $am->balance(12);
163              
164             Returns the balance of the amortization after the period given in the
165             argument
166              
167             =cut
168              
169             sub balance {
170 2     2 1 4 my $am = shift;
171 2         3 my $period = shift;
172 2 100       10 return $am->principal() if $period == 0;
173              
174 1 50 33     6 return 0 if ($period < 1 or $period > $am->periods);
175              
176 1         5 my $rate = $am->rate;
177 1         3 my $rate1 = $rate + 1;
178 1         3 my $periods = $am->periods();
179 1         9 my $principal = $am->principal;
180 1         4 my $pmt = $am->payment();
181              
182 1         10 return sprintf('%.*f', $am->{'precision'},
183             $principal*$rate1**$period-$pmt*($rate1**$period - 1)/$rate);
184              
185             }
186              
187             =head2 interest(n)
188              
189             $interest = $am->interest(12);
190              
191             Returns the interest paid in the period given in the argument
192              
193             =cut
194              
195             sub interest {
196 1     1 1 2 my $am = shift;
197 1         2 my $period = shift;
198              
199 1 50 33     7 return 0 if ($period < 1 or $period > $am->periods);
200              
201 1         2 my $rate = $am->rate;
202              
203 1         4 return sprintf('%.*f', $am->{'precision'},
204             $rate * $am->balance($period - 1));
205             }
206              
207             =head1 BUGS
208              
209             This module uses perl's floating point for financial calculations. This
210             may introduce inaccuracies and/or make this module unsuitable for serious
211             financial applications.
212              
213             Please report any bugs or feature requests to
214             C, or through the web interface at
215             L.
216              
217             =head1 TODO
218              
219             Use Math::BigRat for the calculations.
220              
221             Provide amortizers for present value, future value, annuities, etc.
222              
223             Allow for caching calculated values.
224              
225             Provide output methods and converters to various table modules.
226             HTML::Table, Text::Table, and Data::Table come to mind.
227              
228             Write better test scripts.
229              
230             Better checking for errors and out of range input. Return undef
231             in these cases.
232              
233             Use a locale dependent value to set an appropriate default for precision
234             in the new() method.
235              
236             =head1 LICENSE
237              
238             None. This entire module is in the public domain.
239              
240             =head1 AUTHOR
241              
242             Nathan Wagner
243              
244             This entire module is written by me and placed into the public domain.
245              
246             =cut
247              
248             1;
249              
250             __END__