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:
- SQLite cache - Local database (platform-specific default location)
- Git repository - Directory specified by
DMON_RATES_REPO
- Supabase - Using
SUPABASE_URL
andSUPABASE_KEY
- 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
Library | Money Type | Historical Rates | Date Association | Offline Mode |
---|---|---|---|---|
dated-money | ✓ | ✓ | ✓ | ✓ |
py-moneyed | ✓ | ✗ | ✗ | N/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