finance_rb 0.0.2 → 0.1.2
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 +26 -1
- data/README.md +40 -10
- data/lib/finance/calculations.rb +99 -0
- data/lib/finance/loan.rb +153 -4
- data/lib/finance/version.rb +1 -1
- data/spec/finance/calculations_spec.rb +63 -1
- data/spec/finance/loan_spec.rb +102 -0
- metadata +7 -5
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 3f920db19ed5b406b8e263c09fac4aba9cd203ebdca989c096b44b441753757e
|
4
|
+
data.tar.gz: 435cd054975eee4b5027245d0301551b1b0cfd889153879f02929f1885d55f77
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 15fa362a606eacc22aeff3c89312bbedbfbe2d78319b66cd3c146a985a6c5a68114fcb1601986facdc823fa14b03a798708faae3adf704aad8c8ee89c59ec07b
|
7
|
+
data.tar.gz: c96d0f272ccee5c88cab312981f7795bac101e7bd3b229b88cfb34f05db3334b63bc61d9f41f13404105c6b6ab6bd4d318ecf3ad98ac89dbe443f1cc920a453a
|
data/CHANGELOG.md
CHANGED
@@ -1,4 +1,29 @@
|
|
1
|
-
## [
|
1
|
+
## [0.1.2] - 2021-04-05
|
2
|
+
|
3
|
+
### Added
|
4
|
+
* Implement `Finance::Loan#ipmt`
|
5
|
+
|
6
|
+
## [0.1.1] - 2021-03-30
|
7
|
+
|
8
|
+
### Added
|
9
|
+
* Implement `Finance::Loan#fv`
|
10
|
+
|
11
|
+
## [0.1.0] - 2021-03-28
|
12
|
+
|
13
|
+
### Added
|
14
|
+
* Create a basic structure for `Finance::Loan`
|
15
|
+
* Implement `Finance::Loan#pmt`
|
16
|
+
|
17
|
+
|
18
|
+
## [0.0.4] - 2021-03-25
|
19
|
+
|
20
|
+
### Added
|
21
|
+
* Implement `Finance::Calculations#mirr`
|
22
|
+
|
23
|
+
## [0.0.3] - 2021-03-23
|
24
|
+
|
25
|
+
### Added
|
26
|
+
* Implement `Finance::Calculations#irr` using Newton's method
|
2
27
|
|
3
28
|
## [0.0.2] - 2021-03-22
|
4
29
|
|
data/README.md
CHANGED
@@ -2,20 +2,50 @@
|
|
2
2
|
|
3
3
|
This package is a ruby native port of the numpy-financial package with some helpful additional functions.
|
4
4
|
|
5
|
-
The functions in this package are a scalar version of their vectorised counterparts in
|
5
|
+
The functions in this package are a scalar version of their vectorised counterparts in the [numpy-financial](https://github.com/numpy/numpy-financial) library.
|
6
|
+
|
7
|
+
[](https://github.com/wowinter13/finance_rb/releases) [](http://makeapullrequest.com) [](https://codeclimate.com/github/wowinter13/finance_rb/maintainability)
|
6
8
|
|
7
9
|
Currently, only some functions are ported,
|
8
10
|
which are as follows:
|
9
11
|
|
10
12
|
| numpy-financial function | ruby native function ported? | info|
|
11
13
|
|:------------------------: |:------------------: | :------------------|
|
12
|
-
| fv | | Computes the future value|
|
13
|
-
| ipmt |
|
14
|
-
| pmt | | Computes the fixed periodic payment(principal + interest) made against a loan amount|
|
15
|
-
| ppmt |
|
16
|
-
| nper |
|
17
|
-
| pv |
|
18
|
-
| rate |
|
19
|
-
| irr |
|
14
|
+
| fv | ✅ | Computes the future value|
|
15
|
+
| ipmt | ✅ | Computes interest payment for a loan|
|
16
|
+
| pmt | ✅ | Computes the fixed periodic payment(principal + interest) made against a loan amount|
|
17
|
+
| ppmt | | Computes principal payment for a loan|
|
18
|
+
| nper | | Computes the number of periodic payments|
|
19
|
+
| pv | | Computes the present value of a payment|
|
20
|
+
| rate | | Computes the rate of interest per period|
|
21
|
+
| irr | ✅ | Computes the internal rate of return|
|
20
22
|
| npv | ✅ | Computes the net present value of a series of cash flow|
|
21
|
-
| mirr |
|
23
|
+
| mirr | ✅ | Computes the modified internal rate of return|
|
24
|
+
|
25
|
+
## Installation
|
26
|
+
|
27
|
+
finance_rb is available as a gem, to install it just install the gem:
|
28
|
+
|
29
|
+
gem install finance_rb
|
30
|
+
|
31
|
+
If you're using Bundler, add the gem to Gemfile.
|
32
|
+
|
33
|
+
gem 'finance_rb'
|
34
|
+
|
35
|
+
Run `bundle install`.
|
36
|
+
|
37
|
+
## Running tests
|
38
|
+
|
39
|
+
bundle exec rspec spec/
|
40
|
+
|
41
|
+
## Contributing
|
42
|
+
|
43
|
+
1. Fork it ( https://github.com/wowinter13/finance_rb/fork )
|
44
|
+
2. Create your feature branch (`git checkout -b my-new-feature`)
|
45
|
+
3. Commit your changes (`git commit -am 'Add some feature'`)
|
46
|
+
4. Push to the branch (`git push origin my-new-feature`)
|
47
|
+
5. Create a new Pull Request
|
48
|
+
|
49
|
+
## License
|
50
|
+
|
51
|
+
MIT License. See LICENSE for details.
|
data/lib/finance/calculations.rb
CHANGED
@@ -1,9 +1,15 @@
|
|
1
1
|
# frozen_string_literal: true
|
2
2
|
|
3
|
+
require 'bigdecimal'
|
4
|
+
require 'bigdecimal/newton'
|
5
|
+
|
6
|
+
include Newton
|
7
|
+
|
3
8
|
module Finance
|
4
9
|
class Calculations
|
5
10
|
class << self
|
6
11
|
# Npv computes the Net Present Value of a cash flow series.
|
12
|
+
#
|
7
13
|
# @return [Numeric] The NPV of the input cash flow series `values` at the discount `rate`.
|
8
14
|
#
|
9
15
|
# @param [Numeric] :rate A discount rate applied once per period.
|
@@ -24,7 +30,100 @@ module Finance
|
|
24
30
|
npv_value
|
25
31
|
end
|
26
32
|
|
33
|
+
# IRR computes the Rate of Interest per period.
|
34
|
+
#
|
35
|
+
# @return [Float] Internal Rate of Return for periodic input values.
|
36
|
+
#
|
37
|
+
# @param [Array<Numeric>] :values Input cash flows per time period.
|
38
|
+
# At least, must contain one positive and one negative value.
|
39
|
+
# Otherwise, irr equals zero.
|
40
|
+
#
|
41
|
+
# @example
|
42
|
+
# require 'finance_rb'
|
43
|
+
# Finance::Calculations.irr([-100, 0, 0, 74]) #=> 0.14299344106053188
|
44
|
+
#
|
45
|
+
# @see http://en.wikipedia.org/wiki/Internal_rate_of_return
|
46
|
+
# @see L. J. Gitman, "Principles of Managerial Finance, Brief," 3rd ed.,
|
47
|
+
# Addison-Wesley, 2003, pg. 348.
|
48
|
+
def irr(values)
|
49
|
+
return 0.0 unless correct_cashflows?(values)
|
50
|
+
|
51
|
+
func = BigDecimal.limit(100)
|
52
|
+
func = Function.new(values)
|
53
|
+
rate = [ func.one ]
|
54
|
+
nlsolve(func, rate)
|
55
|
+
rate[0].to_f
|
56
|
+
end
|
57
|
+
|
58
|
+
# MIRR computes the modified Rate of Interest.
|
59
|
+
#
|
60
|
+
# @return [Float] Modified Internal Rate of Return.
|
61
|
+
#
|
62
|
+
# @param [Array<Numeric>] :values
|
63
|
+
# At least, must contain one positive and one negative value.
|
64
|
+
# Otherwise, mirr equals zero.
|
65
|
+
# @param [Numeric] :rate Interest rate paid on the cash flows
|
66
|
+
# @param [Numeric] :reinvest_rate Interest rate received on the cash flows upon reinvestment
|
67
|
+
#
|
68
|
+
# @example
|
69
|
+
# require 'finance_rb'
|
70
|
+
# Finance::Calculations.mirr([100, 200, -50, 300, -200], 0.05, 0.06) => 0.2979256979689131
|
71
|
+
#
|
72
|
+
# @see https://en.wikipedia.org/wiki/Modified_internal_rate_of_return
|
73
|
+
def mirr(values, rate, reinvest_rate)
|
74
|
+
inflows = [];
|
75
|
+
outflows = [];
|
76
|
+
# We prefer manual enumeration over the partition
|
77
|
+
# because of the need to replace outflows with zeros.
|
78
|
+
values.each do |val|
|
79
|
+
if val >= 0
|
80
|
+
inflows << val
|
81
|
+
outflows << 0.0
|
82
|
+
else
|
83
|
+
outflows << val
|
84
|
+
inflows << 0.0
|
85
|
+
end
|
86
|
+
end
|
87
|
+
if outflows.all?(0.0) || inflows.all?(0.0)
|
88
|
+
return 0.0
|
89
|
+
end
|
90
|
+
fv = npv(reinvest_rate, inflows).abs
|
91
|
+
pv = npv(rate, outflows).abs
|
92
|
+
|
93
|
+
return (fv/pv) ** (1.0/(values.size - 1)) * (1 + reinvest_rate) - 1
|
94
|
+
end
|
95
|
+
|
27
96
|
alias net_present_value npv
|
97
|
+
alias internal_return_rate irr
|
98
|
+
|
99
|
+
private
|
100
|
+
|
101
|
+
def correct_cashflows?(values)
|
102
|
+
inflows, outflows = values.partition{ |i| i >= 0 }
|
103
|
+
!(inflows.empty? || outflows.empty?)
|
104
|
+
end
|
105
|
+
|
106
|
+
# Base class for working with Newton's Method.
|
107
|
+
# For more details, see Bigdecimal::Newton.
|
108
|
+
# @api private
|
109
|
+
class Function
|
110
|
+
def initialize(values)
|
111
|
+
@zero = BigDecimal("0.0")
|
112
|
+
@one = BigDecimal("1.0")
|
113
|
+
@two = BigDecimal("2.0")
|
114
|
+
@ten = BigDecimal("10.0")
|
115
|
+
@eps = BigDecimal("1.0e-16")
|
116
|
+
@values = values
|
117
|
+
end
|
118
|
+
|
119
|
+
def zero; @zero; end
|
120
|
+
def one ; @one; end
|
121
|
+
def two ; @two; end
|
122
|
+
def ten ; @ten; end
|
123
|
+
def eps ; @eps; end
|
124
|
+
|
125
|
+
def values(x); [Finance::Calculations.npv(x[0], @values)]; end
|
126
|
+
end
|
28
127
|
end
|
29
128
|
end
|
30
129
|
end
|
data/lib/finance/loan.rb
CHANGED
@@ -2,12 +2,161 @@
|
|
2
2
|
|
3
3
|
module Finance
|
4
4
|
class Loan
|
5
|
-
|
5
|
+
PAYMENT_TYPE_MAPPING = { end: 0, beginning: 1 }.freeze
|
6
6
|
|
7
|
-
|
7
|
+
# @return [Float] The amount of loan request (I.e. a present value)
|
8
|
+
# You can use #pv method to calculate value if param is not defined.
|
9
|
+
# Defaults to 0.
|
10
|
+
attr_accessor :amount
|
8
11
|
|
9
|
-
|
12
|
+
# @return [Integer] Specification of whether payment is made
|
13
|
+
# at the beginning (ptype = 1) or the end (ptype = 0) of each period.
|
14
|
+
# Defaults to {:end, 0}.
|
15
|
+
attr_accessor :ptype
|
10
16
|
|
11
|
-
|
17
|
+
# @return [Float] The nominal annual rate of interest as decimal (not per cent).
|
18
|
+
# (e.g., 13% -> 0.13)
|
19
|
+
# Defaults to 0.
|
20
|
+
attr_accessor :nominal_rate
|
21
|
+
|
22
|
+
# @return [Float] The monthly rate is the nominal annual rate divided by 12.
|
23
|
+
# Defaults to 0.
|
24
|
+
attr_reader :monthly_rate
|
25
|
+
|
26
|
+
# @return [Float] The number of periods to be compounded for. (I.e. Nper())
|
27
|
+
# Defaults to 1.
|
28
|
+
attr_accessor :duration
|
29
|
+
|
30
|
+
# @return [Float] Future value.
|
31
|
+
# You can use #fv method to calculate value if param is not defined.
|
32
|
+
# Defaults to 0.
|
33
|
+
attr_accessor :future_value
|
34
|
+
|
35
|
+
# @return [Float] The (fixed) periodic payment.
|
36
|
+
# You can use #pmt method to calculate value if param is not defined.
|
37
|
+
attr_accessor :payment
|
38
|
+
|
39
|
+
# @return [Float] Period under consideration.
|
40
|
+
attr_accessor :period
|
41
|
+
|
42
|
+
# Create a new Loan instance.
|
43
|
+
def initialize(**options)
|
44
|
+
initialize_payment_type(options[:ptype])
|
45
|
+
@nominal_rate = options.fetch(:nominal_rate, 0).to_f
|
46
|
+
@duration = options.fetch(:duration, 1).to_f
|
47
|
+
@amount = options.fetch(:amount, 0).to_f
|
48
|
+
@future_value = options.fetch(:future_value, 0).to_f
|
49
|
+
@period = options[:period]
|
50
|
+
@payment = options[:payment]
|
51
|
+
@monthly_rate = @nominal_rate / 12
|
52
|
+
end
|
53
|
+
|
54
|
+
# Pmt computes the payment against a loan principal plus interest (future_value = 0).
|
55
|
+
# It can also be used to calculate the recurring payments needed to achieve
|
56
|
+
# a certain future value given an initial deposit,
|
57
|
+
# a fixed periodically compounded interest rate, and the total number of periods.
|
58
|
+
#
|
59
|
+
# Required Loan arguments: nominal_rate, duration, amount, future_value*
|
60
|
+
#
|
61
|
+
# @return [Numeric] The (fixed) periodic payment.
|
62
|
+
#
|
63
|
+
# @example
|
64
|
+
# require 'finance_rb'
|
65
|
+
# Finance::Loan.new(nominal_rate: 0.1, duration: 12, amount: 1000, ptype: :end).pmt
|
66
|
+
# #=> 87.9158872300099
|
67
|
+
#
|
68
|
+
# @see http://www.oasis-open.org/committees/documents.php?wg_abbrev=office-formulaOpenDocument-formula-20090508.odt
|
69
|
+
# @see [WRW] Wheeler, D. A., E. Rathke, and R. Weir (Eds.) (2009, May).
|
70
|
+
# Open Document Format for Office Applications (OpenDocument)v1.2,
|
71
|
+
# Part 2: Recalculated Formula (OpenFormula) Format - Annotated Version,
|
72
|
+
# Pre-Draft 12. Organization for the Advancement of Structured Information
|
73
|
+
# Standards (OASIS). Billerica, MA, USA. [ODT Document].
|
74
|
+
def pmt
|
75
|
+
factor = (1.0 + monthly_rate)**duration
|
76
|
+
second_factor =
|
77
|
+
if monthly_rate.zero?
|
78
|
+
duration
|
79
|
+
else
|
80
|
+
(factor - 1) * (1 + monthly_rate * ptype) / monthly_rate
|
81
|
+
end
|
82
|
+
|
83
|
+
(-future_value + amount * factor) / second_factor
|
84
|
+
end
|
85
|
+
|
86
|
+
# IPmt computes interest payment for a loan under a given period.
|
87
|
+
#
|
88
|
+
# Required Loan arguments: period, nominal_rate, duration, amount, future_value*
|
89
|
+
#
|
90
|
+
# @return [Float] Interest payment for a loan.
|
91
|
+
#
|
92
|
+
# @example
|
93
|
+
# require 'finance_rb'
|
94
|
+
# Finance::Loan.new(nominal_rate: 0.0824, duration: 12, amount: 2500, period: 1).ipmt
|
95
|
+
# #=> -17.166666666666668
|
96
|
+
#
|
97
|
+
# @see http://www.oasis-open.org/committees/documents.php?wg_abbrev=office-formulaOpenDocument-formula-20090508.odt
|
98
|
+
# @see [WRW] Wheeler, D. A., E. Rathke, and R. Weir (Eds.) (2009, May).
|
99
|
+
# Open Document Format for Office Applications (OpenDocument)v1.2,
|
100
|
+
# Part 2: Recalculated Formula (OpenFormula) Format - Annotated Version,
|
101
|
+
# Pre-Draft 12. Organization for the Advancement of Structured Information
|
102
|
+
# Standards (OASIS). Billerica, MA, USA. [ODT Document].
|
103
|
+
def ipmt
|
104
|
+
raise ArgumentError, 'no period given' if period.nil?
|
105
|
+
|
106
|
+
ipmt_val = remaining_balance * monthly_rate
|
107
|
+
if ptype == PAYMENT_TYPE_MAPPING[:beginning]
|
108
|
+
period == 1 ? 0.0 : (ipmt_val / 1 + monthly_rate)
|
109
|
+
else
|
110
|
+
ipmt_val
|
111
|
+
end
|
112
|
+
end
|
113
|
+
|
114
|
+
# Fv computes future value at the end of some periods (duration).
|
115
|
+
# Required Loan arguments: nominal_rate, duration, payment, amount*
|
116
|
+
#
|
117
|
+
# @param payment [Float] The (fixed) periodic payment.
|
118
|
+
# In case you don't want to modify the original loan, use this parameter to recalculate fv.
|
119
|
+
#
|
120
|
+
# @return [Float] The value at the end of the `duration` periods.
|
121
|
+
#
|
122
|
+
# @example
|
123
|
+
# require 'finance_rb'
|
124
|
+
# Finance::Loan.new(nominal_rate: 0.05, duration: 120, amount: -100, payment: -200).fv
|
125
|
+
# #=> 15692.928894335748
|
126
|
+
#
|
127
|
+
# @see http://www.oasis-open.org/committees/documents.php?wg_abbrev=office-formulaOpenDocument-formula-20090508.odt
|
128
|
+
# @see [WRW] Wheeler, D. A., E. Rathke, and R. Weir (Eds.) (2009, May).
|
129
|
+
# Open Document Format for Office Applications (OpenDocument)v1.2,
|
130
|
+
# Part 2: Recalculated Formula (OpenFormula) Format - Annotated Version,
|
131
|
+
# Pre-Draft 12. Organization for the Advancement of Structured Information
|
132
|
+
# Standards (OASIS). Billerica, MA, USA. [ODT Document].
|
133
|
+
def fv(payment: nil)
|
134
|
+
raise ArgumentError, 'no payment given' if self.payment.nil? && payment.nil?
|
135
|
+
|
136
|
+
final_payment = payment || self.payment
|
137
|
+
|
138
|
+
factor = (1.0 + monthly_rate)**duration
|
139
|
+
second_factor = (factor - 1) * (1 + monthly_rate * ptype) / monthly_rate
|
140
|
+
|
141
|
+
-((amount * factor) + (final_payment.to_f * second_factor))
|
142
|
+
end
|
143
|
+
|
144
|
+
private
|
145
|
+
|
146
|
+
def initialize_payment_type(ptype)
|
147
|
+
@ptype =
|
148
|
+
if ptype.nil? || !PAYMENT_TYPE_MAPPING.keys.include?(ptype)
|
149
|
+
PAYMENT_TYPE_MAPPING[:end]
|
150
|
+
else
|
151
|
+
PAYMENT_TYPE_MAPPING[ptype]
|
152
|
+
end
|
153
|
+
end
|
154
|
+
|
155
|
+
def remaining_balance
|
156
|
+
self.class.new(
|
157
|
+
nominal_rate: nominal_rate.to_f, duration: period - 1.0,
|
158
|
+
amount: amount.to_f, ptype: PAYMENT_TYPE_MAPPING.key(ptype)
|
159
|
+
).fv(payment: -pmt)
|
160
|
+
end
|
12
161
|
end
|
13
162
|
end
|
data/lib/finance/version.rb
CHANGED
@@ -12,10 +12,72 @@ RSpec.describe Finance::Calculations do
|
|
12
12
|
).to eq(-85.07888805409468)
|
13
13
|
end
|
14
14
|
|
15
|
-
it '
|
15
|
+
it 'calculates correct npv for zero rates' do
|
16
16
|
expect(
|
17
17
|
Finance::Calculations.net_present_value(0.0, [-2000, 55, 55, 55])
|
18
18
|
).to eq(-1835.0)
|
19
19
|
end
|
20
20
|
end
|
21
|
+
|
22
|
+
describe '#irr' do
|
23
|
+
it 'calculates correct irr value' do
|
24
|
+
expect(
|
25
|
+
Finance::Calculations.irr([-4000,1200,1410,1875,1050])
|
26
|
+
).to eq(0.14299344106053188)
|
27
|
+
end
|
28
|
+
|
29
|
+
it 'calculates zero for cashflows w/o any inflows' do
|
30
|
+
expect(
|
31
|
+
Finance::Calculations.irr([100,500,200,50])
|
32
|
+
).to eq(0.0)
|
33
|
+
end
|
34
|
+
|
35
|
+
it 'is available through alias name' do
|
36
|
+
expect(
|
37
|
+
Finance::Calculations.internal_return_rate([-100, 0, 0, 74])
|
38
|
+
).to eq(-0.09549583035161031)
|
39
|
+
end
|
40
|
+
end
|
41
|
+
|
42
|
+
describe '#mirr' do
|
43
|
+
it 'calculates correct mirr value' do
|
44
|
+
expect(
|
45
|
+
Finance::Calculations.mirr(
|
46
|
+
[-120000.0, 39000.0, 30000.0, 21000.0, 37000.0, 46000.0], 0.10, 0.12
|
47
|
+
)
|
48
|
+
).to eq(0.1260941303659051)
|
49
|
+
end
|
50
|
+
|
51
|
+
it 'calculates correct mirr value with integers' do
|
52
|
+
expect(
|
53
|
+
Finance::Calculations.mirr(
|
54
|
+
[-4500, -800, 800, 800, 600, 600, 800, 800, 700, 3000], 0.08, 0.055
|
55
|
+
)
|
56
|
+
).to eq(0.06659717503155349)
|
57
|
+
end
|
58
|
+
|
59
|
+
it 'calculates zero for cashflows w/o any outflows' do
|
60
|
+
expect(
|
61
|
+
Finance::Calculations.mirr(
|
62
|
+
[39000, 30000, 21000, 37000, 46000], 0.10, 0.12
|
63
|
+
)
|
64
|
+
).to eq(0.0)
|
65
|
+
end
|
66
|
+
|
67
|
+
it 'calculates zero for cashflows w/o any inflows' do
|
68
|
+
expect(
|
69
|
+
Finance::Calculations.mirr(
|
70
|
+
[-1000, -5000, -2000, -100, -50], 0.10, 0.12
|
71
|
+
)
|
72
|
+
).to eq(0.0)
|
73
|
+
end
|
74
|
+
|
75
|
+
it 'calculates correct mirr value for a shuffled order' do
|
76
|
+
expect(
|
77
|
+
Finance::Calculations.mirr(
|
78
|
+
[100, 200, -50, 300, -200], 0.05, 0.06
|
79
|
+
)
|
80
|
+
).to eq(0.3428233878421769)
|
81
|
+
end
|
82
|
+
end
|
21
83
|
end
|
@@ -0,0 +1,102 @@
|
|
1
|
+
require 'spec_helper'
|
2
|
+
|
3
|
+
RSpec.describe Finance::Loan do
|
4
|
+
describe '#pmt' do
|
5
|
+
context 'w/o a full set of params' do
|
6
|
+
it 'calculates correct pmt value w/o :ptype' do
|
7
|
+
loan = Finance::Loan.new(nominal_rate: 0.1, duration: 12, amount: 1000)
|
8
|
+
expect(loan.pmt).to eq(87.9158872300099)
|
9
|
+
end
|
10
|
+
|
11
|
+
it 'calculates correct pmt value w/o :nominal_rate' do
|
12
|
+
loan = Finance::Loan.new(duration: 12, amount: 1200, ptype: :end)
|
13
|
+
expect(loan.pmt).to eq(100)
|
14
|
+
end
|
15
|
+
end
|
16
|
+
|
17
|
+
context 'with zero rates' do
|
18
|
+
it 'calculates correct pmt value for 3 years' do
|
19
|
+
loan = Finance::Loan.new(nominal_rate: 0, duration: 36, amount: 10_000, ptype: :end)
|
20
|
+
expect(loan.pmt).to eq(277.77777777777777)
|
21
|
+
end
|
22
|
+
|
23
|
+
it 'calculates correct pmt value for 6 months' do
|
24
|
+
loan = Finance::Loan.new(nominal_rate: 0, duration: 6, amount: 10_000, ptype: :end)
|
25
|
+
expect(loan.pmt).to eq(1666.6666666666667)
|
26
|
+
end
|
27
|
+
end
|
28
|
+
|
29
|
+
context 'with :beginning ptype' do
|
30
|
+
it 'calculates correct pmt value' do
|
31
|
+
loan = Finance::Loan.new(nominal_rate: 0.12, duration: 6, amount: 1000, ptype: :beginning)
|
32
|
+
expect(loan.pmt).to eq(170.8399670404763)
|
33
|
+
end
|
34
|
+
end
|
35
|
+
|
36
|
+
it 'calculates correct pmt value' do
|
37
|
+
loan = Finance::Loan.new(nominal_rate: 0.13, duration: 90, amount: 1_000_000, ptype: :end)
|
38
|
+
expect(loan.pmt).to eq(17_449.90775727763)
|
39
|
+
end
|
40
|
+
end
|
41
|
+
|
42
|
+
describe '#fv' do
|
43
|
+
context 'with loan arguments' do
|
44
|
+
it 'calculates correct fv value' do
|
45
|
+
loan = Finance::Loan.new(nominal_rate: 0.05, duration: 120, amount: -100, payment: -100)
|
46
|
+
expect(loan.fv).to eq(15_692.928894335748)
|
47
|
+
end
|
48
|
+
|
49
|
+
context 'with :ptype' do
|
50
|
+
it 'calculates correct fv value' do
|
51
|
+
loan = Finance::Loan.new(
|
52
|
+
nominal_rate: 0.9, duration: 20, amount: 0, payment: -2000, ptype: :beginning
|
53
|
+
)
|
54
|
+
expect(loan.fv).to eq(93_105.06487352113)
|
55
|
+
end
|
56
|
+
end
|
57
|
+
end
|
58
|
+
|
59
|
+
context 'with an optional :payment argument' do
|
60
|
+
it 'calculates correct fv value' do
|
61
|
+
loan = Finance::Loan.new(nominal_rate: 0.05, duration: 120, amount: -100, payment: -200)
|
62
|
+
expect(loan.fv(payment: -100)).to eq(15_692.928894335748)
|
63
|
+
end
|
64
|
+
end
|
65
|
+
|
66
|
+
context 'w/o any payments' do
|
67
|
+
it 'raises an ArgumentError exception w/o loan arguments' do
|
68
|
+
loan = Finance::Loan.new(nominal_rate: 0.05, duration: 120, amount: -100)
|
69
|
+
expect { loan.fv }.to raise_error(ArgumentError, "no payment given")
|
70
|
+
end
|
71
|
+
end
|
72
|
+
end
|
73
|
+
|
74
|
+
describe '#ipmt' do
|
75
|
+
context 'when 1 period' do
|
76
|
+
it 'calculates correct ipmt value' do
|
77
|
+
loan = Finance::Loan.new(
|
78
|
+
nominal_rate: 0.0824, duration: 12, amount: 2500, period: 1
|
79
|
+
)
|
80
|
+
expect(loan.ipmt).to eq(-17.166666666666668)
|
81
|
+
end
|
82
|
+
end
|
83
|
+
|
84
|
+
context 'when 2 periods' do
|
85
|
+
it 'calculates correct ipmt value' do
|
86
|
+
loan = Finance::Loan.new(
|
87
|
+
nominal_rate: 0.0824, duration: 12, amount: 2500, period: 2
|
88
|
+
)
|
89
|
+
expect(loan.ipmt).to eq(-15.789337457350777)
|
90
|
+
end
|
91
|
+
end
|
92
|
+
|
93
|
+
context 'when 3 periods' do
|
94
|
+
it 'calculates correct ipmt value' do
|
95
|
+
loan = Finance::Loan.new(
|
96
|
+
nominal_rate: 0.0824, duration: 12.0, amount: 2500.0, period: 3.0, fv: 0.0
|
97
|
+
)
|
98
|
+
expect(loan.ipmt).to eq(-14.402550587464257)
|
99
|
+
end
|
100
|
+
end
|
101
|
+
end
|
102
|
+
end
|
metadata
CHANGED
@@ -1,14 +1,14 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: finance_rb
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 0.
|
4
|
+
version: 0.1.2
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Vlad Dyachenko
|
8
8
|
autorequire:
|
9
9
|
bindir: exe
|
10
10
|
cert_chain: []
|
11
|
-
date: 2021-
|
11
|
+
date: 2021-04-04 00:00:00.000000000 Z
|
12
12
|
dependencies: []
|
13
13
|
description: A ruby port of numpy-financial functions. This library provides a Ruby
|
14
14
|
interface for working with interest rates, mortgage amortization, and cashflows
|
@@ -27,15 +27,16 @@ files:
|
|
27
27
|
- lib/finance/version.rb
|
28
28
|
- lib/finance_rb.rb
|
29
29
|
- spec/finance/calculations_spec.rb
|
30
|
+
- spec/finance/loan_spec.rb
|
30
31
|
- spec/spec_helper.rb
|
31
32
|
homepage: https://github.com/wowinter13/finance_rb
|
32
33
|
licenses:
|
33
34
|
- MIT
|
34
35
|
metadata:
|
35
36
|
bug_tracker_uri: https://github.com/wowinter13/finance_rb/issues
|
36
|
-
changelog_uri: https://github.com/wowinter13/finance_rb/blob/
|
37
|
-
documentation_uri: https://www.rubydoc.info/wowinter13/finance_rb
|
38
|
-
source_code_uri: https://github.com/wowinter13/finance_rb
|
37
|
+
changelog_uri: https://github.com/wowinter13/finance_rb/blob/master/CHANGELOG.md
|
38
|
+
documentation_uri: https://www.rubydoc.info/github/wowinter13/finance_rb
|
39
|
+
source_code_uri: https://github.com/wowinter13/finance_rb
|
39
40
|
post_install_message:
|
40
41
|
rdoc_options: []
|
41
42
|
require_paths:
|
@@ -57,4 +58,5 @@ specification_version: 4
|
|
57
58
|
summary: A library for finance manipulations in Ruby.
|
58
59
|
test_files:
|
59
60
|
- spec/finance/calculations_spec.rb
|
61
|
+
- spec/finance/loan_spec.rb
|
60
62
|
- spec/spec_helper.rb
|