Liking cljdoc? Tell your friends :D

ecbjure

A pure Clojure library for accessing European Central Bank (ECB) data, starting with historical currency conversion. Designed to grow into a broader ECB data client.

Clojars Project CI cljdoc License

Features

  • Historical FX rates from the ECB (daily reference rates, ~42 currencies since 1999)
  • Triangulated conversion through EUR between any two supported currencies
  • Always fetches fresh data from the ECB — no stale bundled rates
  • Load from ECB URL (default), local file, or any custom URL
  • Functional, data-oriented design — the converter is a plain Clojure map
  • No interpolation of missing rates — missing data throws, never fabricates
  • SDMX client — fetch interest rates, inflation, and other ECB series via clojure-finance.ecbjure.sdmx; ergonomic series-key builders (build-series-key, exr-series-key) for composing multi-currency queries
  • tech.ml.dataset integration — wide and tidy/long format datasets (optional alias)

Installation

;; deps.edn
com.github.clojure-finance/ecbjure {:mvn/version "0.1.4"}

Quick Start

(require '[clojure-finance.ecbjure.fx :as fx])
(import '[java.time LocalDate])

;; Fetch latest data from ECB (default)
(def c (fx/make-converter))

;; Convert 100 USD to JPY using the latest available rate
(fx/convert c 100 "USD" "JPY")
;; => 15791.89

;; Convert on a specific date
(fx/convert c 100 "USD" "EUR" (LocalDate/of 2013 3 21))
;; => 77.46

;; Omit target currency — defaults to EUR
(fx/convert c 100 "USD")
;; => 90.91

API

Construction

;; Fetch from ECB URL (default)
(fx/make-converter)

;; From ECB URL (fetches latest)
(fx/make-converter fx/ecb-url)

;; From local file (ZIP or CSV)
(fx/make-converter "/path/to/eurofxref-hist.zip")

;; With options
(fx/make-converter fx/ecb-url {:fallback :nearest
                                :cast-fn  bigdec})

;; From a seq of CSV lines (useful for testing or custom data)
(fx/make-converter-from-lines lines opts)

Options:

KeyDefaultDescription
:cast-fndoubleRate coercion function. Use bigdec for exact arithmetic.
:fallbackfalseOut-of-bounds date behaviour: false (throw), :nearest, :before, :after, or true (alias for :nearest).
:ref-currency"EUR"Reference currency (triangulation pivot).

Conversion

;; amount from → EUR (default target)
(fx/convert c 100 "USD")

;; amount from → to, latest available date
(fx/convert c 100 "USD" "JPY")

;; amount from → to, specific date
(fx/convert c 100 "USD" "JPY" (LocalDate/of 2014 3 28))

Rate Queries

;; EUR-referenced rate for a currency on a date
(fx/get-rate c "USD" (LocalDate/of 2014 3 28))
;; => 1.3759

;; get-rate on the ref-currency always returns 1.0
(fx/get-rate c "EUR" (LocalDate/of 2014 3 28))
;; => 1.0

;; Cross rate between any two currencies — EUR may appear as either argument
(fx/cross-rate c "USD" "GBP" (LocalDate/of 2014 3 28))
;; => 0.5999...
(fx/cross-rate c "EUR" "USD" (LocalDate/of 2014 3 28))
;; => 1.3759

;; Full sorted date→rate history for a currency
(fx/rate-history c "USD")
;; => {#object[LocalDate "1999-01-04"] 1.1789, ...}

Metadata

The converter is a plain map — inspect it directly:

(:currencies c)   ;; => #{"EUR" "USD" "JPY" "GBP" ...}
(:bounds c)       ;; => {"USD" {:first-date #object[LocalDate "1999-01-04"]
                  ;;            :last-date  #object[LocalDate "2026-03-06"]} ...}
(:ref-currency c) ;; => "EUR"

Constants

fx/ecb-url
;; => "https://www.ecb.europa.eu/stats/eurofxref/eurofxref-hist.zip"

Dataset Output (optional)

Requires the :dataset alias (techascent/tech.ml.dataset):

(require '[clojure-finance.ecbjure.dataset :as ds])

;; Wide format — one row per date, one column per currency
(ds/rates-wide c)
;; => ecb-rates-wide [6846 43]:
;; |      :date |    USD |    JPY | ... |
;; |------------|-------:|-------:|
;; | 1999-01-04 | 1.1789 | 133.73 | ... |
;; | ...        |    ... |    ... | ... |

;; Long/tidy format — one row per (date, currency) observation
(ds/rates-long c)
;; => ecb-rates-long [~280000 3]:
;; |      :date | :currency |   :rate |
;; |------------|-----------|--------:|
;; | 1999-01-04 |       EUR |  1.0000 |
;; | 1999-01-04 |       GBP |  0.7111 |
;; | 1999-01-04 |       USD |  1.1789 |
;; | ...        |       ... |     ... |

Start the REPL with both aliases: clj -M:nrepl:dataset

Error Handling

All errors are ex-info with a :type key:

:typeCause
:unknown-currencyCurrency code not in the dataset
:rate-not-foundNo rate available for the requested date
(try
  (fx/convert c 100 "USD" "EUR" (LocalDate/of 2024 1 6)) ; Saturday — no ECB data
  (catch clojure.lang.ExceptionInfo e
    (ex-data e)))
;; => {:type :rate-not-found, :currency "USD", :date #object[LocalDate "2024-01-06"]}

Use :fallback :nearest (or :before/:after) to clamp out-of-bounds dates to a boundary instead of throwing.

Design Notes

No interpolation. The ECB publishes rates on business days only. When a date has no rate (weekends, holidays), ecbjure throws rather than silently inventing a number. Interpolating financial data introduces look-ahead bias in backtesting. If you need gap-filling, do it explicitly in your own pipeline where the assumption is visible.

The converter is a map. No defrecord, no mutable state. make-converter returns a plain Clojure map; convert is a pure function. The converter is inspectable, serializable, and composable.

Minimal dependencies. Core library depends only on org.clojure/data.csv. No HTTP client needed — the ECB ZIP is fetched via java.net.URI/.openStream.

CLI

With the :cli alias:

clj -M:cli 100 USD --to EUR
# 100.000 USD = 90.909 EUR on 2026-03-06

clj -M:cli 100 USD --to JPY --date 2013-03-21
# 100.000 USD = 12051.282 JPY on 2013-03-21

clj -M:cli 100 USD -v
# 100.000 USD
# 41 available currencies:
# AUD BGN BRL CAD CHF CNY CZK DKK EUR GBP ...

Options: --to <currency>, --date <yyyy-MM-dd>, --source <url-or-path>.

Roadmap

  • Enhancements: clj-yfinance integration for live spot rates.

SDMX Client

Access broader ECB statistical data via clojure-finance.ecbjure.sdmx:

(require '[clojure-finance.ecbjure.sdmx :as sdmx])

;; EURIBOR 3-month, last 3 months
(sdmx/get-series sdmx/euribor-3m {:last-n 3})
;; => [{:time-period #object[LocalDate "2025-12-01"] :obs-value 2.0457 :currency "EUR" ...} ...]

;; Euro area HICP inflation since 2020
(sdmx/get-series sdmx/hicp-euro-area {:start-period "2020-01"})

;; Arbitrary ECB SDMX series key
(sdmx/get-series "EXR/D.USD.EUR.SP00.A"
                 {:start-period "2024-01-01" :end-period "2024-01-31"})

Predefined constants: exr-daily, exr-monthly, euribor-1w, euribor-1m, euribor-3m, euribor-6m, euribor-1y, euribor-overnight, estr-daily, hicp-euro-area.

Each observation is a map with :time-period (LocalDate), :obs-value (double), and all dimension columns from the ECB CSV response. No additional dependencies — uses data.csv and JDK HTTP.

Dataflow discovery:

;; List all ~100 available ECB dataflows
(sdmx/list-dataflows)
;; => {"AME" "AMECO", "BKN" "Banknotes statistics", "EXR" "Exchange Rates", "ICP" "HICP", ...}

Series-key builders:

;; Generic builder — strings, nil (wildcard), or sets (multi-value)
(sdmx/build-series-key "EXR" ["D" #{"USD" "JPY"} nil "SP00" "A"])
;; => "EXR/D.JPY+USD..SP00.A"

;; EXR convenience — named keys with sensible defaults
(sdmx/exr-series-key {:currency #{"USD" "JPY"}})
;; => "EXR/D.JPY+USD.EUR.SP00.A"

(sdmx/exr-series-key {:freq "M" :currency "GBP"})
;; => "EXR/M.GBP.EUR.SP00.A"

;; Compose with get-series
(sdmx/get-series (sdmx/exr-series-key {:currency #{"USD" "JPY"}})
                 {:last-n 5})

License

Copyright © 2026 clojure-finance

Released under the Eclipse Public License 2.0.

Can you improve this documentation?Edit on GitHub

cljdoc builds & hosts documentation for Clojure/Script libraries

Keyboard shortcuts
Ctrl+kJump to recent docs
Move to previous article
Move to next article
Ctrl+/Jump to the search field
× close