Module: Finrb::Cashflow

Included in:
Array
Defined in:
lib/finrb/cashflows.rb

Overview

Provides methods for working with cash flows (collections of transactions)

Defined Under Namespace

Classes: Function

Instance Method Summary collapse

Dynamic Method Handling

This class handles dynamic methods through the method_missing method

#method_missing(name, *args, &block) ⇒ Object



61
62
63
64
65
# File 'lib/finrb/cashflows.rb', line 61

def method_missing(name, *args, &block)
  return sum if name.to_s == 'sum'

  super
end

Instance Method Details

#irr(guess = nil) ⇒ Flt::DecNum

calculate the internal rate of return for a sequence of cash flows

Examples:

[-4000,1200,1410,1875,1050].irr #=> 0.143

Parameters:

  • guess (Numeric) (defaults to: nil)

    Initial guess rate, Defaults to 1.0

Returns:

  • (Flt::DecNum)

    the internal rate of return

Raises:

  • (ArgumentError)

See Also:



50
51
52
53
54
55
56
57
58
59
# File 'lib/finrb/cashflows.rb', line 50

def irr(guess = nil)
  # Make sure we have a valid sequence of cash flows.
  positives, negatives = partition { |i| i >= 0 }
  raise(ArgumentError, 'Calculation does not converge.') if positives.empty? || negatives.empty?

  func = Function.new(self, :npv)
  rate = [valid(guess)]
  nlsolve(func, rate)
  rate[0]
end

#npv(rate) ⇒ Flt::DecNum

calculate the net present value of a sequence of cash flows

Examples:

[-100.0, 60, 60, 60].npv(0.1) #=> 49.211

Parameters:

  • rate (Numeric)

    the discount rate to be applied

Returns:

  • (Flt::DecNum)

    the net present value

See Also:



78
79
80
81
82
83
84
85
86
87
88
# File 'lib/finrb/cashflows.rb', line 78

def npv(rate)
  cashflows = map { |entry| Flt::DecNum.new(entry.to_s) }

  rate = Flt::DecNum.new(rate.to_s)
  total = Flt::DecNum.new(0.to_s)
  cashflows.each_with_index do |cashflow, index|
    total += cashflow / ((rate + 1)**index)
  end

  total
end

#respond_to_missing?(name, include_private = false) ⇒ Boolean

Returns:

  • (Boolean)


67
68
69
# File 'lib/finrb/cashflows.rb', line 67

def respond_to_missing?(name, include_private = false)
  name.to_s == 'sum' || super
end

#xirr(guess = nil) ⇒ Rate

calculate the internal rate of return for a sequence of cash flows with dates @param Initial guess rate, Deafults to 1.0

Examples:

@transactions = []
@transactions << Transaction.new(-1000, :date => Time.new(1985,01,01))
@transactions << Transaction.new(  600, :date => Time.new(1990,01,01))
@transactions << Transaction.new(  600, :date => Time.new(1995,01,01))
@transactions.xirr(0.6) #=> Rate("0.024851", :apr, :compounds => :annually)

Returns:

  • (Rate)

    the internal rate of return



100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
# File 'lib/finrb/cashflows.rb', line 100

def xirr(guess = nil)
  # Make sure we have a valid sequence of cash flows.
  positives, negatives = partition { |t| t.amount >= 0 }
  if positives.empty? || negatives.empty?
    raise(
      ArgumentError,
      'Calculation does not converge. Cashflow needs to have a least one positive and one negative value.'
    )
  end

  func = Function.new(self, :xnpv)
  rate = [valid(guess)]
  nlsolve(func, rate)
  Rate.new(rate[0], :apr, compounds: Finrb.config.periodic_compound ? :continuously : :annually)
end

#xnpv(rate) ⇒ Flt::DecNum

calculate the net present value of a sequence of cash flows

Examples:

@transactions = []
@transactions << Transaction.new(-1000, :date => Time.new(1985,01,01))
@transactions << Transaction.new(  600, :date => Time.new(1990,01,01))
@transactions << Transaction.new(  600, :date => Time.new(1995,01,01))
@transactions.xnpv(0.6).round(2) #=> -937.41

Returns:

  • (Flt::DecNum)


125
126
127
128
129
130
131
# File 'lib/finrb/cashflows.rb', line 125

def xnpv(rate)
  rate = Flt::DecNum.new(rate.to_s)

  sum do |t|
    t.amount / ((rate + 1)**(date_diff(start, t.date) / days_in_period))
  end
end