defmodule Money.Financial do @moduledoc """ A set of financial functions, primarily related to discounted cash flows. Some of the algorithms are from [finance formulas](http://www.financeformulas.net) """ alias Cldr.Math @doc """ Calculates the future value for a present value, an interest rate and a number of periods. * `present_value` is a %Money{} representation of the present value * `interest_rate` is a float representation of an interest rate. For example, 12% would be represented as `0.12` * `periods` in an integer number of periods ## Examples iex> Money.Financial.future_value Money.new(:USD, 10000), 0.08, 1 #Money<:USD, 10800.00> iex> Money.Financial.future_value Money.new(:USD, 10000), 0.04, 2 #Money<:USD, 10816.0000> iex> Money.Financial.future_value Money.new(:USD, 10000), 0.02, 4 #Money<:USD, 10824.32160000> """ @spec future_value(Money.t(), number, number) :: Money.t() @one Decimal.new(1) def future_value(%Money{currency: currency, amount: amount}, interest_rate, periods) when is_number(interest_rate) and is_number(periods) do fv = interest_rate |> Decimal.from_float() |> Decimal.add(@one) |> Math.power(periods) |> Decimal.mult(amount) Money.new(currency, fv) end @doc """ Calculates the future value for a list of cash flows and an interest rate. * `flows` is a list of tuples representing a cash flow. Each flow is represented as a tuple of the form `{period, %Money{}}` * `interest_rate` is a float representation of an interest rate. For example, 12% would be represented as `0.12` ## Example iex> Money.Financial.future_value([{4, Money.new(:USD, 10000)}, {5, Money.new(:USD, 10000)}, {6, Money.new(:USD, 10000)}], 0.13) #Money<:USD, 34068.99999999999999999999999> iex> Money.Financial.future_value [{0, Money.new(:USD, 5000)},{1, Money.new(:USD, 2000)}], 0.12 #Money<:USD, 7600.000000000000000000000000> """ @spec future_value(list({number, Money.t()}), number) :: Money.t() def future_value(flows, interest_rate) def future_value([{period, %Money{}} | _other_flows] = flows, interest_rate) when is_integer(period) and is_number(interest_rate) do {max_period, _} = Enum.max(flows) present_value(flows, interest_rate) |> future_value(interest_rate, max_period) end @doc """ Calculates the present value for future value, an interest rate and a number of periods * `future_value` is a %Money{} representation of the future value * `interest_rate` is a float representation of an interest rate. For example, 12% would be represented as `0.12` * `periods` in an integer number of periods ## Examples iex> Money.Financial.present_value Money.new(:USD, 100), 0.08, 2 #Money<:USD, 85.73388203017832647462277092> iex> Money.Financial.present_value Money.new(:USD, 1000), 0.10, 20 #Money<:USD, 148.6436280241436864020760472> """ @spec present_value(Money.t(), number, number) :: Money.t() def present_value(%Money{currency: currency, amount: amount}, interest_rate, periods) when is_number(interest_rate) and is_number(periods) and periods >= 0 do pv_1 = interest_rate |> Decimal.from_float() |> Decimal.add(@one) |> Math.power(periods) pv = Decimal.div(amount, pv_1) Money.new(currency, pv) end @doc """ Calculates the present value for a list of cash flows and an interest rate. * `flows` is a list of tuples representing a cash flow. Each flow is represented as a tuple of the form `{period, %Money{}}` * `interest_rate` is a float representation of an interest rate. For example, 12% would be represented as `0.12` ## Example iex> Money.Financial.present_value([{4, Money.new(:USD, 10000)}, {5, Money.new(:USD, 10000)}, {6, Money.new(:USD, 10000)}], 0.13) #Money<:USD, 16363.97191111964880256655144> iex> Money.Financial.present_value [{0, Money.new(:USD, -1000)},{1, Money.new(:USD, -4000)}], 0.1 #Money<:USD, -4636.363636363636363636363636> """ @spec present_value(list({integer, Money.t()}), number) :: Money.t() def present_value(flows, interest_rate) def present_value([{period, %Money{}} | _other_flows] = flows, interest_rate) when is_integer(period) and is_number(interest_rate) do validate_same_currency!(flows) do_present_value(flows, interest_rate) end defp do_present_value({period, %Money{} = flow}, interest_rate) when is_integer(period) and is_number(interest_rate) do present_value(flow, interest_rate, period) end defp do_present_value([{period, %Money{}} = flow | []], interest_rate) when is_integer(period) and is_number(interest_rate) do do_present_value(flow, interest_rate) end defp do_present_value([{period, %Money{}} = flow | other_flows], interest_rate) when is_integer(period) and is_number(interest_rate) do do_present_value(flow, interest_rate) |> Money.add!(do_present_value(other_flows, interest_rate)) end @doc """ Calculates the net present value of an initial investment, a list of cash flows and an interest rate. * `flows` is a list of tuples representing a cash flow. Each flow is represented as a tuple of the form `{period, %Money{}}` * `interest_rate` is a float representation of an interest rate. For example, 12% would be represented as `0.12` * `investment` is a %Money{} struct representing the initial investment ## Example iex> flows = [{0, Money.new(:USD, 5000)},{1, Money.new(:USD, 2000)},{2, Money.new(:USD, 500)},{3, Money.new(:USD,10_000)}] iex> Money.Financial.net_present_value flows, 0.08, Money.new(:USD, 100) #Money<:USD, 15118.84367220444038002337042> iex> Money.Financial.net_present_value flows, 0.08 #Money<:USD, 15218.84367220444038002337042> """ @spec net_present_value(list({integer, Money.t()}), number) :: Money.t() def net_present_value([{period, %Money{currency: currency}} | _] = flows, interest_rate) when is_integer(period) and is_number(interest_rate) do net_present_value(flows, interest_rate, Money.zero(currency)) end @spec net_present_value(list({integer, Money.t()}), number, Money.t()) :: Money.t() def net_present_value([{period, %Money{}} | _] = flows, interest_rate, %Money{} = investment) when is_integer(period) and is_number(interest_rate) do validate_same_currency!(investment, flows) present_value(flows, interest_rate) |> Money.sub!(investment) end @doc """ Calculates the net present value of an initial investment, a recurring payment, an interest rate and a number of periods * `investment` is a %Money{} struct representing the initial investment * `future_value` is a %Money{} representation of the future value * `interest_rate` is a float representation of an interest rate. For example, 12% would be represented as `0.12` * `periods` in an integer number of a period ## Example iex> Money.Financial.net_present_value Money.new(:USD, 10000), 0.13, 2 #Money<:USD, 7831.466833737959119743127888> iex> Money.Financial.net_present_value Money.new(:USD, 10000), 0.13, 2, Money.new(:USD, 100) #Money<:USD, 7731.466833737959119743127888> """ @spec net_present_value(Money.t(), number, number) :: Money.t() def net_present_value(%Money{currency: currency} = future_value, interest_rate, periods) do net_present_value(future_value, interest_rate, periods, Money.new(currency, 0)) end @spec net_present_value(Money.t(), number, number, Money.t()) :: Money.t() def net_present_value(%Money{} = future_value, interest_rate, periods, %Money{} = investment) do present_value(future_value, interest_rate, periods) |> Money.sub!(investment) end @doc """ Calculates the interal rate of return for a given list of cash flows. * `flows` is a list of tuples representing a cash flow. Each flow is represented as a tuple of the form `{period, %Money{}}` """ @spec internal_rate_of_return(list({integer, Money.t()})) :: number() def internal_rate_of_return([{_period, %Money{}} | _other_flows] = flows) do # estimate_m = sum_of_inflows(flows) # |> Kernel./(abs(Math.to_float(amount))) # |> :math.pow(2 / (number_of_flows(flows) + 1)) # |> Kernel.-(1) # estimate_n = :math.pow(1 + estimate_m, ) estimate_n = 0.2 estimate_m = 0.1 do_internal_rate_of_return(flows, estimate_m, estimate_n) end @irr_precision 0.000001 defp do_internal_rate_of_return(flows, estimate_m, estimate_n) do npv_n = net_present_value(flows, estimate_n).amount |> Math.to_float() npv_m = net_present_value(flows, estimate_m).amount |> Math.to_float() if abs(npv_n - npv_m) > @irr_precision do estimate_o = estimate_n - (estimate_n - estimate_m) / (npv_n - npv_m) * npv_n do_internal_rate_of_return(flows, estimate_n, estimate_o) else estimate_n end end @doc """ Calculates the effective interest rate for a given present value, a future value and a number of periods. * `present_value` is a %Money{} representation of the present value * `future_value` is a %Money{} representation of the future value * `periods` is an integer number of a period ## Examples iex> Money.Financial.interest_rate Money.new(:USD, 10000), Money.new(:USD, 10816), 2 #Decimal<0.04> iex> Money.Financial.interest_rate Money.new(:USD, 10000), Money.new(:USD, "10824.3216"), 4 #Decimal<0.02> """ @spec interest_rate(Money.t(), Money.t(), number) :: Decimal.t() def interest_rate( %Money{currency: pv_currency, amount: pv_amount} = _present_value, %Money{currency: fv_currency, amount: fv_amount} = _future_value, periods ) when pv_currency == fv_currency and is_integer(periods) and periods > 0 do fv_amount |> Decimal.div(pv_amount) |> Math.root(periods) |> Decimal.sub(@one) end @doc """ Calculates the number of periods between a present value and a future value with a given interest rate. * `present_value` is a %Money{} representation of the present value * `future_value` is a %Money{} representation of the future value * `interest_rate` is a float representation of an interest rate. For example, 12% would be represented as `0.12` ## Example iex> Money.Financial.periods Money.new(:USD, 1500), Money.new(:USD, 2000), 0.005 #Decimal<57.68013595323872502502238648> """ @spec periods(Money.t(), Money.t(), float) :: Decimal.t() def periods( %Money{currency: pv_currency, amount: pv_amount} = _present_value, %Money{currency: fv_currency, amount: fv_amount} = _future_value, interest_rate ) when pv_currency == fv_currency and is_float(interest_rate) and interest_rate > 0 do Decimal.div( Math.log(Decimal.div(fv_amount, pv_amount)), Math.log(Decimal.add(@one, Decimal.from_float(interest_rate))) ) end @doc """ Calculates the payment for a given loan or annuity given a present value, an interest rate and a number of periods. * `present_value` is a %Money{} representation of the present value * `interest_rate` is a float representation of an interest rate. For example, 12% would be represented as `0.12` * `periods` is an integer number of periods ## Example iex> Money.Financial.payment Money.new(:USD, 100), 0.12, 20 #Money<:USD, 13.38787800396606622792492299> """ @spec payment(Money.t(), float, number) :: Money.t() def payment( %Money{currency: pv_currency, amount: pv_amount} = _present_value, interest_rate, periods ) when is_float(interest_rate) and interest_rate > 0 and is_number(periods) and periods > 0 do interest_rate = Decimal.from_float(interest_rate) p1 = Decimal.mult(pv_amount, interest_rate) p2 = Decimal.sub(@one, Decimal.add(@one, interest_rate) |> Math.power(-periods)) Money.new(pv_currency, Decimal.div(p1, p2)) end defp validate_same_currency!(%Money{} = flow, flows) do validate_same_currency!([{0, flow} | flows]) end defp validate_same_currency!(flows) do number_of_currencies = flows |> Enum.map(fn {_period, %Money{currency: currency}} -> currency end) |> Enum.uniq() |> Enum.count() if number_of_currencies > 1 do raise ArgumentError, message: "More than one currency found in cash flows; " <> "implicit currency conversion is not supported. Cash flows: " <> inspect(flows) end end end