xirr 0.3.1 → 0.4.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/CHANGE_LOG.md +6 -0
- data/lib/xirr/cashflow.rb +33 -14
- data/lib/xirr/config.rb +7 -6
- data/lib/xirr/version.rb +1 -1
- data/test/test_cashflow.rb +42 -5
- 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: 50b1c197309bdd06705daf0badab16e913e3dd2b
|
4
|
+
data.tar.gz: 7a8b870eb1de0e1fdc0c6eead98de46c9e6084de
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 5d23ecb62b426a25d8d97341c40da0fc5a058c4a13d3945010511ef34bb1206430e7883199f49b28d836d7f98dfc8b8e5343dbe9f689bd7c5be17de4f26f6648
|
7
|
+
data.tar.gz: e91a1d141d2d64167e9b473921bb4e5f1f656e36953d5ffbecb0acb77aa81ea1f1e93010661231063a7035007d15528fc0b806af009ffe98bca17bf295488006
|
data/CHANGE_LOG.md
CHANGED
@@ -1,3 +1,9 @@
|
|
1
|
+
## Version 0.4.0
|
2
|
+
|
3
|
+
* Xirr returns nil and there is now a default settings to replace nil rate.
|
4
|
+
* It will compact the flow automatically, unless specified in the defaults.
|
5
|
+
* Attention to the new way, the Cashflow is created. Cashflow.new requires a Compacted boolean before the array of flow.
|
6
|
+
|
1
7
|
## Version 0.3.1
|
2
8
|
|
3
9
|
* Added fallback to secondary calculation method.
|
data/lib/xirr/cashflow.rb
CHANGED
@@ -11,11 +11,20 @@ module Xirr
|
|
11
11
|
# cf << Transaction.new(-1234, date: '2013-03-31'.to_date)
|
12
12
|
# Or
|
13
13
|
# cf = Cashflow.new Transaction.new( 1000, date: '2013-01-01'.to_date), Transaction.new(-1234, date: '2013-03-31'.to_date)
|
14
|
-
def initialize(*args)
|
14
|
+
def initialize(compacted = false, *args)
|
15
|
+
@compacted = compacted
|
15
16
|
args.each { |a| self << a }
|
16
17
|
self.flatten!
|
17
18
|
end
|
18
19
|
|
20
|
+
def compacted?
|
21
|
+
@compacted && self.count > uniq_dates.count
|
22
|
+
end
|
23
|
+
|
24
|
+
def uniq_dates
|
25
|
+
@uniq_dates = self.map(&:date).uniq
|
26
|
+
end
|
27
|
+
|
19
28
|
# Check if Cashflow is invalid
|
20
29
|
# @return [Boolean]
|
21
30
|
def invalid?
|
@@ -43,30 +52,40 @@ module Xirr
|
|
43
52
|
# Calculates a simple IRR guess based on period of investment and multiples.
|
44
53
|
# @return [Float]
|
45
54
|
def irr_guess
|
46
|
-
valid? ? ((multiple ** (1 / years_of_investment)) - 1).round(3) : false
|
55
|
+
@irr_guess = valid? ? ((multiple ** (1 / years_of_investment)) - 1).round(3) : false
|
56
|
+
@irr_guess == 1.0/0 ? 0.0 : @irr_guess
|
47
57
|
end
|
48
58
|
|
49
59
|
# @param guess [Float]
|
50
60
|
# @param method [Symbol]
|
51
61
|
# @return [Float]
|
52
62
|
# Finds the XIRR according to the method provided.
|
53
|
-
def xirr_with_exception(guess = nil, method = Xirr.config.default_method)
|
54
|
-
if
|
55
|
-
|
56
|
-
|
57
|
-
|
58
|
-
|
63
|
+
def xirr_with_exception(guess = nil, method = Xirr.config.default_method, compact = Xirr.config.compact)
|
64
|
+
raise ArgumentError, invalid_message if invalid?
|
65
|
+
xirr = choose_(method, compact).send(:xirr, guess) || choose_(other_calculation_method(method), compact).send(:xirr, guess)
|
66
|
+
xirr.nil? ? Xirr.config.replace_for_nil : xirr
|
67
|
+
end
|
68
|
+
|
69
|
+
def other_calculation_method(method)
|
70
|
+
method == :newton_method ? :bisection : :newton_method
|
71
|
+
end
|
72
|
+
|
73
|
+
def compact_cf
|
74
|
+
compact = Hash.new
|
75
|
+
uniq_dates.each { |date| compact[date] = 0 }
|
76
|
+
self.each { |flow| compact[flow.date] += flow.amount }
|
77
|
+
Cashflow.new(true, compact.map { |key, value| Transaction.new(value, date: key.to_date) })
|
59
78
|
end
|
60
79
|
|
61
80
|
# Calls XIRR but throws no exception and returns with 0
|
62
81
|
# @param guess [Float]
|
63
82
|
# @param method [Symbol]
|
64
83
|
# @return [Float]
|
65
|
-
def xirr(guess = nil, method = Xirr.config.default_method)
|
84
|
+
def xirr(guess = nil, method = Xirr.config.default_method, compact = Xirr.config.compact)
|
66
85
|
if invalid?
|
67
86
|
BigDecimal.new(0, Xirr::PRECISION)
|
68
87
|
else
|
69
|
-
xirr_with_exception(guess, method)
|
88
|
+
xirr_with_exception(guess, method, compact)
|
70
89
|
end
|
71
90
|
end
|
72
91
|
|
@@ -89,12 +108,12 @@ module Xirr
|
|
89
108
|
# @param method [Symbol]
|
90
109
|
# Choose a Method to call.
|
91
110
|
# @return [Class]
|
92
|
-
def choose_(method)
|
111
|
+
def choose_(method, compact)
|
93
112
|
case method
|
94
113
|
when :bisection
|
95
|
-
Bisection.new(self)
|
114
|
+
Bisection.new(compact && compacted? ? compact_cf : self)
|
96
115
|
when :newton_method
|
97
|
-
NewtonMethod.new(self)
|
116
|
+
NewtonMethod.new(compact && compacted? ? compact_cf : self)
|
98
117
|
else
|
99
118
|
raise ArgumentError, "There is no method called #{method} "
|
100
119
|
end
|
@@ -124,7 +143,7 @@ module Xirr
|
|
124
143
|
# Counts how many years from first to last transaction in the cashflow
|
125
144
|
# @return
|
126
145
|
def years_of_investment
|
127
|
-
(max_date - min_date) / (365)
|
146
|
+
(max_date - min_date) / (365.0)
|
128
147
|
end
|
129
148
|
|
130
149
|
# @api private
|
data/lib/xirr/config.rb
CHANGED
@@ -3,13 +3,14 @@ module Xirr
|
|
3
3
|
|
4
4
|
# Sets as constants all the entries in the Hash Default values
|
5
5
|
default_values = {
|
6
|
-
eps:
|
7
|
-
days_in_year:
|
6
|
+
eps: '1.0e-6'.to_f,
|
7
|
+
days_in_year: 365.0,
|
8
8
|
iteration_limit: 50,
|
9
|
-
precision:
|
10
|
-
|
11
|
-
|
12
|
-
|
9
|
+
precision: 6,
|
10
|
+
default_method: :newton_method,
|
11
|
+
fallback: true,
|
12
|
+
replace_for_nil: 0.0,
|
13
|
+
compact: true
|
13
14
|
}
|
14
15
|
|
15
16
|
# Iterates though default values and sets in config
|
data/lib/xirr/version.rb
CHANGED
data/test/test_cashflow.rb
CHANGED
@@ -129,7 +129,7 @@ describe 'Cashflows' do
|
|
129
129
|
assert true, !@cf.valid?
|
130
130
|
end
|
131
131
|
|
132
|
-
it 'returns 0 instead of
|
132
|
+
it 'returns 0 instead of exception ' do
|
133
133
|
assert_equal BigDecimal.new(0, 6), @cf.xirr
|
134
134
|
end
|
135
135
|
|
@@ -193,9 +193,7 @@ describe 'Cashflows' do
|
|
193
193
|
|
194
194
|
describe 'of a long investment' do
|
195
195
|
before(:all) do
|
196
|
-
@cf = Cashflow.new
|
197
|
-
@cf << Transaction.new(-1000, date: Date.new(1957, 1, 1))
|
198
|
-
@cf << Transaction.new(390000, date: Date.new(2013, 1, 1))
|
196
|
+
@cf = Cashflow.new false, Transaction.new(-1000, date: Date.new(1957, 1, 1)), Transaction.new(390000, date: Date.new(2013, 1, 1))
|
199
197
|
end
|
200
198
|
|
201
199
|
it 'has an Internal Rate of Return on Bisection Method' do
|
@@ -212,6 +210,25 @@ describe 'Cashflows' do
|
|
212
210
|
|
213
211
|
end
|
214
212
|
|
213
|
+
describe 'reapeated cashflow' do
|
214
|
+
before(:all) do
|
215
|
+
@cf = Cashflow.new
|
216
|
+
@cf << Transaction.new(1000.0, date: '2011-12-07'.to_date)
|
217
|
+
@cf << Transaction.new(2000.0, date: '2011-12-07'.to_date)
|
218
|
+
@cf << Transaction.new(-2000.0, date: '2013-05-21'.to_date)
|
219
|
+
@cf << Transaction.new(-4000.0, date: '2013-05-21'.to_date)
|
220
|
+
end
|
221
|
+
|
222
|
+
it 'has a compact cashflow' do
|
223
|
+
assert_equal 2, @cf.compact_cf.count
|
224
|
+
end
|
225
|
+
|
226
|
+
it 'sums all transactions' do
|
227
|
+
assert_equal -3000.0, @cf.compact_cf.map(&:amount).inject(&:+)
|
228
|
+
end
|
229
|
+
|
230
|
+
end
|
231
|
+
|
215
232
|
describe 'of a real case' do
|
216
233
|
before(:all) do
|
217
234
|
@cf = Cashflow.new
|
@@ -249,12 +266,32 @@ describe 'Cashflows' do
|
|
249
266
|
@cf << Transaction.new(84710.65, date: '2013-05-21'.to_date)
|
250
267
|
@cf << Transaction.new(-84710.65, date: '2013-05-21'.to_date)
|
251
268
|
@cf << Transaction.new(-144413.24, date: '2013-05-21'.to_date)
|
269
|
+
|
252
270
|
end
|
253
271
|
|
254
|
-
it '
|
272
|
+
it 'is a long and bad investment and newton generates an error' do
|
255
273
|
assert_equal '-0.99'.to_f, @cf.xirr(nil, :newton_method)
|
256
274
|
end
|
257
275
|
|
276
|
+
it 'compacted ' do
|
277
|
+
cf = @cf
|
278
|
+
cf << Transaction.new(-1000000.0, date: '2013-05-21'.to_date)
|
279
|
+
assert_kind_of(Cashflow, cf.compact_cf)
|
280
|
+
assert_equal -0.885744, cf.xirr(nil, :newton_method, true)
|
281
|
+
assert_equal -0.885744, cf.xirr(nil, :bisection, true)
|
282
|
+
end
|
283
|
+
|
284
|
+
it 'has zero for years of investment' do
|
285
|
+
cf = Cashflow.new false, Transaction.new(105187.06, date: '2011-12-07'.to_date), Transaction.new(-105187.06 * 1.0697668105671994, date: '2011-12-07'.to_date)
|
286
|
+
assert_equal 0.0, cf.irr_guess
|
287
|
+
assert_equal Xirr.config.replace_for_nil, cf.xirr(nil, :newton_method)
|
288
|
+
end
|
289
|
+
|
290
|
+
it 'has is valid even if compacted' do
|
291
|
+
cf = Cashflow.new false, Transaction.new(100, date: Date.today), Transaction.new(-100, date: Date.today)
|
292
|
+
assert_equal 0.0, cf.xirr(nil, :newton_method, true)
|
293
|
+
end
|
294
|
+
|
258
295
|
end
|
259
296
|
|
260
297
|
end
|