[QUIZ] Internal Rate of Return (#156)

The three rules of Ruby Quiz:

1. Please do not post any solutions or spoiler discussion for this quiz until
48 hours have passed from the time on this message.

2. Support Ruby Quiz by submitting ideas as often as you can:

http://www.rubyquiz.com/

3. Enjoy!

Suggestion: A [QUIZ] in the subject of emails about the problem helps everyone
on Ruby Talk follow the discussion. Please reply to the original quiz message,
if you can.

···

-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=

by Harrison Reiser

Internal Rate of Return (IRR –
http://en.wikipedia.org/wiki/Internal_rate_of_return) is a common financial
metric, used by investment firms to predict the profitability of a company or
project. Finding the IRR of a company amounts to solving for it in the equation
for Net Present Value (NPV – http://en.wikipedia.org/wiki/Net_present_value),
another valuable decision-making metric:

         N C_t
  NPV = Σ ------------
        t=0 (1 + IRR)**t

This week's quiz is to calculate the IRR for any given variable-length list of
numbers, which represent yearly cash flows, the C_t's in the formula above: C_0,
C_1, etc. (C_0 is typically a negative value, corresponding to the initial
investment into the project.) From the example in the Wikipedia article
(http://en.wikipedia.org/wiki/Internal_rate_of_return), for instance, you should
be able to produce a rate of 17.09% (to four decimal places, let's say) from
this or a similar command:

  irr([-100,+30,+35,+40,+45])
  => 0.1709...

Keep in mind that an IRR greater than 100% is possible. Extra credit if you can
also correctly handle input that produces negative rates, disregarding the fact
that they make no sense.

Internal Rate of Return (IRR -http://en.wikipedia.org/wiki/Internal_rate_of_return\) is a common financial
metric, used by investment firms to predict the profitability of a company or
project. Finding the IRR of a company amounts to solving for it in the equation
for Net Present Value (NPV -http://en.wikipedia.org/wiki/Net_present_value\),
another valuable decision-making metric:

               N C_t
        NPV = Σ ------------
              t=0 (1 + IRR)**t

This week's quiz is to calculate the IRR for any given variable-length list of
numbers, which represent yearly cash flows, the C_t's in the formula above: C_0,
C_1, etc. (C_0 is typically a negative value, corresponding to the initial
investment into the project.) From the example in the Wikipedia article
(http://en.wikipedia.org/wiki/Internal_rate_of_return\), for instance, you should
be able to produce a rate of 17.09% (to four decimal places, let's say) from
this or a similar command:

        irr([-100,+30,+35,+40,+45])
        => 0.1709...

I think one point, which isn't brought out here and not well in the
wikipedia article either, is that given all of the C_t, you still have
two unknowns: IRR (which we are attempting to solve for) and NPV.

In this case, you want NPV set to zero in order to solve for IRR. Or
did I miss something?

This must be a strange moment for you or maybe it is too early to say
THANK YOU FOR ALL THE FISH ;), as you still have a summary to do, but
I feel very strange by reading the "last Ruby Quiz".

Robert

···

On Feb 8, 2008 3:01 PM, Ruby Quiz <james@grayproductions.net> wrote:

The three rules of Ruby Quiz:

1. Please do not post any solutions or spoiler discussion for this quiz until
48 hours have passed from the time on this message.

2. Support Ruby Quiz by submitting ideas as often as you can:

http://www.rubyquiz.com/

3. Enjoy!

Suggestion: A [QUIZ] in the subject of emails about the problem helps everyone
on Ruby Talk follow the discussion. Please reply to the original quiz message,
if you can.

-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=

by Harrison Reiser

Internal Rate of Return (IRR ¨C
http://en.wikipedia.org/wiki/Internal_rate_of_return\) is a common financial
metric, used by investment firms to predict the profitability of a company or
project. Finding the IRR of a company amounts to solving for it in the equation
for Net Present Value (NPV ¨C http://en.wikipedia.org/wiki/Net_present_value\),
another valuable decision-making metric:

               N C_t
        NPV = ¦² ------------
              t=0 (1 + IRR)**t

This week's quiz is to calculate the IRR for any given variable-length list of
numbers, which represent yearly cash flows, the C_t's in the formula above: C_0,
C_1, etc. (C_0 is typically a negative value, corresponding to the initial
investment into the project.) From the example in the Wikipedia article
(http://en.wikipedia.org/wiki/Internal_rate_of_return\), for instance, you should
be able to produce a rate of 17.09% (to four decimal places, let's say) from
this or a similar command:

        irr([-100,+30,+35,+40,+45])
        => 0.1709...

Keep in mind that an IRR greater than 100% is possible. Extra credit if you can
also correctly handle input that produces negative rates, disregarding the fact
that they make no sense.

--
http://ruby-smalltalk.blogspot.com/

---
Whereof one cannot speak, thereof one must be silent.
Ludwig Wittgenstein

James Gray wrote:

by Harrison Reiser

(...)

This week's quiz is to calculate the IRR for any given variable-length
list of numbers, which represent yearly cash flows,

In real life, it's not yearly. IIR needs a time dimension.

irr([-100, a_date],[+30,another_date],[+35,yet_a _date])

James, I learned a LOT thanks to you.

regards,

Siep

···

--
Posted via http://www.ruby-forum.com/\.

Can we return 1/0.0 if NPV does not evaluate to 0 for a reasonably
large IRR?

irr([+100,+10,+10,+10])
=> Infinity

Many thanks to James for your great job as the Quizmaster! :slight_smile:

···

On Feb 8, 4:01 pm, Ruby Quiz <ja...@grayproductions.net> wrote:

Keep in mind that an IRR greater than 100% is possible. Extra credit if you can
also correctly handle input that produces negative rates, disregarding the fact
that they make no sense.

--
Alex

Here's my first solution, attached and pastied here:
http://pastie.caboo.se/149976

Its basically a binary search, repeatedly calculating the NVP and adjusting
lower and upper bounds. Not too complicated, so let me just note a couple
things:

* Line 12 could have been a mirror of line 10, but I wanted to emphasize the
symmetry.
* Rationals are used throughout to ensure accuracy.
* The "+ 1" on line 10 prevents 0 from being a fixed point.
* The given accuracy guarantees the result will be within 1 / 10 ** [accuracy]
of the true answer. This is not the same thing as having [accuracy] number of
correct digits. (Though the program could be made to do that if the guesses
fell on negative powers of ten).

I haven't done all that many quizzes, but those I have were very enjoyable.
Thank you James.

-Jesse Merriman

irr.rb (1.33 KB)

Hi,

here's my solution (using Newton's method, too).

def evaluate( cost_flows, x, n = 0 )
    if n >= cost_flows.size
        0.0
    else
        cost_flows[n] + evaluate(cost_flows, x, n+1) / (1.0 + x)
    end
end

def irr( cost_flows, x = 0 )
    d_cost_flows = (0...cost_flows.size).map{|t| -t*cost_flows[t]}
    
    begin
        y = evaluate( cost_flows, x )
        yd = evaluate( d_cost_flows, x ) / (1.0+x)
        x -= y/yd
    end until y.abs < 1e-9
    
    return x
end

Here is my solution using Newton's method. This works since the original
equation can be expressed as a polynomial by replacing (1-IRR) with X and
dividing by the highest order of X. This solution is generally accurate
although for certain values it is possible for the solution to either not
converge or converge to the wrong value:

# Solve polynomial for given value of X and coefficients
def poly(x, coeff)
  (0...coeff.size).inject(0) {|sum, i| sum + coeff[i] * (x ** (coeff.size -
i - 1))}
end

# Solve for first derivative of a polynomial for given value of X and
coefficients
def poly_first_deriv(x, coeff)
  poly(x, (0...coeff.size-1).inject(){|deriv, i| deriv << coeff[i] * (
coeff.size - i - 1)})
end

def irr(coeff)
  x, last_x = 1.0, 0.0
  10.times do ||
    last_x = x
    x = x - (poly(x, coeff) / poly_first_deriv(x, coeff))
    return (x - 1) * 100 if (last_x - x).abs < 0.00001
  end
  nil
end
So long James, and best of luck to you.
Thanks,

Justin

···

On 2/8/08, Ruby Quiz <james@grayproductions.net> wrote:

The three rules of Ruby Quiz:

1. Please do not post any solutions or spoiler discussion for this quiz
until
48 hours have passed from the time on this message.

2. Support Ruby Quiz by submitting ideas as often as you can:

http://www.rubyquiz.com/

3. Enjoy!

Suggestion: A [QUIZ] in the subject of emails about the problem helps
everyone
on Ruby Talk follow the discussion. Please reply to the original quiz
message,
if you can.

-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=

by Harrison Reiser

Internal Rate of Return (IRR ¨C
http://en.wikipedia.org/wiki/Internal_rate_of_return\) is a common
financial
metric, used by investment firms to predict the profitability of a company
or
project. Finding the IRR of a company amounts to solving for it in the
equation
for Net Present Value (NPV ¨C
http://en.wikipedia.org/wiki/Net_present_value\),
another valuable decision-making metric:

              N C_t
       NPV = ¦² ------------
             t=0 (1 + IRR)**t

This week's quiz is to calculate the IRR for any given variable-length
list of
numbers, which represent yearly cash flows, the C_t's in the formula
above: C_0,
C_1, etc. (C_0 is typically a negative value, corresponding to the initial
investment into the project.) From the example in the Wikipedia article
(http://en.wikipedia.org/wiki/Internal_rate_of_return\), for instance, you
should
be able to produce a rate of 17.09% (to four decimal places, let's say)
from
this or a similar command:

       irr([-100,+30,+35,+40,+45])
       => 0.1709...

Keep in mind that an IRR greater than 100% is possible. Extra credit if
you can
also correctly handle input that produces negative rates, disregarding the
fact
that they make no sense.

Here's my binary search solution. It can return negative values
(extra credit) and returns nil for undefined behavior.

require "enumerator"
require "rubygems"
require "facets/numeric/round"

def npv(irr, cash_flows)
  cash_flows.enum_with_index.inject(0) do |sum, (c_t, t)|
    sum + c_t / (1+irr)**t
  end
end

def irr(cash_flows, precision=10 ** -4)

  # establish an upper bound, return nil if none
  max = 1.0
  max *= 2 until npv(max, cash_flows) < 0 or max.infinite?
  return nil if max.infinite?

  # initialize search variables
  last_irr, irr, radius = max, 0.0, max

  # binary search until precision is met
  until irr.approx?(last_irr, precision/10)
    last_irr = irr

    # improve approximation of irr
    if npv(irr, cash_flows) < 0
      irr -= radius
    else
      irr += radius
    end

    # reduce the search space by half
    radius /= 2
  end

  irr.round_to(precision)
end

if __FILE__ == $PROGRAM_NAME
  puts irr(ARGV.map { |e| Float(e) }) || "Undefined"
end

···

On Feb 8, 6:01 am, Ruby Quiz <ja...@grayproductions.net> wrote:

The three rules of RubyQuiz:

1. Please do not post any solutions or spoiler discussion for thisquizuntil
48 hours have passed from the time on this message.

2. Support RubyQuizby submitting ideas as often as you can:

http://www.rubyquiz.com/

3. Enjoy!

Suggestion: A [QUIZ] in the subject of emails about the problem helps everyone
on Ruby Talk follow the discussion. Please reply to the originalquizmessage,
if you can.

-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=- =-=-=

by Harrison Reiser

Internal Rate of Return (IRR -http://en.wikipedia.org/wiki/Internal_rate_of_return\) is a common financial
metric, used by investment firms to predict the profitability of a company or
project. Finding the IRR of a company amounts to solving for it in the equation
for Net Present Value (NPV -http://en.wikipedia.org/wiki/Net_present_value\),
another valuable decision-making metric:

               N C_t
        NPV = Σ ------------
              t=0 (1 + IRR)**t

This week'squizis to calculate the IRR for any given variable-length list of
numbers, which represent yearly cash flows, the C_t's in the formula above: C_0,
C_1, etc. (C_0 is typically a negative value, corresponding to the initial
investment into the project.) From the example in the Wikipedia article
(http://en.wikipedia.org/wiki/Internal_rate_of_return\), for instance, you should
be able to produce a rate of 17.09% (to four decimal places, let's say) from
this or a similar command:

        irr([-100,+30,+35,+40,+45])
        => 0.1709...

Keep in mind that an IRR greater than 100% is possible. Extra credit if you can
also correctly handle input that produces negative rates, disregarding the fact
that they make no sense.

Here's a second solution from me:
http://pastie.caboo.se/150029

Well, maybe its a half-solution, since it defers the real work to an external
program. A while ago I wrote some code for communicating with a Maxima
process [1]. This solution uses it to directly solve the NVP equation (for
both real and complex IRRs). Note that there is a bug in version 0.0.4 of my
library that incorrectly returns only the first line of a multi-line result,
so it doesn't always work. I've had it fixed in a newer version for a while,
but never released it. I'll try to get it up within the next few days.

[1] http://www.jessemerriman.com/project/ltf

irr_maxima.rb (911 Bytes)

Hi,

I don't have a solution for today's quiz, but I just wanted to say a
big Thank You !! to James for the Ruby Quiz. It has been and it is a
great asset of the Ruby community and that you have done a wonderful
job as the Quizmaster.

Again thanks.

Jesus.

···

On Feb 8, 2008 3:01 PM, Ruby Quiz <james@grayproductions.net> wrote:

The three rules of Ruby Quiz:

1. Please do not post any solutions or spoiler discussion for this quiz until
48 hours have passed from the time on this message.

2. Support Ruby Quiz by submitting ideas as often as you can:

http://www.rubyquiz.com/

3. Enjoy!

Here is my solution.

I'm too lazy to use Newton's method, so I've employed a binary
search. The most difficult part was, no surprise, determining the
bounds of search. :slight_smile:

#!/usr/bin/env ruby

def npv(ct, i)
  sum = 0
  ct.each_with_index{ |c,t| sum += c/(1 + i.to_f)**t }
  sum
end

def irr(ct)
  l = -0.9999
  sign = npv(ct, l)
# p sign
  r = 1.0
  while npv(ct, r)*sign > 0 do
# p npv(ct, r)
    r *= 2
    break if r > 1000
  end
  if r > 1000
    l = -1.0001
    sign = npv(ct, l)
    r = -2.0
    while npv(ct, r)*sign > 0 do
      r *= 2
      return 0.0/0.0 if r.abs > 1000
    end
  end

  m = 0
  loop do
    m = (l + r)/2.0
    v = npv(ct, m)
# p v
    break if v.abs < 1e-8
    if v*sign < 0
      r = m
    else
      l = m
    end
  end
  m
end

if __FILE__ == $0
  p irr([-100,+30,+35,+40,+45])
  p irr([-100,+10,+10,+10])
  p irr([+100,+10,+10,+10])
  p irr([+100,-90,-90,-90])
  p irr([+0,+10,+10,+10])
end

···

On Feb 8, 4:01 pm, Ruby Quiz <ja...@grayproductions.net> wrote:

This week's quiz is to calculate the IRR for any given variable-length list of
numbers...

--
Alex

Here's my solution:

irr.rb:

require 'algebra'

class IRR

  def self.calculate(profits)
    begin
      function(profits).zero
    rescue Algebra::MaximumIterationsReached => mir
      nil
    end
  end

  private

  def self.function(profits)
    Algebra::Function.new do |x|
      sumands = Array.new
      profits.each_with_index {|profit, index| sumands <<
profit.to_f / (1 + x) ** index }
      sumands.inject(0) {|sum, sumand| sum + sumand }
    end
  end

end

puts IRR.calculate([-100, 30, 35, 40, 45])
puts IRR.calculate([-1, 1])
puts IRR.calculate()

algebra.rb:

module Algebra

  class MaximumIterationsReached < Exception
  end

  class NewtonsMethod

    def self.calculate(function, x)
      x - function.evaluated_at(x) / function.derivative_at(x)
    end

  end

  class NewtonsDifferenceQuotient

    def self.calculate(function, x, delta=0.1)
      (function.evaluated_at(x + delta) -
function.evaluated_at(x) ).to_f / delta
    end

  end

  class Function

attr_accessor :differentiation_method, :root_method, :maximum_iterations, :tolerance

    def initialize(differentiation_method=NewtonsDifferenceQuotient,
root_method=NewtonsMethod, &block)
      @definition = block
      @differentiation_method, @root_method = differentiation_method,
root_method
      @maximum_iterations = 1000
      @tolerance = 0.0001
    end

    def evaluated_at(x)
      @definition.call(x)
    end

    def derivative_at(x)
      differentiation_method.calculate(self, x)
    end

    def zero(initial_value=0)
      recursive_zero(initial_value, 1)
    end

    private

    def recursive_zero(guess, iteration)
      raise MaximumIterationsReached if iteration >=
@maximum_iterations

      better_guess = @root_method.calculate(self, guess)

      if (better_guess - guess).abs <= @tolerance
        better_guess
      else
        recursive_zero(better_guess, iteration + 1)
      end
    end

  end

end

Comments welcomed. Thanks,

···

On Feb 8, 9:01 am, Ruby Quiz <ja...@grayproductions.net> wrote:

The three rules of Ruby Quiz:

1. Please do not post any solutions or spoiler discussion for this quiz until
48 hours have passed from the time on this message.

2. Support Ruby Quiz by submitting ideas as often as you can:

http://www.rubyquiz.com/

3. Enjoy!

Suggestion: A [QUIZ] in the subject of emails about the problem helps everyone
on Ruby Talk follow the discussion. Please reply to the original quiz message,
if you can.

-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=- =-=-=

by Harrison Reiser

Internal Rate of Return (IRR -http://en.wikipedia.org/wiki/Internal_rate_of_return\) is a common financial
metric, used by investment firms to predict the profitability of a company or
project. Finding the IRR of a company amounts to solving for it in the equation
for Net Present Value (NPV -http://en.wikipedia.org/wiki/Net_present_value\),
another valuable decision-making metric:

               N C_t
        NPV = Σ ------------
              t=0 (1 + IRR)**t

This week's quiz is to calculate the IRR for any given variable-length list of
numbers, which represent yearly cash flows, the C_t's in the formula above: C_0,
C_1, etc. (C_0 is typically a negative value, corresponding to the initial
investment into the project.) From the example in the Wikipedia article
(http://en.wikipedia.org/wiki/Internal_rate_of_return\), for instance, you should
be able to produce a rate of 17.09% (to four decimal places, let's say) from
this or a similar command:

        irr([-100,+30,+35,+40,+45])
        => 0.1709...

Keep in mind that an IRR greater than 100% is possible. Extra credit if you can
also correctly handle input that produces negative rates, disregarding the fact
that they make no sense.

Here's my iterative solution. It should produce at least as many
significant digits as you specify, and return +/-Infinity for
solutions which don't converge.

···

On 2/8/08, Ruby Quiz <james@grayproductions.net> wrote:

This week's quiz is to calculate the IRR for any given variable-length list of
numbers,

------
class Array; def sum;inject(0){|s,v|s+v};end;end

def npv c,irr
  npv=0
  c.each_with_index{|c_t,t|
    npv+= c_t.to_f / (1.0+irr)**t.to_f
  }
  npv
end

def irr c, significant_digits=4
  limit = 10**-(significant_digits)
  estimate = c.sum/(c.size-1.0)/75.0
  delta = estimate.abs
  n=npv(c,estimate)
  while n.abs > limit
    sign = n/n.abs
# p "%.6f -> %.6f"%[estimate,n]
    if (delta.abs < limit/1000)
      delta=estimate.abs #if we aren't getting anywhere,
take a big jump
      return sign/0.0 if (n-c[0]).abs < limit
    end
    estimate += (delta/=2) * sign
    n=npv c,estimate
  end
  estimate
end
---------

Thank you for all the work that went into producing these quizzes,
James. I've really enjoyed them.

-Adam

This must be a strange moment for you

Yes, it was odd to think as I launched the quiz this morning, this is the last time I will do this.

I will miss it, for sure. I am glad to be stopping while it's still going strong though; we all would have enjoyed it a lot less if I had let it beat me down.

or maybe it is too early to say THANK YOU FOR ALL THE FISH ;), as you still have a summary to do,

Well, I appreciate the thought anyway.

but I feel very strange by reading the "last Ruby Quiz".

I think of it more as James's last Ruby Quiz.

I'm switching sides. I'll be solving and occasionally contributing now. I'm looking forward to that.

I'm sure the new team will take great care of the quiz. I have faith.

James Edward Gray II

···

On Feb 8, 2008, at 9:48 AM, Robert Dober wrote:

I think one point, which isn't brought out here and not well in the
wikipedia article either, is that given all of the C_t, you still have
two unknowns: IRR (which we are attempting to solve for) and NPV.

In this case, you want NPV set to zero in order to solve for IRR. Or
did I miss something?

Yes. At least I get the right answer if the "irr" function solves for
NPV=0. :slight_smile:

Paolo

That's correct. I forgot to add that detail when I submitted the
question. Sorry!

Harrison Reiser

···

On Feb 8, 8:11 am, Matthew Moss <matthew.m...@gmail.com> wrote:

In this case, you want NPV set to zero in order to solve for IRR. Or
did I miss something?

How should the following values be handeld? According to some info I
found somewhere, 0..2 should be considered illegal, which doesn't
quite match my computations though.

[-1.0, 1.0]
[-1000.0, 999.99]
[-1000.0, 999.0]
[0.0]
[]

Regards,
Thomas.

Isn't IRR defined as the discount rate that results in an NPV of 0?

···

2008/2/8 Matthew Moss <matthew.moss@gmail.com>:

> Internal Rate of Return (IRR -http://en.wikipedia.org/wiki/Internal_rate_of_return\) is a common financial
> metric, used by investment firms to predict the profitability of a company or
> project. Finding the IRR of a company amounts to solving for it in the equation
> for Net Present Value (NPV -http://en.wikipedia.org/wiki/Net_present_value\),
> another valuable decision-making metric:
>
> N C_t
> NPV = Σ ------------
> t=0 (1 + IRR)**t
>
> This week's quiz is to calculate the IRR for any given variable-length list of
> numbers, which represent yearly cash flows, the C_t's in the formula above: C_0,
> C_1, etc. (C_0 is typically a negative value, corresponding to the initial
> investment into the project.) From the example in the Wikipedia article
> (http://en.wikipedia.org/wiki/Internal_rate_of_return\), for instance, you should
> be able to produce a rate of 17.09% (to four decimal places, let's say) from
> this or a similar command:
>
> irr([-100,+30,+35,+40,+45])
> => 0.1709...

I think one point, which isn't brought out here and not well in the
wikipedia article either, is that given all of the C_t, you still have
two unknowns: IRR (which we are attempting to solve for) and NPV.

In this case, you want NPV set to zero in order to solve for IRR. Or
did I miss something?

Such cases have an undefined IRR, and thus the behavior is undefined.

Harrison Reiser

···

On Feb 9, 5:48 am, Alex Shulgin <alex.shul...@gmail.com> wrote:

On Feb 8, 4:01 pm, Ruby Quiz <ja...@grayproductions.net> wrote:

> Keep in mind that an IRR greater than 100% is possible. Extra credit if you can
> also correctly handle input that produces negative rates, disregarding the fact
> that they make no sense.

Can we return 1/0.0 if NPV does not evaluate to 0 for a reasonably
large IRR?

irr([+100,+10,+10,+10])
=> Infinity

Many thanks to James for your great job as the Quizmaster! :slight_smile:
--
Alex