Module: Xirr::Returns
- Defined in:
- lib/xirr/returns.rb
Overview
Performance and risk metrics: Returns.volatility, Returns.cagr, Returns.payback_period, Returns.discounted_payback_period, Returns.profitability_index, and Returns.twr.
The cash-flow functions follow the same convention as npv: the initial outlay sits at index 0 (undiscounted) and later flows fall at periods 1, 2, ….
Class Method Summary collapse
-
.cagr(begin_value, end_value, years, precision: Xirr.config.precision) ⇒ Float
Compound annual growth rate — the constant yearly rate that grows
begin_valueintoend_valueoveryears. -
.discounted_payback_period(cash_flows, rate, precision: Xirr.config.precision) ⇒ Float
Like Returns.payback_period, but recovers the outlay from cash flows discounted at
rate, so it accounts for the time value of money. -
.payback_period(cash_flows, precision: Xirr.config.precision) ⇒ Float
Payback period — how many periods of cash flow it takes to recover the initial outlay, interpolating within the recovering period.
-
.profitability_index(cash_flows, rate, precision: Xirr.config.precision) ⇒ Float
Profitability index — the present value of future inflows per unit of initial investment, discounted at
rate. -
.twr(period_returns, periods_per_year: nil, precision: Xirr.config.precision) ⇒ Float
Time-weighted return — period returns linked geometrically, +∏(1 + rᵢ) − 1+.
-
.volatility(prices, periods_per_year: 252, returns: :simple, precision: Xirr.config.precision) ⇒ Float
Annualised volatility of a price series — the sample standard deviation of its period-over-period returns, scaled up by
√periods_per_year.
Class Method Details
.cagr(begin_value, end_value, years, precision: Xirr.config.precision) ⇒ Float
Compound annual growth rate — the constant yearly rate that grows
begin_value into end_value over years.
27 28 29 30 31 32 33 |
# File 'lib/xirr/returns.rb', line 27 def cagr(begin_value, end_value, years, precision: Xirr.config.precision) if begin_value <= 0 || years <= 0 || end_value.to_f / begin_value < 0 raise ArgumentError, 'undefined' end round_value((end_value.to_f / begin_value)**(1.0 / years) - 1, precision) end |
.discounted_payback_period(cash_flows, rate, precision: Xirr.config.precision) ⇒ Float
Like payback_period, but recovers the outlay from cash flows discounted
at rate, so it accounts for the time value of money.
46 47 48 |
# File 'lib/xirr/returns.rb', line 46 def discounted_payback_period(cash_flows, rate, precision: Xirr.config.precision) recovery(discount_flows(cash_flows, rate), precision) end |
.payback_period(cash_flows, precision: Xirr.config.precision) ⇒ Float
Payback period — how many periods of cash flow it takes to recover the initial outlay, interpolating within the recovering period. The first amount is the outlay (negative), the rest are inflows.
39 40 41 |
# File 'lib/xirr/returns.rb', line 39 def payback_period(cash_flows, precision: Xirr.config.precision) recovery(cash_flows, precision) end |
.profitability_index(cash_flows, rate, precision: Xirr.config.precision) ⇒ Float
Profitability index — the present value of future inflows per unit of
initial investment, discounted at rate. Above 1 means the project adds
value. Equivalent to +1 + NPV / initial investment+.
54 55 56 57 58 59 60 |
# File 'lib/xirr/returns.rb', line 54 def profitability_index(cash_flows, rate, precision: Xirr.config.precision) raise ArgumentError, 'need at least one flow' if cash_flows.empty? raise ArgumentError, 'the first flow must be an outlay (negative)' if cash_flows.first >= 0 npv = Xirr.npv(rate, cash_flows) round_value(1 + npv / -cash_flows.first, precision) end |
.twr(period_returns, periods_per_year: nil, precision: Xirr.config.precision) ⇒ Float
Time-weighted return — period returns linked geometrically,
+∏(1 + rᵢ) − 1+. Immune to the timing of cash flows. Pass
periods_per_year to annualise.
66 67 68 69 70 71 |
# File 'lib/xirr/returns.rb', line 66 def twr(period_returns, periods_per_year: nil, precision: Xirr.config.precision) raise ArgumentError, 'need at least one return' if period_returns.empty? raise ArgumentError, 'returns must be numbers' unless period_returns.all? { |r| r.is_a?(Numeric) } round_value(time_weighted(period_returns, periods_per_year), precision) end |
.volatility(prices, periods_per_year: 252, returns: :simple, precision: Xirr.config.precision) ⇒ Float
Annualised volatility of a price series — the sample standard deviation of
its period-over-period returns, scaled up by √periods_per_year. Needs at
least three positive prices, in time order.
17 18 19 20 21 22 |
# File 'lib/xirr/returns.rb', line 17 def volatility(prices, periods_per_year: 252, returns: :simple, precision: Xirr.config.precision) rets = period_returns(prices, returns) raise ArgumentError, 'need at least three prices' if rets.length < 2 round_value(annualise(rets, periods_per_year), precision) end |