finance_velocity 2.0.3

Sign up to get free protection for your applications and to get access to all the features.
data/COPYING.LESSER ADDED
@@ -0,0 +1,167 @@
1
+ # @title COPYING.LESSER
2
+ # @markup none
3
+ GNU LESSER GENERAL PUBLIC LICENSE
4
+ Version 3, 29 June 2007
5
+
6
+ Copyright (C) 2007 Free Software Foundation, Inc. <http://fsf.org/>
7
+ Everyone is permitted to copy and distribute verbatim copies
8
+ of this license document, but changing it is not allowed.
9
+
10
+
11
+ This version of the GNU Lesser General Public License incorporates
12
+ the terms and conditions of version 3 of the GNU General Public
13
+ License, supplemented by the additional permissions listed below.
14
+
15
+ 0. Additional Definitions.
16
+
17
+ As used herein, "this License" refers to version 3 of the GNU Lesser
18
+ General Public License, and the "GNU GPL" refers to version 3 of the GNU
19
+ General Public License.
20
+
21
+ "The Library" refers to a covered work governed by this License,
22
+ other than an Application or a Combined Work as defined below.
23
+
24
+ An "Application" is any work that makes use of an interface provided
25
+ by the Library, but which is not otherwise based on the Library.
26
+ Defining a subclass of a class defined by the Library is deemed a mode
27
+ of using an interface provided by the Library.
28
+
29
+ A "Combined Work" is a work produced by combining or linking an
30
+ Application with the Library. The particular version of the Library
31
+ with which the Combined Work was made is also called the "Linked
32
+ Version".
33
+
34
+ The "Minimal Corresponding Source" for a Combined Work means the
35
+ Corresponding Source for the Combined Work, excluding any source code
36
+ for portions of the Combined Work that, considered in isolation, are
37
+ based on the Application, and not on the Linked Version.
38
+
39
+ The "Corresponding Application Code" for a Combined Work means the
40
+ object code and/or source code for the Application, including any data
41
+ and utility programs needed for reproducing the Combined Work from the
42
+ Application, but excluding the System Libraries of the Combined Work.
43
+
44
+ 1. Exception to Section 3 of the GNU GPL.
45
+
46
+ You may convey a covered work under sections 3 and 4 of this License
47
+ without being bound by section 3 of the GNU GPL.
48
+
49
+ 2. Conveying Modified Versions.
50
+
51
+ If you modify a copy of the Library, and, in your modifications, a
52
+ facility refers to a function or data to be supplied by an Application
53
+ that uses the facility (other than as an argument passed when the
54
+ facility is invoked), then you may convey a copy of the modified
55
+ version:
56
+
57
+ a) under this License, provided that you make a good faith effort to
58
+ ensure that, in the event an Application does not supply the
59
+ function or data, the facility still operates, and performs
60
+ whatever part of its purpose remains meaningful, or
61
+
62
+ b) under the GNU GPL, with none of the additional permissions of
63
+ this License applicable to that copy.
64
+
65
+ 3. Object Code Incorporating Material from Library Header Files.
66
+
67
+ The object code form of an Application may incorporate material from
68
+ a header file that is part of the Library. You may convey such object
69
+ code under terms of your choice, provided that, if the incorporated
70
+ material is not limited to numerical parameters, data structure
71
+ layouts and accessors, or small macros, inline functions and templates
72
+ (ten or fewer lines in length), you do both of the following:
73
+
74
+ a) Give prominent notice with each copy of the object code that the
75
+ Library is used in it and that the Library and its use are
76
+ covered by this License.
77
+
78
+ b) Accompany the object code with a copy of the GNU GPL and this license
79
+ document.
80
+
81
+ 4. Combined Works.
82
+
83
+ You may convey a Combined Work under terms of your choice that,
84
+ taken together, effectively do not restrict modification of the
85
+ portions of the Library contained in the Combined Work and reverse
86
+ engineering for debugging such modifications, if you also do each of
87
+ the following:
88
+
89
+ a) Give prominent notice with each copy of the Combined Work that
90
+ the Library is used in it and that the Library and its use are
91
+ covered by this License.
92
+
93
+ b) Accompany the Combined Work with a copy of the GNU GPL and this license
94
+ document.
95
+
96
+ c) For a Combined Work that displays copyright notices during
97
+ execution, include the copyright notice for the Library among
98
+ these notices, as well as a reference directing the user to the
99
+ copies of the GNU GPL and this license document.
100
+
101
+ d) Do one of the following:
102
+
103
+ 0) Convey the Minimal Corresponding Source under the terms of this
104
+ License, and the Corresponding Application Code in a form
105
+ suitable for, and under terms that permit, the user to
106
+ recombine or relink the Application with a modified version of
107
+ the Linked Version to produce a modified Combined Work, in the
108
+ manner specified by section 6 of the GNU GPL for conveying
109
+ Corresponding Source.
110
+
111
+ 1) Use a suitable shared library mechanism for linking with the
112
+ Library. A suitable mechanism is one that (a) uses at run time
113
+ a copy of the Library already present on the user's computer
114
+ system, and (b) will operate properly with a modified version
115
+ of the Library that is interface-compatible with the Linked
116
+ Version.
117
+
118
+ e) Provide Installation Information, but only if you would otherwise
119
+ be required to provide such information under section 6 of the
120
+ GNU GPL, and only to the extent that such information is
121
+ necessary to install and execute a modified version of the
122
+ Combined Work produced by recombining or relinking the
123
+ Application with a modified version of the Linked Version. (If
124
+ you use option 4d0, the Installation Information must accompany
125
+ the Minimal Corresponding Source and Corresponding Application
126
+ Code. If you use option 4d1, you must provide the Installation
127
+ Information in the manner specified by section 6 of the GNU GPL
128
+ for conveying Corresponding Source.)
129
+
130
+ 5. Combined Libraries.
131
+
132
+ You may place library facilities that are a work based on the
133
+ Library side by side in a single library together with other library
134
+ facilities that are not Applications and are not covered by this
135
+ License, and convey such a combined library under terms of your
136
+ choice, if you do both of the following:
137
+
138
+ a) Accompany the combined library with a copy of the same work based
139
+ on the Library, uncombined with any other library facilities,
140
+ conveyed under the terms of this License.
141
+
142
+ b) Give prominent notice with the combined library that part of it
143
+ is a work based on the Library, and explaining where to find the
144
+ accompanying uncombined form of the same work.
145
+
146
+ 6. Revised Versions of the GNU Lesser General Public License.
147
+
148
+ The Free Software Foundation may publish revised and/or new versions
149
+ of the GNU Lesser General Public License from time to time. Such new
150
+ versions will be similar in spirit to the present version, but may
151
+ differ in detail to address new problems or concerns.
152
+
153
+ Each version is given a distinguishing version number. If the
154
+ Library as you received it specifies that a certain numbered version
155
+ of the GNU Lesser General Public License "or any later version"
156
+ applies to it, you have the option of following the terms and
157
+ conditions either of that published version or of any later version
158
+ published by the Free Software Foundation. If the Library as you
159
+ received it does not specify a version number of the GNU Lesser
160
+ General Public License, you may choose any version of the GNU Lesser
161
+ General Public License ever published by the Free Software Foundation.
162
+
163
+ If the Library as you received it specifies that a proxy can decide
164
+ whether future versions of the GNU Lesser General Public License shall
165
+ apply, that proxy's public statement of acceptance of any version is
166
+ permanent authorization for you to choose that version for the
167
+ Library.
data/Gemfile ADDED
@@ -0,0 +1,3 @@
1
+ source "http://rubygems.org"
2
+
3
+ gemspec
data/HISTORY ADDED
@@ -0,0 +1,47 @@
1
+ = Version 2.0.0
2
+ 23 Jul 2013
3
+
4
+ * Removed Integer#months, Integer#years, and replaced Numeric#to_d by Numeric#to_s in the interest of Rails compatibility.
5
+ * Converted unit tests from the shoulda framework to minitest.
6
+ * Removed octal numbers in test_cashflow.rb
7
+ * Thanks to @thadd, @bramswenson, and @xpe for their contributions to this release!
8
+
9
+ = Version 1.1.2
10
+ 16 Jun 2012
11
+
12
+ * Bugfix: Array#irr and Array#xirr check for a valid sequence of cash flows.
13
+ * Bugfix: Integer#months and Integer#years no longer collide with Rails methods.
14
+
15
+ = Version 1.1.0
16
+ 11 Sep 2011
17
+
18
+ * Added XNPV and XIRR functions, with basic testing.
19
+ * Bugfix: Array#sum no longer collides with the Array#sum defined in Rails.
20
+ * Bugfix: Numeric#amortize now correctly calls Finance::Amortization#new.
21
+
22
+ = Version 1.0.0
23
+ 20 Jul 2011
24
+
25
+ * Moved to Ruby 1.9.
26
+ * All classes are now contained within the +Finance+ namespace.
27
+ * LOTS of additional documentation and examples.
28
+ * Introduced _shoulda_ for unit tests, to make things a little more readable.
29
+ * Bugfix: The +amortize+ Numeric method now accepts a variable number of rates.
30
+ * Some code refactoring and clean-up for a small performance increase.
31
+
32
+ = Version 0.2.0
33
+ 28 Jun 2011
34
+
35
+ * Added support for adjustable rate mortgages.
36
+ * Added support for additional payments.
37
+
38
+ = Version 0.1.1
39
+ 21 Jun 2011
40
+
41
+ * Code examples in README now display correctly in the online documentation.
42
+
43
+ = Version 0.1.0
44
+ 21 Jun 2011
45
+
46
+ * Support for fixed-rate mortgage amortization.
47
+ * NPV, IRR array methods for cash flow analysis.
data/README.md ADDED
@@ -0,0 +1,166 @@
1
+ # FINANCE
2
+
3
+ a library for financial modelling in Ruby.
4
+
5
+ ## INSTALL
6
+
7
+ $ sudo gem install finance
8
+
9
+ ## IMPORTANT CHANGES
10
+
11
+ Contributions by [@thadd](https://github.com/thadd) and
12
+ [@bramswenson](https://github.com/bramswenson) have made the `finance`
13
+ library fully compatible with rails, at the cost of the `#years` and
14
+ `#months` convenience methods on `Integer`, as well as the `#to_d` method for
15
+ converting `Numerics` into `DecNums`. These methods have been removed, due to
16
+ conflicts with existing rails methods.
17
+
18
+ Correspondingly, `finance` has been bumped up to version 2.0.
19
+
20
+ ## OVERVIEW
21
+
22
+ ### GETTING STARTED
23
+
24
+ >> require 'finance'
25
+
26
+ *Note:* As of version 1.0.0, the entire library is contained under the
27
+ Finance namespace. Existing code will not work unless you add:
28
+
29
+ >> include Finance
30
+
31
+ for all of the examples below, we'll assume that you have done this.
32
+
33
+ ### AMORTIZATION
34
+
35
+ You are interested in borrowing $250,000 under a 30 year, fixed-rate
36
+ loan with a 4.25% APR.
37
+
38
+ >> rate = Rate.new(0.0425, :apr, :duration => (30 * 12))
39
+ >> amortization = Amortization.new(250000, rate)
40
+
41
+ Find the standard monthly payment:
42
+
43
+ >> amortization.payment
44
+ => DecNum('-1229.91')
45
+
46
+ Find the total cost of the loan:
47
+
48
+ >> amortization.payments.sum
49
+ => DecNum('-442766.55')
50
+
51
+ How much will you pay in interest?
52
+
53
+ >> amortization.interest.sum
54
+ => DecNum('192766.55')
55
+
56
+ How much interest in the first six months?
57
+
58
+ >> amortization.interest[0,6].sum
59
+ => DecNum('5294.62')
60
+
61
+ If your loan has an adjustable rate, no problem. You can pass an
62
+ arbitrary number of rates, and they will be used in the amortization.
63
+ For example, we can look at an amortization of $250000, where the APR
64
+ starts at 4.25%, and increases by 1% every five years.
65
+
66
+ >> values = %w{ 0.0425 0.0525 0.0625 0.0725 0.0825 0.0925 }
67
+ >> rates = values.collect { |value| Rate.new( value, :apr, :duration => (5 * 12) }
68
+ >> arm = Amortization.new(250000, *rates)
69
+
70
+ Since we are looking at an ARM, there is no longer a single "payment" value.
71
+
72
+ >> arm.payment
73
+ => nil
74
+
75
+ But we can look at the different payments over time.
76
+
77
+ >> arm.payments.uniq
78
+ => [DecNum('-1229.85'), DecNum('-1360.41'), DecNum('-1475.65'), DecNum('-1571.07'), ... snipped ... ]
79
+
80
+ The other methods previously discussed can be accessed in the same way:
81
+
82
+ >> arm.interest.sum
83
+ => DecNum('287515.45')
84
+ >> arm.payments.sum
85
+ => DecNum('-537515.45')
86
+
87
+ Last, but not least, you may pass a block when creating an Amortization
88
+ which returns a modified monthly payment. For example, to increase your
89
+ payment by $150, do:
90
+
91
+ >> rate = Rate.new(0.0425, :apr, :duration => (30 * 12))
92
+ >> extra_payments = 250000.amortize(rate){ |period| period.payment - 150 }
93
+
94
+ Disregarding the block, we have used the same parameters as the first
95
+ example. Notice the difference in the results:
96
+
97
+ >> amortization.payments.sum
98
+ => DecNum('-442745.98')
99
+ >> extra_payments.payments.sum
100
+ => DecNum('-400566.24')
101
+ >> amortization.interest.sum
102
+ => DecNum('192745.98')
103
+ >> extra_payments.interest.sum
104
+ => DecNum('150566.24')
105
+
106
+ You can also increase your payment to a specific amount:
107
+
108
+ >> extra_payments_2 = 250000.amortize(rate){ -1500 }
109
+
110
+ ## ABOUT
111
+
112
+ I started developing `finance` while analyzing mortgages as a personal
113
+ project. Spreadsheets have convenient formulas for doing this type of
114
+ work, until you want to do something semi-complex (like ARMs or extra
115
+ payments), at which point you need to create your own amortization
116
+ table. I thought I could create a better interface for this type of
117
+ work in Ruby, and since I couldn't find an existing resource for these
118
+ tools, I am hoping to save other folks some time by releasing what I
119
+ have as a gem.
120
+
121
+ More broadly, I believe there are many calculations that are necessary
122
+ for the effective management of personal finances, but are difficult
123
+ (or impossible) to do with spreadsheets or other existing open source
124
+ tools. My hope is that the `finance` library will grow to provide a set
125
+ of open, tested tools to fill this gap.
126
+
127
+ If you have used `finance` and find it useful, I would enjoy hearing
128
+ about it!
129
+
130
+ ## FEATURES
131
+
132
+ Currently implemented features include:
133
+
134
+ * Uses the [flt](http://flt.rubyforge.org/) library to ensure precision decimal arithmetic in all calculations.
135
+ * Fixed-rate mortgage amortization (30/360).
136
+ * Interest rates
137
+ * Various cash flow computations, such as NPV and IRR.
138
+ * Adjustable rate mortgage amortization.
139
+ * Payment modifications (i.e., how does paying an additional $75 per month affect the amortization?)
140
+
141
+ ## RESOURCES
142
+
143
+ * [RubyGems Page](https://rubygems.org/gems/finance)
144
+ * [Source Code](http://github.com/wkranec/finance)
145
+ * [Bug Tracker](https://github.com/wkranec/finance/issues)
146
+ * [Google Group](http://groups.google.com/group/finance-gem/topics?pli=1)
147
+
148
+ ## COPYRIGHT
149
+
150
+ This library is released under the terms of the LGPL license.
151
+
152
+ Copyright (c) 2011, William Kranec.
153
+ All rights reserved.
154
+
155
+ This program is free software: you can redistribute it and/or modify it
156
+ under the terms of the GNU Lesser General Public License as published by the
157
+ Free Software Foundation, either version 3 of the License, or (at your
158
+ option) any later version.
159
+
160
+ This program is distributed in the hope that it will be useful,
161
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
162
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
163
+ Lesser General Public License for more details.
164
+
165
+ You should have received a copy of the GNU Lesser General Public License along
166
+ with this program. If not, see <http://www.gnu.org/licenses/>.
data/Rakefile ADDED
@@ -0,0 +1,10 @@
1
+ require 'rake'
2
+ require 'rake/testtask'
3
+
4
+ task :default => [:test_units]
5
+
6
+ Rake::TestTask.new("test_units") do |t|
7
+ t.pattern = 'test/*.rb'
8
+ t.verbose = true
9
+ t.warning = true
10
+ end
data/finance.gemspec ADDED
@@ -0,0 +1,19 @@
1
+ SPEC = Gem::Specification.new do |s|
2
+ s.name = "finance_velocity"
3
+ s.version = "2.0.3"
4
+ s.author = "Mani Bhushan"
5
+ s.license = "LGPL-3.0"
6
+ s.email = "manilnct@gmail.com"
7
+ s.platform = Gem::Platform::RUBY
8
+ s.summary = "a library for financial modelling in Ruby."
9
+ s.description = "The finance library provides a Ruby interface for working with interest rates, mortgage amortization, and cashflows (NPV, IRR, etc.)."
10
+ s.homepage = "https://rubygems.org/gems/finance"
11
+
12
+ s.required_ruby_version = '>=1.9'
13
+ s.add_runtime_dependency 'flt', '~> 1.3', '>= 1.3.0'
14
+ s.add_development_dependency 'minitest', '~> 4.7', '>= 4.7.5'
15
+ s.add_development_dependency 'activesupport', '~> 4.0', '>= 4.0.0'
16
+ s.files = `git ls-files`.split("\n")
17
+
18
+ s.extra_rdoc_files = ['README.md', 'COPYING', 'COPYING.LESSER', 'HISTORY']
19
+ end
@@ -0,0 +1,201 @@
1
+ require_relative 'cashflows'
2
+ require_relative 'decimal'
3
+ require_relative 'transaction'
4
+
5
+ module Finance
6
+ # the Amortization class provides an interface for working with loan amortizations.
7
+ # @note There are _two_ ways to create an amortization. The first
8
+ # example uses the amortize method for the Numeric class. The second
9
+ # calls Amortization.new directly.
10
+ # @example Borrow $250,000 under a 30 year, fixed-rate loan with a 4.25% APR
11
+ # rate = Rate.new(0.0425, :apr, :duration => (30 * 12))
12
+ # amortization = 250000.amortize(rate)
13
+ # @example Borrow $250,000 under a 30 year, adjustable rate loan, with an APR starting at 4.25%, and increasing by 1% every five years
14
+ # values = %w{ 0.0425 0.0525 0.0625 0.0725 0.0825 0.0925 }
15
+ # rates = values.collect { |value| Rate.new( value, :apr, :duration = (5 * 12) ) }
16
+ # arm = Amortization.new(250000, *rates)
17
+ # @example Borrow $250,000 under a 30 year, fixed-rate loan with a 4.25% APR, but pay $150 extra each month
18
+ # rate = Rate.new(0.0425, :apr, :duration => (5 * 12))
19
+ # extra_payments = 250000.amortize(rate){ |period| period.payment - 150 }
20
+ # @api public
21
+ class Amortization
22
+ # @return [DecNum] the balance of the loan at the end of the amortization period (usually zero)
23
+ # @api public
24
+ attr_reader :balance
25
+ # @return [DecNum] the required monthly payment. For loans with more than one rate, returns nil
26
+ # @api public
27
+ attr_reader :payment
28
+ # @return [DecNum] the principal amount of the loan
29
+ # @api public
30
+ attr_reader :principal
31
+ # @return [Array] the interest rates used for calculating the amortization
32
+ # @api public
33
+ attr_reader :rates
34
+
35
+ # compare two Amortization instances
36
+ # @return [Numeric] -1, 0, or +1
37
+ # @param [Amortization]
38
+ # @api public
39
+ def ==(amortization)
40
+ self.principal == amortization.principal and self.rates == amortization.rates and self.payments == amortization.payments
41
+ end
42
+
43
+ # @return [Array] the amount of any additional payments in each period
44
+ # @example
45
+ # rate = Rate.new(0.0375, :apr, :duration => (30 * 12))
46
+ # amt = 300000.amortize(rate){ |payment| payment.amount-100}
47
+ # amt.additional_payments #=> [DecNum('-100.00'), DecNum('-100.00'), ... ]
48
+ # @api public
49
+ def additional_payments
50
+ @transactions.find_all(&:payment?).collect{ |p| p.difference }
51
+ end
52
+
53
+ # amortize the balance of loan with the given interest rate
54
+ # @return none
55
+ # @param [Rate] rate the interest rate to use in the amortization
56
+ # @api private
57
+ def amortize(rate)
58
+ # For the purposes of calculating a payment, the relevant time
59
+ # period is the remaining number of periods in the loan, not
60
+ # necessarily the duration of the rate itself.
61
+ periods = @periods - @period
62
+ amount = Amortization.payment @balance, rate.monthly, periods
63
+
64
+ pmt = Payment.new(amount, :period => @period)
65
+ if @block then pmt.modify(&@block) end
66
+
67
+ rate.duration.to_i.times do
68
+ # Do this first in case the balance is zero already.
69
+ if @balance.zero? then break end
70
+
71
+ # Compute and record interest on the outstanding balance.
72
+ int = (@balance * rate.monthly).round(2)
73
+ interest = Interest.new(int, :period => @period)
74
+ @balance += interest.amount
75
+ @transactions << interest.dup
76
+
77
+ # Record payment. Don't pay more than the outstanding balance.
78
+ if pmt.amount.abs > @balance then pmt.amount = -@balance end
79
+ @transactions << pmt.dup
80
+ @balance += pmt.amount
81
+
82
+ @period += 1
83
+ end
84
+ end
85
+
86
+ # compute the amortization of the principal
87
+ # @return none
88
+ # @api private
89
+ def compute
90
+ @balance = @principal
91
+ @transactions = []
92
+
93
+ @rates.each do |rate|
94
+ amortize(rate)
95
+ end
96
+
97
+ # Add any remaining balance due to rounding error to the last payment.
98
+ unless @balance.zero?
99
+ @transactions.find_all(&:payment?)[-1].amount -= @balance
100
+ @balance = 0
101
+ end
102
+
103
+ if @rates.length == 1
104
+ @payment = self.payments[0]
105
+ else
106
+ @payment = nil
107
+ end
108
+
109
+ @transactions.freeze
110
+ end
111
+
112
+ # @return [Integer] the time required to pay off the loan, in months
113
+ # @example In most cases, the duration is equal to the total duration of all rates
114
+ # rate = Rate.new(0.0375, :apr, :duration => (30 * 12))
115
+ # amt = 300000.amortize(rate)
116
+ # amt.duration #=> 360
117
+ # @example Extra payments may reduce the duration
118
+ # rate = Rate.new(0.0375, :apr, :duration => (30 * 12))
119
+ # amt = 300000.amortize(rate){ |payment| payment.amount-100}
120
+ # amt.duration #=> 319
121
+ # @api public
122
+ def duration
123
+ self.payments.length
124
+ end
125
+
126
+ # create a new Amortization instance
127
+ # @return [Amortization]
128
+ # @param [DecNum] principal the initial amount of the loan or investment
129
+ # @param [Rate] rates the applicable interest rates
130
+ # @param [Proc] block
131
+ # @api public
132
+ def initialize(principal, *rates, &block)
133
+ @principal = Flt::DecNum.new(principal.to_s)
134
+ @rates = rates
135
+ @block = block
136
+
137
+ # compute the total duration from all of the rates.
138
+ @periods = (rates.collect { |r| r.duration }).sum
139
+ @period = 0
140
+
141
+ compute
142
+ end
143
+
144
+ # @api public
145
+ def inspect
146
+ "Amortization.new(#{@principal})"
147
+ end
148
+
149
+ # @return [Array] the amount of interest charged in each period
150
+ # @example find the total cost of interest for a loan
151
+ # rate = Rate.new(0.0375, :apr, :duration => (30 * 12))
152
+ # amt = 300000.amortize(rate)
153
+ # amt.interest.sum #=> DecNum('200163.94')
154
+ # @example find the total interest charges in the first six months
155
+ # rate = Rate.new(0.0375, :apr, :duration => (30 * 12))
156
+ # amt = 300000.amortize(rate)
157
+ # amt.interest[0,6].sum #=> DecNum('5603.74')
158
+ # @api public
159
+ def interest
160
+ @transactions.find_all(&:interest?).collect{ |p| p.amount }
161
+ end
162
+
163
+ # @return [DecNum] the periodic payment due on a loan
164
+ # @param [DecNum] principal the initial amount of the loan or investment
165
+ # @param [Rate] rate the applicable interest rate (per period)
166
+ # @param [Integer] periods the number of periods needed for repayment
167
+ # @note in most cases, you will probably want to use rate.monthly when calling this function outside of an Amortization instance.
168
+ # @example
169
+ # rate = Rate.new(0.0375, :apr, :duration => (30 * 12))
170
+ # rate.duration #=> 360
171
+ # Amortization.payment(200000, rate.monthly, rate.duration) #=> DecNum('-926.23')
172
+ # @see http://en.wikipedia.org/wiki/Amortization_calculator
173
+ # @api public
174
+ def Amortization.payment(principal, rate, periods)
175
+ if rate.zero?
176
+ # simplified formula to avoid division-by-zero when interest rate is zero
177
+ return -(principal / periods).round(2)
178
+ else
179
+ return -(principal * (rate + (rate / ((1 + rate) ** periods - 1)))).round(2)
180
+ end
181
+ end
182
+
183
+ # @return [Array] the amount of the payment in each period
184
+ # @example find the total payments for a loan
185
+ # rate = Rate.new(0.0375, :apr, :duration => (30 * 12))
186
+ # amt = 300000.amortize(rate)
187
+ # amt.payments.sum #=> DecNum('-500163.94')
188
+ # @api public
189
+ def payments
190
+ @transactions.find_all(&:payment?).collect{ |p| p.amount }
191
+ end
192
+ end
193
+ end
194
+
195
+ class Numeric
196
+ # @see Amortization#new
197
+ # @api public
198
+ def amortize(*rates, &block)
199
+ Finance::Amortization.new(self, *rates, &block)
200
+ end
201
+ end