finrb 0.0.1 → 0.1.1
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 +41 -26
- data/README.md +121 -63
- data/lib/finrb/amortization.rb +24 -24
- data/lib/finrb/cashflows.rb +7 -8
- data/lib/finrb/decimal.rb +2 -2
- data/lib/finrb/rates.rb +42 -43
- data/lib/finrb/utils.rb +459 -532
- data/lib/finrb.rb +1 -1
- metadata +29 -91
- data/.dockerignore +0 -2
- data/.gitattributes +0 -83
- data/.gitignore +0 -114
- data/.rubocop.yml +0 -19
- data/.ruby-version +0 -1
- data/.semver +0 -5
- data/.travis.yml +0 -7
- data/.yardopts +0 -1
- data/Dockerfile +0 -35
- data/Gemfile +0 -5
- data/Gemfile.lock +0 -116
- data/Rakefile +0 -33
- data/docs/.gitkeep +0 -0
- data/docs/api.md +0 -1104
- data/finrb.gemspec +0 -98
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: b999dd6faadf24d9020be497526bbf8a2614fbcc994232e84b8d2cc693eb283f
|
4
|
+
data.tar.gz: 5611c3ab6cc8abc69e95fdb58e3d5913c1e47eaaf599f0aa097ba82f1377893b
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: a631f3777e169081228e91e08f94835780e479a63604828a89c649cf8ca0303d2ef2a9ad11de60376c14bc14d2652e700455523d232d45d4581ddb06b430e8c4
|
7
|
+
data.tar.gz: 74d40ad091e7cc2756e58371ea53bf10ade3b5e6573be78813cfadfb90c67e54d7595bc57947d4b9f5b5b74fb9423dc835a10541144a65b9747c0017f6cb297f
|
data/CHANGELOG.md
CHANGED
@@ -1,74 +1,89 @@
|
|
1
1
|
# finrb changelog
|
2
2
|
|
3
|
+
## 0.1.1
|
4
|
+
|
5
|
+
- documentation additions and fixes
|
6
|
+
- refactoring
|
7
|
+
- bugfixes and test suite migration to rspec
|
8
|
+
|
9
|
+
## 0.1.0
|
10
|
+
|
11
|
+
- sets ruby dependency to >= 3
|
12
|
+
- update/fix dependencies
|
13
|
+
- adds docker files for develop/test
|
14
|
+
- fix license files
|
15
|
+
- port [FinCal](https://github.com/felixfan/FinCal) library to ruby.
|
16
|
+
- readme update, api reference file outside main readme.
|
17
|
+
|
3
18
|
# Finance gem changelog
|
4
19
|
|
5
20
|
## Version 2.0.2
|
6
21
|
|
7
22
|
19 March 2019
|
8
23
|
|
9
|
-
|
10
|
-
|
11
|
-
|
24
|
+
- Fix BigDecimal deprecation warning
|
25
|
+
- Support Ruby 2.6.2
|
26
|
+
- Update dependencies
|
12
27
|
|
13
28
|
## Version 2.0.1
|
14
29
|
|
15
30
|
17 October 2017
|
16
31
|
|
17
|
-
|
18
|
-
|
19
|
-
|
32
|
+
- Added Support for configuration file to set up default eps and guess for IRR & XIRR
|
33
|
+
- Added guess rate for IRR & XIRR
|
34
|
+
- NVP now does not change the given cashflow array
|
20
35
|
|
21
36
|
## Version 2.0.0
|
22
37
|
|
23
38
|
23 Jul 2013
|
24
39
|
|
25
|
-
|
26
|
-
|
27
|
-
|
28
|
-
|
40
|
+
- Removed Integer#months, Integer#years, and replaced Numeric#to_d by Numeric#to_s in the interest of Rails compatibility.
|
41
|
+
- Converted unit tests from the shoulda framework to minitest.
|
42
|
+
- Removed octal numbers in test_cashflow.rb
|
43
|
+
- Thanks to @thadd, @bramswenson, and @xpe for their contributions to this release!
|
29
44
|
|
30
45
|
## Version 1.1.2
|
31
46
|
|
32
47
|
16 Jun 2012
|
33
48
|
|
34
|
-
|
35
|
-
|
49
|
+
- Bugfix: Array#irr and Array#xirr check for a valid sequence of cash flows.
|
50
|
+
- Bugfix: Integer#months and Integer#years no longer collide with Rails methods.
|
36
51
|
|
37
52
|
## Version 1.1.0
|
38
53
|
|
39
54
|
11 Sep 2011
|
40
55
|
|
41
|
-
|
42
|
-
|
43
|
-
|
56
|
+
- Added XNPV and XIRR functions, with basic testing.
|
57
|
+
- Bugfix: Array#sum no longer collides with the Array#sum defined in Rails.
|
58
|
+
- Bugfix: Numeric#amortize now correctly calls Finrb::Amortization#new.
|
44
59
|
|
45
60
|
## Version 1.0.0
|
46
61
|
|
47
62
|
20 Jul 2011
|
48
63
|
|
49
|
-
|
50
|
-
|
51
|
-
|
52
|
-
|
53
|
-
|
54
|
-
|
64
|
+
- Moved to Ruby 1.9.
|
65
|
+
- All classes are now contained within the +Finrb+ namespace.
|
66
|
+
- LOTS of additional documentation and examples.
|
67
|
+
- Introduced _shoulda_ for unit tests, to make things a little more readable.
|
68
|
+
- Bugfix: The +amortize+ Numeric method now accepts a variable number of rates.
|
69
|
+
- Some code refactoring and clean-up for a small performance increase.
|
55
70
|
|
56
71
|
## Version 0.2.0
|
57
72
|
|
58
73
|
28 Jun 2011
|
59
74
|
|
60
|
-
|
61
|
-
|
75
|
+
- Added support for adjustable rate mortgages.
|
76
|
+
- Added support for additional payments.
|
62
77
|
|
63
78
|
## Version 0.1.1
|
64
79
|
|
65
80
|
21 Jun 2011
|
66
81
|
|
67
|
-
|
82
|
+
- Code examples in README now display correctly in the online documentation.
|
68
83
|
|
69
84
|
## Version 0.1.0
|
70
85
|
|
71
86
|
21 Jun 2011
|
72
87
|
|
73
|
-
|
74
|
-
|
88
|
+
- Support for fixed-rate mortgage amortization.
|
89
|
+
- NPV, IRR array methods for cash flow analysis.
|
data/README.md
CHANGED
@@ -1,5 +1,21 @@
|
|
1
1
|
# finrb
|
2
2
|
|
3
|
+
[](https://github.com/ncs1/finrb/actions/workflows/ci.yml)
|
4
|
+
[](https://github.com/ncs1/finrb/actions/workflows/codeql.yml)
|
5
|
+
[](https://github.com/ncs1/finrb/actions/workflows/rubocop.yml)
|
6
|
+
|
7
|
+
<!-- TOC depthfrom:2 -->
|
8
|
+
|
9
|
+
- [Overview](#overview)
|
10
|
+
- [Features](#features)
|
11
|
+
- [Configuration](#configuration)
|
12
|
+
- [API and examples](#api-and-examples)
|
13
|
+
- [Resources](#resources)
|
14
|
+
- [Acknowledgements](#acknowledgements)
|
15
|
+
- [License](#license)
|
16
|
+
|
17
|
+
<!-- /TOC -->
|
18
|
+
|
3
19
|
Ruby gem for financial calculations/modeling.
|
4
20
|
|
5
21
|
finrb forked from the ruby [finance](https://github.com/Edward-Intelligence/finance) gem.
|
@@ -10,66 +26,103 @@ finrb forked from the ruby [finance](https://github.com/Edward-Intelligence/fina
|
|
10
26
|
|
11
27
|
Currently implemented features include:
|
12
28
|
|
13
|
-
|
14
|
-
|
15
|
-
|
16
|
-
|
17
|
-
|
18
|
-
|
19
|
-
|
20
|
-
|
21
|
-
|
22
|
-
|
23
|
-
|
24
|
-
|
25
|
-
|
26
|
-
|
27
|
-
|
28
|
-
|
29
|
-
|
30
|
-
|
31
|
-
|
32
|
-
|
33
|
-
|
34
|
-
|
35
|
-
|
36
|
-
|
37
|
-
|
38
|
-
|
39
|
-
|
40
|
-
|
41
|
-
|
42
|
-
|
43
|
-
|
44
|
-
|
45
|
-
|
46
|
-
|
47
|
-
|
48
|
-
|
49
|
-
|
50
|
-
|
51
|
-
|
52
|
-
|
53
|
-
|
54
|
-
|
55
|
-
|
56
|
-
|
57
|
-
|
58
|
-
|
59
|
-
|
60
|
-
|
61
|
-
|
62
|
-
|
63
|
-
|
64
|
-
|
65
|
-
|
66
|
-
|
67
|
-
|
68
|
-
|
69
|
-
|
70
|
-
|
71
|
-
|
72
|
-
|
29
|
+
- Uses the [flt](https://github.com/jgoizueta/flt) gem to ensure precision decimal arithmetic in all calculations.
|
30
|
+
- Fixed-rate mortgage amortization (30/360).
|
31
|
+
- Interest rates
|
32
|
+
- Various cash flow computations, such as NPV and IRR.
|
33
|
+
- Adjustable rate mortgage amortization.
|
34
|
+
- Payment modifications (i.e., how does paying an additional $75 per month affect the amortization?)
|
35
|
+
- Utils class provides basic financial calculation utilities (ported from R's [FinCal](https://github.com/felixfan/FinCal) library):
|
36
|
+
|
37
|
+
- Basic Earnings Per Share
|
38
|
+
|
39
|
+
- Bond-equivalent yield (BEY), 2 x the semiannual discount rate
|
40
|
+
|
41
|
+
- Calculate the net increase in common shares from the potential exercise of stock options or warrants
|
42
|
+
|
43
|
+
- Calculate weighted average shares - weighted average number of common shares
|
44
|
+
|
45
|
+
- Cash ratio - Liquidity ratios measure the firm's ability to satisfy its short-term obligations as they come due.
|
46
|
+
|
47
|
+
- Computing Coefficient of variation
|
48
|
+
|
49
|
+
- Computing HPR, the holding period return
|
50
|
+
|
51
|
+
- Computing IRR, the internal rate of return
|
52
|
+
|
53
|
+
- Computing NPV, the PV of the cash flows less the initial (time = 0) outlay
|
54
|
+
|
55
|
+
- Computing Roy's safety-first ratio
|
56
|
+
|
57
|
+
- Computing Sampling error
|
58
|
+
|
59
|
+
- Computing Sharpe Ratio
|
60
|
+
|
61
|
+
- Computing TWRR, the time-weighted rate of return
|
62
|
+
|
63
|
+
- Computing bank discount yield (BDY) for a T-bill
|
64
|
+
|
65
|
+
- Computing money market yield (MMY) for a T-bill
|
66
|
+
|
67
|
+
- Computing the future value of an uneven cash flow series
|
68
|
+
|
69
|
+
- Computing the present value of an uneven cash flow series
|
70
|
+
|
71
|
+
- Computing the rate of return for each period
|
72
|
+
|
73
|
+
- Convert a given continuous compounded rate to a norminal rate
|
74
|
+
|
75
|
+
- Convert a given norminal rate to a continuous compounded rate
|
76
|
+
|
77
|
+
- Convert holding period return to the effective annual rate
|
78
|
+
|
79
|
+
- Convert stated annual rate to the effective annual rate (with continuous compounding)
|
80
|
+
|
81
|
+
- Cost of goods sold and ending inventory under three methods (FIFO,LIFO,Weighted average)
|
82
|
+
|
83
|
+
- Current ratio - Liquidity ratios measure the firm's ability to satisfy its short-term obligations as they come due.
|
84
|
+
|
85
|
+
- Debt ratio - Solvency ratios measure the firm's ability to satisfy its long-term obligations.
|
86
|
+
|
87
|
+
- Depreciation Expense Recognition - Straight-line depreciation (SL) allocates an equal amount of depreciation each year over the asset's useful life
|
88
|
+
|
89
|
+
- Depreciation Expense Recognition - double-declining balance (DDB), the most common declining balance method, which applies two times the straight-line rate to the declining balance.
|
90
|
+
|
91
|
+
- Diluted Earnings Per Share
|
92
|
+
|
93
|
+
- Equivalent/proportional Interest Rates
|
94
|
+
|
95
|
+
- Estimate future value (fv) (of a single sum)
|
96
|
+
|
97
|
+
- Estimate future value of an annuity
|
98
|
+
|
99
|
+
- Estimate period payment
|
100
|
+
|
101
|
+
- Estimate present value (pv) (of a single sum) (of an annuity)
|
102
|
+
|
103
|
+
- Estimate present value of a perpetuity
|
104
|
+
|
105
|
+
- Estimate the number of periods
|
106
|
+
|
107
|
+
- Financial leverage - Solvency ratios measure the firm's ability to satisfy its long-term obligations.
|
108
|
+
|
109
|
+
- Geometric mean return
|
110
|
+
|
111
|
+
- Gross profit margin - Evaluate a company's financial performance
|
112
|
+
|
113
|
+
- Harmonic mean, average price
|
114
|
+
|
115
|
+
- Long-term debt-to-equity - Solvency ratios measure the firm's ability to satisfy its long-term obligations.
|
116
|
+
|
117
|
+
- Net profit margin - Evaluate a company's financial performance
|
118
|
+
|
119
|
+
- Quick ratio - Liquidity ratios measure the firm's ability to satisfy its short-term obligations as they come due.
|
120
|
+
|
121
|
+
- Rate of return for a perpetuity
|
122
|
+
|
123
|
+
- Total debt-to-equity - Solvency ratios measure the firm's ability to satisfy its long-term obligations.
|
124
|
+
|
125
|
+
- Weighted mean as a portfolio return
|
73
126
|
|
74
127
|
### Configuration
|
75
128
|
|
@@ -90,9 +143,14 @@ See [api.md](docs/api.md)
|
|
90
143
|
|
91
144
|
## Resources
|
92
145
|
|
93
|
-
|
94
|
-
|
95
|
-
|
146
|
+
- [RubyGems Page](https://rubygems.org/gems/finrb)
|
147
|
+
- [Source Code](https://github.com/ncs1/finrb)
|
148
|
+
- [Bug Tracker](https://github.com/ncs1/finrb/issues)
|
149
|
+
|
150
|
+
## Acknowledgements
|
151
|
+
|
152
|
+
- Martin Bjeldbak Madsen (@martinbjeldbak), Bill Kranec (@wkranec) - original [finance](https://github.com/Edward-Intelligence/finance) gem maintainers.
|
153
|
+
- Yanhui Fan (@felixfan) - maintainer of [FinCal](https://github.com/felixfan/FinCal) library.
|
96
154
|
|
97
155
|
## License
|
98
156
|
|
data/lib/finrb/amortization.rb
CHANGED
@@ -34,6 +34,26 @@ module Finrb
|
|
34
34
|
# @api public
|
35
35
|
attr_reader :rates
|
36
36
|
|
37
|
+
# @return [DecNum] the periodic payment due on a loan
|
38
|
+
# @param [DecNum] principal the initial amount of the loan or investment
|
39
|
+
# @param [Rate] rate the applicable interest rate (per period)
|
40
|
+
# @param [Integer] periods the number of periods needed for repayment
|
41
|
+
# @note in most cases, you will probably want to use rate.monthly when calling this function outside of an Amortization instance.
|
42
|
+
# @example
|
43
|
+
# rate = Rate.new(0.0375, :apr, :duration => (30 * 12))
|
44
|
+
# rate.duration #=> 360
|
45
|
+
# Amortization.payment(200000, rate.monthly, rate.duration) #=> DecNum('-926.23')
|
46
|
+
# @see https://en.wikipedia.org/wiki/Amortization_calculator
|
47
|
+
# @api public
|
48
|
+
def self.payment(principal, rate, periods)
|
49
|
+
if rate.zero?
|
50
|
+
# simplified formula to avoid division-by-zero when interest rate is zero
|
51
|
+
-(principal / periods).round(2)
|
52
|
+
else
|
53
|
+
-(principal * (rate + (rate / (((rate + 1)**periods) - 1)))).round(2)
|
54
|
+
end
|
55
|
+
end
|
56
|
+
|
37
57
|
# create a new Amortization instance
|
38
58
|
# @return [Amortization]
|
39
59
|
# @param [DecNum] principal the initial amount of the loan or investment
|
@@ -54,7 +74,7 @@ module Finrb
|
|
54
74
|
|
55
75
|
# compare two Amortization instances
|
56
76
|
# @return [Numeric] -1, 0, or +1
|
57
|
-
# @param [Amortization]
|
77
|
+
# @param [Amortization] other
|
58
78
|
# @api public
|
59
79
|
def ==(other)
|
60
80
|
(principal == other.principal) && (rates == other.rates) && (payments == other.payments)
|
@@ -67,7 +87,7 @@ module Finrb
|
|
67
87
|
# amt.additional_payments #=> [DecNum('-100.00'), DecNum('-100.00'), ... ]
|
68
88
|
# @api public
|
69
89
|
def additional_payments
|
70
|
-
@transactions.
|
90
|
+
@transactions.filter_map { |trans| trans.difference if trans.payment? }
|
71
91
|
end
|
72
92
|
|
73
93
|
# amortize the balance of loan with the given interest rate
|
@@ -155,27 +175,7 @@ module Finrb
|
|
155
175
|
# amt.interest[0,6].sum #=> DecNum('5603.74')
|
156
176
|
# @api public
|
157
177
|
def interest
|
158
|
-
@transactions.
|
159
|
-
end
|
160
|
-
|
161
|
-
# @return [DecNum] the periodic payment due on a loan
|
162
|
-
# @param [DecNum] principal the initial amount of the loan or investment
|
163
|
-
# @param [Rate] rate the applicable interest rate (per period)
|
164
|
-
# @param [Integer] periods the number of periods needed for repayment
|
165
|
-
# @note in most cases, you will probably want to use rate.monthly when calling this function outside of an Amortization instance.
|
166
|
-
# @example
|
167
|
-
# rate = Rate.new(0.0375, :apr, :duration => (30 * 12))
|
168
|
-
# rate.duration #=> 360
|
169
|
-
# Amortization.payment(200000, rate.monthly, rate.duration) #=> DecNum('-926.23')
|
170
|
-
# @see http://en.wikipedia.org/wiki/Amortization_calculator
|
171
|
-
# @api public
|
172
|
-
def self.payment(principal, rate, periods)
|
173
|
-
if rate.zero?
|
174
|
-
# simplified formula to avoid division-by-zero when interest rate is zero
|
175
|
-
-(principal / periods).round(2)
|
176
|
-
else
|
177
|
-
-(principal * (rate + (rate / (((1 + rate)**periods) - 1)))).round(2)
|
178
|
-
end
|
178
|
+
@transactions.filter_map { |trans| trans.amount if trans.interest? }
|
179
179
|
end
|
180
180
|
|
181
181
|
# @return [Array] the amount of the payment in each period
|
@@ -185,7 +185,7 @@ module Finrb
|
|
185
185
|
# amt.payments.sum #=> DecNum('-500163.94')
|
186
186
|
# @api public
|
187
187
|
def payments
|
188
|
-
@transactions.
|
188
|
+
@transactions.filter_map { |trans| trans.amount if trans.payment? }
|
189
189
|
end
|
190
190
|
end
|
191
191
|
end
|
data/lib/finrb/cashflows.rb
CHANGED
@@ -1,5 +1,6 @@
|
|
1
1
|
# frozen_string_literal: true
|
2
2
|
|
3
|
+
require_relative 'config'
|
3
4
|
require_relative 'decimal'
|
4
5
|
require_relative 'rates'
|
5
6
|
|
@@ -40,10 +41,10 @@ module Finrb
|
|
40
41
|
|
41
42
|
# calculate the internal rate of return for a sequence of cash flows
|
42
43
|
# @return [DecNum] the internal rate of return
|
43
|
-
# @param [Numeric] Initial guess rate, Defaults to 1.0
|
44
|
+
# @param [Numeric] guess Initial guess rate, Defaults to 1.0
|
44
45
|
# @example
|
45
46
|
# [-4000,1200,1410,1875,1050].irr #=> 0.143
|
46
|
-
# @see
|
47
|
+
# @see https://en.wikipedia.org/wiki/Internal_rate_of_return
|
47
48
|
# @api public
|
48
49
|
def irr(guess = nil)
|
49
50
|
# Make sure we have a valid sequence of cash flows.
|
@@ -67,7 +68,7 @@ module Finrb
|
|
67
68
|
# @param [Numeric] rate the discount rate to be applied
|
68
69
|
# @example
|
69
70
|
# [-100.0, 60, 60, 60].npv(0.1) #=> 49.211
|
70
|
-
# @see
|
71
|
+
# @see https://en.wikipedia.org/wiki/Net_present_value
|
71
72
|
# @api public
|
72
73
|
def npv(rate)
|
73
74
|
cashflows = map { |entry| Flt::DecNum.new(entry.to_s) }
|
@@ -75,7 +76,7 @@ module Finrb
|
|
75
76
|
rate = Flt::DecNum.new(rate.to_s)
|
76
77
|
total = Flt::DecNum.new(0.to_s)
|
77
78
|
cashflows.each_with_index do |cashflow, index|
|
78
|
-
total += cashflow / ((
|
79
|
+
total += cashflow / ((rate + 1)**index)
|
79
80
|
end
|
80
81
|
|
81
82
|
total
|
@@ -120,7 +121,7 @@ module Finrb
|
|
120
121
|
rate = Flt::DecNum.new(rate.to_s)
|
121
122
|
|
122
123
|
sum do |t|
|
123
|
-
t.amount / ((
|
124
|
+
t.amount / ((rate + 1)**(date_diff(start, t.date) / days_in_period))
|
124
125
|
end
|
125
126
|
end
|
126
127
|
|
@@ -152,9 +153,7 @@ module Finrb
|
|
152
153
|
|
153
154
|
def valid(guess)
|
154
155
|
if guess.nil?
|
155
|
-
unless Finrb.config.guess.is_a?(Numeric)
|
156
|
-
raise(ArgumentError, 'Invalid Guess. Default guess should be a [Numeric] value.')
|
157
|
-
end
|
156
|
+
raise(ArgumentError, 'Invalid Guess. Default guess should be a [Numeric] value.') unless Finrb.config.guess.is_a?(Numeric)
|
158
157
|
|
159
158
|
Finrb.config.guess
|
160
159
|
else
|
data/lib/finrb/decimal.rb
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
# frozen_string_literal: true
|
2
2
|
|
3
|
-
require 'rubygems'
|
4
3
|
require 'flt'
|
4
|
+
require 'rubygems'
|
5
5
|
include Flt
|
6
6
|
|
7
7
|
DecNum.context.define_conversion_from(BigDecimal) do |x, _context|
|
@@ -13,7 +13,7 @@ DecNum.context.define_conversion_to(BigDecimal) do |x|
|
|
13
13
|
end
|
14
14
|
|
15
15
|
class Numeric
|
16
|
-
def
|
16
|
+
def to_dec
|
17
17
|
if instance_of?(DecNum)
|
18
18
|
self
|
19
19
|
else
|
data/lib/finrb/rates.rb
CHANGED
@@ -8,6 +8,45 @@ module Finrb
|
|
8
8
|
# @api public
|
9
9
|
class Rate
|
10
10
|
include Comparable
|
11
|
+
# Accepted rate types
|
12
|
+
TYPES = { apr: 'effective', apy: 'effective', effective: 'effective', nominal: 'nominal' }.freeze
|
13
|
+
# convert a nominal interest rate to an effective interest rate
|
14
|
+
# @return [DecNum] the effective interest rate
|
15
|
+
# @param [Numeric] rate the nominal interest rate
|
16
|
+
# @param [Numeric] periods the number of compounding periods per year
|
17
|
+
# @example
|
18
|
+
# Rate.to_effective(0.05, 4) #=> DecNum('0.05095')
|
19
|
+
# @api public
|
20
|
+
def self.to_effective(rate, periods)
|
21
|
+
rate = Flt::DecNum.new(rate.to_s)
|
22
|
+
periods = Flt::DecNum.new(periods.to_s)
|
23
|
+
|
24
|
+
if periods.infinite?
|
25
|
+
rate.exp - 1
|
26
|
+
else
|
27
|
+
(((rate / periods) + 1)**periods) - 1
|
28
|
+
end
|
29
|
+
end
|
30
|
+
|
31
|
+
# convert an effective interest rate to a nominal interest rate
|
32
|
+
# @return [DecNum] the nominal interest rate
|
33
|
+
# @param [Numeric] rate the effective interest rate
|
34
|
+
# @param [Numeric] periods the number of compounding periods per year
|
35
|
+
# @example
|
36
|
+
# Rate.to_nominal(0.06, 365) #=> DecNum('0.05827')
|
37
|
+
# @see https://www.miniwebtool.com/nominal-interest-rate-calculator/
|
38
|
+
# @api public
|
39
|
+
def self.to_nominal(rate, periods)
|
40
|
+
rate = Flt::DecNum.new(rate.to_s)
|
41
|
+
periods = Flt::DecNum.new(periods.to_s)
|
42
|
+
|
43
|
+
if periods.infinite?
|
44
|
+
(rate + 1).log
|
45
|
+
else
|
46
|
+
periods * (((rate + 1)**(1.to_f / periods)) - 1)
|
47
|
+
end
|
48
|
+
end
|
49
|
+
|
11
50
|
# create a new Rate instance
|
12
51
|
# @return [Rate]
|
13
52
|
# @param [Numeric] rate the decimal value of the interest rate
|
@@ -17,8 +56,8 @@ module Finrb
|
|
17
56
|
# @option opts [String] :compounds (:monthly) the number of compounding periods per year
|
18
57
|
# @example create a 3.5% APR rate
|
19
58
|
# Rate.new(0.035, :apr) #=> Rate(0.035, :apr)
|
20
|
-
# @see
|
21
|
-
# @see
|
59
|
+
# @see https://en.wikipedia.org/wiki/Effective_interest_rate
|
60
|
+
# @see https://en.wikipedia.org/wiki/Nominal_interest_rate
|
22
61
|
# @api public
|
23
62
|
def initialize(rate, type, opts = {})
|
24
63
|
# Default monthly compounding.
|
@@ -37,9 +76,6 @@ module Finrb
|
|
37
76
|
end
|
38
77
|
end
|
39
78
|
|
40
|
-
# Accepted rate types
|
41
|
-
TYPES = { apr: 'effective', apy: 'effective', effective: 'effective', nominal: 'nominal' }.freeze
|
42
|
-
|
43
79
|
# @return [Integer] the duration for which the rate is valid, in months
|
44
80
|
# @api public
|
45
81
|
attr_accessor :duration
|
@@ -52,7 +88,7 @@ module Finrb
|
|
52
88
|
|
53
89
|
# compare two Rates, using the effective rate
|
54
90
|
# @return [Numeric] one of -1, 0, +1
|
55
|
-
# @param [Rate]
|
91
|
+
# @param [Rate] other the comparison Rate
|
56
92
|
# @example Which is better, a nominal rate of 15% compounded monthly, or 15.5% compounded semiannually?
|
57
93
|
# r1 = Rate.new(0.15, :nominal) #=> Rate.new(0.160755, :apr)
|
58
94
|
# r2 = Rate.new(0.155, :nominal, :compounds => :semiannually) #=> Rate.new(0.161006, :apr)
|
@@ -125,43 +161,6 @@ module Finrb
|
|
125
161
|
@effective = Rate.to_effective(rate, @periods)
|
126
162
|
end
|
127
163
|
|
128
|
-
# convert a nominal interest rate to an effective interest rate
|
129
|
-
# @return [DecNum] the effective interest rate
|
130
|
-
# @param [Numeric] rate the nominal interest rate
|
131
|
-
# @param [Numeric] periods the number of compounding periods per year
|
132
|
-
# @example
|
133
|
-
# Rate.to_effective(0.05, 4) #=> DecNum('0.05095')
|
134
|
-
# @api public
|
135
|
-
def self.to_effective(rate, periods)
|
136
|
-
rate = Flt::DecNum.new(rate.to_s)
|
137
|
-
periods = Flt::DecNum.new(periods.to_s)
|
138
|
-
|
139
|
-
if periods.infinite?
|
140
|
-
rate.exp - 1
|
141
|
-
else
|
142
|
-
((1 + (rate / periods))**periods) - 1
|
143
|
-
end
|
144
|
-
end
|
145
|
-
|
146
|
-
# convert an effective interest rate to a nominal interest rate
|
147
|
-
# @return [DecNum] the nominal interest rate
|
148
|
-
# @param [Numeric] rate the effective interest rate
|
149
|
-
# @param [Numeric] periods the number of compounding periods per year
|
150
|
-
# @example
|
151
|
-
# Rate.to_nominal(0.06, 365) #=> DecNum('0.05827')
|
152
|
-
# @see http://www.miniwebtool.com/nominal-interest-rate-calculator/
|
153
|
-
# @api public
|
154
|
-
def self.to_nominal(rate, periods)
|
155
|
-
rate = Flt::DecNum.new(rate.to_s)
|
156
|
-
periods = Flt::DecNum.new(periods.to_s)
|
157
|
-
|
158
|
-
if periods.infinite?
|
159
|
-
(rate + 1).log
|
160
|
-
else
|
161
|
-
periods * (((1 + rate)**(1 / periods)) - 1)
|
162
|
-
end
|
163
|
-
end
|
164
|
-
|
165
164
|
private :compounds=, :effective=, :nominal=
|
166
165
|
end
|
167
166
|
end
|