xirr 0.3.0 → 0.3.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/.travis.yml +1 -1
- data/CHANGE_LOG.md +4 -0
- data/lib/xirr/base.rb +7 -6
- data/lib/xirr/bisection.rb +2 -1
- data/lib/xirr/cashflow.rb +7 -19
- data/lib/xirr/config.rb +4 -3
- data/lib/xirr/newton_method.rb +10 -6
- data/lib/xirr/version.rb +1 -1
- data/test/test_cashflow.rb +60 -11
- data/xirr.gemspec +13 -13
- metadata +8 -8
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA1:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: bfe1ad8aa4e571c85347827c80894312fcc0c2e1
|
4
|
+
data.tar.gz: dd2705ba456cccc2b6e01f1d10febdaf65cd7aef
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 5e6b8f6b854db434c1e295f5421adaa5b4860c254e76bf7e38663a666c1cac34a925ccc11b44ebefd8f49ff72d2f2082275d7251432360341c0d7efbb2b8a60d
|
7
|
+
data.tar.gz: edd72d68efe8fc75192d01b1ad432b6f88b94c8d5c9767b7141a8ffa28bdb9148bcfa4b19d03501e3f7ecc8ea12e219c2ac6e785d0410c15c5d3884028d6388c
|
data/.travis.yml
CHANGED
data/CHANGE_LOG.md
CHANGED
data/lib/xirr/base.rb
CHANGED
@@ -19,8 +19,9 @@ module Xirr
|
|
19
19
|
(date - cf.min_date) / Xirr::DAYS_IN_YEAR
|
20
20
|
end
|
21
21
|
|
22
|
-
# Net Present Value
|
22
|
+
# Net Present Value function that will be used to reduce the cashflow
|
23
23
|
# @param rate [BigDecimal]
|
24
|
+
# @return [BigDecimal]
|
24
25
|
def xnpv(rate)
|
25
26
|
cf.inject(0) do |sum, t|
|
26
27
|
# sum += t.amount / (1 + rate) ** t_in_days(t.date)
|
@@ -28,13 +29,13 @@ module Xirr
|
|
28
29
|
end
|
29
30
|
end
|
30
31
|
|
31
|
-
inline
|
32
|
-
builder.include
|
33
|
-
builder.c
|
32
|
+
inline { |builder|
|
33
|
+
builder.include '<math.h>'
|
34
|
+
builder.c '
|
34
35
|
double xnpv_c(double rate, double amount, double days) {
|
35
36
|
return amount / pow(1 + rate, days);
|
36
|
-
}
|
37
|
-
|
37
|
+
}'
|
38
|
+
}
|
38
39
|
|
39
40
|
end
|
40
41
|
end
|
data/lib/xirr/bisection.rb
CHANGED
@@ -32,7 +32,8 @@ module Xirr
|
|
32
32
|
|
33
33
|
# If enabled, will retry XIRR with NewtonMethod
|
34
34
|
if Xirr::FALLBACK && right_limit_reached?(midpoint)
|
35
|
-
return NewtonMethod.new(cf).xirr
|
35
|
+
# return NewtonMethod.new(cf).xirr
|
36
|
+
return nil
|
36
37
|
end
|
37
38
|
|
38
39
|
return midpoint.round Xirr::PRECISION
|
data/lib/xirr/cashflow.rb
CHANGED
@@ -49,10 +49,10 @@ module Xirr
|
|
49
49
|
# @param guess [Float]
|
50
50
|
# @param method [Symbol]
|
51
51
|
# @return [Float]
|
52
|
-
# Finds the XIRR according to the method provided.
|
52
|
+
# Finds the XIRR according to the method provided.
|
53
53
|
def xirr_with_exception(guess = nil, method = Xirr.config.default_method)
|
54
54
|
if valid?
|
55
|
-
choose_(method).send :xirr, guess
|
55
|
+
choose_(method).send(:xirr, guess) || choose_(method == :newton_method ? :bisection : :newton_method).send(:xirr, guess)
|
56
56
|
else
|
57
57
|
raise ArgumentError, invalid_message
|
58
58
|
end
|
@@ -103,7 +103,7 @@ module Xirr
|
|
103
103
|
# @api private
|
104
104
|
# Sorts the {Cashflow} by date ascending
|
105
105
|
# and finds the signal of the first transaction.
|
106
|
-
# This implies the first transaction is a
|
106
|
+
# This implies the first transaction is a disbursement
|
107
107
|
# @return [Integer]
|
108
108
|
def first_transaction_direction
|
109
109
|
self.sort! { |x, y| x.date <=> y.date }
|
@@ -130,29 +130,17 @@ module Xirr
|
|
130
130
|
# @api private
|
131
131
|
# @return [Array]
|
132
132
|
# @see #negatives
|
133
|
-
#
|
134
|
-
# Finds all transactions income from Cashflow
|
133
|
+
# Selects all positives transactions from Cashflow
|
135
134
|
def positives
|
136
|
-
|
137
|
-
@positives
|
135
|
+
@positives ||= self.select { |x| x.amount < 0 }
|
138
136
|
end
|
139
137
|
|
140
138
|
# @api private
|
141
139
|
# @return [Array]
|
142
140
|
# @see #positives
|
143
|
-
#
|
144
|
-
# Finds all transactions investments from Cashflow
|
141
|
+
# Selects all negatives transactions from Cashflow
|
145
142
|
def negatives
|
146
|
-
|
147
|
-
@negatives
|
148
|
-
end
|
149
|
-
|
150
|
-
# @api private
|
151
|
-
# @see #positives
|
152
|
-
# @see #negatives
|
153
|
-
# Uses partition to separate the investment transactions Negatives and the income transactions (Positives)
|
154
|
-
def split_transactions
|
155
|
-
@negatives, @positives = self.partition { |x| x.amount > 0 } # Inverted as negative amount is good
|
143
|
+
@negatives ||= self.select { |x| x.amount > 0 }
|
156
144
|
end
|
157
145
|
|
158
146
|
end
|
data/lib/xirr/config.rb
CHANGED
@@ -7,13 +7,14 @@ module Xirr
|
|
7
7
|
days_in_year: 365,
|
8
8
|
iteration_limit: 50,
|
9
9
|
precision: 6,
|
10
|
-
default_method: :bisection,
|
10
|
+
# default_method: :bisection,
|
11
|
+
default_method: :newton_method,
|
11
12
|
fallback: true
|
12
13
|
}
|
13
14
|
|
14
|
-
# Iterates
|
15
|
+
# Iterates though default values and sets in config
|
15
16
|
default_values.each do |key, value|
|
16
17
|
self.config.send("#{key.to_sym}=", value)
|
17
18
|
const_set key.to_s.upcase.to_sym, value
|
18
19
|
end
|
19
|
-
end
|
20
|
+
end
|
data/lib/xirr/newton_method.rb
CHANGED
@@ -11,10 +11,10 @@ module Xirr
|
|
11
11
|
class Function
|
12
12
|
values = {
|
13
13
|
eps: Xirr::EPS,
|
14
|
-
one:
|
15
|
-
two:
|
16
|
-
ten:
|
17
|
-
zero:
|
14
|
+
one: '1.0',
|
15
|
+
two: '2.0',
|
16
|
+
ten: '10.0',
|
17
|
+
zero: '0.0'
|
18
18
|
}
|
19
19
|
|
20
20
|
# define default values
|
@@ -46,8 +46,12 @@ module Xirr
|
|
46
46
|
def xirr(guess=nil)
|
47
47
|
func = Function.new(self, :xnpv)
|
48
48
|
rate = [guess || cf.irr_guess.to_f]
|
49
|
-
|
50
|
-
|
49
|
+
begin
|
50
|
+
nlsolve(func, rate)
|
51
|
+
rate[0].round Xirr::PRECISION
|
52
|
+
rescue
|
53
|
+
nil
|
54
|
+
end
|
51
55
|
end
|
52
56
|
end
|
53
57
|
end
|
data/lib/xirr/version.rb
CHANGED
data/test/test_cashflow.rb
CHANGED
@@ -2,7 +2,7 @@ require_relative 'test_helper'
|
|
2
2
|
|
3
3
|
describe 'Cashflows' do
|
4
4
|
|
5
|
-
describe 'of
|
5
|
+
describe 'of an ok investment' do
|
6
6
|
before(:all) do
|
7
7
|
@cf = Cashflow.new
|
8
8
|
@cf << Transaction.new(1000, date: '1985-01-01'.to_date)
|
@@ -10,6 +10,10 @@ describe 'Cashflows' do
|
|
10
10
|
@cf << Transaction.new(-6000, date: '1995-01-01'.to_date)
|
11
11
|
end
|
12
12
|
|
13
|
+
it 'has a sum of its transactions' do
|
14
|
+
assert_equal '-5600'.to_f, @cf.sum
|
15
|
+
end
|
16
|
+
|
13
17
|
it 'with a wrong method is invalid' do
|
14
18
|
assert_raises(ArgumentError) { @cf.xirr(nil, :no_method) }
|
15
19
|
end
|
@@ -18,12 +22,12 @@ describe 'Cashflows' do
|
|
18
22
|
assert_equal '0.225683'.to_f, @cf.xirr
|
19
23
|
end
|
20
24
|
|
21
|
-
it 'has an Internal Rate of Return on
|
25
|
+
it 'has an Internal Rate of Return on Bisection Method' do
|
22
26
|
assert_equal '0.225683'.to_f, @cf.xirr(nil, :bisection)
|
23
27
|
end
|
24
28
|
|
25
29
|
it 'has an Internal Rate of Return on Bisection Method using a Guess' do
|
26
|
-
assert_in_delta 0.225683, @cf.xirr(0.15).to_f, 0.000002
|
30
|
+
assert_in_delta '0.225683'.to_f, @cf.xirr(0.15).to_f, 0.000002
|
27
31
|
end
|
28
32
|
|
29
33
|
it 'has an Internal Rate of Return on Newton Method' do
|
@@ -49,11 +53,11 @@ describe 'Cashflows' do
|
|
49
53
|
end
|
50
54
|
|
51
55
|
it 'has an Internal Rate of Return on Bisection Method' do
|
52
|
-
assert_equal '0.225683'.to_f, @cf.xirr
|
56
|
+
assert_equal '0.225683'.to_f, @cf.xirr(nil, :bisection)
|
53
57
|
end
|
54
58
|
|
55
59
|
it 'has an Internal Rate of Return on Bisection Method using a Guess' do
|
56
|
-
assert_in_delta 0.225683, @cf.xirr(0.15).to_f, 0.000002
|
60
|
+
assert_in_delta '0.225683'.to_f, @cf.xirr(0.15).to_f, 0.000002
|
57
61
|
end
|
58
62
|
|
59
63
|
it 'has an Internal Rate of Return on Newton Method' do
|
@@ -74,7 +78,7 @@ describe 'Cashflows' do
|
|
74
78
|
end
|
75
79
|
|
76
80
|
it 'has an Internal Rate of Return on Bisection Method' do
|
77
|
-
assert_equal '22.352206 '.to_f, @cf.xirr
|
81
|
+
assert_equal '22.352206 '.to_f, @cf.xirr(nil, :bisection)
|
78
82
|
end
|
79
83
|
|
80
84
|
it 'has a sum of its transactions' do
|
@@ -98,11 +102,11 @@ describe 'Cashflows' do
|
|
98
102
|
end
|
99
103
|
|
100
104
|
it 'has an Internal Rate of Return on Bisection Method' do
|
101
|
-
assert_equal '1.0597572345993451e+284'.to_f, @cf.xirr
|
105
|
+
assert_equal '1.0597572345993451e+284'.to_f, @cf.xirr(nil, :bisection)
|
102
106
|
end
|
103
107
|
|
104
108
|
it 'has an Internal Rate of Return on Bisection Method using a bad Guess' do
|
105
|
-
assert_raises(ArgumentError) { @cf.xirr(0.15) }
|
109
|
+
assert_raises(ArgumentError) { @cf.xirr(0.15, :bisection) }
|
106
110
|
end
|
107
111
|
|
108
112
|
it 'has an Internal Rate of Return on Newton Method' do
|
@@ -155,11 +159,11 @@ describe 'Cashflows' do
|
|
155
159
|
assert true, !@cf.valid?
|
156
160
|
end
|
157
161
|
|
158
|
-
it 'raises error when xirr is called' do
|
162
|
+
it 'raises error when #xirr is called' do
|
159
163
|
assert_raises(ArgumentError) { @cf.xirr_with_exception }
|
160
164
|
end
|
161
165
|
|
162
|
-
it '
|
166
|
+
it 'is invalid when #irr_guess is called' do
|
163
167
|
assert true, !@cf.irr_guess
|
164
168
|
end
|
165
169
|
end
|
@@ -195,7 +199,7 @@ describe 'Cashflows' do
|
|
195
199
|
end
|
196
200
|
|
197
201
|
it 'has an Internal Rate of Return on Bisection Method' do
|
198
|
-
assert_equal '0.112339'.to_f, @cf.xirr
|
202
|
+
assert_equal '0.112339'.to_f, @cf.xirr(nil, :bisection)
|
199
203
|
end
|
200
204
|
|
201
205
|
it 'has an Internal Rate of Return on Newton Method' do
|
@@ -208,4 +212,49 @@ describe 'Cashflows' do
|
|
208
212
|
|
209
213
|
end
|
210
214
|
|
215
|
+
describe 'of a real case' do
|
216
|
+
before(:all) do
|
217
|
+
@cf = Cashflow.new
|
218
|
+
@cf << Transaction.new(105187.06, date: '2011-12-07'.to_date)
|
219
|
+
@cf << Transaction.new(816709.66, date: '2011-12-07'.to_date)
|
220
|
+
@cf << Transaction.new(479069.684, date: '2011-12-07'.to_date)
|
221
|
+
@cf << Transaction.new(937309.708, date: '2012-01-18'.to_date)
|
222
|
+
@cf << Transaction.new(88622.661, date: '2012-07-03'.to_date)
|
223
|
+
@cf << Transaction.new(100000.0, date: '2012-07-03'.to_date)
|
224
|
+
@cf << Transaction.new(80000.0, date: '2012-07-19'.to_date)
|
225
|
+
@cf << Transaction.new(403627.95, date: '2012-07-23'.to_date)
|
226
|
+
@cf << Transaction.new(508117.9, date: '2012-07-23'.to_date)
|
227
|
+
@cf << Transaction.new(789706.87, date: '2012-07-23'.to_date)
|
228
|
+
@cf << Transaction.new(-88622.661, date: '2012-09-11'.to_date)
|
229
|
+
@cf << Transaction.new(-789706.871, date: '2012-09-11'.to_date)
|
230
|
+
@cf << Transaction.new(-688117.9, date: '2012-09-11'.to_date)
|
231
|
+
@cf << Transaction.new(-403627.95, date: '2012-09-11'.to_date)
|
232
|
+
@cf << Transaction.new(403627.95, date: '2012-09-12'.to_date)
|
233
|
+
@cf << Transaction.new(789706.871, date: '2012-09-12'.to_date)
|
234
|
+
@cf << Transaction.new(88622.661, date: '2012-09-12'.to_date)
|
235
|
+
@cf << Transaction.new(688117.9, date: '2012-09-12'.to_date)
|
236
|
+
@cf << Transaction.new(45129.14, date: '2013-03-11'.to_date)
|
237
|
+
@cf << Transaction.new(26472.08, date: '2013-03-11'.to_date)
|
238
|
+
@cf << Transaction.new(51793.2, date: '2013-03-11'.to_date)
|
239
|
+
@cf << Transaction.new(126605.59, date: '2013-03-11'.to_date)
|
240
|
+
@cf << Transaction.new(278532.29, date: '2013-03-28'.to_date)
|
241
|
+
@cf << Transaction.new(99284.1, date: '2013-03-28'.to_date)
|
242
|
+
@cf << Transaction.new(58238.57, date: '2013-03-28'.to_date)
|
243
|
+
@cf << Transaction.new(113945.03, date: '2013-03-28'.to_date)
|
244
|
+
@cf << Transaction.new(405137.88, date: '2013-05-21'.to_date)
|
245
|
+
@cf << Transaction.new(-405137.88, date: '2013-05-21'.to_date)
|
246
|
+
@cf << Transaction.new(165738.23, date: '2013-05-21'.to_date)
|
247
|
+
@cf << Transaction.new(-165738.23, date: '2013-05-21'.to_date)
|
248
|
+
@cf << Transaction.new(144413.24, date: '2013-05-21'.to_date)
|
249
|
+
@cf << Transaction.new(84710.65, date: '2013-05-21'.to_date)
|
250
|
+
@cf << Transaction.new(-84710.65, date: '2013-05-21'.to_date)
|
251
|
+
@cf << Transaction.new(-144413.24, date: '2013-05-21'.to_date)
|
252
|
+
end
|
253
|
+
|
254
|
+
it 'has an Internal Rate of Return on Newton Method HERE' do
|
255
|
+
assert_equal '-0.99'.to_f, @cf.xirr(nil, :newton_method)
|
256
|
+
end
|
257
|
+
|
258
|
+
end
|
259
|
+
|
211
260
|
end
|
data/xirr.gemspec
CHANGED
@@ -4,26 +4,26 @@ $LOAD_PATH.unshift(lib) unless $LOAD_PATH.include?(lib)
|
|
4
4
|
require 'xirr/version'
|
5
5
|
|
6
6
|
Gem::Specification.new do |spec|
|
7
|
-
spec.name
|
8
|
-
spec.version
|
9
|
-
spec.authors
|
10
|
-
spec.email
|
11
|
-
spec.summary
|
12
|
-
spec.description = %q{Calculates IRR of a Cashflow,
|
13
|
-
spec.homepage
|
14
|
-
spec.license
|
7
|
+
spec.name = 'xirr'
|
8
|
+
spec.version = Xirr::VERSION
|
9
|
+
spec.authors = ['tubedude']
|
10
|
+
spec.email = ['beto@trevisan.me']
|
11
|
+
spec.summary = %q{Calculates XIRR (Bisection and Newton method) of a cashflow}
|
12
|
+
spec.description = %q{Calculates IRR of a Cashflow, similar to Excel's, XIRR formula. It defaults to Newton Method, but will calculate Bisection as well.}
|
13
|
+
spec.homepage = 'https://github.com/tubedude/xirr'
|
14
|
+
spec.license = 'MIT'
|
15
15
|
|
16
|
-
spec.files
|
17
|
-
spec.executables
|
18
|
-
spec.test_files
|
16
|
+
spec.files = `git ls-files -z`.split("\x0")
|
17
|
+
spec.executables = spec.files.grep(%r{^bin/}) { |f| File.basename(f) }
|
18
|
+
spec.test_files = spec.files.grep(%r{^(test|spec|features)/})
|
19
19
|
spec.require_paths = ['lib']
|
20
20
|
|
21
21
|
spec.add_development_dependency 'bundler', '~> 1.6'
|
22
22
|
spec.add_development_dependency 'rake', '~> 10'
|
23
23
|
|
24
24
|
spec.required_ruby_version = '>=2.0'
|
25
|
-
spec.add_dependency 'activesupport', '~> 4
|
26
|
-
spec.add_dependency 'RubyInline', '
|
25
|
+
spec.add_dependency 'activesupport', '~> 4'
|
26
|
+
spec.add_dependency 'RubyInline', '~> 3'
|
27
27
|
spec.add_development_dependency 'minitest', '~> 5.4'
|
28
28
|
spec.add_development_dependency 'coveralls', '~> 0'
|
29
29
|
|
metadata
CHANGED
@@ -1,14 +1,14 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: xirr
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 0.3.
|
4
|
+
version: 0.3.1
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- tubedude
|
8
8
|
autorequire:
|
9
9
|
bindir: bin
|
10
10
|
cert_chain: []
|
11
|
-
date: 2014-11-
|
11
|
+
date: 2014-11-19 00:00:00.000000000 Z
|
12
12
|
dependencies:
|
13
13
|
- !ruby/object:Gem::Dependency
|
14
14
|
name: bundler
|
@@ -44,26 +44,26 @@ dependencies:
|
|
44
44
|
requirements:
|
45
45
|
- - "~>"
|
46
46
|
- !ruby/object:Gem::Version
|
47
|
-
version: '4
|
47
|
+
version: '4'
|
48
48
|
type: :runtime
|
49
49
|
prerelease: false
|
50
50
|
version_requirements: !ruby/object:Gem::Requirement
|
51
51
|
requirements:
|
52
52
|
- - "~>"
|
53
53
|
- !ruby/object:Gem::Version
|
54
|
-
version: '4
|
54
|
+
version: '4'
|
55
55
|
- !ruby/object:Gem::Dependency
|
56
56
|
name: RubyInline
|
57
57
|
requirement: !ruby/object:Gem::Requirement
|
58
58
|
requirements:
|
59
|
-
- - "
|
59
|
+
- - "~>"
|
60
60
|
- !ruby/object:Gem::Version
|
61
61
|
version: '3'
|
62
62
|
type: :runtime
|
63
63
|
prerelease: false
|
64
64
|
version_requirements: !ruby/object:Gem::Requirement
|
65
65
|
requirements:
|
66
|
-
- - "
|
66
|
+
- - "~>"
|
67
67
|
- !ruby/object:Gem::Version
|
68
68
|
version: '3'
|
69
69
|
- !ruby/object:Gem::Dependency
|
@@ -94,8 +94,8 @@ dependencies:
|
|
94
94
|
- - "~>"
|
95
95
|
- !ruby/object:Gem::Version
|
96
96
|
version: '0'
|
97
|
-
description: Calculates IRR of a Cashflow,
|
98
|
-
|
97
|
+
description: Calculates IRR of a Cashflow, similar to Excel's, XIRR formula. It defaults
|
98
|
+
to Newton Method, but will calculate Bisection as well.
|
99
99
|
email:
|
100
100
|
- beto@trevisan.me
|
101
101
|
executables: []
|