[QUIZ] Internal Rate of Return (#156)

S

Sander Land

Here is my golfed solution, using Newton's method.
No sanity checks for the input or anything, but usually gives a result
which is accurate to >10 digits.

def irr(i)
x=1;9.times{x=x-(n=d=t=0;i.map{|e|n+=_=e*x**t-=1;d+=t*_/x};n/d)};x-1
end

And James: So long, and thanks for all the quiz'.
 
J

Jesse Merriman

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

I've taken out the net-negative check, since it was throwing out cases where
there is a solution (like [-1000, 999]). I was trying to toss out the cases
where the NVP graph is increasing past -1 (like [-100, -30, 35, -40, -45]).
How can these be detected?

-Jesse Merriman
 
F

Frank Fischer

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
 
J

Jesse Merriman

Agh, hate to keep replying to myself, but I've made one more tweak (in the
pastie) to manually check for increasing NVPs.

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

I've taken out the net-negative check, since it was throwing out cases where
there is a solution (like [-1000, 999]). I was trying to toss out the cases
where the NVP graph is increasing past -1 (like [-100, -30, 35, -40, -45]).
How can these be detected?

-Jesse Merriman
 
P

Paolo Bonzini

Mine is less concise, but it uses, guess what, Newton's method.

# compute NPV given cash flows and IRR
def npv (cf, irr)
(0...cf.length).inject(0) { |npv, t| npv + (cf[t]/(1+irr)**t) }
end

# compute derivative of the NPV with respect to IRR
# d(C_t * (1+IRR)**t)/dIRR = -t * C_t / (1+IRR)**(t-1)
#
def dnpv (cf, irr)
(1...cf.length).inject(0) { |npv, t| npv - (t*cf[t]/
(1+irr)**(t-1)) }
end

# solve for IRR with newton's method: x_{n+1} = x_n - f(x) / f'(x)
def irr (cf)
irr = 0.5
it = 0
begin
begin
oirr = irr
irr -= npv(cf, irr) / dnpv(cf, irr)
it += 1
return nil if it > 50
end until (irr - oirr).abs < 0.0001
rescue ZeroDivisionError
return nil
end
irr
end

puts irr([-100,30,35,40,45])
puts irr([-1.0,1.0])
puts irr([-1000.0,999.99])
puts irr([-1000.0,999.0])
puts irr([100,10,10,10])
puts irr([0.0])
puts irr([])
 
C

Carl Porth

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=3D10 ** -4)

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

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

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

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

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

irr.round_to(precision)
end

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

The three rules of RubyQuiz:

1. Please do not post any solutions or spoiler discussion for thisquizunt= il
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 eve= ryone
on Ruby Talk follow the discussion. Please reply to the originalquizmessa= ge,
if you can.

-=3D-=3D-=3D-=3D-=3D-=3D-=3D-=3D-=3D-=3D-=3D-=3D-=3D-=3D-=3D-=3D-=3D-=3D-= =3D-=3D-=3D-=3D-=3D-=3D-=3D-=3D-=3D-=3D-=3D-=3D-=3D-=3D-=3D-=3D-=3D-=3D-=3D-=
=3D-=3D-=3D

by Harrison Reiser

Internal Rate of Return (IRR -http://en.wikipedia.org/wiki/Internal_rate_o=
f_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 equ= ation
for Net Present Value (NPV -http://en.wikipedia.org/wiki/Net_present_value= ),
another valuable decision-making metric:

N C_t
NPV =3D =D3 ------------
t=3D0 (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) fr= om
this or a similar command:

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

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

ThoML

x=1;9.times{x=x-(n=d=t=0;i.map{|e|n+=_=e*x**t-=1;d+=t*_/x};n/d)};x-1

Well, my solution is slightly more verbose. Which doesn't imply it
yields better results. It uses the Secant method (which no other
solution has used yet, ha!) and the code in the core method was
shamelessly copied from wikipedia. Yeah! By default, my solution tries
to find values that converge with with Float::EPSILON. If the values
diverge, the interval is scanned for zero-values, which could also be
done in the first place (since this allows to find multiple roots).

Regards,
Thomas.



#!/usr/bin/env ruby19

require 'date'

module IRR
module_function

# Use Secant method to find the roots. In case, the upper and
lower
# limit diverge, reset the start values.
#
# This method may miss certain IRRs outside the [0.0..1.0]
# intervall. In such a case use #irrer or set the optional :a
# (lower limit) and :b (upper limit) arguments.
#
# Based on:
# http://en.wikipedia.org/w/index.php?title=Secant_method
def irr(values, args={})
a = a0 = args[:a] || 0.0
b = b0 = args[:b] || 1.0
n = args[:n] || 100
e = args[:e] || Float::EPSILON
c0 = (a - b).abs
ab = nil
n.times do
fa = npv(values, a)
fb = npv(values, b)
c = (a - b) / (fa - fb) * fa;
if c.nan?
does_not_compute(values)
elsif c.infinite?
# break
return c
elsif c.abs < e
return a
end
# Protect against bad start values.
if c.abs > c0
ab ||= guess_start_values(values, args.merge:)min =>
a0, :max => b0))
if !ab.empty?
a, b, _ = ab.shift
c0 = (a - b).abs
next
end
end
b = a
a = a - c
c0 = c.abs
end
does_not_compute(values)
end

# Guess appropriate start values, return all solutions as Array.
def irrer(values, args={})
guess_start_values(values, args).map do |a, b|
irr(values, args.merge:)a => a, :b => b))
end
end

# Calculate the NPV for a hypothetical IRR.
# Values are either an array of cash flows or of pairs [cash,
# date or days].
def npv(values, irr)
sum = 0
d0 = nil
values.each_with_index do |(v, d), t|
# I have no idea if this is the right way to deal with
# irregular time series.
if d
if d0
t = (d - d0).to_f / 365.25
else
d0 = d
end
end
sum += v / (1 + irr) ** t
end
sum
end

def does_not_compute(values)
raise RuntimeError, %{Does not compute: %s} % values.inspect
end

# Check whether computation will converge easily.
def check_values(values)
csgn = 0
val, dat = values[-1]
values.reverse.each do |v, d|
csgn += 1 if val * v < 0
val = v
end
return csgn == 1
end

# Try to find appropriate start values.
def guess_start_values(values, args={})
min = args[:min] || -1.0
max = args[:max] || 2.0
delta = args[:delta] || (max - min).to_f / (args[:steps] ||
100)
vals = []
b, fb = nil
# The NPV is undefined for IRR < -100% or so they say.
min.step(max, delta) do |a|
fa = npv(values, a)
if fb and !fa.infinite? and !fb.infinite? and fa * fb < 0
vals << [b, a]
end
b = a
fb = fa
end
return vals
end

end


if __FILE__ == $0
values = ARGV.map do |e|
v, d = e.split(/,/)
v = v.to_f
d ? [v, Date.parse(d)] : v
end
puts "Default solution: #{IRR.irr(values) rescue puts $!.message}"
begin
IRR.irrer(values).zip(IRR.guess_start_values(values)) do |irr,
(a, b)|
puts '[%5.2f..%5.2f] %13.10f -> %13.10f' % [a, b, irr,
IRR.npv(values, irr)]
end
rescue RuntimeError => e
puts e.message
end
puts "Possibly incorrect IRR value(s)" unless
IRR.check_values(values)
end
 
A

Andrés Suárez

My solution, looping the Bolzano's Theorem:


class Irr

attr_accessor :data, :irr

def initialize(data)
@data = data
@irr = 0
irr_calculus
end

def npv_calculus(rate)
npv = 0
@data.each do |c|
t = @data.index(c)
npv = npv + c / (1 + rate)**t
end
npv
end

def irr_calculus
r1 = 0.0
r2 = 1.0
npv1 = npv_calculus(r1)
npv2 = npv_calculus(r2)

# calcule initial interval
while npv1*npv2 > 0
r1 = r1 + 1
r2 = r2 + 1
npv1 = npv_calculus(r1)
npv2 = npv_calculus(r2)
end

# halfing interval to achieve precission
value = 1
while value > (1.0/10**4)
r3 = (r1+r2)/2
npv3 = npv_calculus(r3)
if npv1*npv3 < 0
r2 = r3
npv2 = npv3
else
r1 = r3
npv1 = npv3
end
value = (r1-r2).abs
end

@irr = (r1*10000).round/10000.0
end

end


data = [-100, 30, 35, 40, 45]
i = Irr.new(data)
puts i.irr
 
J

Jesús Gabriel y Galán

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!

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.
 
A

Alex Shulgin

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

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. :)

#!/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
 
J

Jason Dew

Here's my solution:

irr.rb:

require 'algebra'

class IRR

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

private

def self.function(profits)
Algebra::Function.new do |x|
sumands =3D 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=3D0.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, :t=
olerance

def initialize(differentiation_method=3DNewtonsDifferenceQuotient,
root_method=3DNewtonsMethod, &block)
@definition =3D block
@differentiation_method, @root_method =3D differentiation_method,
root_method
@maximum_iterations =3D 1000
@tolerance =3D 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=3D0)
recursive_zero(initial_value, 1)
end

private

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

better_guess =3D @root_method.calculate(self, guess)

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

end

end

Comments welcomed. Thanks,

The three rules of Ruby Quiz:

1. Please do not post any solutions or spoiler discussion for this quiz u= ntil
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 eve= ryone
on Ruby Talk follow the discussion. Please reply to the original quiz mes= sage,
if you can.

-=3D-=3D-=3D-=3D-=3D-=3D-=3D-=3D-=3D-=3D-=3D-=3D-=3D-=3D-=3D-=3D-=3D-=3D-= =3D-=3D-=3D-=3D-=3D-=3D-=3D-=3D-=3D-=3D-=3D-=3D-=3D-=3D-=3D-=3D-=3D-=3D-=3D-=
=3D-=3D-=3D

by Harrison Reiser

Internal Rate of Return (IRR -http://en.wikipedia.org/wiki/Internal_rate_o=
f_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 equ= ation
for Net Present Value (NPV -http://en.wikipedia.org/wiki/Net_present_value= ),
another valuable decision-making metric:

N C_t
NPV =3D =D3 ------------
t=3D0 (1 + IRR)**t

This week's quiz is to calculate the IRR for any given variable-length lis= t 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) fr= om
this or a similar command:

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

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

James Gray

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.

Thanks so much to you for participating in many of the problems.

James Edward Gray II=
 
A

Adam Shelly

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

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.

------
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
 
J

James Gray

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

Thank you Adam. It's great to hear from another regular.

James Edward Gray II
 
S

Serg Koren

What's the best way to find all occurances of a string in a hash's
index?

That is,

x = { 'Hello' => 1, 'Goodbye' => 2, 'Hello there' => 3}

How do I most efficiently get 1 and 3 to return on a search for
'Hello' in the index of the hash? I know I can iterate over the
index, but is there a better way?

Thanks.
 
E

Eric I.

What's the best way to find all occurances of a string in a hash's  
index?

That is,

x = { 'Hello' => 1, 'Goodbye' => 2, 'Hello there' => 3}

How do I most efficiently get  1 and 3 to return on a search for  
'Hello' in the index of the hash?  I know I can iterate over the  
index, but is there a better way?

Thanks.

Hi Serg,

Here's one way to do it:


x = { 'Hello' => 1, 'Goodbye' => 2, 'Hello there' => 3}

result = x.keys.select { |k| k.include? "Hello" }.map { |k| x[k] }
p result


There's really no way to avoid iterating over the keys in the hash.
In this case, Enumerable#select is doing that when it produces an
array of the keys that contain "Hello". That array of keys is
converted into an array of values by using Enumerable#map.

Eric

P.S. It looks like you replied to a posting for Ruby Quiz #156. Since
this is unrelated to the Ruby Quiz, it would have been preferable to
make a new post.

====

Are you interested in on-site Ruby training that uses well-designed,
real-world, hands-on exercises? http://LearnRuby.com
 
S

Serg Koren

oops i took the easy way and replied. sorry.
Thanks for the help/explanation. I was hoping there was a shorter/
more efficient way than iterating maybe a nested hash of some sort.

Guess I'll settle for what we have.
Thanks much.

What's the best way to find all occurances of a string in a hash's
index?

That is,

x = { 'Hello' => 1, 'Goodbye' => 2, 'Hello there' => 3}

How do I most efficiently get 1 and 3 to return on a search for
'Hello' in the index of the hash? I know I can iterate over the
index, but is there a better way?

Thanks.

Hi Serg,

Here's one way to do it:


x = { 'Hello' => 1, 'Goodbye' => 2, 'Hello there' => 3}

result = x.keys.select { |k| k.include? "Hello" }.map { |k| x[k] }
p result


There's really no way to avoid iterating over the keys in the hash.
In this case, Enumerable#select is doing that when it produces an
array of the keys that contain "Hello". That array of keys is
converted into an array of values by using Enumerable#map.

Eric

P.S. It looks like you replied to a posting for Ruby Quiz #156. Since
this is unrelated to the Ruby Quiz, it would have been preferable to
make a new post.

====

Are you interested in on-site Ruby training that uses well-designed,
real-world, hands-on exercises? http://LearnRuby.com
 
T

Todd Benson

What's the best way to find all occurances of a string in a hash's
index?

That is,

x = { 'Hello' => 1, 'Goodbye' => 2, 'Hello there' => 3}

How do I most efficiently get 1 and 3 to return on a search for
'Hello' in the index of the hash? I know I can iterate over the
index, but is there a better way?

Thanks.

Hi Serg,

Here's one way to do it:


x = { 'Hello' => 1, 'Goodbye' => 2, 'Hello there' => 3}

result = x.keys.select { |k| k.include? "Hello" }.map { |k| x[k] }
p result

I could only come up with an iterative solution also...

h = Hash['Hello', 1, 'Goodbye', 2, 'Hello there', 3]
a = h.select {|k, v| k=~/Hello/}.map {|v| v[1]}
p a
 

Ask a Question

Want to reply to this thread or ask your own question?

You'll need to choose a username for the site, which only take a couple of moments. After that, you can post your question and our members will help you out.

Ask a Question

Members online

No members online now.

Forum statistics

Threads
473,995
Messages
2,570,230
Members
46,819
Latest member
masterdaster

Latest Threads

Top