xirr 0.1.1 → 0.2.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 CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA1:
3
- metadata.gz: ac4ff922b29d910ccb51a3238c6602d0309bc685
4
- data.tar.gz: 912024facef6c25c236f99f8e86fa0ee75efa496
3
+ metadata.gz: 4503151b19d0e3540c2f88066fcbefe12799d70d
4
+ data.tar.gz: 70a4f1a010d6d4a084babadeb326d59f02e7d284
5
5
  SHA512:
6
- metadata.gz: 68610190f330d76c57f1b638d92210002400a4011387816b1cb369c502055a7e2922545cfd2dfc997abcd0c560b0db4e48e4248342d0a09ee1eb6206b7eb8be3
7
- data.tar.gz: b31417b22db2501bf5547610340c38031d8d83d372832335196d286ec51893968cb62e94f03b73cb61dca813e469bca4310e671fd5e51bf7967a7347cceece36
6
+ metadata.gz: f433267dae83354352f38d3b861ae02a2b7d0d83ef009b350ea059a8d479f8f323b05f9ba197b16c2b363c3db5e2ce2ed23d05d53008749ec87276647d118dcd
7
+ data.tar.gz: 59c0511149b7473f4fee70eddfb1cb67f8bc060fd5cea1255e110724448cc15eb9f6501e83d9b3ecefbc8dc863cfbe3f02c82cf2f50fafe36fb05ecf2ba3aa51
@@ -0,0 +1,13 @@
1
+ ## Version 0.2.1
2
+
3
+ * Output is rounded to default precision.
4
+
5
+ ## Version 0.2.0
6
+
7
+ * Added Newton Method, but Bisection is still the default.
8
+ * Added new configs: precision, iteration limit.
9
+ * Raises a simple error if iteration limit is reached.
10
+ * Output is now BigDecimal.
11
+ * Fixed calculation of Bisection#npv
12
+ * Amounts in Transactions are now converted to Float
13
+ * Transactions now take Date Argument as Date instead of Time.
data/README.md CHANGED
@@ -1,8 +1,8 @@
1
1
  # Xirr
2
- [![Build Status](https://travis-ci.org/tubedude/xirr.svg)](https://travis-ci.org/tubedude/xirr)[![Coverage Status](https://img.shields.io/coveralls/tubedude/xirr.svg)](https://coveralls.io/r/tubedude/xirr)[![Code Climate](https://codeclimate.com/github/tubedude/xirr/badges/gpa.svg)](https://codeclimate.com/github/tubedude/xirr)
2
+ [![Build Status](https://travis-ci.org/tubedude/xirr.svg)](https://travis-ci.org/tubedude/xirr)[![Coverage Status](https://img.shields.io/coveralls/tubedude/xirr.svg)](https://coveralls.io/r/tubedude/xirr)[![Code Climate](https://codeclimate.com/github/tubedude/xirr/badges/gpa.svg)](https://codeclimate.com/github/tubedude/xirr)[![Dependency Status](https://gemnasium.com/tubedude/xirr.svg)](https://gemnasium.com/tubedude/xirr)
3
3
 
4
- This is a simple gem to calculate XIRR on Bisection Method based on this gist http://puneinvestor.wordpress.com/2013/10/01/calculate-xirr-in-ruby-bisection-method/
5
- and ont Finance gem https://github.com/wkranec/finance
4
+
5
+ This is a gem to calculate XIRR on Bisection Method or Newton Method.
6
6
 
7
7
  ## Installation
8
8
 
@@ -24,11 +24,30 @@ Or install it yourself as:
24
24
  include Xirr
25
25
 
26
26
  cf = Cashflow.new
27
- cf << Transaction.new(-1000, date: '2014-01-01'.to_time(:utc))
28
- cf << Transaction.new(-2000, date: '2014-03-01'.to_time(:utc))
29
- cf << Transaction.new( 4500, date: '2015-12-01'.to_time(:utc))
27
+ cf << Transaction.new(-1000, date: '2014-01-01'.to_date)
28
+ cf << Transaction.new(-2000, date: '2014-03-01'.to_date)
29
+ cf << Transaction.new( 4500, date: '2015-12-01'.to_date)
30
30
  cf.xirr
31
- # 0.25159694345042327 # [Float]
31
+ # 0.25159694345042327 # [BigDecimal]
32
+
33
+ ## Configuration
34
+
35
+ # intializer/xirr.rb
36
+
37
+ Xirr.configure do |config|
38
+ config.eps = '1.0e-12'
39
+ config.days_in_year = 365.25
40
+ end
41
+
42
+
43
+ ## Documentation
44
+
45
+ http://rubydoc.info/github/tubedude/xirr/master/frames
46
+
47
+ ## Thanks
48
+
49
+ http://puneinvestor.wordpress.com/2013/10/01/calculate-xirr-in-ruby-bisection-method/
50
+ https://github.com/wkranec/finance
32
51
 
33
52
  ## Contributing
34
53
 
@@ -1,13 +1,17 @@
1
1
  require 'xirr/version'
2
+ require 'bigdecimal'
2
3
  require 'active_support/configurable'
4
+ require 'active_support/concern'
3
5
  require 'xirr/config'
4
- require 'xirr/main'
6
+ require 'xirr/base'
7
+ require 'xirr/bisection'
8
+ require 'xirr/newton_method'
5
9
 
6
10
  # @abstract adds a {Xirr::Cashflow} and {Xirr::Transaction} classes to calculate IRR of irregular transactions.
7
11
  # Calculates Xirr
8
12
  module Xirr
9
13
 
10
- autoload :Transaction, 'xirr/transaction'
11
- autoload :Cashflow, 'xirr/cashflow'
14
+ autoload :Transaction, 'xirr/transaction'
15
+ autoload :Cashflow, 'xirr/cashflow'
12
16
 
13
17
  end
@@ -0,0 +1,37 @@
1
+ module Xirr
2
+
3
+ # Precision for BigDecimal
4
+ PRECISION = Xirr.config.precision.to_i
5
+ # Days in a year
6
+ DAYS_IN_YEAR = Xirr.config.days_in_year.to_f
7
+ # Epsilon: error margin
8
+ EPS = Xirr.config.eps
9
+
10
+ # Base module for XIRR calculation Methods
11
+ module Base
12
+ extend ActiveSupport::Concern
13
+ attr_reader :cf
14
+
15
+ # @param cf [Cashflow]
16
+ # Must provide the calling Cashflow in order to calculate
17
+ def initialize(cf)
18
+ @cf = cf
19
+ end
20
+
21
+ # Calculates days until last transaction
22
+ # @return [Rational]
23
+ # @param date [Date]
24
+ def t_in_days(date)
25
+ (date - cf.min_date) / Xirr::DAYS_IN_YEAR
26
+ end
27
+
28
+ # Net Present Value funtion that will be used to reduce the cashflow
29
+ # @param rate [BigDecimal]
30
+ def xnpv(rate)
31
+ cf.inject(0) do |sum, t|
32
+ sum += t.amount / (1 + rate) ** t_in_days(t.date)
33
+ end
34
+ end
35
+
36
+ end
37
+ end
@@ -0,0 +1,59 @@
1
+ module Xirr
2
+
3
+ # Methods that will be included in Cashflow to calculate XIRR
4
+ class Bisection
5
+ include Base
6
+
7
+ # Calculates yearly Internal Rate of Return
8
+ # @return [BigDecimal]
9
+ # @param midpoint [Float]
10
+ # An initial guess rate will override the {Cashflow#irr_guess}
11
+ def xirr(midpoint = nil)
12
+
13
+ # Raises error if Cashflow is not valid
14
+ cf.valid?
15
+
16
+ # Bisection method finding the rate to zero nfv
17
+
18
+ # Initial values
19
+ left = BigDecimal.new -0.99, Xirr::PRECISION
20
+ right = BigDecimal.new 9.99, Xirr::PRECISION
21
+ eps = Xirr::EPS
22
+ midpoint ||= cf.irr_guess
23
+ limit = Xirr.config.iteration_limit.to_i
24
+ runs = 0
25
+
26
+ # Loops until difference is within error margin
27
+ while ((right-left).abs > eps) do
28
+
29
+ raise 'Did not converge' if runs == limit
30
+ runs += 1
31
+ npv_positive?(midpoint) ? right = midpoint : left = midpoint
32
+ midpoint = format_irr(left, right)
33
+
34
+ end
35
+
36
+ return midpoint.round Xirr::PRECISION
37
+
38
+ end
39
+
40
+ private
41
+
42
+ # @param midpoint [Float]
43
+ # @return [Bolean]
44
+ # Returns true if result is to the right ot the range
45
+ def npv_positive?(midpoint)
46
+ xnpv(midpoint) > 0
47
+ end
48
+
49
+ # @param left [Float]
50
+ # @param right [Float]
51
+ # @return [Float] IRR of the Cashflow
52
+ def format_irr(left, right)
53
+ irr = (right+left) / 2
54
+ end
55
+
56
+ end
57
+
58
+ end
59
+
@@ -3,17 +3,15 @@ module Xirr
3
3
  # Expands [Array] to store a set of transactions which will be used to calculate the XIRR
4
4
  # @note A Cashflow should consist of at least two transactions, one positive and one negative.
5
5
  class Cashflow < Array
6
- include Xirr::Main
7
6
 
8
- # @api public
9
7
  # @param args [Transaction]
10
8
  # @example Creating a Cashflow
11
9
  # cf = Cashflow.new
12
- # cf << Transaction.new( 1000, date: '2013-01-01'.to_time(:utc))
13
- # cf << Transaction.new(-1234, date: '2013-03-31'.to_time(:utc))
10
+ # cf << Transaction.new( 1000, date: '2013-01-01'.to_date)
11
+ # cf << Transaction.new(-1234, date: '2013-03-31'.to_date)
14
12
  # Or
15
- # cf = Cashflow.new Transaction.new( 1000, date: '2013-01-01'.to_time(:utc)), Transaction.new(-1234, date: '2013-03-31'.to_time(:utc))
16
- def initialize(*args) # :nodoc:
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)
17
15
  args.each { |a| self << a }
18
16
  self.flatten!
19
17
  end
@@ -36,13 +34,13 @@ module Xirr
36
34
 
37
35
  # @return [Float]
38
36
  # Sums all amounts in a cashflow
39
- def sum # :nodoc:
37
+ def sum
40
38
  self.map(&:amount).sum
41
39
  end
42
40
 
43
41
  # Last investment date
44
42
  # @return [Time]
45
- def max_date # :nodoc:
43
+ def max_date
46
44
  @max_date ||= self.map(&:date).max
47
45
  end
48
46
 
@@ -52,6 +50,20 @@ module Xirr
52
50
  ((multiple ** (1 / years_of_investment)) - 1).round(3)
53
51
  end
54
52
 
53
+ # @param guess [Float]
54
+ # @param method [Symbol]
55
+ # @return [Float]
56
+ # Finds the XIRR according to the method provided. Default to Bisection
57
+ def xirr(guess = nil, method = Xirr.config.default_method)
58
+ method == :bisection ? Bisection.new(self).xirr(guess) : NewtonMethod.new(self).xirr(guess)
59
+ end
60
+
61
+ # First investment date
62
+ # @return [Time]
63
+ def min_date
64
+ @min_date ||= self.map(&:date).min
65
+ end
66
+
55
67
  private
56
68
 
57
69
  # @api private
@@ -69,7 +81,7 @@ module Xirr
69
81
  # [100,100,-300] and [-100,-100,300] returns 1.5
70
82
  # @api private
71
83
  # @return [Float]
72
- def multiple # :nodoc:
84
+ def multiple
73
85
  result = positives.sum(&:amount) / -negatives.sum(&:amount)
74
86
  first_transaction_direction > 0 ? result : 1 / result
75
87
  end
@@ -77,15 +89,9 @@ module Xirr
77
89
  # @api private
78
90
  # Counts how many years from first to last transaction in the cashflow
79
91
  # @return
80
- def years_of_investment # :nodoc:
81
- (max_date - min_date) / (365 * 24 * 60 * 60).to_f
82
- end
83
-
84
- # @api private
85
- # First investment date
86
- # @return [Time]
87
- def min_date # :nodoc:
88
- @min_date ||= self.map(&:date).min
92
+ def years_of_investment
93
+ (max_date - min_date) / (365).to_f
94
+ # (max_date - min_date) / (365 * 24 * 60 * 60).to_f
89
95
  end
90
96
 
91
97
  # @api private
@@ -93,7 +99,7 @@ module Xirr
93
99
  # @see #negatives
94
100
  # @see #split_transactions
95
101
  # Finds all transactions income from Cashflow
96
- def positives # :nodoc:
102
+ def positives
97
103
  split_transactions
98
104
  @positives
99
105
  end
@@ -103,7 +109,7 @@ module Xirr
103
109
  # @see #positives
104
110
  # @see #split_transactions
105
111
  # Finds all transactions investments from Cashflow
106
- def negatives # :nodoc:
112
+ def negatives
107
113
  split_transactions
108
114
  @negatives
109
115
  end
@@ -112,14 +118,14 @@ module Xirr
112
118
  # @see #positives
113
119
  # @see #negatives
114
120
  # Uses partition to separate the investment transactions Negatives and the income transactions (Positives)
115
- def split_transactions # :nodoc:
121
+ def split_transactions
116
122
  @negatives, @positives = self.partition { |x| x.amount >= 0 } # Inverted as negative amount is good
117
123
  end
118
124
 
119
125
  # @api private
120
126
  # @return [String]
121
127
  # Error message depending on the missing transaction
122
- def invalid_message # :nodoc:
128
+ def invalid_message
123
129
  return 'No positive transaction' if positives.empty?
124
130
  return 'No negatives transaction' if negatives.empty?
125
131
  end
@@ -5,6 +5,8 @@ module Xirr
5
5
  default_values = {
6
6
  eps: '1.0e-12',
7
7
  days_in_year: 365,
8
+ iteration_limit: 100,
9
+ precision: 6
8
10
  }
9
11
 
10
12
  # Iterates trhough default values and sets in config
@@ -0,0 +1,54 @@
1
+ require 'bigdecimal/newton'
2
+ include Newton
3
+
4
+ module Xirr
5
+ # Class to calculate IRR using Newton Method
6
+ class NewtonMethod
7
+ include Base
8
+
9
+ # Base class for working with Newton's Method.
10
+ # @api private
11
+ class Function
12
+ values = {
13
+ eps: Xirr::EPS,
14
+ one: "1.0",
15
+ two: "2.0",
16
+ ten: "10.0",
17
+ zero: "0.0"
18
+ }
19
+
20
+ # define default values
21
+ values.each do |key, value|
22
+ define_method key do
23
+ BigDecimal.new(value, Xirr::PRECISION)
24
+ end
25
+ end
26
+
27
+ # @param transactions [Cashflow]
28
+ # @param function [Symbol]
29
+ # Initializes the Function with the Cashflow it will use as data source and the funcion to reduce it.
30
+ def initialize(transactions, function)
31
+ @transactions = transactions
32
+ @function = function
33
+ end
34
+
35
+ # Necessary for #nlsolve
36
+ # @param x [BigDecimal]
37
+ def values(x)
38
+ value = @transactions.send(@function, BigDecimal.new(x[0].to_s, Xirr::PRECISION))
39
+ [BigDecimal.new(value.to_s, Xirr::PRECISION)]
40
+ end
41
+ end
42
+
43
+ # Calculates XIRR using Newton method
44
+ # @return [BigDecimal]
45
+ # @param guess [Float]
46
+ def xirr(guess=nil)
47
+ func = Function.new(self, :xnpv)
48
+ rate = [guess || cf.irr_guess.to_f]
49
+ nlsolve(func, rate)
50
+ rate[0].round Xirr::PRECISION
51
+ end
52
+
53
+ end
54
+ end
@@ -6,9 +6,12 @@ module Xirr
6
6
  attr_accessor :date
7
7
 
8
8
  # @example
9
- # Transaction.new -1000, date: Time.now
9
+ # Transaction.new -1000, date: Date.now
10
+ # @param amount [Numeric]
11
+ # @param opts [Hash]
12
+ # @note Don't forget to add date: [Date] in the opts hash.
10
13
  def initialize(amount, opts={})
11
- @amount = amount
14
+ @amount = amount.to_f
12
15
 
13
16
  # Set optional attributes..
14
17
  opts.each do |key, value|
@@ -17,10 +20,10 @@ module Xirr
17
20
  end
18
21
 
19
22
  # Sets the amount
20
- # @param value [Float, Integer]
23
+ # @param value [Numeric]
21
24
  # @return [Float]
22
25
  def amount=(value)
23
- @amount = value.to_f || 0
26
+ @amount = value.to_f || 0.0
24
27
  end
25
28
 
26
29
  # @return [String]
@@ -1,4 +1,4 @@
1
1
  module Xirr
2
2
  # Version of the Gem
3
- VERSION = "0.1.1"
3
+ VERSION = "0.2.1"
4
4
  end
@@ -2,29 +2,76 @@ require_relative 'test_helper'
2
2
 
3
3
  describe 'Cashflows' do
4
4
 
5
- describe 'an array of Transactions' do
5
+ describe 'of a good investment' do
6
6
  before(:all) do
7
- @xactions = Cashflow.new
8
- @xactions << Transaction.new( 1000, date: '1985-01-01'.to_time(:utc))
9
- @xactions << Transaction.new( -600, date: '1990-01-01'.to_time(:utc))
10
- @xactions << Transaction.new(-6000, date: '1995-01-01'.to_time(:utc))
7
+ @cf = Cashflow.new
8
+ @cf << Transaction.new(1000, date: '1985-01-01'.to_date)
9
+ @cf << Transaction.new(-600, date: '1990-01-01'.to_date)
10
+ @cf << Transaction.new(-6000, date: '1995-01-01'.to_date)
11
11
  end
12
12
 
13
- it 'haves an Internal Rate of Return' do
14
- assert_equal '0.225683'.to_f, @xactions.xirr.round(6)
13
+ it 'has an Internal Rate of Return on Bisection Method' do
14
+ assert_equal '0.225683'.to_f, @cf.xirr
15
+ end
16
+
17
+ it 'has an Internal Rate of Return on Bisection Method using a Guess' do
18
+ assert_equal '0.225683'.to_f, @cf.xirr(0.15)
19
+ end
20
+
21
+ it 'has an Internal Rate of Return on Newton Method' do
22
+ assert_equal '0.225683'.to_f, @cf.xirr(nil, :newton_method)
15
23
  end
16
24
 
17
25
  it 'has an educated guess' do
18
- assert_equal '0.196'.to_f, @xactions.irr_guess.round(6)
26
+ assert_equal '0.208'.to_f, @cf.irr_guess
27
+ end
28
+ end
29
+ describe 'an invalid array of Transactions' do
30
+ it 'should have an Internal Rate of Return' do
31
+ @cf = Cashflow.new
32
+ @cf << Transaction.new(-600, date: '1990-01-01'.to_date)
33
+ @cf << Transaction.new(-600, date: '1995-01-01'.to_date)
34
+ assert_raises(ArgumentError) { @cf.valid? }
35
+ end
36
+ end
37
+ describe 'of a bad investment' do
38
+ before(:all) do
39
+ @cf = Cashflow.new
40
+ @cf << Transaction.new(1000, date: '1985-01-01'.to_date)
41
+ @cf << Transaction.new(-600, date: '1990-01-01'.to_date)
42
+ @cf << Transaction.new(-200, date: '1995-01-01'.to_date)
43
+ end
44
+
45
+ it 'has an Internal Rate of Return on Bisection Method' do
46
+ assert_equal '-0.034592'.to_f, @cf.xirr
47
+ end
48
+
49
+ it 'has an Internal Rate of Return on Newton Method' do
50
+ assert true, @cf.xirr(nil, :newton_method).nan?
19
51
  end
20
52
 
21
- describe 'an invalid array of Transactions' do
22
- it 'should have an Internal Rate of Return' do
23
- @xactions = Cashflow.new
24
- @xactions << Transaction.new(-600, date: '1990-01-01'.to_time(:utc))
25
- @xactions << Transaction.new(-600, date: '1995-01-01'.to_time(:utc))
26
- assert_raises(ArgumentError) { @xactions.valid? }
27
- end
53
+ it 'has an educated guess' do
54
+ assert_equal -0.022, @cf.irr_guess
55
+ end
56
+
57
+ end
58
+ describe 'of a long investment' do
59
+ before(:all) do
60
+ @cf = Cashflow.new
61
+ @cf << Transaction.new(-1000, date: Date.new(1957, 1, 1))
62
+ @cf << Transaction.new(390000, date: Date.new(2013, 1, 1))
63
+ end
64
+
65
+ it 'has an Internal Rate of Return on Bisection Method' do
66
+ assert_equal '0.112339'.to_f, @cf.xirr
67
+ end
68
+
69
+ it 'has an Internal Rate of Return on Newton Method' do
70
+ assert_equal '0.112339'.to_f, @cf.xirr(nil, :newton_method)
71
+ end
72
+
73
+ it 'has an educated guess' do
74
+ assert_equal 0.112, @cf.irr_guess.round(6)
28
75
  end
29
76
 
30
77
  end
@@ -7,7 +7,9 @@ require 'minitest/spec'
7
7
  require 'active_support/all'
8
8
 
9
9
  require 'xirr/config.rb'
10
- require 'xirr/main.rb'
10
+ require 'xirr/base.rb'
11
+ require 'xirr/bisection.rb'
12
+ require 'xirr/newton_method.rb'
11
13
  require 'xirr/cashflow.rb'
12
14
  require 'xirr/transaction.rb'
13
15
  include Xirr
@@ -0,0 +1,13 @@
1
+ require_relative 'test_helper'
2
+
3
+ describe 'Transaction' do
4
+ before(:all) do
5
+ @t = Transaction.new(1000, date: Date.today)
6
+ end
7
+ it 'converts amount to float' do
8
+ assert true, @t.amount.kind_of?(Float)
9
+ end
10
+ it 'retreives the date' do
11
+ assert_equal Date.today, @t.date
12
+ end
13
+ end
@@ -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 = 'xirr'
8
- spec.version = Xirr::VERSION
9
- spec.authors = ['tubedude']
10
- spec.email = ['beto@trevisan.me']
11
- spec.summary = %q{Calculates XIRR (Bisection method) of a cashflow}
12
- spec.description = %q{Calculates IRR of a Cashflow, simillar to Excels, XIRR formula, but using the Bisection Method. It was heavily based the much more complete https://github.com/wkranec/finance and http://puneinvestor.wordpress.com/2013/10/01/calculate-xirr-in-ruby-bisection-method/. }
13
- spec.homepage = 'https://github.com/tubedude/xirr'
14
- spec.license = 'MIT'
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, simillar to Excels, XIRR formula, but using the Bisection Method and Newton Method.}
13
+ spec.homepage = 'https://github.com/tubedude/xirr'
14
+ spec.license = 'MIT'
15
15
 
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)/})
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
- spec.add_development_dependency 'rake','~> 10'
22
+ spec.add_development_dependency 'rake', '~> 10'
23
23
 
24
24
  spec.required_ruby_version = '>=1.9'
25
25
  spec.add_dependency 'activesupport', '~> 4.0'
26
- spec.add_development_dependency 'minitest', '~> 4.7'
27
- spec.add_development_dependency 'coveralls','~> 0'
26
+ spec.add_development_dependency 'minitest', '~> 5.4'
27
+ spec.add_development_dependency 'coveralls', '~> 0'
28
28
 
29
29
  end
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.1.1
4
+ version: 0.2.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-08-04 00:00:00.000000000 Z
11
+ date: 2014-08-07 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: bundler
@@ -58,14 +58,14 @@ dependencies:
58
58
  requirements:
59
59
  - - ~>
60
60
  - !ruby/object:Gem::Version
61
- version: '4.7'
61
+ version: '5.4'
62
62
  type: :development
63
63
  prerelease: false
64
64
  version_requirements: !ruby/object:Gem::Requirement
65
65
  requirements:
66
66
  - - ~>
67
67
  - !ruby/object:Gem::Version
68
- version: '4.7'
68
+ version: '5.4'
69
69
  - !ruby/object:Gem::Dependency
70
70
  name: coveralls
71
71
  requirement: !ruby/object:Gem::Requirement
@@ -80,9 +80,8 @@ dependencies:
80
80
  - - ~>
81
81
  - !ruby/object:Gem::Version
82
82
  version: '0'
83
- description: 'Calculates IRR of a Cashflow, simillar to Excels, XIRR formula, but
84
- using the Bisection Method. It was heavily based the much more complete https://github.com/wkranec/finance
85
- and http://puneinvestor.wordpress.com/2013/10/01/calculate-xirr-in-ruby-bisection-method/. '
83
+ description: Calculates IRR of a Cashflow, simillar to Excels, XIRR formula, but using
84
+ the Bisection Method and Newton Method.
86
85
  email:
87
86
  - beto@trevisan.me
88
87
  executables: []
@@ -92,18 +91,22 @@ files:
92
91
  - .coveralls.yml
93
92
  - .gitignore
94
93
  - .travis.yml
94
+ - CHANGE_LOG.md
95
95
  - Gemfile
96
96
  - LICENSE.txt
97
97
  - README.md
98
98
  - Rakefile
99
99
  - lib/xirr.rb
100
+ - lib/xirr/base.rb
101
+ - lib/xirr/bisection.rb
100
102
  - lib/xirr/cashflow.rb
101
103
  - lib/xirr/config.rb
102
- - lib/xirr/main.rb
104
+ - lib/xirr/newton_method.rb
103
105
  - lib/xirr/transaction.rb
104
106
  - lib/xirr/version.rb
105
107
  - test/test_cashflow.rb
106
108
  - test/test_helper.rb
109
+ - test/test_transactions.rb
107
110
  - xirr.gemspec
108
111
  homepage: https://github.com/tubedude/xirr
109
112
  licenses:
@@ -128,7 +131,8 @@ rubyforge_project:
128
131
  rubygems_version: 2.0.14
129
132
  signing_key:
130
133
  specification_version: 4
131
- summary: Calculates XIRR (Bisection method) of a cashflow
134
+ summary: Calculates XIRR (Bisection and Newton method) of a cashflow
132
135
  test_files:
133
136
  - test/test_cashflow.rb
134
137
  - test/test_helper.rb
138
+ - test/test_transactions.rb
@@ -1,81 +0,0 @@
1
- require 'active_support/concern'
2
-
3
- module Xirr
4
-
5
- # Methods that will be included in Cashflow to calculate XIRR
6
- module Main
7
- extend ActiveSupport::Concern
8
-
9
- # Calculates yearly Internal Rate of Return
10
- # @return [Float]
11
- # @param guess [Float] an initial guess rate that will override the {Cashflow#irr_guess}
12
- def xirr(guess = nil)
13
-
14
- # Raises error if Cashflow is not valid
15
- self.valid?
16
-
17
- # Bisection method finding the rate to zero nfv
18
-
19
- # Initial values
20
- days_in_year = Xirr.config.days_in_year.to_f
21
- left = -0.99/days_in_year
22
- right = 9.99/days_in_year
23
- epsilon = Xirr.config.eps.to_f
24
- guess = self.irr_guess.to_f
25
-
26
- # Loops until difference is within error margin
27
- while ((right-left).abs > 2 * epsilon) do
28
-
29
- midpoint = guess || (right + left)/2
30
- guess = nil
31
- nfv_positive?(left, midpoint) ? left = midpoint : right = midpoint
32
-
33
- end
34
-
35
- return format_irr(left, right)
36
-
37
- end
38
-
39
- private
40
-
41
- # @param left [Float]
42
- # @param midpoint [Float]
43
- # @return [Bolean]
44
- # Returns true if result is to the right ot the range
45
- def nfv_positive?(left, midpoint)
46
- (nfv(left) * nfv(midpoint) > 0)
47
- end
48
-
49
- # @param left [Float]
50
- # @param right [Float]
51
- # @return [Float] IRR of the Cashflow
52
- def format_irr(left, right)
53
- days_in_year = Xirr.config.days_in_year.to_f
54
- # Irr for daily cashflow (not in percentage format)
55
- irr = (right+left) / 2
56
- # Irr for daily cashflow multiplied by 365 to get yearly return
57
- irr = irr * days_in_year
58
- # Annualized yield (return) reflecting compounding effect of daily returns
59
- irr = (1 + irr / days_in_year) ** days_in_year - 1
60
- end
61
-
62
- # Returns the Net future value of the flow given a Rate
63
- # @param rate [Float]
64
- # @return [Float]
65
- def nfv(rate) # :nodoc:
66
- self.inject(0) do |nfv,t|
67
- nfv = nfv + t.amount * ((1 + rate) ** t_in_days(t.date))
68
- end
69
- end
70
-
71
- # Calculates days until last transaction
72
- # @return [Rational]
73
- # @param date [Time]
74
- def t_in_days(date)
75
- Date.parse(max_date.to_s) - Date.parse(date.to_s)
76
- end
77
-
78
- end
79
-
80
- end
81
-