Skip to main content

MoneyWarp โ€“ Bend time. Model cash.

Project description

MoneyWarp ๐Ÿ’ฐโฐ

Bend time. Model cash.

Release Build status codecov License

โš ๏ธ Development Stage Notice

MoneyWarp is currently in active development and should be considered alpha/pre-release software. While the core functionality is implemented and tested, the API may change between versions. Use in production environments at your own risk.

  • โœ… Core classes (Money, InterestRate, CashFlow, Loan) are stable
  • โœ… Comprehensive test suite with 700+ tests
  • โš ๏ธ API may evolve based on user feedback
  • โœ… Published to PyPI
  • ๐Ÿšง Additional features and schedulers in development

MoneyWarp is a Python library for working with the time value of money. It treats loans, annuities, and investments as simple cash flows through time โ€” and gives you the tools to warp them back and forth between present, future, and everything in between.

๐Ÿš€ Features

  • ๐Ÿ•ฐ๏ธ Time Machine (Warp) - Travel to any date and see loan state as of that moment
  • ๐Ÿ”ข Calculate PMT, NPV, IRR, MIRR and other core finance functions with scipy
  • โณ Track loans and repayments as evolving cash-flow streams
  • ๐ŸŒ€ Explore "what if" timelines by bending payments across time
  • ๐Ÿ’ฐ High-precision calculations using Decimal arithmetic
  • ๐Ÿ“Š Progressive Price Schedules (French amortization system)
  • ๐Ÿ“ˆ Inverted Price Schedules (Constant Amortization System - SAC)
  • ๐ŸŽฏ Flexible payment scheduling with irregular due dates
  • ๐Ÿ“… Easy date generation with smart month-end handling via python-dateutil
  • ๐Ÿ”’ Type-safe interest rates with explicit percentage handling
  • ๐Ÿงฎ Robust numerics powered by scipy for IRR and financial calculations
  • โš–๏ธ Fine engine with fines, mora interest, and configurable grace periods
  • ๐Ÿญ Sugar payment methods โ€” pay_installment() and anticipate_payment() for natural workflows
  • ๐Ÿ‡ง๐Ÿ‡ท Tax module โ€” Brazilian IOF with pluggable tax strategy, grossup, and preset rates

๐Ÿ“ฆ Installation

pip install money-warp

Or with Poetry:

poetry add money-warp

๐ŸŽฏ Quick Start

Basic Loan Analysis

from datetime import datetime
from money_warp import Money, InterestRate, Loan, generate_monthly_dates

# Create a $10,000 loan at 5% annual interest
principal = Money("10000.00")
rate = InterestRate("5% a")  # 5% annually

# Generate monthly payment dates easily
start_date = datetime(2024, 1, 15)
due_dates = generate_monthly_dates(start_date, 12)

# Generate the loan
loan = Loan(principal, rate, due_dates)

# Get the payment schedule
schedule = loan.get_amortization_schedule()
print(f"Monthly payment: {schedule[0].payment_amount}")
print(f"Total interest: {schedule.total_interest}")

# Track actual payments
loan.record_payment(Money("856.07"), datetime(2024, 2, 1))
print(f"Remaining balance: {loan.current_balance}")

Cash Flow Analysis

from money_warp import CashFlow, CashFlowItem, Money
from datetime import datetime

# Create cash flow items
items = [
    CashFlowItem(Money("1000.00"), datetime(2024, 1, 1), "Initial deposit", "deposit"),
    CashFlowItem(Money("-50.00"), datetime(2024, 2, 1), "Monthly fee", "fee"),
    CashFlowItem(Money("200.00"), datetime(2024, 3, 1), "Interest payment", "interest"),
]

# Analyze the cash flow
cash_flow = CashFlow(items)
print(f"Net cash flow: {cash_flow.net_present_value()}")
print(f"Total deposits: {cash_flow.query.filter_by(category='deposit').sum_amounts()}")

# Filter by date range
recent = cash_flow.query.filter_by(datetime__gte=datetime(2024, 2, 1))
print(f"Recent activity: {recent.sum_amounts()}")

High-Precision Money Handling

from money_warp import Money
from decimal import Decimal

# Create money with high internal precision
money = Money("100.123456789")
print(f"Internal precision: {money.raw_amount}")      # 100.123456789
print(f"Real money (2 decimals): {money.real_amount}") # 100.12
print(f"Display: {money}")                            # 100.12

# Arithmetic maintains precision internally
result = money * 3 / 7
print(f"Calculation result: {result}")  # Precise to 2 decimals for display

Time Machine - Warp to Any Date ๐Ÿ•ฐ๏ธ

Core Philosophy: The loan is always time sensitive... it always filters based on present date regardless if it is warped or not... the warp just changes the present date.

from money_warp import Warp, Loan, Money, InterestRate
from datetime import datetime

# Create a loan and make some payments
loan = Loan(Money("10000"), InterestRate("5% a"), [datetime(2024, 1, 15)])
loan.record_payment(Money("500"), datetime(2024, 1, 10), "Payment 1")
loan.record_payment(Money("600"), datetime(2024, 2, 10), "Payment 2") 
loan.record_payment(Money("700"), datetime(2024, 3, 10), "Payment 3")

print(f"Current balance: {loan.current_balance}")  # All payments applied

# Warp to the past - only see payments made by that date
with Warp(loan, datetime(2024, 1, 20)) as past_loan:
    print(f"Balance on Jan 20: {past_loan.current_balance}")  # Only first payment
    print(f"Payments made: {len(past_loan._actual_payments)}")  # 2 items (interest + principal)

# Warp to the future - see all payments up to that date  
with Warp(loan, datetime(2025, 1, 1)) as future_loan:
    print(f"Balance in future: {future_loan.current_balance}")  # All payments applied
    print(f"Days since last payment: {future_loan.days_since_last_payment()}")  # From warped date

# Original loan unchanged
print(f"Back to present: {loan.current_balance}")

Key Features:

  • ๐Ÿ•ฐ๏ธ Natural time filtering: Loans automatically show state as of any date
  • ๐Ÿ”„ Safe cloning: Original loan never modified during time travel
  • ๐Ÿ“… Flexible date formats: Accepts strings, datetime objects, or date objects
  • ๐Ÿšซ No nested warps: Prevents dangerous time paradoxes
  • โšก Instant calculations: Balance and payment history update automatically

Interest Rate Conversions

from money_warp import InterestRate, CompoundingFrequency

# Create rates with explicit formats
annual_rate = InterestRate("5.25% a")     # 5.25% annually  
monthly_rate = InterestRate("0.4167% m")  # 0.4167% monthly
daily_rate = InterestRate("3% d")         # 3% daily (extreme example)

# Convert between frequencies
print(f"Annual: {annual_rate}")
print(f"As monthly: {annual_rate.to_monthly()}")
print(f"As daily: {annual_rate.to_daily()}")

# Safe decimal/percentage handling
print(f"As decimal: {annual_rate.as_decimal}")      # 0.0525
print(f"As percentage: {annual_rate.as_percentage}") # 5.25

# Abbreviated notation (Brazilian/LatAm convention)
rate = InterestRate("5.25% a.a.")   # parsed as 5.25% annually
print(rate)                         # "5.250% a.a." โ€” round-trips automatically

# Or set the style explicitly on numeric rates
rate = InterestRate(1.5, CompoundingFrequency.MONTHLY, as_percentage=True, str_style="abbrev")
print(rate)                         # "1.500% a.m."

Easy Date Generation ๐Ÿ“…

Simplified with python-dateutil for robust date handling:

from datetime import datetime
from money_warp import (
    generate_monthly_dates,
    generate_biweekly_dates,
    generate_weekly_dates,
    generate_quarterly_dates,
    generate_annual_dates,
    generate_custom_interval_dates,
)

# Monthly payments (handles end-of-month intelligently)
monthly_dates = generate_monthly_dates(datetime(2024, 1, 31), 12)
print(f"Jan 31 โ†’ Feb 29 โ†’ Mar 29...")  # Smart month-end handling

# Bi-weekly payments (every 14 days)
biweekly_dates = generate_biweekly_dates(datetime(2024, 1, 1), 26)
print(f"26 payments over ~1 year")

# Weekly payments
weekly_dates = generate_weekly_dates(datetime(2024, 1, 1), 52)

# Quarterly payments
quarterly_dates = generate_quarterly_dates(datetime(2024, 1, 15), 4)

# Annual payments
annual_dates = generate_annual_dates(datetime(2024, 1, 1), 30)  # 30-year loan

# Custom intervals (every N days)
custom_dates = generate_custom_interval_dates(datetime(2024, 1, 1), 10, 45)  # Every 45 days

# Use with loans immediately
loan = Loan(
    principal=Money("50000"),
    interest_rate=InterestRate("3.5% annual"),
    due_dates=monthly_dates  # Just plug in the generated dates!
)

Key Features:

  • ๐Ÿ—“๏ธ Smart date handling: Uses python-dateutil for robust month arithmetic
  • ๐Ÿ“… End-of-month intelligence: Jan 31 โ†’ Feb 29 โ†’ Mar 29 (maintains consistency)
  • ๐ŸŽฏ Simple API: Just datetime and int parameters, no complex options
  • โšก Instant integration: Generated dates work directly with Loan objects
  • ๐Ÿ”’ Type-safe: Full type annotations and validation

### Present Value and IRR Analysis ๐Ÿงฎ

**Powered by scipy for robust numerical calculations:**

```python
from money_warp import CashFlow, CashFlowItem, Money, InterestRate
from money_warp import present_value, irr, modified_internal_rate_of_return
from datetime import datetime

# Create an investment cash flow
items = [
    CashFlowItem(Money("-10000"), datetime(2024, 1, 1), "Initial investment", "investment"),
    CashFlowItem(Money("3000"), datetime(2024, 12, 31), "Year 1 return", "return"),
    CashFlowItem(Money("4000"), datetime(2025, 12, 31), "Year 2 return", "return"),
    CashFlowItem(Money("5000"), datetime(2026, 12, 31), "Year 3 return", "return"),
]
cash_flow = CashFlow(items)

# Calculate Present Value at 8% discount rate
discount_rate = InterestRate("8% annual")
pv = present_value(cash_flow, discount_rate)
print(f"Present Value at 8%: {pv}")

# Calculate Internal Rate of Return
investment_irr = irr(cash_flow)
print(f"IRR: {investment_irr}")  # Should be ~9.7%

# Calculate Modified IRR with different reinvestment assumptions
finance_rate = InterestRate("10% annual")      # Cost of capital
reinvestment_rate = InterestRate("6% annual")  # Reinvestment rate
mirr = modified_internal_rate_of_return(cash_flow, finance_rate, reinvestment_rate)
print(f"MIRR: {mirr}")

# Loan IRR (borrower's perspective)
loan = Loan(Money("10000"), InterestRate("5% annual"), [datetime(2024, 12, 31)])
loan_irr = loan.irr()  # Sugar syntax using loan's expected cash flow
print(f"Loan IRR: {loan_irr}")  # Should be ~5% (loan's own rate)

# Present Value of loan using different discount rate
loan_pv = loan.present_value(InterestRate("8% annual"))
print(f"Loan PV at 8%: {loan_pv}")  # Negative from borrower's perspective

Key Features:

  • ๐Ÿ”ฌ Scipy-powered: Uses scipy.optimize.brentq for robust root finding
  • ๐Ÿ“Š Automatic bracketing: Finds IRR solutions reliably across complex cash flows
  • ๐Ÿ•ฐ๏ธ Time Machine integration: Use Warp to calculate IRR from any date
  • ๐Ÿญ Sugar syntax: loan.irr() and loan.present_value() convenience methods
  • ๐Ÿ’ฐ High precision: Maintains decimal precision throughout calculations

### Tax & Grossup (Brazilian IOF) ๐Ÿ‡ง๐Ÿ‡ท

**Pluggable tax system with built-in IOF support and grossup for financed taxes:**

```python
from datetime import datetime
from money_warp import (
    Money, InterestRate, Loan, IndividualIOF, CorporateIOF,
    grossup_loan, PriceScheduler, generate_monthly_dates,
)

# Use preset rates for individual borrowers (PF)
iof = IndividualIOF()  # daily=0.0082%, additional=0.38%

# Or for corporate borrowers (PJ)
iof_pj = CorporateIOF()  # daily=0.0041%, additional=0.38%

# Attach tax to a loan for reporting
due_dates = generate_monthly_dates(datetime(2024, 2, 1), 12)
loan = Loan(
    Money("10000"), InterestRate("1% m"), due_dates,
    disbursement_date=datetime(2024, 1, 1),
    taxes=[iof],
)
print(f"Total IOF: {loan.total_tax}")
print(f"Net disbursement: {loan.net_disbursement}")  # principal - tax

# Grossup: borrower wants to receive exactly 10,000 after tax
grossed_loan = grossup_loan(
    requested_amount=Money("10000"),
    interest_rate=InterestRate("1% m"),
    due_dates=due_dates,
    disbursement_date=datetime(2024, 1, 1),
    scheduler=PriceScheduler,
    taxes=[iof],
)
print(f"Grossed-up principal: {grossed_loan.principal}")    # > 10,000
print(f"Net to borrower: {grossed_loan.net_disbursement}")  # ~= 10,000

Key Features:

  • ๐Ÿ”Œ Pluggable taxes: Implement BaseTax for any tax type
  • ๐Ÿ‡ง๐Ÿ‡ท IOF built-in: Brazilian IOF with daily + additional rate components
  • ๐Ÿ‘ค Presets: IndividualIOF() and CorporateIOF() with standard rates (overridable)
  • ๐Ÿ”„ Grossup: Scipy-powered solver finds the principal so principal - tax = requested_amount
  • ๐Ÿ“Š Rounding modes: PRECISE (default) or PER_COMPONENT to match external systems

๐Ÿ—๏ธ Architecture

MoneyWarp is built around five core concepts:

๐Ÿ’ฐ Money

High-precision monetary amounts using Python's Decimal for accuracy:

  • Internal precision: Stores full decimal precision
  • Display precision: Shows 2 decimal places for "real money"
  • Arithmetic safety: No floating-point errors

๐Ÿ“ˆ InterestRate

Type-safe interest rate handling with explicit conversions:

  • Clear representation: Eliminates 0.05 vs 5% confusion
  • Frequency conversion: Annual โ†” Monthly โ†” Daily โ†” Quarterly
  • String parsing: "5.25% annual" or "0.004167 monthly"
  • Abbreviated notation: "5.25% a.a.", "1.5% a.m." (Brazilian/LatAm convention)

๐Ÿ’ธ CashFlow

Container for cash flow analysis with SQLAlchemy-style querying:

  • CashFlowItem: Individual transactions with amount, date, description, category
  • CashFlow: Collection with filtering, summing, and analysis methods
  • Query interface: cashflow.query.filter_by(category='interest').sum_amounts()

๐Ÿฆ Loan

State machine for loan analysis with configurable schedulers:

  • Expected vs Actual: Compare planned payments with reality
  • Payment allocation: Fines โ†’ Interest โ†’ Principal priority
  • Fine engine: Automatic fines and mora interest for overdue payments
  • Sugar methods: pay_installment() and anticipate_payment() for natural workflows
  • Flexible scheduling: Any list of due dates, not just monthly
  • Multiple schedulers: PMT-based, fixed payment, custom algorithms

๐Ÿ‡ง๐Ÿ‡ท Tax

Pluggable tax strategy with Brazilian IOF and grossup:

  • BaseTax interface: Implement calculate() for any tax type
  • IOF: Daily rate + additional rate components with configurable rounding
  • Presets: IndividualIOF (PF) and CorporateIOF (PJ) with standard rates
  • Grossup: Scipy-powered solver for financed tax calculations

๐Ÿ“Š Supported Calculations

Loan Schedules

  • Progressive Price Schedule (French amortization system)
  • Inverted Price Schedule (Constant Amortization System - SAC)
  • Fixed payment amounts with interest/principal allocation
  • Irregular payment dates with daily compounding
  • Bullet loans (single payment at maturity)

Time Value of Money Functions

  • Present Value (PV): Discount future cash flows to present value
  • Net Present Value (NPV): Sum of discounted cash flows
  • Internal Rate of Return (IRR): Rate where NPV equals zero
  • Modified IRR (MIRR): IRR with different financing/reinvestment rates
  • Present Value of Annuities: Regular payment streams
  • Present Value of Perpetuities: Infinite payment streams
  • Discount Factors: Time value calculations

Financial Functions

  • PMT: Payment calculation for loans and annuities
  • Daily compounding: Precise interest calculations
  • Amortization schedules: Complete payment breakdowns
  • Balance tracking: Outstanding principal over time
  • Robust numerics: Scipy-powered calculations for complex scenarios

Tax Calculations

  • IOF (Brazilian): Daily + additional rate on each installment's principal
  • Grossup: Find the principal so borrower receives the exact requested amount
  • Per-installment breakdown: Tax detail for every installment in the schedule
  • Rounding modes: Precise (sum-then-round) or per-component (round-then-sum)
  • Custom taxes: Extend BaseTax for any jurisdiction or tax type

๐Ÿงช Testing & Validation

MoneyWarp includes comprehensive test coverage with validation against established financial libraries:

  • 700+ total tests with 100% core functionality coverage
  • Reference validation against cartaorobbin/loan-calculator
  • External IOF validation against a production Brazilian lending platform
  • Edge case handling: Zero interest, irregular schedules, high precision
  • Property-based testing: Parametrized tests across various scenarios

๐ŸŽฏ Use Cases

Personal Finance

  • Mortgage analysis: Compare different loan terms and rates
  • Payment tracking: Monitor actual vs expected payments
  • Refinancing decisions: Calculate savings from rate changes

Investment Analysis

  • Cash flow modeling: Track investment returns over time
  • Scenario analysis: "What if" calculations with different assumptions
  • Performance measurement: Calculate actual returns vs projections

Financial Planning

  • Loan comparison: Evaluate different lending options
  • Payment scheduling: Optimize payment timing for cash flow
  • Interest calculation: Precise daily compounding for any scenario

๐Ÿ”ฎ Roadmap

  • โœ… Time Machine (Warp): Travel to any date and analyze loan state - COMPLETED
  • โœ… Inverted Price Scheduler: Constant Amortization System (SAC) - COMPLETED
  • โœ… Present Value Functions: PV, NPV, annuities, perpetuities - COMPLETED
  • โœ… IRR Functions: IRR, MIRR with scipy-powered numerics - COMPLETED
  • โœ… Date Generation Utilities: Smart payment scheduling - COMPLETED
  • โœ… Fine Engine: Fines, mora interest, grace periods - COMPLETED
  • โœ… Payment Sugar Methods: pay_installment(), anticipate_payment() - COMPLETED
  • โœ… Tax Module: Brazilian IOF, grossup, pluggable tax strategy - COMPLETED
  • Additional Schedulers: Custom schedules, balloon payments
  • Performance optimization: Vectorized calculations for large datasets
  • Advanced TVM: Bond pricing, option valuation

๐Ÿค Contributing

We welcome contributions! Please see our Contributing Guide for details.

๐Ÿ“„ License

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

๐Ÿ™ Acknowledgments

  • Inspired by the time value of money concepts and validated against cartaorobbin/loan-calculator
  • Built with modern Python practices using Poetry, pytest, and pre-commit hooks
  • Follows the Zen of Python: "Beautiful is better than ugly. Explicit is better than implicit."

MoneyWarp - Because time is money, and money should bend to your will. ๐Ÿ’ฐโฐ

Project details


Download files

Download the file for your platform. If you're not sure which to choose, learn more about installing packages.

Source Distribution

money_warp-0.1.2.tar.gz (43.2 kB view details)

Uploaded Source

Built Distribution

If you're not sure about the file name format, learn more about wheel file names.

money_warp-0.1.2-py3-none-any.whl (45.7 kB view details)

Uploaded Python 3

File details

Details for the file money_warp-0.1.2.tar.gz.

File metadata

  • Download URL: money_warp-0.1.2.tar.gz
  • Upload date:
  • Size: 43.2 kB
  • Tags: Source
  • Uploaded using Trusted Publishing? Yes
  • Uploaded via: twine/6.1.0 CPython/3.13.7

File hashes

Hashes for money_warp-0.1.2.tar.gz
Algorithm Hash digest
SHA256 3aba021ace019271c0a397cab6562239939cdd4f0bef376ff7f803e0d3c7b5e6
MD5 6a5cf1e7d8be84f66434479335d9d368
BLAKE2b-256 2b3af703004516d333ed9b8ec7a977418c71e00a2e5cc57826a1937ad72527bf

See more details on using hashes here.

Provenance

The following attestation bundles were made for money_warp-0.1.2.tar.gz:

Publisher: on-release-pypi.yml on tomascorrea/money-warp

Attestations: Values shown here reflect the state when the release was signed and may no longer be current.

File details

Details for the file money_warp-0.1.2-py3-none-any.whl.

File metadata

  • Download URL: money_warp-0.1.2-py3-none-any.whl
  • Upload date:
  • Size: 45.7 kB
  • Tags: Python 3
  • Uploaded using Trusted Publishing? Yes
  • Uploaded via: twine/6.1.0 CPython/3.13.7

File hashes

Hashes for money_warp-0.1.2-py3-none-any.whl
Algorithm Hash digest
SHA256 c483b5677046961826776b18c95063bc627ad0d4049ec311abedc38d823b5200
MD5 fd7e2795a3516a6da50086bc8d3ec084
BLAKE2b-256 c79fc85942855c463662079e41ad799ef9e4946df59de4c27a58f6f2b8184e96

See more details on using hashes here.

Provenance

The following attestation bundles were made for money_warp-0.1.2-py3-none-any.whl:

Publisher: on-release-pypi.yml on tomascorrea/money-warp

Attestations: Values shown here reflect the state when the release was signed and may no longer be current.

Supported by

AWS Cloud computing and Security Sponsor Datadog Monitoring Depot Continuous Integration Fastly CDN Google Download Analytics Pingdom Monitoring Sentry Error logging StatusPage Status page