Skip to main content

ecbfx

PyPI version Python License: MIT

A minimal command-line tool and Python library for fetching EUR foreign exchange rates directly from the ECB SDMX API.


Install

pip install ecbfx

Working on ecbfx:

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, and multiple currencies at once
ecbfx USD 2025-01-15
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
RATE=$(ecbfx USD --quiet)

# CSV output (pipe-friendly)
ecbfx USD --from 2025-01-01 --to 2025-01-31 --csv > rates.csv

# Read (date, currency) pairs from a file or stdin — one HTTP call per currency
ecbfx --pairs transactions.csv --direct --csv
cat transactions.csv | ecbfx --pairs - --direct --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
--from YYYY-MM-DD — Start of date range (inclusive). Also filters --pairs input
--to YYYY-MM-DD — End of date range (inclusive). Requires --from in range mode
--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 / --decimals N 4 Output precision. Decimal places normally; with --direct a floor on significant digits, so high-ratio currencies (JPY, HUF, KRW) keep full precision instead of collapsing to 0.0007
--timeout N 30 HTTP timeout in seconds, per attempt (3 attempts with backoff)
--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. Comma, tab or space separated; surrounding quotes are tolerated, so spreadsheet exports work as-is
--version — Print the installed version and exit

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.

A future date is never substituted, so a rate is never influenced by information that did not exist on the date requested. If no trading day exists within 5 calendar days before the target — the widest real gap in the ECB calendar — the lookup fails rather than reaching further back for a staler rate.

Use --no-gap-fill to disable substitution entirely 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"),
]
rows = fetch_rates_for_pairs(pairs, direct=True)

# Strict mode — raises ECBError on weekends/holidays instead of substituting
rows = fetch_rates(["USD"], date(2025, 1, 13), date(2025, 1, 13), gap_fill=False)

for r in rows:
    print(r["date"], r["currency"], r["convention"], r["rate"])

Both fetch functions accept direct, decimals, gap_fill, timeout and an optional session for connection reuse.

Behaviour worth knowing

  • fetch_rates is all-or-nothing across currencies. A malformed code raises before any request. A well-formed code the ECB will not serve raises partway through: rows already fetched are discarded and later currencies are never requested. Call once per currency if you need per-currency isolation.
  • Duplicate currencies collapse. ["USD", "usd"] is one HTTP call and one row per date.
  • Output order differs between the two functions — see the table below.

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 a 3-letter ISO 4217 code, e.g. USD, GBP, CHF.

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
Order sorted by (date, currency) matches input order, repeats included
Best for daily pipelines, backfill transaction enrichment, broker CSVs

Both make one HTTP call per currency.

Note: fetch_rates_for_pairs fetches the full span from the earliest to the latest date per currency in that single call. For very sparse data (e.g. two transactions 10 years apart) this pulls the entire intervening range; a warning is logged when the span exceeds one year.


License

MIT

Metadata

Release files for ecbfx 0.3.0

For a detailed explanation of source distributions (sdists) and built distributions (wheels), please see the package formats documentation.

Source distribution (sdist)

Source distribution for ecbfx 0.3.0
File Size Uploaded
ecbfx-0.3.0.tar.gz 35.7 kB Details

Built distribution (wheel)

Table of built distributions (wheels) for ecbfx 0.3.0
File Interpreter ABI Platform
ecbfx-0.3.0-py3-none-any.whl Python 3 none any Details

Total release size: 57.0 kB

Release files / ecbfx-0.3.0.tar.gz

Download URL ecbfx-0.3.0.tar.gz
Size 35.7 kB
Tags Source
SHA-256 checksum
How to use checksums
95e23d4a452d9bd49638dc58be03d498ca7d352a2ed201e74582d79e974e5e6d
BLAKE2b-256 checksum
How to use checksums
84b4298b7c5dd8e80a4147688409d857eab7ead642c64c5a69e47c7fd35cf82b
Upload date
Uploaded using Trusted Publishing?
What is trusted publishing?
No
Uploaded via twine/6.2.0 CPython/3.14.6

Release files / ecbfx-0.3.0-py3-none-any.whl

Download URL ecbfx-0.3.0-py3-none-any.whl
Size 21.3 kB
Tags Python 3
SHA-256 checksum
How to use checksums
fe1325c66c9aa339e8551d5a7721bfde106cc342bef504931f7974f199020e52
BLAKE2b-256 checksum
How to use checksums
cc0b5d1a0359a03612a0a7039006867e51fc1cc94157f6d5e1c8eb005c4726b8
Upload date
Uploaded using Trusted Publishing?
What is trusted publishing?
No
Uploaded via twine/6.2.0 CPython/3.14.6

Release history Release notifications | RSS feed

This release

0.3.0 This release

2 release files

0.2.0

2 release files

Anthropic, PBC Visionary sponsor Bloomberg Visionary sponsor Hudson River Trading Visionary sponsor Meta Visionary sponsor NVIDIA Visionary sponsor Microsoft Sustainability sponsor Depot Continuous Integration AWS Cloud computing and Security Sponsor Datadog Monitoring Fastly CDN Google Download Analytics Sentry Error logging StatusPage Status page