Module: Finrb::Cashflow
- Included in:
- Array
- Defined in:
- lib/finrb/cashflows.rb,
sig/finrb.rbs
Overview
Provides methods for working with cash flows (collections of transactions)
Class Method Summary collapse
- .irr(cashflows, guess = nil) ⇒ decimal
- .mirr(cashflows, finance_rate:, reinvestment_rate:) ⇒ decimal
- .npv(cashflows, rate) ⇒ decimal
- .xirr(transactions, guess = nil) ⇒ Rate
- .xnpv(transactions, rate) ⇒ decimal
Instance Method Summary collapse
-
#irr(guess = nil) ⇒ Flt::DecNum
Calculate the per-period internal rate of return for an ordered sequence of equally spaced cashflows.
-
#mirr(finance_rate:, reinvestment_rate:) ⇒ Flt::DecNum
Calculate the modified internal rate of return for equally spaced cashflows using separate financing and reinvestment assumptions.
-
#npv(rate) ⇒ Flt::DecNum
calculate the net present value of a sequence of cash flows.
-
#xirr(guess = nil) ⇒ Rate
Calculate the effective annual internal rate of return for an ordered sequence of dated transactions.
-
#xnpv(rate) ⇒ Flt::DecNum
calculate the net present value of a sequence of cash flows.
Class Method Details
.irr(cashflows, guess = nil) ⇒ decimal
17 18 19 |
# File 'lib/finrb/cashflows.rb', line 17 def irr(cashflows, guess = nil) sequence(cashflows).irr(guess) end |
.mirr(cashflows, finance_rate:, reinvestment_rate:) ⇒ decimal
25 26 27 |
# File 'lib/finrb/cashflows.rb', line 25 def mirr(cashflows, finance_rate:, reinvestment_rate:) sequence(cashflows).mirr(finance_rate:, reinvestment_rate:) end |
.npv(cashflows, rate) ⇒ decimal
21 22 23 |
# File 'lib/finrb/cashflows.rb', line 21 def npv(cashflows, rate) sequence(cashflows).npv(rate) end |
.xirr(transactions, guess = nil) ⇒ Rate
29 30 31 |
# File 'lib/finrb/cashflows.rb', line 29 def xirr(transactions, guess = nil) sequence(transactions).xirr(guess) end |
.xnpv(transactions, rate) ⇒ decimal
33 34 35 |
# File 'lib/finrb/cashflows.rb', line 33 def xnpv(transactions, rate) sequence(transactions).xnpv(rate) end |
Instance Method Details
#irr(guess = nil) ⇒ Flt::DecNum
Calculate the per-period internal rate of return for an ordered sequence of equally spaced cashflows.
For cashflows with multiple sign-changing roots, the guess determines which nearby root is selected. Rates must be greater than -1.
61 62 63 64 65 66 67 68 |
# File 'lib/finrb/cashflows.rb', line 61 def irr(guess = nil) validate_numeric_cashflows! # Make sure we have a valid sequence of cash flows. raise(InvalidCashflowError, 'Cashflow needs at least one positive and one negative value.') if none?(&:positive?) || none?(&:negative?) solve(:npv, valid(guess)) end |
#mirr(finance_rate:, reinvestment_rate:) ⇒ Flt::DecNum
Calculate the modified internal rate of return for equally spaced cashflows using separate financing and reinvestment assumptions.
93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 |
# File 'lib/finrb/cashflows.rb', line 93 def mirr(finance_rate:, reinvestment_rate:) validate_numeric_cashflows! raise(InvalidCashflowError, 'MIRR requires at least two cashflows.') if size < 2 cashflows = map { |entry| Validation.decimal(entry, name: 'cashflow amount') } raise(InvalidCashflowError, 'Cashflow needs at least one positive and one negative value.') if cashflows.none?(&:positive?) || cashflows.none?(&:negative?) finance_rate = Validation.decimal_greater_than(finance_rate, minimum: -1, name: 'finance rate', error: DomainError) reinvestment_rate = Validation.decimal_greater_than(reinvestment_rate, minimum: -1, name: 'reinvestment rate', error: DomainError) last_period = cashflows.size - 1 future_positive = cashflows.each_with_index.sum do |amount, index| amount.positive? ? amount * ((reinvestment_rate + 1)**(last_period - index)) : Flt::DecNum(0) end present_negative = cashflows.each_with_index.sum do |amount, index| amount.negative? ? amount / ((finance_rate + 1)**index) : Flt::DecNum(0) end ((future_positive / -present_negative)**(Flt::DecNum(1) / last_period)) - 1 end |
#npv(rate) ⇒ Flt::DecNum
calculate the net present value of a sequence of cash flows
76 77 78 79 80 81 82 83 84 85 86 87 88 |
# File 'lib/finrb/cashflows.rb', line 76 def npv(rate) validate_numeric_cashflows! cashflows = map { |entry| Validation.decimal(entry, name: 'cashflow amount') } rate = Validation.decimal_greater_than(rate, minimum: -1, name: 'rate', error: DomainError) total = Flt::DecNum.new(0.to_s) cashflows.each_with_index do |cashflow, index| total += cashflow / ((rate + 1)**index) end total end |
#xirr(guess = nil) ⇒ Rate
Calculate the effective annual internal rate of return for an ordered sequence of dated transactions.
Under the default configuration, date offsets are actual calendar days
from the first transaction and a 365-day year is used. Transactions
should be supplied chronologically and their dates must respond to
to_date. For multiple roots, the guess determines which nearby root is
selected. Rates must be greater than -1.
137 138 139 140 141 142 143 144 145 |
# File 'lib/finrb/cashflows.rb', line 137 def xirr(guess = nil) validate_dated_cashflows! # Make sure we have a valid sequence of cash flows. raise(InvalidCashflowError, 'Cashflow needs at least one positive and one negative value.') if none? { |transaction| transaction.amount.positive? } || none? { |transaction| transaction.amount.negative? } rate = solve(:xnpv, valid(guess)) Rate.new(rate, :effective, compounds: Finrb.config.periodic_compound ? :continuously : :annually) end |
#xnpv(rate) ⇒ Flt::DecNum
calculate the net present value of a sequence of cash flows
155 156 157 158 159 160 161 162 |
# File 'lib/finrb/cashflows.rb', line 155 def xnpv(rate) validate_dated_cashflows! rate = Validation.decimal_greater_than(rate, minimum: -1, name: 'rate', error: DomainError) sum do |t| t.amount / ((rate + 1)**(date_diff(start, t.date) / days_in_period)) end end |