financial_calculator 3.0.0 → 3.1.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- checksums.yaml +4 -4
- data/CHANGELOG.md +9 -1
- data/README.md +4 -3
- data/lib/financial_calculator.rb +2 -0
- data/lib/financial_calculator/pmt.rb +67 -0
- data/lib/financial_calculator/pv.rb +1 -8
- data/lib/financial_calculator/validator.rb +10 -0
- data/lib/financial_calculator/version.rb +1 -1
- data/spec/pmt_spec.rb +71 -0
- data/spec/pv_spec.rb +10 -10
- data/spec/spec_helper.rb +6 -0
- metadata +6 -2
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 7ece241c288b5547ac0946a57d3488646427c41744219e0a7f2d86a164a8e088
|
4
|
+
data.tar.gz: b8fa7e3149792486255d06c54b53998e5f1846fe013d1152d511642349e5080c
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 880f9c1d6b98aadc025c246ba46e88b4f09928c92d395f59bfccb816796c806e2d1a9e3ce1358bf0c6ae288f4068c92bf9c466cd5d8013e68ff675a33dd0d6a4
|
7
|
+
data.tar.gz: bc10cbf71ecc8478778b46f61cbd25adbebb79dc074203200a14d31688ea6c4cf123cf728957a0f3c6a99e300648d15cf52718dee74fc395eb23128953dcd499
|
data/CHANGELOG.md
CHANGED
@@ -6,9 +6,17 @@ and this project adheres to [Semantic Versioning](http://semver.org/spec/v2.0.0.
|
|
6
6
|
|
7
7
|
## [Unreleased]
|
8
8
|
|
9
|
+
## [3.1.0] - 2018-05-20
|
10
|
+
### Added
|
11
|
+
- New class for PMT calculation (FinancialCalculator::Pmt)
|
12
|
+
|
13
|
+
### Removed
|
14
|
+
|
15
|
+
### Changed
|
16
|
+
|
9
17
|
## [3.0.0] - 2018-05-12
|
10
18
|
### Added
|
11
|
-
- New class for Present Value calculation (FinancialCalculator::
|
19
|
+
- New class for Present Value calculation (FinancialCalculator::Pv)
|
12
20
|
|
13
21
|
### Removed
|
14
22
|
- Monkey patched cashflow methods on Array
|
data/README.md
CHANGED
@@ -107,11 +107,12 @@ You can also increase your payment to a specific amount:
|
|
107
107
|
>> extra_payments_2 = 250000.amortize(rate){ -1500 }
|
108
108
|
|
109
109
|
## SUPPORTED STANDARD CALCUALTIONS
|
110
|
-
- PV
|
111
|
-
- NPV
|
112
|
-
- XNPV
|
113
110
|
- IRR
|
111
|
+
- NPV
|
112
|
+
- PMT
|
113
|
+
- PV
|
114
114
|
- XIRR
|
115
|
+
- XNPV
|
115
116
|
|
116
117
|
## FEATURES
|
117
118
|
|
data/lib/financial_calculator.rb
CHANGED
@@ -8,6 +8,7 @@ require 'flt/d'
|
|
8
8
|
# * *principal* represents the outstanding balance of a loan or annuity.
|
9
9
|
# * *rate* represents the interest rate _per period_.
|
10
10
|
module FinancialCalculator
|
11
|
+
require 'financial_calculator/validator'
|
11
12
|
require 'financial_calculator/amortization'
|
12
13
|
require 'financial_calculator/rates'
|
13
14
|
require 'financial_calculator/transaction'
|
@@ -16,4 +17,5 @@ module FinancialCalculator
|
|
16
17
|
require 'financial_calculator/npv'
|
17
18
|
require 'financial_calculator/xnpv'
|
18
19
|
require 'financial_calculator/xirr'
|
20
|
+
require 'financial_calculator/pmt'
|
19
21
|
end
|
@@ -0,0 +1,67 @@
|
|
1
|
+
module FinancialCalculator
|
2
|
+
# Calculates the payment of an ordinary annuity
|
3
|
+
class Pmt
|
4
|
+
include ::Validator
|
5
|
+
|
6
|
+
# @return [Numeric] The rate used for calculating the payment amount
|
7
|
+
# @api public
|
8
|
+
attr_reader :rate
|
9
|
+
|
10
|
+
# @return [Numeric] The number of payments to be made
|
11
|
+
# @api public
|
12
|
+
attr_reader :num_periods
|
13
|
+
|
14
|
+
# @return [Numeric] The current value of the annuity
|
15
|
+
# @api public
|
16
|
+
attr_reader :present_value
|
17
|
+
|
18
|
+
# @return [Numeric] The ending value of the annuity. Defaults to 0
|
19
|
+
# @api public
|
20
|
+
attr_reader :future_value
|
21
|
+
|
22
|
+
# @return [Boolean] Whether the payment is made at the beginning of the
|
23
|
+
# period (true) or end of the period (false)
|
24
|
+
# @api public
|
25
|
+
attr_reader :pay_at_beginning
|
26
|
+
|
27
|
+
# @return [DecNum] Result of the PMT calculation
|
28
|
+
# @api public
|
29
|
+
attr_reader :result
|
30
|
+
|
31
|
+
# Create a new object for calculating the periodic payment of an ordinary annuity
|
32
|
+
# @param [Numeric] rate The discount (interest) rate
|
33
|
+
# @param [Numeric] num_periods The number of payments to be made
|
34
|
+
# @param [Numeric] present_value The current value of the annuity
|
35
|
+
# @param [Numeric] future_value The ending value of the annuity
|
36
|
+
# @param [Boolean] pay_at_beginning. Whether the payment is made at the beginning
|
37
|
+
# of the period (true) or end of the period (false)
|
38
|
+
# @return [FinancialCalculator::Npv] An instance of a PMT calculation
|
39
|
+
def initialize(rate, num_periods, present_value, future_value = 0, pay_at_beginning = false)
|
40
|
+
validate_numerics(rate: rate, num_periods: num_periods, present_value: present_value, future_value: future_value)
|
41
|
+
|
42
|
+
@rate = Flt::DecNum(rate.to_s)
|
43
|
+
@num_periods = Flt::DecNum(num_periods.to_s)
|
44
|
+
@present_value = Flt::DecNum(present_value.to_s)
|
45
|
+
@future_value = Flt::DecNum(future_value || "0")
|
46
|
+
@pay_at_beginning = pay_at_beginning || false
|
47
|
+
@result = solve(@rate, @num_periods, @present_value, @future_value, @pay_at_beginning)
|
48
|
+
end
|
49
|
+
|
50
|
+
# @return [Boolean] Whether the payments are made at the beginning of each period
|
51
|
+
# @api public
|
52
|
+
def pays_at_beginning?
|
53
|
+
@pay_at_beginning
|
54
|
+
end
|
55
|
+
|
56
|
+
def inspect
|
57
|
+
"PMT(#{result})"
|
58
|
+
end
|
59
|
+
|
60
|
+
private
|
61
|
+
|
62
|
+
def solve(i, nper, pv, fv, pay_at_beginning)
|
63
|
+
type = pay_at_beginning ? 1 : 0
|
64
|
+
@result = ((pv * (1 + i) ** nper) + fv) * i / ((1 + i * type) * (1 - (1 + i) ** nper))
|
65
|
+
end
|
66
|
+
end
|
67
|
+
end
|
@@ -1,6 +1,7 @@
|
|
1
1
|
module FinancialCalculator
|
2
2
|
# Calculate the future value of a series of equal of payments
|
3
3
|
class Pv
|
4
|
+
include ::Validator
|
4
5
|
|
5
6
|
# @return [Numeric] The discount rate used in the calculation
|
6
7
|
attr_reader :rate
|
@@ -71,14 +72,6 @@ module FinancialCalculator
|
|
71
72
|
return 0 if amount.zero?
|
72
73
|
amount / (1 + rate) ** periods
|
73
74
|
end
|
74
|
-
|
75
|
-
def validate_numerics(numerics)
|
76
|
-
numerics.each do |key, value|
|
77
|
-
unless value.is_a? Numeric
|
78
|
-
raise ArgumentError.new("#{key} must be a type of Numeric. Got #{value.class} instead.")
|
79
|
-
end
|
80
|
-
end
|
81
|
-
end
|
82
75
|
end
|
83
76
|
|
84
77
|
PresentValue = Pv
|
data/spec/pmt_spec.rb
ADDED
@@ -0,0 +1,71 @@
|
|
1
|
+
require_relative 'spec_helper'
|
2
|
+
|
3
|
+
describe "Pmt" do
|
4
|
+
let(:rate) { 0.1 }
|
5
|
+
let(:present_value) { 1000 }
|
6
|
+
let(:num_periods) { 20 }
|
7
|
+
let(:future_value) { 0 }
|
8
|
+
let(:pay_at_beginning) { false }
|
9
|
+
let(:pmt) { Pmt.new(rate, num_periods, present_value, future_value, pay_at_beginning) }
|
10
|
+
|
11
|
+
subject { pmt }
|
12
|
+
|
13
|
+
it 'has a rate attribute' do
|
14
|
+
expect(subject).to have_attributes(rate: rate)
|
15
|
+
end
|
16
|
+
|
17
|
+
it 'has a num_periods attribute' do
|
18
|
+
expect(subject).to have_attributes(num_periods: num_periods)
|
19
|
+
end
|
20
|
+
|
21
|
+
it 'has a present_value attribute' do
|
22
|
+
expect(subject).to have_attributes(present_value: present_value)
|
23
|
+
end
|
24
|
+
|
25
|
+
it 'has a future_value attribute' do
|
26
|
+
expect(subject).to have_attributes(future_value: future_value)
|
27
|
+
end
|
28
|
+
|
29
|
+
it 'has a pays_at_beginning? attribute that defaults to false' do
|
30
|
+
expect(subject).to have_attributes(pays_at_beginning?: false)
|
31
|
+
end
|
32
|
+
|
33
|
+
it 'has a result attribute' do
|
34
|
+
expect(subject.result).to be_a Numeric
|
35
|
+
end
|
36
|
+
|
37
|
+
context 'when payments occur at the beginning of each period' do
|
38
|
+
let(:pay_at_beginning) { true }
|
39
|
+
it { is_expected.to have_attributes(pays_at_beginning?: true) }
|
40
|
+
end
|
41
|
+
|
42
|
+
context 'when given a non-numeric rate' do
|
43
|
+
let(:rate) { 'string' }
|
44
|
+
it_behaves_like 'it has invalid arguments'
|
45
|
+
end
|
46
|
+
|
47
|
+
context 'when given a non-numeric number of periods' do
|
48
|
+
let(:num_periods) { 'string' }
|
49
|
+
it_behaves_like 'it has invalid arguments'
|
50
|
+
end
|
51
|
+
|
52
|
+
context 'when given a non-numeric present value' do
|
53
|
+
let(:present_value) { 'string' }
|
54
|
+
it_behaves_like 'it has invalid arguments'
|
55
|
+
end
|
56
|
+
|
57
|
+
context 'when give a non-numeric future value' do
|
58
|
+
let(:future_value) { 'string' }
|
59
|
+
it_behaves_like 'it has invalid arguments'
|
60
|
+
end
|
61
|
+
|
62
|
+
describe '#inspect' do
|
63
|
+
subject { pmt.inspect }
|
64
|
+
|
65
|
+
it { is_expected.to be_a String }
|
66
|
+
it { is_expected.to include 'PMT' }
|
67
|
+
it 'includes the result of the PMT calculation' do
|
68
|
+
expect(subject).to include pmt.result.to_s
|
69
|
+
end
|
70
|
+
end
|
71
|
+
end
|
data/spec/pv_spec.rb
CHANGED
@@ -1,10 +1,10 @@
|
|
1
1
|
require_relative 'spec_helper'
|
2
2
|
|
3
|
-
shared_examples_for 'it has invalid
|
4
|
-
|
5
|
-
|
6
|
-
|
7
|
-
end
|
3
|
+
# shared_examples_for 'it has invalid arguments' do
|
4
|
+
# it 'raises an ArgumentError' do
|
5
|
+
# expect { subject }.to raise_error ArgumentError
|
6
|
+
# end
|
7
|
+
# end
|
8
8
|
|
9
9
|
describe "Pv" do
|
10
10
|
let(:rate) { 0.1 }
|
@@ -58,22 +58,22 @@ describe "Pv" do
|
|
58
58
|
|
59
59
|
context 'when the number of periods is negative' do
|
60
60
|
let(:num_periods) { -1 }
|
61
|
-
it_behaves_like 'it has invalid
|
61
|
+
it_behaves_like 'it has invalid arguments'
|
62
62
|
end
|
63
63
|
|
64
64
|
context 'when rate is non-numeric' do
|
65
65
|
let(:rate) { 'string' }
|
66
|
-
it_behaves_like 'it has invalid
|
66
|
+
it_behaves_like 'it has invalid arguments'
|
67
67
|
end
|
68
68
|
|
69
69
|
context 'when num_periods is non-numeric' do
|
70
70
|
let(:num_periods) { 'string' }
|
71
|
-
it_behaves_like 'it has invalid
|
71
|
+
it_behaves_like 'it has invalid arguments'
|
72
72
|
end
|
73
73
|
|
74
74
|
context 'when payment is non-numeric' do
|
75
75
|
let(:payment) { 'string' }
|
76
|
-
it_behaves_like 'it has invalid
|
76
|
+
it_behaves_like 'it has invalid arguments'
|
77
77
|
end
|
78
78
|
|
79
79
|
context 'when future_values non-numeric' do
|
@@ -81,7 +81,7 @@ describe "Pv" do
|
|
81
81
|
|
82
82
|
subject { Pv.new(rate, num_periods, payment, future_value) }
|
83
83
|
|
84
|
-
it_behaves_like 'it has invalid
|
84
|
+
it_behaves_like 'it has invalid arguments'
|
85
85
|
end
|
86
86
|
|
87
87
|
context 'when the number of periods is 0' do
|
data/spec/spec_helper.rb
CHANGED
@@ -21,4 +21,10 @@ shared_examples_for 'the values do not converge' do
|
|
21
21
|
it 'should raise an ArgumentError' do
|
22
22
|
expect { subject }.to raise_error ArgumentError
|
23
23
|
end
|
24
|
+
end
|
25
|
+
|
26
|
+
shared_examples_for 'it has invalid arguments' do
|
27
|
+
it 'raises an ArgumentError' do
|
28
|
+
expect { subject }.to raise_error ArgumentError
|
29
|
+
end
|
24
30
|
end
|
metadata
CHANGED
@@ -1,14 +1,14 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: financial_calculator
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 3.
|
4
|
+
version: 3.1.0
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Mike Falzone
|
8
8
|
autorequire:
|
9
9
|
bindir: bin
|
10
10
|
cert_chain: []
|
11
|
-
date: 2018-05-
|
11
|
+
date: 2018-05-20 00:00:00.000000000 Z
|
12
12
|
dependencies:
|
13
13
|
- !ruby/object:Gem::Dependency
|
14
14
|
name: flt
|
@@ -162,15 +162,18 @@ files:
|
|
162
162
|
- lib/financial_calculator/amortization.rb
|
163
163
|
- lib/financial_calculator/irr.rb
|
164
164
|
- lib/financial_calculator/npv.rb
|
165
|
+
- lib/financial_calculator/pmt.rb
|
165
166
|
- lib/financial_calculator/pv.rb
|
166
167
|
- lib/financial_calculator/rates.rb
|
167
168
|
- lib/financial_calculator/transaction.rb
|
169
|
+
- lib/financial_calculator/validator.rb
|
168
170
|
- lib/financial_calculator/version.rb
|
169
171
|
- lib/financial_calculator/xirr.rb
|
170
172
|
- lib/financial_calculator/xnpv.rb
|
171
173
|
- spec/amortization_spec.rb
|
172
174
|
- spec/irr_spec.rb
|
173
175
|
- spec/npv_spec.rb
|
176
|
+
- spec/pmt_spec.rb
|
174
177
|
- spec/pv_spec.rb
|
175
178
|
- spec/rates_spec.rb
|
176
179
|
- spec/spec_helper.rb
|
@@ -207,6 +210,7 @@ test_files:
|
|
207
210
|
- spec/amortization_spec.rb
|
208
211
|
- spec/irr_spec.rb
|
209
212
|
- spec/npv_spec.rb
|
213
|
+
- spec/pmt_spec.rb
|
210
214
|
- spec/pv_spec.rb
|
211
215
|
- spec/rates_spec.rb
|
212
216
|
- spec/spec_helper.rb
|