From: Christopher Dicely Date: 2008-02-11T01:53:29+09:00 Subject: Re: [QUIZ] Internal Rate of Return (#156) Here's my solution. Like others, it uses Newton's method. It may have problems because it always uses zero as its initial estimate, though all the test data I've thrown at it seems to work properly. The one thing that is different than the other solutions I've seen posted is that I wrapped everything up in a class. This is also my first quiz. class IncomeStream DEFAULT_TOLERANCE = 0.000_000_05 def self.irr(*flows) self.new(flows).irr end def self.npv(discount_rate, *flows) self.new(flows).npv(discount_rate) end def self.dpv(amount, time) lambda {|rate| (-time) * amount * (1+rate)**(-time-1)} end def self.pv(amount, time) lambda {|rate| amount * (1+rate)**(-time)} end def initialize(flows) @flows = flows.to_a if (not @flows.empty?) and (@flows.first.kind_of? Numeric) @flows.each_index {|idx| @flows[idx]=[@flows[idx],idx]} end @pvs = @flows.map {|amount, time| IncomeStream.pv(amount, time)} @dpvs = @flows.map {|amount, time| IncomeStream.dpv(amount, time)} end def npv(rate) @pvs.inject(0) {|npv, item| npv+item.call(rate)} end def dnpv(rate) @dpvs.inject(0) {|dnpv, item| dnpv+item.call(rate)} end def irr(tolerance=DEFAULT_TOLERANCE) return nil if @flows.empty? or @flows.all? {|amount, time| amount = 0} rate = 0.0 while (((n=npv(rate)).abs > tolerance) and rate.finite?) d = dnpv(rate) rate -= n/d end rate end