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

Download files

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

Source Distribution

ecbfx-0.3.0.tar.gz (35.7 kB view details)

Uploaded Source

Built Distribution

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

ecbfx-0.3.0-py3-none-any.whl (21.3 kB view details)

Uploaded Python 3

File details

Details for the file ecbfx-0.3.0.tar.gz.

File metadata

  • Download URL: ecbfx-0.3.0.tar.gz
  • Upload date:
  • Size: 35.7 kB
  • Tags: Source
  • Uploaded using Trusted Publishing? No
  • Uploaded via: twine/6.2.0 CPython/3.14.6

File hashes

Hashes for ecbfx-0.3.0.tar.gz
Algorithm Hash digest
SHA256 95e23d4a452d9bd49638dc58be03d498ca7d352a2ed201e74582d79e974e5e6d
MD5 ea02a7a2612d6e664baffdabba2e5ca0
BLAKE2b-256 84b4298b7c5dd8e80a4147688409d857eab7ead642c64c5a69e47c7fd35cf82b

See more details on using hashes here.

File details

Details for the file ecbfx-0.3.0-py3-none-any.whl.

File metadata

  • Download URL: ecbfx-0.3.0-py3-none-any.whl
  • Upload date:
  • Size: 21.3 kB
  • Tags: Python 3
  • Uploaded using Trusted Publishing? No
  • Uploaded via: twine/6.2.0 CPython/3.14.6

File hashes

Hashes for ecbfx-0.3.0-py3-none-any.whl
Algorithm Hash digest
SHA256 fe1325c66c9aa339e8551d5a7721bfde106cc342bef504931f7974f199020e52
MD5 a14a6da8f8fba0cd278adb4cf6a996e3
BLAKE2b-256 cc0b5d1a0359a03612a0a7039006867e51fc1cc94157f6d5e1c8eb005c4726b8

See more details on using hashes here.

Release history Release notifications | RSS feed

This release

0.3.0 This release

2 files

0.2.0

2 files

Supported by

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