Discounting a series of cash flows: net present value (npv/2, xnpv/2) and
internal rate of return (irr/1, xirr/2, mirr/3).
The dated functions (xirr, xnpv) take {date, amount} flows at
arbitrary dates and discount on an Actual/365 basis, matching the spreadsheet
XIRR/XNPV. The periodic functions (irr, npv) take a plain list of
amounts at equally spaced periods 0, 1, 2, ….
xirr/irr find the rate r that brings the net present value to zero,
Σ cf_i / (1 + r)^t_i = 0, using Finance.Solver (a safeguarded
Newton-Raphson by default, with a derivative-free Finance.Solver.Brent
available).
Options
The rate-finding and value functions take an optional keyword list. Options are
validated with nimble_options: an unknown key or bad value raises, while
problems with the data come back as {:error, reason}.
:guess- initial rate for the solver; for a series with more than one rate, selects the one nearest the guess The default value is0.1.:tolerance- convergence threshold on the rate: iterating stops once the step (or bracket width) falls below it The default value is1.0e-9.:max_iterations(pos_integer/0) - cap on solver iterations; the current bracketed estimate is returned if it is reached The default value is100.:precision- decimal places the result is rounded to (0..15) The default value is6.:solver(atom/0) - module implementing theFinance.Solverbehaviour; defaults toFinance.Solver.Newton, with the derivative-freeFinance.Solver.Brentalso available:basis(atom/0) - day-count convention for dated flows (xirr/xnpv): a built-in atom or a module implementingFinance.DayCount(see that module) The default value is:actual_365.
Summary
Functions
Whether a series is conventional — its amounts change sign exactly once, so it
has a single, unambiguous internal rate of return. More than one sign change
makes it non-conventional: it may admit several valid IRRs, and xirr/irr
return the one nearest :guess. Use this to detect that case before solving.
Computes the IRR — the internal rate of return for a list of amounts that
occur at equally spaced periods 0, 1, 2, …. Think of it as xirr/2 for the
common case where your flows land at regular intervals and you don't need to
track exact dates.
Same as irr/1, but returns the rate directly and raises ArgumentError on error.
Finds the IRR of many independent series at once — irr/1 for a whole batch.
Each element is its own list of amounts at periods 0, 1, 2, …, and the result
is a list of {:ok, rate} / {:error, reason} in the same order.
Computes the MIRR — the modified internal rate of return for periodic
amounts. It refines the idea behind IRR by letting you set two separate
rates: positive flows are assumed to be reinvested at reinvest_rate, and
negative flows are assumed to be financed at finance_rate.
Same as mirr/3, but returns the rate directly and raises ArgumentError on error.
Computes the periodic NPV — the net present value of amounts occurring at
equally spaced periods 0, 1, 2, …, discounted at rate.
Same as npv/2, but returns the value directly and raises ArgumentError on error.
Finds the XIRR of a list of {date, amount} cash flows — the annual rate of
return that the flows imply, given when each one lands.
Finds the XIRR, accepting either {date, amount} pairs together with options,
or two parallel lists — one of dates, one of amounts.
Finds the XIRR from two parallel lists — dates and amounts — while also taking options for the solver.
Same as xirr/1, but returns the rate directly and raises ArgumentError on error.
Same as xirr/2, but returns the rate directly and raises ArgumentError on error.
Finds the XIRR of many independent series at once — xirr/1 for a whole
portfolio. Each element is its own list of {date, amount} flows, and the
result is a list of {:ok, rate} / {:error, reason} in the same order (one
bad series doesn't sink the batch).
Net future value of dated cash flows at rate — their combined worth at the
latest date. The future-value mirror of xnpv/2 (which values them at the
earliest date); the two differ by compounding over the series' span.
Same as xnfv/2, but returns the value directly and raises ArgumentError on error.
Computes the XNPV — the net present value of a set of dated cash flows,
discounted back at rate.
Same as xnpv/2, but returns the value directly and raises ArgumentError on error.
Types
@type amount() :: Finance.amount()
@type cash_flow() :: Finance.cash_flow()
@type date() :: Finance.date()
@type error() :: Finance.error()
@type option() :: Finance.option()
@type rate() :: Finance.rate()
Functions
Whether a series is conventional — its amounts change sign exactly once, so it
has a single, unambiguous internal rate of return. More than one sign change
makes it non-conventional: it may admit several valid IRRs, and xirr/irr
return the one nearest :guess. Use this to detect that case before solving.
Accepts periodic amounts or {date, amount} pairs (ordered by date first).
iex> Finance.CashFlow.conventional?([-1000, 300, 400, 500])
true
iex> Finance.CashFlow.conventional?([-1000, 3000, -2500])
false
Computes the IRR — the internal rate of return for a list of amounts that
occur at equally spaced periods 0, 1, 2, …. Think of it as xirr/2 for the
common case where your flows land at regular intervals and you don't need to
track exact dates.
What comes back is the rate per period. As with xirr/2, the series has to
contain at least one positive amount and one negative one.
iex> Finance.CashFlow.irr([-1000, 1100])
{:ok, 0.1}
iex> Finance.CashFlow.irr([-1000, 500, 500, 300])
{:ok, 0.156579}
Same as irr/1, and additionally takes the same options as xirr/2.
Same as irr/1, but returns the rate directly and raises ArgumentError on error.
Finds the IRR of many independent series at once — irr/1 for a whole batch.
Each element is its own list of amounts at periods 0, 1, 2, …, and the result
is a list of {:ok, rate} / {:error, reason} in the same order.
Like xirr_many/2, the batch runs on the configured solver (see
Finance.Solver).
iex> Finance.CashFlow.irr_many([[-1000, 1100], [-1000, 500, 500, 300]])
[{:ok, 0.1}, {:ok, 0.156579}]
Computes the MIRR — the modified internal rate of return for periodic
amounts. It refines the idea behind IRR by letting you set two separate
rates: positive flows are assumed to be reinvested at reinvest_rate, and
negative flows are assumed to be financed at finance_rate.
Because those assumptions are spelled out, MIRR has a closed form and a single
answer, which sidesteps the multiple-root and convergence trouble that IRR can
run into. As with irr/1, the series has to contain at least one positive
amount and one negative one.
iex> Finance.CashFlow.mirr([-120_000, 39_000, 30_000, 21_000, 37_000, 46_000], 0.10, 0.12)
{:ok, 0.126094}
Same as mirr/3, but returns the rate directly and raises ArgumentError on error.
Computes the periodic NPV — the net present value of amounts occurring at
equally spaced periods 0, 1, 2, …, discounted at rate.
Σ amount_i / (1 + rate)^i (i starting at 0)Convention
The first amount sits at period 0 and so is left undiscounted. That is what
lets npv/2 and irr/1 line up: npv(irr(a), a) comes out to roughly
zero. It also means the result differs from a spreadsheet NPV, which
places the first amount at period 1. If you want to match a spreadsheet,
discount the first amount yourself or prepend a leading 0 to the list.
iex> Finance.CashFlow.npv(0.1, [-1000, 1100])
{:ok, 0.0}
iex> Finance.CashFlow.npv(0.1, [-1000, 600, 600])
{:ok, 41.322314}
Same as npv/2, and additionally takes a :precision option. See npv/2.
Same as npv/2, but returns the value directly and raises ArgumentError on error.
Finds the XIRR of a list of {date, amount} cash flows — the annual rate of
return that the flows imply, given when each one lands.
Reach for this when your cash flows happen on irregular dates rather than at
neat intervals. See xirr/2 if you want to pass options or use the two-list
form.
iex> Finance.CashFlow.xirr([{~D[2015-06-01], 1_000_000}, {~D[2015-10-01], -2_200_000}, {~D[2015-11-01], -800_000}])
{:ok, 21.118359}
@spec xirr([cash_flow()], [option()]) :: {:ok, rate()} | {:error, error()}
@spec xirr([date()], [amount()]) :: {:ok, rate()} | {:error, error()}
Finds the XIRR, accepting either {date, amount} pairs together with options,
or two parallel lists — one of dates, one of amounts.
The result is {:ok, rate} on success or {:error, reason} when the data
can't yield a rate. Flows that fall on the same date are added together first.
The series has to include at least one positive amount and one negative one,
because without money flowing both in and out there is no return to solve for.
iex> Finance.CashFlow.xirr([{~D[2019-01-01], -1000}, {~D[2020-01-01], 1100}], guess: 0.5)
{:ok, 0.1}
iex> Finance.CashFlow.xirr([~D[2019-01-01], ~D[2020-01-01]], [-1000, 1100])
{:ok, 0.1}
Finds the XIRR from two parallel lists — dates and amounts — while also taking options for the solver.
iex> Finance.CashFlow.xirr([~D[2019-01-01], ~D[2020-01-01]], [-1000, 1100], precision: 2)
{:ok, 0.1}
Same as xirr/1, but returns the rate directly and raises ArgumentError on error.
Same as xirr/2, but returns the rate directly and raises ArgumentError on error.
Finds the XIRR of many independent series at once — xirr/1 for a whole
portfolio. Each element is its own list of {date, amount} flows, and the
result is a list of {:ok, rate} / {:error, reason} in the same order (one
bad series doesn't sink the batch).
The work runs on the configured solver (see Finance.Solver): the default
pure-Elixir solver parallelizes across schedulers, while a native backend
(a Rustler or Nx solver) runs the whole batch in one call.
iex> Finance.CashFlow.xirr_many([
...> [{~D[2019-01-01], -1000}, {~D[2020-01-01], 1100}],
...> [{~D[2019-01-01], -1000}, {~D[2020-01-01], 1200}]
...> ])
[{:ok, 0.1}, {:ok, 0.2}]
Net future value of dated cash flows at rate — their combined worth at the
latest date. The future-value mirror of xnpv/2 (which values them at the
earliest date); the two differ by compounding over the series' span.
iex> flows = [{~D[2021-01-01], -1000}, {~D[2022-01-01], 1100}]
iex> Finance.CashFlow.xnfv(0.1, flows)
{:ok, 0.0}
Same as xnfv/2, and additionally takes :precision and :basis. See xnfv/2.
Same as xnfv/2, but returns the value directly and raises ArgumentError on error.
Computes the XNPV — the net present value of a set of dated cash flows,
discounted back at rate.
Σ cf_i / (1 + rate)^t_iEach time t_i is measured in years from the earliest flow on an Actual/365
basis, the same day-count convention xirr/2 uses. That shared convention is
what ties the two together: xnpv(r, flows) comes out to roughly zero when
r is xirr(flows), so this is a handy way to check an XIRR result. And
because a net present value is defined for any series, the flows here don't
have to change sign the way xirr/2 requires.
The result is {:ok, value} or {:error, reason}. Flows on the same date are
added together first, and you can pass the same :precision option as
xirr/2 (it defaults to 6).
iex> Finance.CashFlow.xnpv(0.1, [{~D[2019-01-01], -1000}, {~D[2020-01-01], 1000}])
{:ok, -90.909091}
iex> Finance.CashFlow.xnpv(0.1, [{~D[2019-01-01], -1000}, {~D[2020-01-01], 1100}])
{:ok, 0.0}
Same as xnpv/2, and additionally takes a :precision option. See xnpv/2.
Same as xnpv/2, but returns the value directly and raises ArgumentError on error.