xirr 0.2.1 → 0.2.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/lib/xirr/bisection.rb +5 -9
- data/lib/xirr/cashflow.rb +8 -4
- data/lib/xirr/transaction.rb +1 -1
- data/lib/xirr/version.rb +1 -1
- data/test/test_cashflow.rb +66 -3
- data/test/test_helper.rb +18 -0
- data/test/test_transactions.rb +4 -0
- metadata +1 -1
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA1:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 408c3bf957bc9651c987e8677e997866cfd170bc
|
4
|
+
data.tar.gz: 7b4ffaef9837569c22bd86f97a878b172d3284c9
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 6dc5b68ea4821d65b771e4d9ae697a7a24c456d10eabe33638ebf67d1b9c37273d85c838200ac47539238197350def7e20501a7233fb820ff9adf006c91518af
|
7
|
+
data.tar.gz: d79dba2d39dfce72b0d5c9fc5844e6768160c83f73019525b2e2c9418866d2efea5d5145b0cca808f0afa73101fe7962bfb04aaa77cf2e28c8e2f38d2b1d0295
|
data/lib/xirr/bisection.rb
CHANGED
@@ -10,29 +10,25 @@ module Xirr
|
|
10
10
|
# An initial guess rate will override the {Cashflow#irr_guess}
|
11
11
|
def xirr(midpoint = nil)
|
12
12
|
|
13
|
-
# Raises error if Cashflow is not valid
|
14
|
-
cf.valid?
|
15
|
-
|
16
|
-
# Bisection method finding the rate to zero nfv
|
17
|
-
|
18
13
|
# Initial values
|
19
14
|
left = BigDecimal.new -0.99, Xirr::PRECISION
|
20
15
|
right = BigDecimal.new 9.99, Xirr::PRECISION
|
21
|
-
eps = Xirr::EPS
|
22
16
|
midpoint ||= cf.irr_guess
|
23
|
-
limit = Xirr.config.iteration_limit.to_i
|
24
17
|
runs = 0
|
25
18
|
|
26
19
|
# Loops until difference is within error margin
|
27
|
-
while ((right-left).abs >
|
20
|
+
while ((right - left).abs > Xirr::EPS && runs < Xirr.config.iteration_limit.to_i) do
|
28
21
|
|
29
|
-
raise 'Did not converge' if runs == limit
|
30
22
|
runs += 1
|
31
23
|
npv_positive?(midpoint) ? right = midpoint : left = midpoint
|
32
24
|
midpoint = format_irr(left, right)
|
33
25
|
|
34
26
|
end
|
35
27
|
|
28
|
+
if runs >= Xirr.config.iteration_limit.to_i
|
29
|
+
raise ArgumentError, 'Did not converge'
|
30
|
+
end
|
31
|
+
|
36
32
|
return midpoint.round Xirr::PRECISION
|
37
33
|
|
38
34
|
end
|
data/lib/xirr/cashflow.rb
CHANGED
@@ -47,7 +47,7 @@ module Xirr
|
|
47
47
|
# Calculates a simple IRR guess based on period of investment and multiples.
|
48
48
|
# @return [Float]
|
49
49
|
def irr_guess
|
50
|
-
((multiple ** (1 / years_of_investment)) - 1).round(3)
|
50
|
+
((multiple ** (1 / years_of_investment)) - 1).round(3) if valid?
|
51
51
|
end
|
52
52
|
|
53
53
|
# @param guess [Float]
|
@@ -55,7 +55,12 @@ module Xirr
|
|
55
55
|
# @return [Float]
|
56
56
|
# Finds the XIRR according to the method provided. Default to Bisection
|
57
57
|
def xirr(guess = nil, method = Xirr.config.default_method)
|
58
|
-
method == :bisection
|
58
|
+
_method = if method == :bisection
|
59
|
+
Bisection.new(self)
|
60
|
+
else
|
61
|
+
NewtonMethod.new(self)
|
62
|
+
end
|
63
|
+
_method.send :xirr, guess if valid?
|
59
64
|
end
|
60
65
|
|
61
66
|
# First investment date
|
@@ -91,7 +96,6 @@ module Xirr
|
|
91
96
|
# @return
|
92
97
|
def years_of_investment
|
93
98
|
(max_date - min_date) / (365).to_f
|
94
|
-
# (max_date - min_date) / (365 * 24 * 60 * 60).to_f
|
95
99
|
end
|
96
100
|
|
97
101
|
# @api private
|
@@ -127,7 +131,7 @@ module Xirr
|
|
127
131
|
# Error message depending on the missing transaction
|
128
132
|
def invalid_message
|
129
133
|
return 'No positive transaction' if positives.empty?
|
130
|
-
return 'No
|
134
|
+
return 'No negative transaction' if negatives.empty?
|
131
135
|
end
|
132
136
|
|
133
137
|
end
|
data/lib/xirr/transaction.rb
CHANGED
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 a
|
5
|
+
describe 'of a 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)
|
@@ -26,15 +26,77 @@ describe 'Cashflows' do
|
|
26
26
|
assert_equal '0.208'.to_f, @cf.irr_guess
|
27
27
|
end
|
28
28
|
end
|
29
|
-
|
30
|
-
|
29
|
+
|
30
|
+
describe 'of a good investment' do
|
31
|
+
before(:all) do
|
32
|
+
@cf = Cashflow.new
|
33
|
+
@cf << Transaction.new(1000, date: '1985-01-01'.to_date)
|
34
|
+
@cf << Transaction.new(-6000, date: '1985-01-02'.to_date)
|
35
|
+
end
|
36
|
+
|
37
|
+
it 'has an Internal Rate of Return on Bisection Method' do
|
38
|
+
assert_equal '1.0597572345993451e+284'.to_f, @cf.xirr
|
39
|
+
end
|
40
|
+
|
41
|
+
=begin
|
42
|
+
# FIXME Check why limit is not being enforced
|
43
|
+
it 'has an Internal Rate of Return on Bisection Method using a Guess' do
|
44
|
+
assert_equal '0.225683'.to_f, @cf.xirr(0.15)
|
45
|
+
end
|
46
|
+
=end
|
47
|
+
|
48
|
+
it 'has an Internal Rate of Return on Newton Method' do
|
49
|
+
assert_equal '1.0597572345993451e+284'.to_f, @cf.xirr(nil, :newton_method)
|
50
|
+
end
|
51
|
+
|
52
|
+
it 'has an educated guess' do
|
53
|
+
assert_equal '1.0597572345993451e+284'.to_f, @cf.irr_guess
|
54
|
+
end
|
55
|
+
end
|
56
|
+
|
57
|
+
describe 'an all-negative Cashflow' do
|
58
|
+
before(:all) do
|
31
59
|
@cf = Cashflow.new
|
32
60
|
@cf << Transaction.new(-600, date: '1990-01-01'.to_date)
|
33
61
|
@cf << Transaction.new(-600, date: '1995-01-01'.to_date)
|
62
|
+
end
|
63
|
+
|
64
|
+
it 'is invalid' do
|
65
|
+
assert_raises(ArgumentError) { @cf.valid? }
|
66
|
+
end
|
67
|
+
|
68
|
+
it 'raises error when xirr is called' do
|
69
|
+
assert_raises(ArgumentError) { @cf.xirr }
|
70
|
+
end
|
71
|
+
|
72
|
+
it 'raises error when xirr is called' do
|
73
|
+
assert_raises(ArgumentError) { @cf.irr_guess }
|
74
|
+
end
|
75
|
+
|
76
|
+
end
|
77
|
+
|
78
|
+
describe 'an all-positive Cashflow' do
|
79
|
+
before(:all) do
|
80
|
+
@cf = Cashflow.new
|
81
|
+
@cf << Transaction.new(600, date: '1990-01-01'.to_date)
|
82
|
+
@cf << Transaction.new(600, date: '1995-01-01'.to_date)
|
83
|
+
end
|
84
|
+
|
85
|
+
it 'is invalid' do
|
34
86
|
assert_raises(ArgumentError) { @cf.valid? }
|
35
87
|
end
|
88
|
+
|
89
|
+
it 'raises error when xirr is called' do
|
90
|
+
assert_raises(ArgumentError) { @cf.xirr }
|
91
|
+
end
|
92
|
+
|
93
|
+
it 'raises error when xirr is called' do
|
94
|
+
assert_raises(ArgumentError) { @cf.irr_guess }
|
95
|
+
end
|
36
96
|
end
|
97
|
+
|
37
98
|
describe 'of a bad investment' do
|
99
|
+
|
38
100
|
before(:all) do
|
39
101
|
@cf = Cashflow.new
|
40
102
|
@cf << Transaction.new(1000, date: '1985-01-01'.to_date)
|
@@ -55,6 +117,7 @@ describe 'Cashflows' do
|
|
55
117
|
end
|
56
118
|
|
57
119
|
end
|
120
|
+
|
58
121
|
describe 'of a long investment' do
|
59
122
|
before(:all) do
|
60
123
|
@cf = Cashflow.new
|
data/test/test_helper.rb
CHANGED
@@ -13,3 +13,21 @@ require 'xirr/newton_method.rb'
|
|
13
13
|
require 'xirr/cashflow.rb'
|
14
14
|
require 'xirr/transaction.rb'
|
15
15
|
include Xirr
|
16
|
+
|
17
|
+
=begin
|
18
|
+
|
19
|
+
require 'active_support/all'
|
20
|
+
require_relative 'lib/xirr.rb'
|
21
|
+
require_relative 'lib/xirr/config.rb'
|
22
|
+
require_relative 'lib/xirr/base.rb'
|
23
|
+
require_relative 'lib/xirr/bisection.rb'
|
24
|
+
require_relative 'lib/xirr/newton_method.rb'
|
25
|
+
require_relative 'lib/xirr/cashflow.rb'
|
26
|
+
require_relative 'lib/xirr/transaction.rb'
|
27
|
+
include Xirr
|
28
|
+
cf = Cashflow.new
|
29
|
+
cf << Transaction.new(1000, date: '1985-01-01'.to_date)
|
30
|
+
cf << Transaction.new(-6000, date: '1985-01-02'.to_date)
|
31
|
+
cf.xirr(0.15)
|
32
|
+
|
33
|
+
=end
|
data/test/test_transactions.rb
CHANGED