Finex ๐Ÿ’ฐ

Professional financial calculations for Elixir applications with fee transparency and decimal precision.

Hex.pmDocumentationLicense

Finex provides accurate, battle-tested financial calculations with decimal precision and comprehensive coverage of real-world financial scenarios. Built for production fintech applications that demand reliability and transparency.

Features

Installation

Add finex to your list of dependencies in mix.exs:

def deps do
  [
    {:finex, "~> 0.1.1"}
  ]
end

Quick Start

# Simple calculation without fees - fees default to 0.0
iex> Finex.investment_outcome(1000, 500, 5, 0.07)
{:ok, %{
  final_amount: #Decimal<37214.07>,
  total_contributions: #Decimal<31000.00>,
  total_interest: #Decimal<6214.07>
}}

# Include fees when needed
iex> Finex.investment_outcome(1000, 500, 5, 0.07, 0.01)
{:ok, %{
  final_amount: #Decimal<36228.25>,
  total_contributions: #Decimal<31000.00>,
  total_interest: #Decimal<5228.25>
}}

# How long to save $10k at $500/month with 5% yield?
iex> Finex.savings_timeline(10_000, 500, 0.05)
{:ok, %{years: 1.6, months: 19.25}}

# What&#39;s $1M worth in 25 years with 3% inflation?
iex> Finex.inflation_impact(1_000_000, 25, 0.03)
{:ok, %{
  purchasing_power: #Decimal<467744.00>,
  purchasing_power_percentage: 46.77
}}

# NEW: How long to save for house down payment with price inflation?
iex> Finex.inflation_adjusted_timeline(80_000, 1500, 0.035, 0.07, 0.01)
{:ok, %{years: 4.34, months: 52.05}}

Core Functions

Investment Calculations

# Calculate final portfolio value
Finex.investment_outcome(initial, monthly, years, annual_yield, annual_fees \\ 0.0)

# Timeline for monthly savings goal
Finex.savings_timeline(target, monthly, annual_yield, annual_fees \\ 0.0)

# Timeline for lump sum to grow
Finex.lump_sum_timeline(target, initial, annual_yield, annual_fees \\ 0.0)

# Required monthly contribution
Finex.monthly_needed(target, initial, years, annual_yield, annual_fees \\ 0.0)

Analysis & Planning

# Goal feasibility analysis
Finex.goal_feasibility(monthly_income, target, initial, years, annual_yield, annual_fees \\ 0.0)

# Track progress toward goal
Finex.goal_progress(current_amount, target_amount)

# Inflation impact analysis
Finex.inflation_impact(amount_today, years, inflation_rate)

# Investment doubling time (Rule of 72)
Finex.doubling_time(annual_yield, annual_fees)

# NEW: Inflation-adjusted goal planning
Finex.inflation_adjusted_timeline(present_goal, monthly_contribution, inflation_rate, annual_yield, annual_fees \\ 0.0)

Real-World Examples

Emergency Fund Planning

# Build 6-month emergency fund
monthly_expenses = 3_500
target_fund = monthly_expenses * 6  # $21,000
monthly_savings = 600

# How long will it take?
{:ok, timeline} = Finex.savings_timeline(target_fund, monthly_savings, 0.025, 0.0)
# => %{years: 2.9, months: 35}

# Is this feasible on $5k income?
{:ok, feasibility} = Finex.goal_feasibility(5000, target_fund, 0, 3, 0.025, 0.0)
# => %{difficulty: :moderate, income_percentage: 12.0}

House Down Payment

# Save for 20% down payment on $400k house
house_price = 400_000
down_payment = house_price * 0.20  # $80,000
timeline_years = 5

# How much to save monthly (assuming fixed price)?
{:ok, monthly_needed} = Finex.monthly_needed(down_payment, 10_000, timeline_years, 0.04, 0.005)
# => #Decimal<1285.67>

# NEW: Account for house price inflation (3.5% annually)
{:ok, inflation_timeline} = Finex.inflation_adjusted_timeline(down_payment, 1500, 0.035, 0.07, 0.01)
# => %{years: 4.73, months: 56.76}

# Check feasibility
{:ok, analysis} = Finex.goal_feasibility(7000, down_payment, 10_000, timeline_years, 0.04, 0.005)
# => %{difficulty: :moderate, income_percentage: 18.37}

Retirement Planning

# 401k contribution analysis
annual_contribution = 23_000  # 2024 limit
monthly_contribution = annual_contribution / 12
years_to_retirement = 30

# Portfolio value at retirement
{:ok, outcome} = Finex.investment_outcome(50_000, monthly_contribution, years_to_retirement, 0.08, 0.005)
# => %{final_amount: #Decimal<2156789.45>}

# Account for inflation impact
{:ok, inflation_impact} = Finex.inflation_impact(outcome.final_amount, 25, 0.025)
# => %{purchasing_power_percentage: 53.94}

Investment Analysis

# Compare investment options
option_a = Finex.doubling_time(0.08, 0.005)  # Index fund: 8% yield, 0.5% fees
option_b = Finex.doubling_time(0.06, 0.02)   # Managed fund: 6% yield, 2% fees

# => Option A: {:ok, 9.31} years
# => Option B: {:ok, 17.67} years

Clean API Design

Finex follows a consistent, intuitive parameter pattern across all functions:

# Pattern: function(core_params..., years, annual_yield, annual_fees \\ 0.0)

# Simple usage - fees default to 0.0
Finex.investment_outcome(1000, 500, 5, 0.07)
Finex.monthly_needed(60_000, 5_000, 5, 0.06)
Finex.goal_feasibility(5000, 50_000, 0, 5, 0.07)

# Include fees when needed (always last parameter)
Finex.investment_outcome(1000, 500, 5, 0.07, 0.01)
Finex.monthly_needed(60_000, 5_000, 5, 0.06, 0.005)
Finex.goal_feasibility(5000, 50_000, 0, 5, 0.07, 0.01)

Fee Transparency

Finex requires separate annual_yield and annual_fees parameters for transparent calculations:

# Clear fee structure
annual_yield = 0.07    # 7% expected return
annual_fees = 0.015    # 1.5% total fees (management + expense ratio)

# Finex calculates net return using: (1 + yield/12) * (1 - fees/12) - 1
{:ok, outcome} = Finex.investment_outcome(10_000, 1000, 10, annual_yield, annual_fees)

# Compare with no-fee alternative
{:ok, no_fee_outcome} = Finex.investment_outcome(10_000, 1000, 10, 0.07)

Precision & Accuracy

Finex uses Decimal arithmetic for all financial calculations:

# No floating-point errors
{:ok, result} = Finex.investment_outcome(1000, 500.33, 7.5, 0.0725, 0.0125)

# All monetary values are precise Decimal structs
result.final_amount  # => #Decimal<52847.39>
result.total_interest  # => #Decimal<6847.39>

Error Handling

Consistent error tuples with descriptive messages:

# Validation errors
{:error, :negative_amount} = Finex.investment_outcome(-1000, 500, 5, 0.07, 0.01)
{:error, :invalid_rate} = Finex.investment_outcome(1000, 500, 5, -0.05, 0.01)
{:error, :invalid_period} = Finex.investment_outcome(1000, 500, -5, 0.07, 0.01)

# Get user-friendly error messages
Finex.Utils.format_error(:negative_amount)  # => "Amount must be non-negative"

Phoenix Integration

Works seamlessly with Phoenix and LiveView:

defmodule MyAppWeb.InvestmentLive do
  use MyAppWeb, :live_view

  def handle_event("calculate", params, socket) do
    %{"initial" => initial, "monthly" => monthly, "yield" => yield, "fees" => fees, "years" => years} = params
    
    case Finex.investment_outcome(initial, monthly, years, yield, fees) do
      {:ok, result} ->
        {:noreply, assign(socket, :result, result)}
      
      {:error, reason} ->
        error_message = Finex.Utils.format_error(reason)
        {:noreply, put_flash(socket, :error, error_message)}
    end
  end
end

Testing

Comprehensive test coverage with real-world scenarios:

mix test
mix test --cover

Documentation

Generate documentation:

mix docs

Roadmap

v0.2.0 - Enhanced Analysis

v0.3.0 - Loan & Debt

v0.4.0 - Advanced Features

v1.0.0 - Enterprise Ready

Contributing

We welcome contributions! Please see CONTRIBUTING.md for details.

  1. Fork the repository
  2. Create your feature branch (git checkout -b feature/amazing-feature)
  3. Add tests for your changes
  4. Ensure all tests pass (mix test)
  5. Run code quality checks (mix credo && mix dialyzer)
  6. Commit your changes (git commit -am 'Add amazing feature')
  7. Push to the branch (git push origin feature/amazing-feature)
  8. Open a Pull Request

Performance

Finex is optimized for production use:

Comparison with Other Libraries

Feature Finex Other Libraries
Decimal Precision โœ… โŒ (most use floats)
Fee Transparency โœ… โŒ
Comprehensive Testing โœ… โš ๏ธ
Production Ready โœ… โš ๏ธ
Phoenix Integration โœ… โŒ
Real-world Scenarios โœ… โŒ

License

This project is licensed under the MIT License - see the LICENSE file for details.

Acknowledgments


Ready to build the next generation of fintech apps with Elixir? Start with Finex! ๐Ÿš€