financial_calculator 2.1.0 → 3.0.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 +31 -0
- data/README.md +7 -23
- data/financial_calculator.gemspec +3 -2
- data/lib/financial_calculator.rb +10 -6
- data/lib/financial_calculator/amortization.rb +0 -2
- data/lib/financial_calculator/irr.rb +100 -0
- data/lib/financial_calculator/npv.rb +44 -0
- data/lib/financial_calculator/pv.rb +85 -0
- data/lib/financial_calculator/rates.rb +0 -2
- data/lib/financial_calculator/transaction.rb +0 -2
- data/lib/financial_calculator/version.rb +1 -1
- data/lib/financial_calculator/xirr.rb +124 -0
- data/lib/financial_calculator/xnpv.rb +91 -0
- data/spec/irr_spec.rb +44 -0
- data/spec/npv_spec.rb +39 -0
- data/spec/pv_spec.rb +127 -0
- data/spec/spec_helper.rb +10 -9
- data/spec/xirr_spec.rb +73 -0
- data/spec/xnpv_spec.rb +81 -0
- metadata +33 -10
- data/HISTORY +0 -47
- data/lib/financial_calculator/cashflows.rb +0 -127
- data/lib/financial_calculator/decimal.rb +0 -21
- data/spec/cashflows_spec.rb +0 -66
- data/spec/decimal_spec.rb +0 -24
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:
|
4
|
+
version: 3.0.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-05-12 00:00:00.000000000 Z
|
12
12
|
dependencies:
|
13
13
|
- !ruby/object:Gem::Dependency
|
14
14
|
name: flt
|
@@ -122,6 +122,20 @@ dependencies:
|
|
122
122
|
- - ">="
|
123
123
|
- !ruby/object:Gem::Version
|
124
124
|
version: 0.16.0
|
125
|
+
- !ruby/object:Gem::Dependency
|
126
|
+
name: yard
|
127
|
+
requirement: !ruby/object:Gem::Requirement
|
128
|
+
requirements:
|
129
|
+
- - ">="
|
130
|
+
- !ruby/object:Gem::Version
|
131
|
+
version: '0'
|
132
|
+
type: :development
|
133
|
+
prerelease: false
|
134
|
+
version_requirements: !ruby/object:Gem::Requirement
|
135
|
+
requirements:
|
136
|
+
- - ">="
|
137
|
+
- !ruby/object:Gem::Version
|
138
|
+
version: '0'
|
125
139
|
description: The financial calculator library provides a Ruby interface for performing
|
126
140
|
common financial calculations (NPV, IRR, etc.).
|
127
141
|
email: michael.falzone@gmail.com
|
@@ -131,34 +145,40 @@ extra_rdoc_files:
|
|
131
145
|
- README.md
|
132
146
|
- COPYING
|
133
147
|
- COPYING.LESSER
|
134
|
-
-
|
148
|
+
- CHANGELOG.md
|
135
149
|
files:
|
136
150
|
- ".gitignore"
|
137
151
|
- ".rbenv-version"
|
138
152
|
- ".travis.yml"
|
139
153
|
- ".yardopts"
|
154
|
+
- CHANGELOG.md
|
140
155
|
- COPYING
|
141
156
|
- COPYING.LESSER
|
142
157
|
- Gemfile
|
143
|
-
- HISTORY
|
144
158
|
- README.md
|
145
159
|
- Rakefile
|
146
160
|
- financial_calculator.gemspec
|
147
161
|
- lib/financial_calculator.rb
|
148
162
|
- lib/financial_calculator/amortization.rb
|
149
|
-
- lib/financial_calculator/
|
150
|
-
- lib/financial_calculator/
|
163
|
+
- lib/financial_calculator/irr.rb
|
164
|
+
- lib/financial_calculator/npv.rb
|
165
|
+
- lib/financial_calculator/pv.rb
|
151
166
|
- lib/financial_calculator/rates.rb
|
152
167
|
- lib/financial_calculator/transaction.rb
|
153
168
|
- lib/financial_calculator/version.rb
|
169
|
+
- lib/financial_calculator/xirr.rb
|
170
|
+
- lib/financial_calculator/xnpv.rb
|
154
171
|
- spec/amortization_spec.rb
|
155
|
-
- spec/
|
156
|
-
- spec/
|
172
|
+
- spec/irr_spec.rb
|
173
|
+
- spec/npv_spec.rb
|
174
|
+
- spec/pv_spec.rb
|
157
175
|
- spec/rates_spec.rb
|
158
176
|
- spec/spec_helper.rb
|
159
177
|
- spec/transaction/interest_spec.rb
|
160
178
|
- spec/transaction/payment_spec.rb
|
161
179
|
- spec/transaction_spec.rb
|
180
|
+
- spec/xirr_spec.rb
|
181
|
+
- spec/xnpv_spec.rb
|
162
182
|
homepage: https://rubygems.org/gems/financial_calculator
|
163
183
|
licenses:
|
164
184
|
- LGPL-3.0
|
@@ -185,10 +205,13 @@ specification_version: 4
|
|
185
205
|
summary: A library for financial modelling in Ruby.
|
186
206
|
test_files:
|
187
207
|
- spec/amortization_spec.rb
|
188
|
-
- spec/
|
189
|
-
- spec/
|
208
|
+
- spec/irr_spec.rb
|
209
|
+
- spec/npv_spec.rb
|
210
|
+
- spec/pv_spec.rb
|
190
211
|
- spec/rates_spec.rb
|
191
212
|
- spec/spec_helper.rb
|
192
213
|
- spec/transaction/interest_spec.rb
|
193
214
|
- spec/transaction/payment_spec.rb
|
194
215
|
- spec/transaction_spec.rb
|
216
|
+
- spec/xirr_spec.rb
|
217
|
+
- spec/xnpv_spec.rb
|
data/HISTORY
DELETED
@@ -1,47 +0,0 @@
|
|
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.
|
@@ -1,127 +0,0 @@
|
|
1
|
-
require_relative 'decimal'
|
2
|
-
require_relative 'rates'
|
3
|
-
|
4
|
-
require 'bigdecimal'
|
5
|
-
require 'bigdecimal/newton'
|
6
|
-
include Newton
|
7
|
-
|
8
|
-
module FinancialCalculator
|
9
|
-
# Provides methods for working with cash flows (collections of transactions)
|
10
|
-
# @api public
|
11
|
-
module Cashflow
|
12
|
-
# Base class for working with Newton's Method.
|
13
|
-
# @api private
|
14
|
-
class Function
|
15
|
-
values = {
|
16
|
-
eps: "1.0e-16",
|
17
|
-
one: "1.0",
|
18
|
-
two: "2.0",
|
19
|
-
ten: "10.0",
|
20
|
-
zero: "0.0"
|
21
|
-
}
|
22
|
-
|
23
|
-
values.each do |key, value|
|
24
|
-
define_method key do
|
25
|
-
BigDecimal(value)
|
26
|
-
end
|
27
|
-
end
|
28
|
-
|
29
|
-
def initialize(transactions, function)
|
30
|
-
@transactions = transactions
|
31
|
-
@function = function
|
32
|
-
end
|
33
|
-
|
34
|
-
def values(x)
|
35
|
-
value = @transactions.send(@function, Flt::DecNum.new(x[0].to_s))
|
36
|
-
[ BigDecimal(value.to_s) ]
|
37
|
-
end
|
38
|
-
end
|
39
|
-
|
40
|
-
# calculate the internal rate of return for a sequence of cash flows
|
41
|
-
# @return [DecNum] the internal rate of return
|
42
|
-
# @example
|
43
|
-
# [-4000,1200,1410,1875,1050].irr #=> 0.143
|
44
|
-
# @see http://en.wikipedia.org/wiki/Internal_rate_of_return
|
45
|
-
# @api public
|
46
|
-
def irr
|
47
|
-
# Make sure we have a valid sequence of cash flows.
|
48
|
-
positives, negatives = self.partition{ |i| i >= 0 }
|
49
|
-
if positives.empty? || negatives.empty?
|
50
|
-
raise ArgumentError, "Calculation does not converge."
|
51
|
-
end
|
52
|
-
|
53
|
-
func = Function.new(self, :npv)
|
54
|
-
rate = [ func.one ]
|
55
|
-
nlsolve( func, rate )
|
56
|
-
rate[0]
|
57
|
-
end
|
58
|
-
|
59
|
-
def method_missing(name, *args, &block)
|
60
|
-
return self.inject(:+) if name.to_s == "sum"
|
61
|
-
super
|
62
|
-
end
|
63
|
-
|
64
|
-
# calculate the net present value of a sequence of cash flows
|
65
|
-
# @return [DecNum] the net present value
|
66
|
-
# @param [Numeric] rate the discount rate to be applied
|
67
|
-
# @example
|
68
|
-
# [-100.0, 60, 60, 60].npv(0.1) #=> 49.211
|
69
|
-
# @see http://en.wikipedia.org/wiki/Net_present_value
|
70
|
-
# @api public
|
71
|
-
def npv(rate)
|
72
|
-
self.collect! { |entry| Flt::DecNum.new(entry.to_s) }
|
73
|
-
|
74
|
-
rate, total = Flt::DecNum.new(rate.to_s), Flt::DecNum.new(0.to_s)
|
75
|
-
self.each_with_index do |cashflow, index|
|
76
|
-
total += cashflow / (1 + rate) ** index
|
77
|
-
end
|
78
|
-
|
79
|
-
total
|
80
|
-
end
|
81
|
-
|
82
|
-
# calculate the internal rate of return for a sequence of cash flows with dates
|
83
|
-
# @return [Rate] the internal rate of return
|
84
|
-
# @example
|
85
|
-
# @transactions = []
|
86
|
-
# @transactions << Transaction.new(-1000, :date => Time.new(1985,01,01))
|
87
|
-
# @transactions << Transaction.new( 600, :date => Time.new(1990,01,01))
|
88
|
-
# @transactions << Transaction.new( 600, :date => Time.new(1995,01,01))
|
89
|
-
# @transactions.xirr(0.6) #=> Rate("0.024851", :apr, :compounds => :annually)
|
90
|
-
# @api public
|
91
|
-
def xirr(iterations=100)
|
92
|
-
# Make sure we have a valid sequence of cash flows.
|
93
|
-
positives, negatives = self.partition{ |t| t.amount >= 0 }
|
94
|
-
if positives.empty? || negatives.empty?
|
95
|
-
raise ArgumentError, "Calculation does not converge."
|
96
|
-
end
|
97
|
-
|
98
|
-
func = Function.new(self, :xnpv)
|
99
|
-
rate = [ func.one ]
|
100
|
-
nlsolve( func, rate )
|
101
|
-
Rate.new(rate[0], :apr, :compounds => :annually)
|
102
|
-
end
|
103
|
-
|
104
|
-
# calculate the net present value of a sequence of cash flows
|
105
|
-
# @return [DecNum]
|
106
|
-
# @example
|
107
|
-
# @transactions = []
|
108
|
-
# @transactions << Transaction.new(-1000, :date => Time.new(1985,01,01))
|
109
|
-
# @transactions << Transaction.new( 600, :date => Time.new(1990,01,01))
|
110
|
-
# @transactions << Transaction.new( 600, :date => Time.new(1995,01,01))
|
111
|
-
# @transactions.xnpv(0.6).round(2) #=> -937.41
|
112
|
-
# @api public
|
113
|
-
def xnpv(rate)
|
114
|
-
rate = Flt::DecNum.new(rate.to_s)
|
115
|
-
start = self[0].date
|
116
|
-
|
117
|
-
self.inject(0) do |sum, t|
|
118
|
-
n = t.amount / ( (1 + rate) ** ((t.date-start) / Flt::DecNum.new(31536000.to_s))) # 365 * 86400
|
119
|
-
sum + n
|
120
|
-
end
|
121
|
-
end
|
122
|
-
end
|
123
|
-
end
|
124
|
-
|
125
|
-
class Array
|
126
|
-
include FinancialCalculator::Cashflow
|
127
|
-
end
|
@@ -1,21 +0,0 @@
|
|
1
|
-
require 'rubygems'
|
2
|
-
require 'flt'
|
3
|
-
include Flt
|
4
|
-
|
5
|
-
DecNum.context.define_conversion_from(BigDecimal) do |x, context|
|
6
|
-
DecNum(x.to_s)
|
7
|
-
end
|
8
|
-
|
9
|
-
DecNum.context.define_conversion_to(BigDecimal) do |x|
|
10
|
-
BigDecimal.new(x.to_s)
|
11
|
-
end
|
12
|
-
|
13
|
-
class Numeric
|
14
|
-
def to_d
|
15
|
-
if self.instance_of? DecNum
|
16
|
-
self
|
17
|
-
else
|
18
|
-
DecNum self.to_s
|
19
|
-
end
|
20
|
-
end
|
21
|
-
end
|
data/spec/cashflows_spec.rb
DELETED
@@ -1,66 +0,0 @@
|
|
1
|
-
require_relative 'spec_helper'
|
2
|
-
|
3
|
-
shared_examples_for 'the values do not converge' do
|
4
|
-
it 'should raise an ArgumentError' do
|
5
|
-
expect { subject }.to raise_error ArgumentError
|
6
|
-
end
|
7
|
-
end
|
8
|
-
|
9
|
-
describe "Cashflows" do
|
10
|
-
let(:transactions) do
|
11
|
-
[
|
12
|
-
Transaction.new(-1000, :date => Time.new(1985, 1, 1)),
|
13
|
-
Transaction.new( 600, :date => Time.new(1990, 1, 1)),
|
14
|
-
Transaction.new( 600, :date => Time.new(1995, 1, 1))
|
15
|
-
]
|
16
|
-
end
|
17
|
-
|
18
|
-
describe '#irr' do
|
19
|
-
let(:flows) { [-4000, 1200, 1410, 1875, 1050] }
|
20
|
-
|
21
|
-
subject { flows.irr.round(3) }
|
22
|
-
|
23
|
-
it { is_expected.to eql D('0.143') }
|
24
|
-
|
25
|
-
context 'when called on an array of Transactions' do
|
26
|
-
let(:flows) { transactions }
|
27
|
-
|
28
|
-
it 'should raise a NoMethodError' do
|
29
|
-
expect { subject }.to raise_error NoMethodError
|
30
|
-
end
|
31
|
-
end
|
32
|
-
|
33
|
-
context 'when the values do not converge' do
|
34
|
-
let(:flows) { [10, 20, 30] }
|
35
|
-
it_behaves_like 'the values do not converge'
|
36
|
-
end
|
37
|
-
end
|
38
|
-
|
39
|
-
describe '#npv' do
|
40
|
-
let(:flows) { [-100.0, 60, 60, 60] }
|
41
|
-
|
42
|
-
subject { flows.npv(0.1).round(3) }
|
43
|
-
|
44
|
-
it { is_expected.to eql D('49.211') }
|
45
|
-
end
|
46
|
-
|
47
|
-
describe '#xirr' do
|
48
|
-
let(:xirr_transactions) { transactions }
|
49
|
-
|
50
|
-
subject { xirr_transactions.xirr.effective.round(6) }
|
51
|
-
|
52
|
-
it { is_expected.to eql D('0.024851') }
|
53
|
-
|
54
|
-
context 'when the values do not converge' do
|
55
|
-
let(:xirr_transactions) { transactions[1,3] }
|
56
|
-
|
57
|
-
it_behaves_like 'the values do not converge'
|
58
|
-
end
|
59
|
-
end
|
60
|
-
|
61
|
-
describe '#xnpv' do
|
62
|
-
subject { transactions.xnpv(0.6).round(2) }
|
63
|
-
|
64
|
-
it { is_expected.to eql -937.41 }
|
65
|
-
end
|
66
|
-
end
|
data/spec/decimal_spec.rb
DELETED
@@ -1,24 +0,0 @@
|
|
1
|
-
require_relative 'spec_helper'
|
2
|
-
require_relative '../lib/financial_calculator/decimal.rb'
|
3
|
-
|
4
|
-
describe "Numeric" do
|
5
|
-
let(:dec_num) { D('12.123') }
|
6
|
-
|
7
|
-
subject { dec_num }
|
8
|
-
|
9
|
-
describe 'it converts to a BigDecimal' do
|
10
|
-
subject { dec_num.convert_to BigDecimal }
|
11
|
-
|
12
|
-
it { is_expected.to be_a BigDecimal }
|
13
|
-
end
|
14
|
-
|
15
|
-
describe '#to_d' do
|
16
|
-
context 'when already a Flt::DecNum' do
|
17
|
-
let(:dec_num) { D('12.123') }
|
18
|
-
|
19
|
-
subject { dec_num.to_d }
|
20
|
-
|
21
|
-
it { is_expected.to be_a Flt::DecNum }
|
22
|
-
end
|
23
|
-
end
|
24
|
-
end
|