defmodule Finex do @moduledoc """ Professional financial calculations for Elixir applications. Finex provides accurate, battle-tested financial calculations with decimal precision and comprehensive coverage of common financial scenarios. ## Features - ๐ŸŽฏ **Precision**: Uses `Decimal` for all calculations to avoid floating-point errors - ๐Ÿ“Š **Comprehensive**: Covers investments, savings, and inflation analysis - ๐Ÿš€ **Performance**: Optimized for production fintech applications - ๐Ÿงช **Tested**: Extensive test coverage with real-world scenarios - ๐Ÿ’ฐ **Fee Transparency**: Separate yield and fees for accurate calculations ## Quick Start # Calculate investment outcome with yield and fees iex> Finex.investment_outcome(1000, 500, 5, 0.07, 0.01) {:ok, %{final_amount: Decimal.new("36228.25"), total_contributions: Decimal.new("31000.00"), total_interest: Decimal.new("5228.25"), initial_growth: Decimal.new("1348.46"), monthly_growth: Decimal.new("34879.79")}} # Calculate timeline for savings goal iex> Finex.savings_timeline(10_000, 500, 0.05, 0.0) {:ok, %{years: 1.6, months: 19.25}} # Analyze inflation impact iex> Finex.inflation_impact(1_000_000, 4, 0.03) {:ok, %{future_nominal_value: Decimal.new("1000000.00"), purchasing_power: Decimal.new("888487.05"), purchasing_power_loss: Decimal.new("111512.95"), purchasing_power_percentage: 88.85}} ## Core Module - `Finex.Investment` - All financial calculations with yield and fee transparency """ alias Finex.Investment @type amount :: Decimal.t() | number() @type rate :: float() @type period :: pos_integer() @doc """ Calculate final investment outcome with initial and monthly contributions. ## Parameters - `initial` - Initial lump sum investment - `monthly` - Monthly contribution amount - `years` - Investment period in years - `annual_yield` - Expected annual return before fees - `annual_fees` - Annual fees and expenses ## Examples iex> Finex.investment_outcome(1000, 500, 5, 0.07, 0.01) {:ok, %{final_amount: Decimal.new("36228.25"), total_contributions: Decimal.new("31000.00"), total_interest: Decimal.new("5228.25"), initial_growth: Decimal.new("1348.46"), monthly_growth: Decimal.new("34879.79")}} """ @spec investment_outcome(amount(), amount(), number(), rate(), rate()) :: {:ok, map()} | {:error, atom()} defdelegate investment_outcome(initial, monthly, years, annual_yield, annual_fees \\ 0.0), to: Investment, as: :calculate_outcome @doc """ Calculate timeline to reach savings goal using monthly contributions. ## Parameters - `target` - Target savings amount - `monthly` - Monthly contribution - `annual_yield` - Expected annual return - `annual_fees` - Annual fees (default: 0) ## Examples iex> Finex.savings_timeline(10_000, 500, 0.05, 0.0) {:ok, %{years: 1.6, months: 19.25}} """ @spec savings_timeline(amount(), amount(), rate(), rate()) :: {:ok, %{years: float(), months: integer()}} | {:error, atom()} defdelegate savings_timeline(target, monthly, annual_yield, annual_fees \\ 0.0), to: Investment, as: :calculate_timeline @doc """ Calculate timeline for lump sum investment to grow to target. ## Parameters - `target` - Target investment amount - `initial` - Initial lump sum investment - `annual_yield` - Expected annual return - `annual_fees` - Annual fees (default: 0) ## Examples iex> Finex.lump_sum_timeline(10_000, 5_000, 0.07, 0.0) {:ok, %{years: 10.0, months: 120}} """ @spec lump_sum_timeline(amount(), amount(), rate(), rate()) :: {:ok, %{years: float(), months: integer()}} | {:error, atom()} defdelegate lump_sum_timeline(target, initial, annual_yield, annual_fees \\ 0.0), to: Investment, as: :calculate_timeline_lump_sum @doc """ Calculate required monthly contribution to reach investment goal. ## Parameters - `target` - Target investment amount - `initial` - Initial investment - `years` - Investment period in years - `annual_yield` - Expected annual return - `annual_fees` - Annual fees (default: 0) ## Examples iex> Finex.monthly_needed(60_000, 5_000, 5, 0.06, 0.01) {:ok, Decimal.new("788.04")} """ @spec monthly_needed(amount(), amount(), number(), rate(), rate()) :: {:ok, Decimal.t()} | {:error, atom()} defdelegate monthly_needed(target, initial, years, annual_yield, annual_fees \\ 0.0), to: Investment, as: :required_monthly_contribution @doc """ Calculate progress percentage toward investment goal. ## Parameters - `current` - Current investment value - `target` - Target investment amount ## Examples iex> Finex.goal_progress(7_500, 10_000) {:ok, 75.0} """ @spec goal_progress(amount(), amount()) :: {:ok, float()} | {:error, atom()} defdelegate goal_progress(current, target), to: Investment, as: :progress_percentage @doc """ Calculate future purchasing power accounting for inflation. ## Parameters - `amount_today` - Current amount - `years` - Number of years in the future - `inflation_rate` - Annual inflation rate ## Examples iex> Finex.inflation_impact(1_000_000, 4, 0.03) {:ok, %{future_nominal_value: Decimal.new("1000000.00"), purchasing_power: Decimal.new("888487.05"), purchasing_power_loss: Decimal.new("111512.95"), purchasing_power_percentage: 88.85}} """ @spec inflation_impact(amount(), number(), rate()) :: {:ok, map()} | {:error, atom()} defdelegate inflation_impact(amount_today, years, inflation_rate), to: Investment, as: :future_value_with_inflation @doc """ Analyze investment goal feasibility given income constraints. ## Parameters - `monthly_income` - Monthly take-home income - `target` - Investment goal - `initial` - Initial investment - `years` - Timeline in years - `annual_yield` - Expected return - `annual_fees` - Annual fees (default: 0) ## Examples iex> Finex.goal_feasibility(5000, 50_000, 0, 5, 0.07, 0.01) {:ok, %{difficulty: :moderate, income_percentage: 14.34, recommendation: "Reasonable goal with good budgeting", required_monthly: Decimal.new("716.75")}} """ @spec goal_feasibility(amount(), amount(), amount(), number(), rate(), rate()) :: {:ok, map()} | {:error, atom()} defdelegate goal_feasibility( monthly_income, target, initial, years, annual_yield, annual_fees \\ 0.0 ), to: Investment, as: :feasibility_analysis @doc """ Calculate the time required to double an investment (Rule of 72 refined). Uses the net return rate after accounting for fees to provide accurate doubling time. ## Parameters - `annual_yield` - Annual return rate before fees - `annual_fees` - Annual fees (default: 0) ## Examples iex> Finex.doubling_time(0.07, 0.01) {:ok, 11.59} iex> Finex.doubling_time(0.08, 0.0) {:ok, 8.69} """ @spec doubling_time(rate(), rate()) :: {:ok, float()} | {:error, atom()} def doubling_time(annual_yield, annual_fees \\ 0.0) def doubling_time(annual_yield, annual_fees) when annual_yield > 0 do with {:ok, yield_decimal} <- Finex.Utils.to_decimal(annual_yield), {:ok, fees_decimal} <- Finex.Utils.to_decimal(annual_fees), :ok <- Finex.Utils.validate_rate(annual_yield), :ok <- Finex.Utils.validate_rate(annual_fees) do monthly_yield = Decimal.div(yield_decimal, Decimal.new(12)) monthly_fees = Decimal.div(fees_decimal, Decimal.new(12)) net_monthly_rate = Decimal.add(Decimal.new(1), monthly_yield) |> Decimal.mult(Decimal.sub(Decimal.new(1), monthly_fees)) |> Decimal.sub(Decimal.new(1)) monthly_rate_float = Decimal.to_float(net_monthly_rate) net_annual_rate = :math.pow(1 + monthly_rate_float, 12) - 1 if net_annual_rate > 0 do years = :math.log(2) / :math.log(1 + net_annual_rate) {:ok, Float.round(years, 2)} else {:error, :invalid_rate} end end end def doubling_time(_, _), do: {:error, :invalid_rate} end