ECB foreign exchange rate lookup — CLI and Python API with gap-fill, strict mode, and scripting support
Project description
ecbfx
A minimal command-line tool and Python library for fetching EUR foreign exchange rates directly from the ECB SDMX API.
Install
pip install ecbfx
For development (includes test dependencies):
git clone https://github.com/edvinassvedas-dev/ecbfx.git
cd ecbfx
pip install -e ".[dev]"
pytest
CLI usage
# Today's rate for USD (indirect: USD per 1 EUR — ECB native)
# Note: ECB publishes rates ~16:00 CET on trading days.
# If today's rate isn't available yet, use --latest instead.
ecbfx USD
# Specific date
ecbfx USD 2025-01-15
# Multiple currencies, specific date
ecbfx USD GBP CHF 2025-01-15
# Date range
ecbfx USD GBP --from 2025-01-01 --to 2025-03-31
# Direct convention: EUR per 1 foreign unit (inverted)
ecbfx USD 2025-01-15 --direct
# Most recent available rate (ignores today being a weekend/holiday)
ecbfx USD --latest
# Single value only — ideal for shell scripting
ecbfx USD --latest --quiet
RATE=$(ecbfx USD --quiet)
# Control decimal precision (default: 4)
ecbfx USD 2025-01-15 --decimal 6
# Strict mode — error instead of gap-filling on weekends/holidays
ecbfx USD 2025-01-15 --no-gap-fill
# CSV output (pipe-friendly)
ecbfx USD --from 2025-01-01 --to 2025-01-31 --csv
ecbfx USD --from 2025-01-01 --to 2025-01-31 --csv > rates.csv
# Read pairs from a file — one HTTP call per currency
ecbfx --pairs transactions.csv --direct --csv
# Read pairs from stdin
cat transactions.csv | ecbfx --pairs - --direct --csv
# Combine with date filter
ecbfx --pairs transactions.csv --from 2025-01-01 --to 2025-03-31 --csv
Convention
| Flag | Formula | Example |
|---|---|---|
| (default) | foreign units per 1 EUR | 1 EUR = 1.0830 USD |
--direct |
EUR per 1 foreign unit | 1 USD = 0.9234 EUR |
ECB publishes indirect natively. --direct inverts the rate.
The convention column in CSV output (USD/EUR or EUR/USD) makes the
direction explicit for downstream pipelines.
Flags reference
| Flag | Default | Description |
|---|---|---|
--direct |
off | EUR per 1 foreign unit instead of ECB native |
--latest |
off | Most recent available rate, regardless of date |
--quiet / -q |
off | Print rate value(s) only — ideal for scripting |
--decimal N |
4 | Decimal places in output rate |
--no-gap-fill |
off | Raise an error on weekends/holidays instead of substituting the nearest rate |
--csv |
off | CSV output instead of formatted table |
--pairs FILE|- |
— | Read date,currency pairs from a file or stdin |
Weekend and holiday gap-filling
ECB only publishes rates on trading days. By default, ecbfx automatically
uses the most recent prior trading day's rate (Last Observation Carried Forward)
when a requested date falls on a weekend or public holiday — including the first
date in a range that starts on a holiday such as January 1st.
Use --no-gap-fill to disable this and receive an explicit error instead —
useful in audit workflows where a substituted rate is not acceptable.
Exit codes
| Code | Meaning |
|---|---|
0 |
Success |
1 |
Runtime error (ECB API failure, network issue, no data returned) |
2 |
Usage error (invalid arguments, bad date format, missing required flag) |
Useful for scripting:
ecbfx USD --quiet || echo "fetch failed, exit $?"
Python API
from datetime import date
from ecbfx import fetch_rates, fetch_rates_for_pairs, fetch_latest
# Indirect (default) — foreign units per 1 EUR, contiguous range
rows = fetch_rates(["USD", "GBP"], date(2025, 1, 1), date(2025, 1, 31))
# Direct — EUR per 1 foreign unit
rows = fetch_rates(["USD"], date(2025, 1, 15), date(2025, 1, 15), direct=True)
# Most recent available rate
rows = fetch_latest(["USD", "CHF"])
# Sparse transaction dates — one HTTP call per currency regardless of pair count
pairs = [
(date(2025, 1, 15), "USD"),
(date(2025, 1, 20), "GBP"),
(date(2025, 2, 3), "USD"),
(date(2025, 2, 3), "CHF"),
]
rows = fetch_rates_for_pairs(pairs, direct=True)
# Custom decimal precision
rows = fetch_rates(["USD"], date(2025, 1, 15), date(2025, 1, 15), decimals=6)
# Strict mode — raises ECBError on weekends/holidays
rows = fetch_rates(["USD"], date(2025, 1, 13), date(2025, 1, 13), gap_fill=False)
rows = fetch_rates_for_pairs([(date(2025, 1, 13), "USD")], gap_fill=False)
for r in rows:
print(r["date"], r["currency"], r["convention"], r["rate"])
Each row is a dict: {date, currency, rate, convention}.
Input validation
from ecbfx import validate_currency, ECBError
# Normalises and validates a currency code — raises ECBError if invalid
print(validate_currency("usd")) # → "USD"
print(validate_currency(" GBP ")) # → "GBP"
try:
validate_currency("US$")
except ECBError as e:
print(e) # Invalid currency code 'US$'. Expected 2–4 ASCII letters.
Use ECBError in try/except blocks when calling any ecbfx function
to handle API failures, network errors, or invalid inputs cleanly.
fetch_rates vs fetch_rates_for_pairs
fetch_rates |
fetch_rates_for_pairs |
|
|---|---|---|
| Input | currency list + date range | list of (date, currency) tuples |
| Returns | every calendar day in range | exactly the requested dates |
| Best for | daily pipelines, backfill | transaction enrichment, broker CSVs |
| HTTP calls | one per currency | one per currency (full span, regardless of gaps) |
Note:
fetch_rates_for_pairsalways fetches the full date span from the earliest to the latest date per currency in a single HTTP call. For very sparse data (e.g. two transactions 10 years apart), this fetches the entire intervening range. A warning is logged when the span exceeds one year.
License
MIT
Project details
Release history Release notifications | RSS feed
Download files
Download the file for your platform. If you're not sure which to choose, learn more about installing packages.
Source Distribution
Built Distribution
Filter files by name, interpreter, ABI, and platform.
If you're not sure about the file name format, learn more about wheel file names.
Copy a direct link to the current filters
File details
Details for the file ecbfx-0.2.0.tar.gz.
File metadata
- Download URL: ecbfx-0.2.0.tar.gz
- Upload date:
- Size: 20.5 kB
- Tags: Source
- Uploaded using Trusted Publishing? No
- Uploaded via: twine/6.2.0 CPython/3.14.4
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
ef919dc178303dc28ce8c10f9aa6118009f87c93c24740ac5932096a97b1be9a
|
|
| MD5 |
d0099d7094873d23972abaa36320e5b9
|
|
| BLAKE2b-256 |
0a2394079292427aeceba195353fe88767d1dcd908507226ecbaaf54a0a6a2f3
|
File details
Details for the file ecbfx-0.2.0-py3-none-any.whl.
File metadata
- Download URL: ecbfx-0.2.0-py3-none-any.whl
- Upload date:
- Size: 16.1 kB
- Tags: Python 3
- Uploaded using Trusted Publishing? No
- Uploaded via: twine/6.2.0 CPython/3.14.4
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
8fc59ccb3ce6c2fce61d1ed1e3c259c1a24047ef8c23137ad81f23dd266e593d
|
|
| MD5 |
ee03c0b6c74896cc7b93ea4e26e75c38
|
|
| BLAKE2b-256 |
92e885bedcb6916badc970dc97081c3d2acdb2a91fc4a93256cea7baa5dcf4c0
|