fin-infra provides production-ready financial calculation functions for cashflow analysis, present value calculations, and investment returns.
Installation
The cashflows module is included in the core fin-infra package:
bash
pip install fin-infraQuick Start
python
from fin_infra.cashflows import npv, irr, xnpv, xirr, pmt, fv, pv
# Calculate Net Present Value
cashflows = [-1000, 200, 300, 400, 500]
discount_rate = 0.08
net_value = npv(discount_rate, cashflows)
print(f"NPV: ${net_value:.2f}")
# Calculate Internal Rate of Return
rate = irr(cashflows)
print(f"IRR: {rate:.2%}")Core Functions
Net Present Value (NPV)
Calculate the present value of a series of cashflows:
python
from fin_infra.cashflows import npv
# Initial investment followed by returns
cashflows = [-10000, 3000, 4000, 5000, 6000]
discount_rate = 0.10
net_value = npv(discount_rate, cashflows)
print(f"NPV: ${net_value:.2f}")
# NPV: $3396.88Internal Rate of Return (IRR)
Find the discount rate that makes NPV = 0:
python
from fin_infra.cashflows import irr
cashflows = [-10000, 3000, 4000, 5000, 6000]
rate = irr(cashflows)
print(f"IRR: {rate:.2%}")
# IRR: 21.16%Extended NPV (XNPV)
Calculate NPV with irregular time periods:
python
from datetime import date
from fin_infra.cashflows import xnpv
dates = [
date(2024, 1, 1),
date(2024, 3, 15),
date(2024, 7, 20),
date(2024, 12, 31),
]
cashflows = [-10000, 3000, 4000, 5000]
discount_rate = 0.10
net_value = xnpv(discount_rate, cashflows, dates)
print(f"XNPV: ${net_value:.2f}")Extended IRR (XIRR)
Calculate IRR with irregular time periods:
python
from fin_infra.cashflows import xirr
dates = [
date(2024, 1, 1),
date(2024, 3, 15),
date(2024, 7, 20),
date(2024, 12, 31),
]
cashflows = [-10000, 3000, 4000, 5000]
rate = xirr(cashflows, dates)
print(f"XIRR: {rate:.2%}")Payment (PMT)
Calculate periodic payment for a loan:
python
from fin_infra.cashflows import pmt
rate = 0.05 / 12 # 5% annual rate, monthly payments
nper = 30 * 12 # 30 years
pv = 200000 # Loan amount
monthly_payment = pmt(rate, nper, pv)
print(f"Monthly Payment: ${-monthly_payment:.2f}")
# Monthly Payment: $1,073.64Future Value (FV)
Calculate future value of an investment:
python
from fin_infra.cashflows import fv
rate = 0.08 / 12 # 8% annual return, monthly compounding
nper = 10 * 12 # 10 years
pmt = -500 # Monthly contribution
pv = -10000 # Initial investment
future_value = fv(rate, nper, pmt, pv)
print(f"Future Value: ${future_value:.2f}")
# Future Value: $101,483.63Present Value (PV)
Calculate present value of future cashflows:
python
from fin_infra.cashflows import pv
rate = 0.06 / 12 # 6% annual rate
nper = 20 * 12 # 20 years
pmt = 1000 # Monthly payment
present_value = pv(rate, nper, pmt)
print(f"Present Value: ${-present_value:.2f}")
# Present Value: $139,580.77Advanced Use Cases
Loan Amortization Schedule
python
from fin_infra.cashflows import pmt, ipmt, ppmt
import pandas as pd
def amortization_schedule(principal, annual_rate, years):
monthly_rate = annual_rate / 12
nper = years * 12
monthly_pmt = -pmt(monthly_rate, nper, principal)
schedule = []
balance = principal
for month in range(1, nper + 1):
interest = ipmt(monthly_rate, month, nper, principal)
principal_pmt = ppmt(monthly_rate, month, nper, principal)
balance -= principal_pmt
schedule.append({
'Month': month,
'Payment': monthly_pmt,
'Principal': principal_pmt,
'Interest': -interest,
'Balance': balance
})
return pd.DataFrame(schedule)
# Create amortization schedule
schedule = amortization_schedule(200000, 0.05, 30)
print(schedule.head())Investment Portfolio Analysis
python
from fin_infra.cashflows import xnpv, xirr
from datetime import date, timedelta
class Portfolio:
def __init__(self):
self.transactions = []
def add_contribution(self, amount, date):
self.transactions.append((-amount, date))
def add_withdrawal(self, amount, date):
self.transactions.append((amount, date))
def calculate_returns(self, current_value, as_of_date):
cashflows = [cf for cf, _ in self.transactions]
dates = [dt for _, dt in self.transactions]
# Add current value as final cashflow
cashflows.append(current_value)
dates.append(as_of_date)
# Calculate returns
irr_value = xirr(cashflows, dates)
return irr_value
# Example usage
portfolio = Portfolio()
portfolio.add_contribution(10000, date(2023, 1, 1))
portfolio.add_contribution(5000, date(2023, 6, 1))
portfolio.add_contribution(5000, date(2024, 1, 1))
returns = portfolio.calculate_returns(
current_value=25000,
as_of_date=date(2024, 12, 31)
)
print(f"Portfolio IRR: {returns:.2%}")Retirement Planning
python
from fin_infra.cashflows import fv, pv
def retirement_calculator(
current_age: int,
retirement_age: int,
current_savings: float,
monthly_contribution: float,
annual_return: float,
retirement_spending: float,
years_in_retirement: int = 30
):
# Years until retirement
years_to_retirement = retirement_age - current_age
# Calculate savings at retirement
savings_at_retirement = fv(
rate=annual_return / 12,
nper=years_to_retirement * 12,
pmt=-monthly_contribution,
pv=-current_savings
)
# Calculate required savings for retirement spending
required_savings = pv(
rate=annual_return / 12,
nper=years_in_retirement * 12,
pmt=retirement_spending
)
shortfall = required_savings + savings_at_retirement
return {
'savings_at_retirement': savings_at_retirement,
'required_savings': -required_savings,
'shortfall': shortfall,
'on_track': shortfall <= 0
}
result = retirement_calculator(
current_age=30,
retirement_age=65,
current_savings=50000,
monthly_contribution=1000,
annual_return=0.07,
retirement_spending=5000
)
print(f"Savings at retirement: ${result['savings_at_retirement']:,.2f}")
print(f"Required savings: ${result['required_savings']:,.2f}")
print(f"On track: {result['on_track']}")Testing
python
import pytest
from fin_infra.cashflows import npv, irr
def test_npv():
cashflows = [-1000, 300, 300, 300, 300]
discount_rate = 0.10
result = npv(discount_rate, cashflows)
assert abs(result - (-48.68)) < 1.0
def test_irr():
cashflows = [-1000, 300, 300, 300, 300]
result = irr(cashflows)
assert abs(result - 0.0779) < 0.01 # ~7.79%Best Practices
- Consistent Sign Convention: Negative for outflows, positive for inflows
- Annualized Returns: Always specify if returns are annual, monthly, etc.
- Time Value: Use XNPV/XIRR for irregular periods
- Rounding: Round financial values appropriately (usually 2 decimals)
- Validation: Validate input cashflows before calculations
- Error Handling: Handle cases where IRR cannot converge