marksweston / finance

A library for financial calculations in Ruby
https://rubygems.org/gems/finance
Other
217 stars 93 forks source link

xirr fails or gives an incorrect answer #27

Open arbrown opened 10 years ago

arbrown commented 10 years ago

This issue is related to issue #24, but with some other cases of getting the wrong answer instead of failing. First, the following sequence gives me an error:

transactions = []
transactions << Transaction.new(-1000, date: Time.new(1990,1,1))
transactions << Transaction.new(390000, date: Time.new(2013,1,1))
transactions.xirr

The error is:

RuntimeError: Failed to reduce function values.
    from /home/drew/.rvm/rubies/ruby-2.0.0-p0/lib/ruby/2.0.0/bigdecimal/newton.rb:66:in `nlsolve'

I was able to coax nlsolve into continuing to try by multiplying minfact by 100 in the BigDecimal module's newton.rb. This made it take longer to get to the fail state, and in the mean time, it ended up finding the answer. I don't know enough about how Newton's method really works to know how to fix this problem from the finance gem's perspective though...

The other error is more perplexing. the following code does not error out, but produces a wildly inaccurate result:

transactions = []
transactions << Transaction.new(-1000, date: Time.new(1957,1,1))
transactions << Transaction.new(390000, date: Time.new(2013,1,1))
transactions.xirr

It returns Rate.new(-9999999999998.948000, :apr) when the answer should be something like: Rate.new(0.112339453419573, :apr) (at least that's what google spreadsheets told me.)

I don't really know what is causing this, but I suspect it is an overflow or some other error that occurs when converting to and from the different number formats used during calculation. Why is there frequent conversion to and from Flt::DecNum, and BigDecimal, and why is :to_s constantly called?

I'll try and look at this this weekend, but I don't know how far I'll get. This issue could really use some attention from someone more familiar with Newton's method and internal rate of return calculations in general.

tubedude commented 10 years ago

Hey, I was able to get the right answer by guessing a rate with the addition I made on the PR https://github.com/wkranec/finance/pull/30.

transactions = []
transactions << Transaction.new(-1000, date: Time.new(1957,1,1))
transactions << Transaction.new(390000, date: Time.new(2013,1,1))
transactions.xirr(0.1)

The default guess of this gem is 100%, which is too high in some extreme cases. I've also added the support for a configuration file config/initializers/finance.rb that takes an input like:

Finance.configure do |config|
  config.eps = '1.0e-12'
  config.guess = 0.5
end