class FinancialCalculator::Irr

Constants

NUM_ITERATIONS

The number of iterations to perform while attempting to minimize the root.

Attributes

result[R]

@return [Numeric] The internal rate of return @example

FinancialCalculator::Irr.new([-123400, 36200, 54800, 48100]).result.round(4) #=> Flt::DecNum('0.0596')

Public Class Methods

new(values, r_1 = nil, r_2 = nil) click to toggle source

Creates a new Irr instance @param [Array<Numeric>] values An array of cash flows. @param [Numeric] r_1 An optional first guess to use for the secant method. @param [Numeric] r_2 An optional second guess to use for the secant method. @return [FinancialClaculator::Irr] An Irr instance @see en.wikipedia.org/wiki/Internal_rate_of_return @api public

# File lib/financial_calculator/irr.rb, line 18
def initialize(values, r_1 = nil, r_2 = nil)
  unless (values[0].positive? ^ values[1].positive?) & values[0].nonzero?
    raise ArgumentError.new('The values do not converge') 
  end
  @eps = 1e-7
  @values = values.map { |val| Flt::DecNum(val.to_s) }

  r_1 ||= initial_r_1
  r_2 ||= initial_r_2

  @result = solve(@values, r_1, r_2)
end

Public Instance Methods

inspect() click to toggle source
# File lib/financial_calculator/irr.rb, line 31
def inspect
  "IRR(#{@result})"
end

Private Instance Methods

abs_c_0() click to toggle source
# File lib/financial_calculator/irr.rb, line 84
def abs_c_0
  @values[0].abs
end
cap_a() click to toggle source
# File lib/financial_calculator/irr.rb, line 80
def cap_a
  @cap_a ||= @values[1..-1].sum
end
cap_a_over_abs_cap_c_0() click to toggle source
# File lib/financial_calculator/irr.rb, line 76
def cap_a_over_abs_cap_c_0
  cap_a / abs_c_0
end
converged?(r_1, r_2) click to toggle source
# File lib/financial_calculator/irr.rb, line 96
def converged?(r_1, r_2)
  (r_1 - r_2).abs < @eps
end
initial_r_1() click to toggle source

Default first guess for use in the secant method @see en.wikipedia.org/wiki/Internal_rate_of_return#Numerical_solution_for_single_outflow_and_multiple_inflows

# File lib/financial_calculator/irr.rb, line 66
def initial_r_1
  @initial_r_1 ||= cap_a_over_abs_cap_c_0 ** (2 / Flt::DecNum(@values.length.to_s)) - 1
end
initial_r_2() click to toggle source

Default second guess for use in the secant method @see en.wikipedia.org/wiki/Internal_rate_of_return#Numerical_solution_for_single_outflow_and_multiple_inflows

# File lib/financial_calculator/irr.rb, line 72
def initial_r_2
  (1 + initial_r_1) ** p - 1
end
iterate(values, r_1, r_2) click to toggle source

Perform a single iteration @param [Array<Numeric>] values @param [DecNum] r_1 @param [DecNum] r_2 @return [DecNum] A rate of return that is closer to the actual internal rate of return

than the previous iteration
# File lib/financial_calculator/irr.rb, line 43
def iterate(values, r_1, r_2)
  fn_1 = Npv.new(r_1, values).result
  fn_2 = Npv.new(r_2, values).result

  r_1 - (fn_1 * (r_1 - r_2)) / (fn_1 - fn_2)
end
npv_1_in(rate) click to toggle source
# File lib/financial_calculator/irr.rb, line 92
def npv_1_in(rate)
  @flows ||= Npv.new(rate, [0] + @values[1..-1].flatten).result
end
p() click to toggle source
# File lib/financial_calculator/irr.rb, line 88
def p
  Flt::DecNum(Math.log(cap_a_over_abs_cap_c_0).to_s) / Flt::DecNum(Math.log(cap_a / npv_1_in(initial_r_1)).to_s)
end
solve(values, r_1, r_2) click to toggle source

Solve the IRR @param [Array<Numeric>] values @param [DecNum] r_1 @param [DecNum] r_2 @return [DecNum] The internal rate of return

# File lib/financial_calculator/irr.rb, line 55
def solve(values, r_1, r_2)
  NUM_ITERATIONS.times do
    break if r_1.infinite? || converged?(r_1, r_2)
    r_2, r_1 = [r_1, iterate(values, r_1, r_2)]
  end

  r_1.infinite? ? r_2 : r_1
end