financial_calculator 3.1.0 → 3.2.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 +8 -0
- data/README.md +4 -1
- data/lib/financial_calculator.rb +3 -0
- data/lib/financial_calculator/fv.rb +69 -0
- data/lib/financial_calculator/ipmt.rb +77 -0
- data/lib/financial_calculator/pmt.rb +3 -3
- data/lib/financial_calculator/ppmt.rb +74 -0
- data/lib/financial_calculator/pv.rb +2 -2
- data/lib/financial_calculator/version.rb +1 -1
- data/spec/fv_spec.rb +121 -0
- data/spec/ipmt_spec.rb +87 -0
- data/spec/ppmt_spec.rb +87 -0
- data/spec/pv_spec.rb +4 -10
- metadata +11 -2
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 57f4d8c3b636807234e0abea5bda7cb5137c0bd8147677f9dec480303c68e214
|
4
|
+
data.tar.gz: 9df5a5db44050c530da5e66bf3afcb2a57dedc5aa28cc96d98520b33a01affdd
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: b43701fce76029ec660519740cfbc31e0ce3b4b9180a5ec47f2b3bcf080c6d6b7650188d852c6e79d44505ba9704b750227ff9b334a646a2bc43596a412216de
|
7
|
+
data.tar.gz: 648eaa40c214a467c40fe148be87fc1fb7b2b4ffec7f04cff640904a7720b96709fa28fd0d92333380e2f638eb838579735dd486d3e0b8ebd51bfd0c352d2040
|
data/CHANGELOG.md
CHANGED
@@ -6,6 +6,14 @@ and this project adheres to [Semantic Versioning](http://semver.org/spec/v2.0.0.
|
|
6
6
|
|
7
7
|
## [Unreleased]
|
8
8
|
|
9
|
+
## [3.2.0] - 2018-06-09
|
10
|
+
### Added
|
11
|
+
- New class for FV, IPMT and PPMT calculations
|
12
|
+
|
13
|
+
### Removed
|
14
|
+
|
15
|
+
### Changed
|
16
|
+
|
9
17
|
## [3.1.0] - 2018-05-20
|
10
18
|
### Added
|
11
19
|
- New class for PMT calculation (FinancialCalculator::Pmt)
|
data/README.md
CHANGED
@@ -106,10 +106,13 @@ You can also increase your payment to a specific amount:
|
|
106
106
|
|
107
107
|
>> extra_payments_2 = 250000.amortize(rate){ -1500 }
|
108
108
|
|
109
|
-
## SUPPORTED
|
109
|
+
## SUPPORTED CALCUALTIONS
|
110
|
+
- FV
|
111
|
+
- IPMT
|
110
112
|
- IRR
|
111
113
|
- NPV
|
112
114
|
- PMT
|
115
|
+
- PPMT
|
113
116
|
- PV
|
114
117
|
- XIRR
|
115
118
|
- XNPV
|
data/lib/financial_calculator.rb
CHANGED
@@ -12,10 +12,13 @@ module FinancialCalculator
|
|
12
12
|
require 'financial_calculator/amortization'
|
13
13
|
require 'financial_calculator/rates'
|
14
14
|
require 'financial_calculator/transaction'
|
15
|
+
require 'financial_calculator/ipmt'
|
15
16
|
require 'financial_calculator/irr'
|
17
|
+
require 'financial_calculator/ppmt'
|
16
18
|
require 'financial_calculator/pv'
|
17
19
|
require 'financial_calculator/npv'
|
18
20
|
require 'financial_calculator/xnpv'
|
19
21
|
require 'financial_calculator/xirr'
|
20
22
|
require 'financial_calculator/pmt'
|
23
|
+
require 'financial_calculator/fv'
|
21
24
|
end
|
@@ -0,0 +1,69 @@
|
|
1
|
+
module FinancialCalculator
|
2
|
+
# Calculate the future value of a series of equal of payments
|
3
|
+
class Fv
|
4
|
+
include ::Validator
|
5
|
+
|
6
|
+
# @return [Numeric] The discount rate used in the calculation
|
7
|
+
attr_reader :rate
|
8
|
+
|
9
|
+
# @return [Numeric] The number of periodic payments
|
10
|
+
attr_reader :num_periods
|
11
|
+
|
12
|
+
# @return [Numeric] The amount of the periodic payment
|
13
|
+
attr_reader :payment
|
14
|
+
|
15
|
+
# @return [Numeric] The current value
|
16
|
+
attr_reader :present_value
|
17
|
+
|
18
|
+
# @return [Numeric] The result of the future value calculation
|
19
|
+
attr_reader :result
|
20
|
+
|
21
|
+
# Create a new future value calculation
|
22
|
+
#
|
23
|
+
# @see https://en.wikipedia.org/wiki/Future_value
|
24
|
+
# @example
|
25
|
+
# FinancialCalculator::Fv.new(0.02, 10, -100) #=> FV(1094.972100)
|
26
|
+
# @param [Numeric] rate The discount (interest) rate to use
|
27
|
+
# @param [Numeric] num_periods The number of periodic payments
|
28
|
+
# @param [Numeric] payment The amount of the periodic payment
|
29
|
+
# @param [Numeric] present_value The current value
|
30
|
+
# @param [Boolean] pay_at_beginning Whether the payments are made at the beginning or end of each period
|
31
|
+
# @return [FinancialCalculator::Fv] A Fv object
|
32
|
+
# @raise [ArgumentError] Raises an ArgumentError if num_periods is less than 0 or a required numeric
|
33
|
+
# field is given a non-numeric value
|
34
|
+
def initialize(rate, num_periods, payment, present_value = 0, pay_at_beginning = false)
|
35
|
+
validate_numerics(rate: rate, num_periods: num_periods, payment: payment, present_value: present_value)
|
36
|
+
|
37
|
+
if num_periods < 0
|
38
|
+
raise ArgumentError.new('Cannot calculate future value with negative periods. Use present value instead.')
|
39
|
+
end
|
40
|
+
|
41
|
+
@rate = Flt::DecNum(rate.to_s)
|
42
|
+
@num_periods = Flt::DecNum(num_periods.to_s)
|
43
|
+
@payment = Flt::DecNum(payment.to_s)
|
44
|
+
@present_value = Flt::DecNum(present_value.to_s)
|
45
|
+
@pay_at_beginning = pay_at_beginning
|
46
|
+
@result = solve(@rate, @num_periods, @payment, @present_value, pay_at_beginning)
|
47
|
+
end
|
48
|
+
|
49
|
+
def inspect
|
50
|
+
"FV(#{result})"
|
51
|
+
end
|
52
|
+
|
53
|
+
# @return [Boolean] Whether the payments are made at the beginning of each period
|
54
|
+
def pays_at_beginning?
|
55
|
+
@pay_at_beginning
|
56
|
+
end
|
57
|
+
|
58
|
+
private
|
59
|
+
|
60
|
+
def solve(rate, num_periods, payment, future_value, pay_at_beginning)
|
61
|
+
annuity_due = pay_at_beginning ? 1 : 0
|
62
|
+
compound = (1 + rate) ** num_periods
|
63
|
+
|
64
|
+
-((present_value * compound) + (payment * (1 + rate * annuity_due) * (compound - 1) / rate))
|
65
|
+
end
|
66
|
+
end
|
67
|
+
|
68
|
+
FutureValue = Fv
|
69
|
+
end
|
@@ -0,0 +1,77 @@
|
|
1
|
+
module FinancialCalculator
|
2
|
+
# Calculates the interest portion of a fixed rate annuity.
|
3
|
+
class Ipmt
|
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 amortization period
|
11
|
+
# @api public
|
12
|
+
attr_reader :period
|
13
|
+
|
14
|
+
# @return [Numeric] The number of payments to be made
|
15
|
+
# @api public
|
16
|
+
attr_reader :num_periods
|
17
|
+
|
18
|
+
# @return [Numeric] The current value of the annuity
|
19
|
+
# @api public
|
20
|
+
attr_reader :present_value
|
21
|
+
|
22
|
+
# @return [Numeric] The ending value of the annuity. Defaults to 0
|
23
|
+
# @api public
|
24
|
+
attr_reader :future_value
|
25
|
+
|
26
|
+
# @return [Boolean] Whether the payment is made at the beginning of the
|
27
|
+
# period (true) or end of the period (false)
|
28
|
+
# @api public
|
29
|
+
attr_reader :pay_at_beginning
|
30
|
+
|
31
|
+
# @return [DecNum] Result of the PMT calculation
|
32
|
+
# @api public
|
33
|
+
attr_reader :result
|
34
|
+
|
35
|
+
# Create a new object for calculating the periodic payment of an ordinary annuity
|
36
|
+
# @param [Numeric] rate The discount (interest) rate
|
37
|
+
# @param [Numeric] period The amortization period
|
38
|
+
# @param [Numeric] num_periods The number of payments to be made
|
39
|
+
# @param [Numeric] present_value The current value of the annuity
|
40
|
+
# @param [Numeric] future_value The ending value of the annuity
|
41
|
+
# @param [Boolean] pay_at_beginning. Whether the payment is made at the beginning
|
42
|
+
# of the period (true) or end of the period (false)
|
43
|
+
# @return [FinancialCalculator::Npv] An instance of a PMT calculation
|
44
|
+
def initialize(rate, period, num_periods, present_value, future_value = 0, pay_at_beginning = false)
|
45
|
+
validate_numerics(rate: rate, period: period, num_periods: num_periods, present_value: present_value, future_value: future_value)
|
46
|
+
|
47
|
+
@rate = Flt::DecNum(rate.to_s)
|
48
|
+
@period = Flt::DecNum(period)
|
49
|
+
@num_periods = Flt::DecNum(num_periods.to_s)
|
50
|
+
@present_value = Flt::DecNum(present_value.to_s)
|
51
|
+
@future_value = Flt::DecNum(future_value.to_s)
|
52
|
+
@pay_at_beginning = pay_at_beginning
|
53
|
+
@result = solve(@rate, @period, @num_periods, @present_value, @future_value, @pay_at_beginning)
|
54
|
+
end
|
55
|
+
|
56
|
+
# @return [Boolean] Whether the payments are made at the beginning of each period
|
57
|
+
# @api public
|
58
|
+
def pays_at_beginning?
|
59
|
+
@pay_at_beginning
|
60
|
+
end
|
61
|
+
|
62
|
+
def inspect
|
63
|
+
"IPMT(#{result})"
|
64
|
+
end
|
65
|
+
|
66
|
+
private
|
67
|
+
|
68
|
+
def solve(rate, period, nper, pv, fv, pay_at_beginning)
|
69
|
+
payment = FinancialCalculator::Pmt.new(rate, nper, pv, fv, pay_at_beginning).result
|
70
|
+
@result = if pays_at_beginning?
|
71
|
+
(FinancialCalculator::FutureValue.new(rate, period - 2, payment, pv, pay_at_beginning).result - payment) * rate
|
72
|
+
else
|
73
|
+
FinancialCalculator::FutureValue.new(rate, period - 1, payment, pv, pay_at_beginning).result * rate
|
74
|
+
end
|
75
|
+
end
|
76
|
+
end
|
77
|
+
end
|
@@ -40,7 +40,7 @@ module FinancialCalculator
|
|
40
40
|
validate_numerics(rate: rate, num_periods: num_periods, present_value: present_value, future_value: future_value)
|
41
41
|
|
42
42
|
@rate = Flt::DecNum(rate.to_s)
|
43
|
-
@num_periods
|
43
|
+
@num_periods = Flt::DecNum(num_periods.to_s)
|
44
44
|
@present_value = Flt::DecNum(present_value.to_s)
|
45
45
|
@future_value = Flt::DecNum(future_value || "0")
|
46
46
|
@pay_at_beginning = pay_at_beginning || false
|
@@ -59,9 +59,9 @@ module FinancialCalculator
|
|
59
59
|
|
60
60
|
private
|
61
61
|
|
62
|
-
def solve(
|
62
|
+
def solve(rate, nper, pv, fv, pay_at_beginning)
|
63
63
|
type = pay_at_beginning ? 1 : 0
|
64
|
-
@result = ((pv * (1 +
|
64
|
+
@result = ((pv * (1 + rate) ** nper) + fv) * rate / ((1 + rate * type) * (1 - (1 + rate) ** nper))
|
65
65
|
end
|
66
66
|
end
|
67
67
|
end
|
@@ -0,0 +1,74 @@
|
|
1
|
+
module FinancialCalculator
|
2
|
+
# Calculates the principal portion of a loan or annuity for a particular period
|
3
|
+
class Ppmt
|
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 amortization period
|
11
|
+
# @api public
|
12
|
+
attr_reader :period
|
13
|
+
|
14
|
+
# @return [Numeric] The number of payments to be made
|
15
|
+
# @api public
|
16
|
+
attr_reader :num_periods
|
17
|
+
|
18
|
+
# @return [Numeric] The current value of the annuity
|
19
|
+
# @api public
|
20
|
+
attr_reader :present_value
|
21
|
+
|
22
|
+
# @return [Numeric] The ending value of the annuity. Defaults to 0
|
23
|
+
# @api public
|
24
|
+
attr_reader :future_value
|
25
|
+
|
26
|
+
# @return [Boolean] Whether the payment is made at the beginning of the
|
27
|
+
# period (true) or end of the period (false)
|
28
|
+
# @api public
|
29
|
+
attr_reader :pay_at_beginning
|
30
|
+
|
31
|
+
# @return [DecNum] Result of the PMT calculation
|
32
|
+
# @api public
|
33
|
+
attr_reader :result
|
34
|
+
|
35
|
+
# Create a new object for calculating the periodic payment of an ordinary annuity
|
36
|
+
# @param [Numeric] rate The discount (interest) rate
|
37
|
+
# @param [Numeric] period The amortization period
|
38
|
+
# @param [Numeric] num_periods The number of payments to be made
|
39
|
+
# @param [Numeric] present_value The current value of the annuity
|
40
|
+
# @param [Numeric] future_value The ending value of the annuity
|
41
|
+
# @param [Boolean] pay_at_beginning. Whether the payment is made at the beginning
|
42
|
+
# of the period (true) or end of the period (false)
|
43
|
+
# @return [FinancialCalculator::Npv] An instance of a PMT calculation
|
44
|
+
def initialize(rate, period, num_periods, present_value, future_value = 0, pay_at_beginning = false)
|
45
|
+
validate_numerics(rate: rate, period: period, num_periods: num_periods, present_value: present_value, future_value: future_value)
|
46
|
+
|
47
|
+
@rate = Flt::DecNum(rate.to_s)
|
48
|
+
@period = Flt::DecNum(period)
|
49
|
+
@num_periods = Flt::DecNum(num_periods.to_s)
|
50
|
+
@present_value = Flt::DecNum(present_value.to_s)
|
51
|
+
@future_value = Flt::DecNum(future_value.to_s)
|
52
|
+
@pay_at_beginning = pay_at_beginning
|
53
|
+
@result = solve(@rate, @period, @num_periods, @present_value, @future_value, @pay_at_beginning)
|
54
|
+
end
|
55
|
+
|
56
|
+
# @return [Boolean] Whether the payments are made at the beginning of each period
|
57
|
+
# @api public
|
58
|
+
def pays_at_beginning?
|
59
|
+
@pay_at_beginning
|
60
|
+
end
|
61
|
+
|
62
|
+
def inspect
|
63
|
+
"PPMT(#{result})"
|
64
|
+
end
|
65
|
+
|
66
|
+
private
|
67
|
+
|
68
|
+
def solve(rate, period, nper, pv, fv, pay_at_beginning)
|
69
|
+
payment = FinancialCalculator::Pmt.new(rate, nper, pv, fv, pay_at_beginning).result
|
70
|
+
ipmt = FinancialCalculator::Ipmt.new(rate, period, nper, pv, fv, pay_at_beginning).result
|
71
|
+
@result = payment - ipmt
|
72
|
+
end
|
73
|
+
end
|
74
|
+
end
|
@@ -1,5 +1,5 @@
|
|
1
1
|
module FinancialCalculator
|
2
|
-
# Calculate the
|
2
|
+
# Calculate the present value of a series of equal of payments
|
3
3
|
class Pv
|
4
4
|
include ::Validator
|
5
5
|
|
@@ -61,7 +61,7 @@ module FinancialCalculator
|
|
61
61
|
start_period = pay_at_beginning ? 0 : 1
|
62
62
|
end_period = pay_at_beginning ? num_periods - 1 : num_periods
|
63
63
|
|
64
|
-
present_value = (start_period..end_period.abs).reduce(0) do |total, t|
|
64
|
+
present_value = (start_period..end_period.abs).reduce(Flt::DecNum('0')) do |total, t|
|
65
65
|
total += discount(payment, rate, t)
|
66
66
|
end
|
67
67
|
|
data/spec/fv_spec.rb
ADDED
@@ -0,0 +1,121 @@
|
|
1
|
+
require_relative 'spec_helper'
|
2
|
+
|
3
|
+
describe "Fv" do
|
4
|
+
let(:rate) { 0.1 }
|
5
|
+
let(:num_periods) { 10 }
|
6
|
+
let(:payment) { -100 }
|
7
|
+
let(:present_value) { 0 }
|
8
|
+
let(:pays_at_beginning) { false }
|
9
|
+
let(:future_value) { Fv.new(rate, num_periods, payment) }
|
10
|
+
|
11
|
+
subject { future_value }
|
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 payment attribute' do
|
22
|
+
expect(subject).to have_attributes(payment: payment)
|
23
|
+
end
|
24
|
+
|
25
|
+
it 'has a present_value attribute that defaults to 0' do
|
26
|
+
expect(subject).to have_attributes(present_value: 0)
|
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 whose value is positive' do
|
34
|
+
expect(subject).to have_attributes(result: (a_value > 0))
|
35
|
+
end
|
36
|
+
|
37
|
+
context 'when the payment is positive' do
|
38
|
+
let(:payment) { 100 }
|
39
|
+
|
40
|
+
it 'has a result attribute whose value is negative' do
|
41
|
+
expect(subject).to have_attributes(result: (a_value < 0))
|
42
|
+
end
|
43
|
+
end
|
44
|
+
|
45
|
+
context 'when the payment is 0' do
|
46
|
+
let(:payment) { 0 }
|
47
|
+
|
48
|
+
it 'has a future value of 0' do
|
49
|
+
expect(subject.result).to eql Flt::DecNum(0)
|
50
|
+
end
|
51
|
+
end
|
52
|
+
|
53
|
+
context 'when the number of periods is negative' do
|
54
|
+
let(:num_periods) { -1 }
|
55
|
+
it_behaves_like 'it has invalid arguments'
|
56
|
+
end
|
57
|
+
|
58
|
+
context 'when rate is non-numeric' do
|
59
|
+
let(:rate) { 'string' }
|
60
|
+
it_behaves_like 'it has invalid arguments'
|
61
|
+
end
|
62
|
+
|
63
|
+
context 'when num_periods is non-numeric' do
|
64
|
+
let(:num_periods) { 'string' }
|
65
|
+
it_behaves_like 'it has invalid arguments'
|
66
|
+
end
|
67
|
+
|
68
|
+
context 'when payment is non-numeric' do
|
69
|
+
let(:payment) { 'string' }
|
70
|
+
it_behaves_like 'it has invalid arguments'
|
71
|
+
end
|
72
|
+
|
73
|
+
context 'when present_value is non-numeric' do
|
74
|
+
let(:present_value) { 'string' }
|
75
|
+
|
76
|
+
subject { Fv.new(rate, num_periods, payment, future_value) }
|
77
|
+
|
78
|
+
it_behaves_like 'it has invalid arguments'
|
79
|
+
end
|
80
|
+
|
81
|
+
context 'when the number of periods is 0' do
|
82
|
+
let(:num_periods) { 0 }
|
83
|
+
|
84
|
+
it 'has a result equal to the present value' do
|
85
|
+
expect(subject.result).to eql Flt::DecNum(present_value.to_s)
|
86
|
+
end
|
87
|
+
end
|
88
|
+
|
89
|
+
context 'when provided with a present value' do
|
90
|
+
let(:present_value) { 100 }
|
91
|
+
|
92
|
+
subject { Fv.new(rate, num_periods, payment, present_value) }
|
93
|
+
|
94
|
+
it { is_expected.to have_attributes(present_value: present_value) }
|
95
|
+
end
|
96
|
+
|
97
|
+
context 'when payments occur at the beginning of each period' do
|
98
|
+
let(:pays_at_beginning) { true }
|
99
|
+
|
100
|
+
subject { Fv.new(rate, num_periods, payment, 0, pays_at_beginning) }
|
101
|
+
|
102
|
+
it { is_expected.to have_attributes(pays_at_beginning?: true) }
|
103
|
+
end
|
104
|
+
|
105
|
+
describe '#inspect' do
|
106
|
+
|
107
|
+
subject { future_value.inspect }
|
108
|
+
|
109
|
+
it { is_expected.to be_a String }
|
110
|
+
it { is_expected.to include 'FV' }
|
111
|
+
it 'includes the result of the future value calculation' do
|
112
|
+
expect(subject).to include future_value.result.to_s
|
113
|
+
end
|
114
|
+
end
|
115
|
+
|
116
|
+
describe '#result' do
|
117
|
+
subject { future_value.result }
|
118
|
+
|
119
|
+
it { is_expected.to be_a Flt::DecNum }
|
120
|
+
end
|
121
|
+
end
|
data/spec/ipmt_spec.rb
ADDED
@@ -0,0 +1,87 @@
|
|
1
|
+
require_relative 'spec_helper'
|
2
|
+
|
3
|
+
describe "Ipmt" do
|
4
|
+
let(:rate) { 0.1 }
|
5
|
+
let(:period) { 10 }
|
6
|
+
let(:num_periods) { 20 }
|
7
|
+
let(:present_value) { 1000 }
|
8
|
+
let(:future_value) { 0 }
|
9
|
+
let(:pay_at_beginning) { false }
|
10
|
+
let(:ipmt) { Ipmt.new(rate, period, num_periods, present_value) }
|
11
|
+
|
12
|
+
subject { ipmt }
|
13
|
+
|
14
|
+
it 'has a rate attribute' do
|
15
|
+
expect(subject).to have_attributes(rate: rate)
|
16
|
+
end
|
17
|
+
|
18
|
+
it 'has a period attribute' do
|
19
|
+
expect(subject).to have_attributes(period: period)
|
20
|
+
end
|
21
|
+
|
22
|
+
it 'has a num_periods attribute' do
|
23
|
+
expect(subject).to have_attributes(num_periods: num_periods)
|
24
|
+
end
|
25
|
+
|
26
|
+
it 'has a present_value attribute' do
|
27
|
+
expect(subject).to have_attributes(present_value: present_value)
|
28
|
+
end
|
29
|
+
|
30
|
+
it 'has a future_value attribute the defaults to 0' do
|
31
|
+
expect(subject).to have_attributes(future_value: future_value)
|
32
|
+
end
|
33
|
+
|
34
|
+
it 'has a pays_at_beginning? attribute that defaults to false' do
|
35
|
+
expect(subject).to have_attributes(pays_at_beginning?: false)
|
36
|
+
end
|
37
|
+
|
38
|
+
it 'has a result attribute' do
|
39
|
+
expect(subject.result).to be_a Numeric
|
40
|
+
end
|
41
|
+
|
42
|
+
context 'when payments occur at the beginning of each period' do
|
43
|
+
let(:pay_at_beginning) { true }
|
44
|
+
|
45
|
+
subject { Ipmt.new(rate, period, num_periods, present_value, 0, pay_at_beginning) }
|
46
|
+
|
47
|
+
it { is_expected.to have_attributes(pays_at_beginning?: true) }
|
48
|
+
end
|
49
|
+
|
50
|
+
context 'when given a non-numeric rate' do
|
51
|
+
let(:rate) { 'string' }
|
52
|
+
it_behaves_like 'it has invalid arguments'
|
53
|
+
end
|
54
|
+
|
55
|
+
context 'when given a non-numeric period' do
|
56
|
+
let(:period) { 'string' }
|
57
|
+
it_behaves_like 'it has invalid arguments'
|
58
|
+
end
|
59
|
+
|
60
|
+
context 'when given a non-numeric number of periods' do
|
61
|
+
let(:num_periods) { 'string' }
|
62
|
+
it_behaves_like 'it has invalid arguments'
|
63
|
+
end
|
64
|
+
|
65
|
+
context 'when given a non-numeric present value' do
|
66
|
+
let(:present_value) { 'string' }
|
67
|
+
it_behaves_like 'it has invalid arguments'
|
68
|
+
end
|
69
|
+
|
70
|
+
context 'when give a non-numeric future value' do
|
71
|
+
let(:future_value) { 'string' }
|
72
|
+
|
73
|
+
subject { Ipmt.new(rate, period, num_periods, present_value, future_value) }
|
74
|
+
|
75
|
+
it_behaves_like 'it has invalid arguments'
|
76
|
+
end
|
77
|
+
|
78
|
+
describe '#inspect' do
|
79
|
+
subject { ipmt.inspect }
|
80
|
+
|
81
|
+
it { is_expected.to be_a String }
|
82
|
+
it { is_expected.to include 'IPMT' }
|
83
|
+
it 'includes the result of the IPMT calculation' do
|
84
|
+
expect(subject).to include ipmt.result.to_s
|
85
|
+
end
|
86
|
+
end
|
87
|
+
end
|
data/spec/ppmt_spec.rb
ADDED
@@ -0,0 +1,87 @@
|
|
1
|
+
require_relative 'spec_helper'
|
2
|
+
|
3
|
+
describe "Ppmt" do
|
4
|
+
let(:rate) { 0.1 }
|
5
|
+
let(:period) { 10 }
|
6
|
+
let(:num_periods) { 20 }
|
7
|
+
let(:present_value) { 1000 }
|
8
|
+
let(:future_value) { 0 }
|
9
|
+
let(:pay_at_beginning) { false }
|
10
|
+
let(:ppmt) { Ppmt.new(rate, period, num_periods, present_value) }
|
11
|
+
|
12
|
+
subject { ppmt }
|
13
|
+
|
14
|
+
it 'has a rate attribute' do
|
15
|
+
expect(subject).to have_attributes(rate: rate)
|
16
|
+
end
|
17
|
+
|
18
|
+
it 'has a period attribute' do
|
19
|
+
expect(subject).to have_attributes(period: period)
|
20
|
+
end
|
21
|
+
|
22
|
+
it 'has a num_periods attribute' do
|
23
|
+
expect(subject).to have_attributes(num_periods: num_periods)
|
24
|
+
end
|
25
|
+
|
26
|
+
it 'has a present_value attribute' do
|
27
|
+
expect(subject).to have_attributes(present_value: present_value)
|
28
|
+
end
|
29
|
+
|
30
|
+
it 'has a future_value attribute the defaults to 0' do
|
31
|
+
expect(subject).to have_attributes(future_value: future_value)
|
32
|
+
end
|
33
|
+
|
34
|
+
it 'has a pays_at_beginning? attribute that defaults to false' do
|
35
|
+
expect(subject).to have_attributes(pays_at_beginning?: false)
|
36
|
+
end
|
37
|
+
|
38
|
+
it 'has a result attribute' do
|
39
|
+
expect(subject.result).to be_a Numeric
|
40
|
+
end
|
41
|
+
|
42
|
+
context 'when payments occur at the beginning of each period' do
|
43
|
+
let(:pay_at_beginning) { true }
|
44
|
+
|
45
|
+
subject { Ppmt.new(rate, period, num_periods, present_value, 0, pay_at_beginning) }
|
46
|
+
|
47
|
+
it { is_expected.to have_attributes(pays_at_beginning?: true) }
|
48
|
+
end
|
49
|
+
|
50
|
+
context 'when given a non-numeric rate' do
|
51
|
+
let(:rate) { 'string' }
|
52
|
+
it_behaves_like 'it has invalid arguments'
|
53
|
+
end
|
54
|
+
|
55
|
+
context 'when given a non-numeric period' do
|
56
|
+
let(:period) { 'string' }
|
57
|
+
it_behaves_like 'it has invalid arguments'
|
58
|
+
end
|
59
|
+
|
60
|
+
context 'when given a non-numeric number of periods' do
|
61
|
+
let(:num_periods) { 'string' }
|
62
|
+
it_behaves_like 'it has invalid arguments'
|
63
|
+
end
|
64
|
+
|
65
|
+
context 'when given a non-numeric present value' do
|
66
|
+
let(:present_value) { 'string' }
|
67
|
+
it_behaves_like 'it has invalid arguments'
|
68
|
+
end
|
69
|
+
|
70
|
+
context 'when give a non-numeric future value' do
|
71
|
+
let(:future_value) { 'string' }
|
72
|
+
|
73
|
+
subject { Ppmt.new(rate, period, num_periods, present_value, future_value) }
|
74
|
+
|
75
|
+
it_behaves_like 'it has invalid arguments'
|
76
|
+
end
|
77
|
+
|
78
|
+
describe '#inspect' do
|
79
|
+
subject { ppmt.inspect }
|
80
|
+
|
81
|
+
it { is_expected.to be_a String }
|
82
|
+
it { is_expected.to include 'PPMT' }
|
83
|
+
it 'includes the result of the PPMT calculation' do
|
84
|
+
expect(subject).to include ppmt.result.to_s
|
85
|
+
end
|
86
|
+
end
|
87
|
+
end
|
data/spec/pv_spec.rb
CHANGED
@@ -1,11 +1,5 @@
|
|
1
1
|
require_relative 'spec_helper'
|
2
2
|
|
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
|
-
|
9
3
|
describe "Pv" do
|
10
4
|
let(:rate) { 0.1 }
|
11
5
|
let(:num_periods) { 10 }
|
@@ -52,7 +46,7 @@ describe "Pv" do
|
|
52
46
|
let(:payment) { 0 }
|
53
47
|
|
54
48
|
it 'has a present value of 0' do
|
55
|
-
expect(subject.result).to eql 0
|
49
|
+
expect(subject.result).to eql Flt::DecNum('0')
|
56
50
|
end
|
57
51
|
end
|
58
52
|
|
@@ -76,7 +70,7 @@ describe "Pv" do
|
|
76
70
|
it_behaves_like 'it has invalid arguments'
|
77
71
|
end
|
78
72
|
|
79
|
-
context 'when
|
73
|
+
context 'when future_value is non-numeric' do
|
80
74
|
let(:future_value) { 'string' }
|
81
75
|
|
82
76
|
subject { Pv.new(rate, num_periods, payment, future_value) }
|
@@ -88,7 +82,7 @@ describe "Pv" do
|
|
88
82
|
let(:num_periods) { 0 }
|
89
83
|
|
90
84
|
it 'has a result equal to the future value' do
|
91
|
-
expect(subject.result).to eql future_value
|
85
|
+
expect(subject.result).to eql Flt::DecNum(future_value.to_s)
|
92
86
|
end
|
93
87
|
end
|
94
88
|
|
@@ -122,6 +116,6 @@ describe "Pv" do
|
|
122
116
|
describe '#result' do
|
123
117
|
subject { present_value.result }
|
124
118
|
|
125
|
-
it {is_expected.to be_a Flt::DecNum }
|
119
|
+
it { is_expected.to be_a Flt::DecNum }
|
126
120
|
end
|
127
121
|
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.2.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-
|
11
|
+
date: 2018-06-10 00:00:00.000000000 Z
|
12
12
|
dependencies:
|
13
13
|
- !ruby/object:Gem::Dependency
|
14
14
|
name: flt
|
@@ -160,9 +160,12 @@ files:
|
|
160
160
|
- financial_calculator.gemspec
|
161
161
|
- lib/financial_calculator.rb
|
162
162
|
- lib/financial_calculator/amortization.rb
|
163
|
+
- lib/financial_calculator/fv.rb
|
164
|
+
- lib/financial_calculator/ipmt.rb
|
163
165
|
- lib/financial_calculator/irr.rb
|
164
166
|
- lib/financial_calculator/npv.rb
|
165
167
|
- lib/financial_calculator/pmt.rb
|
168
|
+
- lib/financial_calculator/ppmt.rb
|
166
169
|
- lib/financial_calculator/pv.rb
|
167
170
|
- lib/financial_calculator/rates.rb
|
168
171
|
- lib/financial_calculator/transaction.rb
|
@@ -171,9 +174,12 @@ files:
|
|
171
174
|
- lib/financial_calculator/xirr.rb
|
172
175
|
- lib/financial_calculator/xnpv.rb
|
173
176
|
- spec/amortization_spec.rb
|
177
|
+
- spec/fv_spec.rb
|
178
|
+
- spec/ipmt_spec.rb
|
174
179
|
- spec/irr_spec.rb
|
175
180
|
- spec/npv_spec.rb
|
176
181
|
- spec/pmt_spec.rb
|
182
|
+
- spec/ppmt_spec.rb
|
177
183
|
- spec/pv_spec.rb
|
178
184
|
- spec/rates_spec.rb
|
179
185
|
- spec/spec_helper.rb
|
@@ -208,9 +214,12 @@ specification_version: 4
|
|
208
214
|
summary: A library for financial modelling in Ruby.
|
209
215
|
test_files:
|
210
216
|
- spec/amortization_spec.rb
|
217
|
+
- spec/fv_spec.rb
|
218
|
+
- spec/ipmt_spec.rb
|
211
219
|
- spec/irr_spec.rb
|
212
220
|
- spec/npv_spec.rb
|
213
221
|
- spec/pmt_spec.rb
|
222
|
+
- spec/ppmt_spec.rb
|
214
223
|
- spec/pv_spec.rb
|
215
224
|
- spec/rates_spec.rb
|
216
225
|
- spec/spec_helper.rb
|