A Python library for date-aware currency operations

dated-money handles monetary values with their associated dates, enabling currency conversions using historical exchange rates. Built for tracking multi-currency transactions at GreaterSkies.

https://github.com/juanre/dated-money

Features

  • Monetary values maintain their date and original currency
  • Automatic currency conversion using historical exchange rates
  • Arithmetic operations between different currencies and dates
  • Multiple exchange rate sources with automatic fallback
  • SQLite caching for performance

Installation

uv add dated-money

or

pip install dated-money

Usage

Basic Operations

from dated_money import Money, Currency

# Money() returns a class, not an instance
EUR = Money(Currency.EUR)

# Create monetary values
payment = EUR(5000, 'THB', on_date='2024-01-15')
print(payment)         # ฿5000.00
print(payment.to('EUR'))  # €130.52

# Operations preserve currency information
eur_100 = EUR(100)
usd_120 = EUR(120, 'USD')
total = eur_100 + usd_120  # Result in EUR

Working with Dates

# Different dates use different exchange rates
jan_payment = EUR(1000, 'USD', on_date='2024-01-15')
feb_payment = EUR(1000, 'USD', on_date='2024-02-15')

# Operations between different dates use the first operand's date
result = jan_payment + feb_payment
assert result.on_date == jan_payment.on_date

Exchange Rate Sources

dated-money checks for rates in this order:

  1. SQLite cache - Local database (platform-specific default location)
  2. Git repository - Directory specified by DMON_RATES_REPO
  3. Supabase - Using SUPABASE_URL and SUPABASE_KEY
  4. exchangerate-api.com - Using DMON_EXCHANGERATE_API_KEY

If rates aren’t available for the requested date, it searches back up to 10 days.

Rate File Format

JSON files named yyyy-mm-dd-rates.json:

{
  "conversion_rates": {
    "USD": 1,
    "EUR": 0.85,
    "GBP": 0.73,
    ...
  }
}

Alternative Libraries

py-moneyed

  • Pros: Mature, handles currency representation well, CLDR locale support
  • Cons: No built-in exchange rates or historical data

forex-python

  • Pros: Provides current and historical exchange rates
  • Cons: Makes API calls for each conversion, no date association with amounts

CurrencyConverter

  • Pros: Offline historical rates (ECB data since 1999)
  • Cons: Limited to 42 currencies, data might be outdated

python-money

  • Pros: Basic money representation
  • Cons: No longer maintained, broken currency exchange

Comparison

LibraryMoney TypeHistorical RatesDate AssociationOffline Mode
dated-money
py-moneyedN/A
forex-python
CurrencyConverter

dated-money combines money representation with historical rates, associating dates directly with monetary values.

Real-World Example

The key insight: each transaction remembers its date. When you convert currencies, it uses the exchange rate from that specific date.

At GreaterSkies, customers pay in various currencies. Each sale gets tracked with its transaction date:

from dated_money import Money, Currency

EUR = Money(Currency.EUR)

# January: Customer in Canada buys a star map for CAD 50
jan_sale = EUR(50, 'CAD', on_date='2024-01-15')

# March: Customer in UK buys a map for £40
mar_sale = EUR(40, 'GBP', on_date='2024-03-10')

# June: Customer in Japan buys a map for ¥8000
jun_sale = EUR(8000, 'JPY', on_date='2024-06-20')

# Each transaction shows its original amount
print(jan_sale)  # CAD 50.00
print(mar_sale)  # £40.00
print(jun_sale)  # ¥8000.00

# Calculate total in EUR - each converts at its own date's rate
total = jan_sale + mar_sale + jun_sale
print(f"Total revenue: {total.to('EUR')}")  # Total revenue: €136.42

# The magic: each amount knows when it happened
# Jan 15: CAD 50 = €34.12 (using Jan 15 rate)
# Mar 10: £40 = €46.51 (using Mar 10 rate)
# Jun 20: ¥8000 = €55.79 (using Jun 20 rate)

This is crucial because £100 from January is different from £100 from June - they came from different original amounts at different exchange rates. By tracking the date with each amount, you always know exactly how much you received in your base currency.

Command-Line Tool

# Create cache database
dmon-rates --create-table

# Fetch historical rates (requires paid API)
dmon-rates --fetch-rates 2023-01-01:2023-12-31

License

MIT