Module: Xirr Abstract

Includes:
ActiveSupport::Configurable
Defined in:
lib/xirr.rb,
lib/xirr/tvm.rb,
lib/xirr/base.rb,
lib/xirr/bonds.rb,
lib/xirr/brent.rb,
lib/xirr/rates.rb,
lib/xirr/config.rb,
lib/xirr/rtsafe.rb,
lib/xirr/returns.rb,
lib/xirr/version.rb,
lib/xirr/cashflow.rb,
lib/xirr/periodic.rb,
lib/xirr/rtsafe_c.rb,
lib/xirr/bisection.rb,
lib/xirr/transaction.rb,
lib/xirr/depreciation.rb,
lib/xirr/newton_method.rb,
ext/xirr/xirr_native.c

Overview

This module is abstract.

adds a Cashflow and Transaction classes to calculate IRR of irregular transactions.

Calculates Xirr

Defined Under Namespace

Modules: Base, Bonds, Depreciation, Native, Rates, Returns, TVM Classes: Bisection, Brent, Cashflow, NewtonMethod, RtSafe, RtSafeC, Transaction

Constant Summary collapse

VERSION =

Version of the Gem

'1.0.0'

Class Method Summary collapse

Class Method Details

.irr(amounts, guess: 0.1) ⇒ Float

Internal rate of return of amounts at periods 0, 1, 2, …

Xirr.irr([-1000, 1100])         # => 0.1
Xirr.irr([-1000, 500, 500, 300]) # => 0.156579

Parameters:

  • amounts (Array<Numeric>)
  • guess (Float) (defaults to: 0.1)

    initial rate for the solver

Returns:

  • (Float)

    the rate per period

Raises:

  • (ArgumentError)

    when there aren't at least one inflow and one outflow



19
20
21
22
23
24
# File 'lib/xirr/periodic.rb', line 19

def irr(amounts, guess: 0.1)
  flows = validated_flows(amounts)
  rate = RtSafe.find(flows, guess: guess)
  raise ArgumentError, 'IRR did not converge' if rate.nil?
  rate
end

.mirr(amounts, finance_rate, reinvest_rate) ⇒ Float

Modified internal rate of return of periodic amounts. Positive flows are assumed reinvested at reinvest_rate, negative flows financed at finance_rate.

Xirr.mirr([-120_000, 39_000, 30_000, 21_000, 37_000, 46_000], 0.10, 0.12)
# => 0.126094

Parameters:

  • amounts (Array<Numeric>)
  • finance_rate (Numeric)
  • reinvest_rate (Numeric)

Returns:

  • (Float)


54
55
56
57
58
59
60
61
62
63
64
65
66
# File 'lib/xirr/periodic.rb', line 54

def mirr(amounts, finance_rate, reinvest_rate)
  values = validated_amounts(amounts)
  periods = values.length - 1

  future_of_inflows = values.each_with_index.inject(0.0) do |acc, (value, i)|
    value > 0 ? acc + value * (1.0 + reinvest_rate) ** (periods - i) : acc
  end
  present_of_outflows = values.each_with_index.inject(0.0) do |acc, (value, i)|
    value < 0 ? acc + value / (1.0 + finance_rate) ** i : acc
  end

  ((future_of_inflows / -present_of_outflows) ** (1.0 / periods) - 1).round(Xirr.config.precision)
end

.npv(rate, amounts) ⇒ Float

Net present value of amounts at periods 0, 1, 2, … discounted at rate. The first amount sits at period 0 and is left undiscounted, so Xirr.npv(Xirr.irr(a), a) comes out to roughly zero. This differs from a spreadsheet NPV, which places the first amount at period 1.

Xirr.npv(0.1, [-1000, 1100])      # => 0.0
Xirr.npv(0.1, [-1000, 600, 600])  # => 41.322314

Parameters:

  • rate (Numeric)
  • amounts (Array<Numeric>)

Returns:

  • (Float)


37
38
39
40
41
# File 'lib/xirr/periodic.rb', line 37

def npv(rate, amounts)
  amounts.each_with_index.inject(0.0) do |sum, (amount, i)|
    sum + amount.to_f / (1.0 + rate) ** i
  end.round(Xirr.config.precision)
end