class Xirr::Cashflow

Expands [Array] to store a set of transactions which will be used to calculate the XIRR @note A Cashflow should consist of at least two transactions, one positive and one negative.

Attributes

fallback[R]
iteration_limit[R]
options[R]
raise_exception[R]

Public Class Methods

new(flow: [], period: Xirr::PERIOD, ** options) click to toggle source

@param args [Transaction] @example Creating a Cashflow

cf = Cashflow.new
cf << Transaction.new( 1000, date: '2013-01-01'.to_date)
cf << Transaction.new(-1234, date: '2013-03-31'.to_date)
Or
cf = Cashflow.new Transaction.new( 1000, date: '2013-01-01'.to_date), Transaction.new(-1234, date: '2013-03-31'.to_date)
# File lib/xirr/cashflow.rb, line 15
def initialize(flow: [], period: Xirr::PERIOD, ** options)
  @period   = period
  @fallback = options[:fallback] || Xirr::FALLBACK
  @options  = options
  self << flow
  self.flatten!
end

Public Instance Methods

<<(arg) click to toggle source
Calls superclass method
# File lib/xirr/cashflow.rb, line 119
def << arg
  super arg
  self.sort! { |x, y| x.date <=> y.date }
  self
end
compact_cf() click to toggle source
# File lib/xirr/cashflow.rb, line 95
def compact_cf
  # self
  compact = Hash.new 0
  self.each { |flow| compact[flow.date] += flow.amount }
  Cashflow.new flow: compact.map { |key, value| Transaction.new(value, date: key) }, options: options, period: period
end
invalid?() click to toggle source

Check if Cashflow is invalid @return [Boolean]

# File lib/xirr/cashflow.rb, line 25
def invalid?
  inflow.empty? || outflows.empty?
end
invalid_message() click to toggle source

@return [String] Error message depending on the missing transaction

# File lib/xirr/cashflow.rb, line 110
def invalid_message
  return 'No positive transaction' if inflow.empty?
  return 'No negative transaction' if outflows.empty?
end
irr_guess() click to toggle source

Calculates a simple IRR guess based on period of investment and multiples. @return [Float]

# File lib/xirr/cashflow.rb, line 49
def irr_guess
  return @irr_guess = 0.0 if periods_of_investment.zero?
  @irr_guess = valid? ? ((multiple ** (1 / periods_of_investment)) - 1).round(3) : false
  @irr_guess == 1.0/0 ? 0.0 : @irr_guess
end
max_date() click to toggle source

Last investment date @return [Time]

# File lib/xirr/cashflow.rb, line 43
def max_date
  @max_date ||= self.map(&:date).max
end
min_date() click to toggle source

First investment date @return [Time]

# File lib/xirr/cashflow.rb, line 104
def min_date
  @min_date ||= self.map(&:date).min
end
other_calculation_method(method) click to toggle source
# File lib/xirr/cashflow.rb, line 91
def other_calculation_method(method)
  method == :newton_method ? :bisection : :newton_method
end
period() click to toggle source
# File lib/xirr/cashflow.rb, line 115
def period
  @temporary_period || @period
end
process_options(method, options) click to toggle source
# File lib/xirr/cashflow.rb, line 70
def process_options(method, options)
  @temporary_period         = options[:period]
  options[:raise_exception] ||= @options[:raise_exception] || Xirr::RAISE_EXCEPTION
  options[:iteration_limit] ||= @options[:iteration_limit] || Xirr::ITERATION_LIMIT
  return switch_fallback(method), options
end
sum() click to toggle source

@return [Float] Sums all amounts in a cashflow

# File lib/xirr/cashflow.rb, line 37
def sum
  self.map(&:amount).sum
end
switch_fallback(method) click to toggle source

If method is defined it will turn off fallback it return either the provided method or the system default @param method [Symbol] @return [Symbol]

# File lib/xirr/cashflow.rb, line 81
def switch_fallback method
  if method
    @fallback = false
    method
  else
    @fallback = Xirr::FALLBACK
    Xirr::DEFAULT_METHOD
  end
end
valid?() click to toggle source

Inverse of invalid? @return [Boolean]

# File lib/xirr/cashflow.rb, line 31
def valid?
  !invalid?
end
xirr(guess: nil, method: nil, ** options) click to toggle source

@param guess [Float] @param method [Symbol] @return [Float]

# File lib/xirr/cashflow.rb, line 58
def xirr(guess: nil, method: nil, ** options)
  method, options = process_options(method, options)
  if invalid?
    raise ArgumentError, invalid_message if options[:raise_exception]
    BigDecimal(0, Xirr::PRECISION)
  else
    xirr = choose_(method).send :xirr, guess, options
    xirr = choose_(other_calculation_method(method)).send(:xirr, guess, options) if (xirr.nil? || xirr.nan?) && fallback
    xirr || Xirr::REPLACE_FOR_NIL
  end
end

Private Instance Methods

choose_(method) click to toggle source

@param method [Symbol] Choose a Method to call. @return [Class]

# File lib/xirr/cashflow.rb, line 130
def choose_(method)
  case method
    when :bisection
      Bisection.new compact_cf
    when :newton_method
      NewtonMethod.new compact_cf
    else
      raise ArgumentError, "There is no method called #{method} "
  end
end
first_transaction_direction() click to toggle source

@api private Sorts the {Cashflow} by date ascending

and finds the signal of the first transaction.

This implies the first transaction is a disbursement @return [Integer]

# File lib/xirr/cashflow.rb, line 146
def first_transaction_direction
  # self.sort! { |x, y| x.date <=> y.date }
  @first_transaction_direction ||= self.first.amount / self.first.amount.abs
end
first_transaction_positive?() click to toggle source
# File lib/xirr/cashflow.rb, line 160
def first_transaction_positive?
  first_transaction_direction > 0
end
inflow() click to toggle source

@api private @return [Array] @see outflows Selects all positives transactions from Cashflow

# File lib/xirr/cashflow.rb, line 175
def inflow
  self.select { |x| x.amount * first_transaction_direction < 0 }
end
multiple() click to toggle source

Based on the direction of the first investment finds the multiple cash-on-cash @example

[100,100,-300] and [-100,-100,300] returns 1.5

@api private @return [Float]

# File lib/xirr/cashflow.rb, line 156
def multiple
  inflow.sum(&:amount).abs / outflows.sum(&:amount).abs
end
outflows() click to toggle source

@api private @return [Array] @see inflow Selects all negatives transactions from Cashflow

# File lib/xirr/cashflow.rb, line 183
def outflows
  self.select { |x| x.amount * first_transaction_direction > 0 }
end
periods_of_investment() click to toggle source

@api private Counts how many years from first to last transaction in the cashflow @return

# File lib/xirr/cashflow.rb, line 167
def periods_of_investment
  (max_date - min_date) / period
end