Finance.Returns (finance v1.4.0)

Copy Markdown View Source

Performance and risk metrics.

Risk is covered by volatility/2 (the annualised standard deviation of a price series' returns). The return metrics are cagr/4 (compound annual growth rate), payback_period/2 and discounted_payback_period/3 (time to recover an outlay), profitability_index/3 (present value of inflows per unit invested), and twr/2 (time-weighted return).

The cash-flow functions follow the same convention as Finance.CashFlow.npv/2: the initial outlay sits at index 0 (undiscounted) and later flows fall at periods 1, 2, ….

Summary

Functions

Compound annual growth rate — the constant yearly rate that grows begin_value into end_value over years.

Same as cagr/4, but returns the value directly and raises ArgumentError on error.

Discounted payback period — like payback_period/2, but recovers the outlay from cash flows discounted at rate (so it accounts for the time value of money and is always at least as long as the plain payback).

Same as discounted_payback_period/3, but returns the value directly and raises ArgumentError on error.

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.

Same as payback_period/2, but returns the value directly and raises ArgumentError on error.

Profitability index — the present value of a project's future inflows per unit of initial investment, discounted at rate. A value above 1 means the project adds value. Equivalent to 1 + NPV / initial investment.

Same as profitability_index/3, but returns the value directly and raises ArgumentError on error.

Time-weighted return — the return of a series of period returns linked geometrically, ∏(1 + rᵢ) − 1. Immune to the timing of cash flows, which is what makes it the standard way to measure manager or fund performance.

Same as twr/2, but returns the value directly and raises ArgumentError on error.

Annualised volatility of a price series — the standard deviation of its period-over-period returns, scaled up to a yearly figure.

Same as volatility/2, but returns the value directly and raises ArgumentError on error.

Types

error()

@type error() :: Finance.error()

Functions

cagr(begin_value, end_value, years, opts \\ [])

@spec cagr(number(), number(), number(), keyword()) ::
  {:ok, float()} | {:error, error()}

Compound annual growth rate — the constant yearly rate that grows begin_value into end_value over years.

(end_value / begin_value)^(1/years)  1

Returns {:error, :undefined} when begin_value or years isn't positive, or the two values have opposite signs (no real rate).

iex> Finance.Returns.cagr(1000, 2000, 10)
{:ok, 0.071773}

cagr!(begin_value, end_value, years, opts \\ [])

@spec cagr!(number(), number(), number(), keyword()) :: float()

Same as cagr/4, but returns the value directly and raises ArgumentError on error.

discounted_payback_period(cash_flows, rate, opts \\ [])

@spec discounted_payback_period([number()], number(), keyword()) ::
  {:ok, float()} | {:error, error()}

Discounted payback period — like payback_period/2, but recovers the outlay from cash flows discounted at rate (so it accounts for the time value of money and is always at least as long as the plain payback).

iex> Finance.Returns.discounted_payback_period([-1000, 600, 600, 600], 0.1)
{:ok, 1.916667}

discounted_payback_period!(cash_flows, rate, opts \\ [])

@spec discounted_payback_period!([number()], number(), keyword()) :: float()

Same as discounted_payback_period/3, but returns the value directly and raises ArgumentError on error.

payback_period(cash_flows, opts \\ [])

@spec payback_period(
  [number()],
  keyword()
) :: {:ok, float()} | {:error, error()}

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.

Returns {:error, :undefined} if the flows never recover, or there's no outlay to recover, and {:error, :insufficient_data} for an empty list.

iex> Finance.Returns.payback_period([-1000, 400, 400, 400])
{:ok, 2.5}

payback_period!(cash_flows, opts \\ [])

@spec payback_period!(
  [number()],
  keyword()
) :: float()

Same as payback_period/2, but returns the value directly and raises ArgumentError on error.

profitability_index(cash_flows, rate, opts \\ [])

@spec profitability_index([number()], number(), keyword()) ::
  {:ok, float()} | {:error, error()}

Profitability index — the present value of a project's future inflows per unit of initial investment, discounted at rate. A value above 1 means the project adds value. Equivalent to 1 + NPV / initial investment.

The first amount is the initial outlay (negative); returns {:error, :undefined} if it isn't, and {:error, :insufficient_data} for an empty list.

iex> Finance.Returns.profitability_index([-1000, 600, 600], 0.1)
{:ok, 1.041322}

profitability_index!(cash_flows, rate, opts \\ [])

@spec profitability_index!([number()], number(), keyword()) :: float()

Same as profitability_index/3, but returns the value directly and raises ArgumentError on error.

twr(period_returns, opts \\ [])

@spec twr(
  [number()],
  keyword()
) :: {:ok, float()} | {:error, error()}

Time-weighted return — the return of a series of period returns linked geometrically, ∏(1 + rᵢ) − 1. Immune to the timing of cash flows, which is what makes it the standard way to measure manager or fund performance.

By default it's the cumulative return over the periods given. Pass :periods_per_year to annualise it.

iex> Finance.Returns.twr([0.10, -0.05, 0.08])
{:ok, 0.1286}

iex> Finance.Returns.twr([0.02, 0.02], periods_per_year: 4)
{:ok, 0.082432}

twr!(period_returns, opts \\ [])

@spec twr!(
  [number()],
  keyword()
) :: float()

Same as twr/2, but returns the value directly and raises ArgumentError on error.

volatility(prices, opts \\ [])

@spec volatility(
  [number()],
  keyword()
) :: {:ok, float()} | {:error, error()}

Annualised volatility of a price series — the standard deviation of its period-over-period returns, scaled up to a yearly figure.

Give it a list of prices in time order (daily closes, say). It measures the return between each consecutive pair, takes their sample standard deviation, and annualises by √periods_per_year. At least three prices are needed, and every price must be positive.

iex> Finance.Returns.volatility([100, 102, 101, 103, 105])
{:ok, 0.234528}

Options

  • :periods_per_year (pos_integer/0) - number of periods in a year, used to annualise (252 trading days by default) The default value is 252.

  • :returns - how to measure each period's return: :simple (b - a) / a or :log ln(b / a) The default value is :simple.

  • :precision (non_neg_integer/0) - decimal places the result is rounded to The default value is 6.

volatility!(prices, opts \\ [])

@spec volatility!(
  [number()],
  keyword()
) :: float()

Same as volatility/2, but returns the value directly and raises ArgumentError on error.