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 CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA1:
3
- metadata.gz: bfe1ad8aa4e571c85347827c80894312fcc0c2e1
4
- data.tar.gz: dd2705ba456cccc2b6e01f1d10febdaf65cd7aef
3
+ metadata.gz: 50b1c197309bdd06705daf0badab16e913e3dd2b
4
+ data.tar.gz: 7a8b870eb1de0e1fdc0c6eead98de46c9e6084de
5
5
  SHA512:
6
- metadata.gz: 5e6b8f6b854db434c1e295f5421adaa5b4860c254e76bf7e38663a666c1cac34a925ccc11b44ebefd8f49ff72d2f2082275d7251432360341c0d7efbb2b8a60d
7
- data.tar.gz: edd72d68efe8fc75192d01b1ad432b6f88b94c8d5c9767b7141a8ffa28bdb9148bcfa4b19d03501e3f7ecc8ea12e219c2ac6e785d0410c15c5d3884028d6388c
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 valid?
55
- choose_(method).send(:xirr, guess) || choose_(method == :newton_method ? :bisection : :newton_method).send(:xirr, guess)
56
- else
57
- raise ArgumentError, invalid_message
58
- end
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).to_f
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: '1.0e-6'.to_f,
7
- days_in_year: 365,
6
+ eps: '1.0e-6'.to_f,
7
+ days_in_year: 365.0,
8
8
  iteration_limit: 50,
9
- precision: 6,
10
- # default_method: :bisection,
11
- default_method: :newton_method,
12
- fallback: true
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
@@ -1,4 +1,4 @@
1
1
  module Xirr
2
2
  # Version of the Gem
3
- VERSION = '0.3.1'
3
+ VERSION = '0.4.0'
4
4
  end
@@ -129,7 +129,7 @@ describe 'Cashflows' do
129
129
  assert true, !@cf.valid?
130
130
  end
131
131
 
132
- it 'returns 0 instead of expection ' do
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 'has an Internal Rate of Return on Newton Method HERE' do
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
metadata CHANGED
@@ -1,7 +1,7 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: xirr
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.3.1
4
+ version: 0.4.0
5
5
  platform: ruby
6
6
  authors:
7
7
  - tubedude