Fast, offline-first currency converter in your terminal. Millisecond conversions,
every currency in the feed including thinly traded ones, historical dates, and a 90-day
anomaly gauge. Dependency-free Rust binary (the rare network fetch shells out to
curl).
$ fx 100 usd cad
139.4703
90d 1.37478 – 1.42319 │ now 1.3947 │ [████░░░░░░] 41%
→ USD mid-range vs CAD — mid 90d range
Today's rate moves, so the figures above will not match yours. A date pins them:
$ fx eur inr 2025-06-29
100.2144
2025-06-29 1 EUR = 100.2144 INR
2025-06-27 Fri 100.1889 -0.3309
2025-06-28 Sat 100.0629 -0.1260
› 2025-06-29 Sun 100.2144 +0.1515
2025-06-30 Mon 100.1875 -0.0269
2025-07-01 Tue 100.9411 +0.7536
On a terminal the output is gently colored (soft cyan focal value, sage/terracotta
signals); piped or with NO_COLOR it's plain text with the identical layout.
- Snappy — the hot path only reads a local cache file, never the network except on a genuinely cold cache. A warm call measured ~2.8 ms end to end (mean of 50 runs including fork/exec, Linux x86-64, release build); your hardware will differ, so treat it as an order of magnitude, not a promise.
- Every currency in the feed (339 as of writing) — fiat, metals and crypto, all as first-class citizens. Thinly traded codes are not second-class.
- Historical rates + trend —
fx usd cad yesterday,fx 50 usd cad 2025-05-04,fx eur gbp 3w. A date query shows a 5-day window centered on the target (neighbors dimmed, day-over-day deltas colored) so the direction is obvious. - 90-day anomaly gauge — shows where today's rate sits in its recent 90-day range, for every currency, with the position marked on a simple bar.
- Inverse hint — when a result is awkwardly small (< 0.1, as
fx 1 inr euris) fx adds one dim, labeled line with the naturally-scaled reverse rate (↔ 1 EUR = 109.9086 INR). The primary line is always what you asked; the hint appears only when it helps, so normal pairs stay uncluttered. - Gentle, TTY-aware color — muted 256-color palette on an interactive
terminal; automatically plain when piped or when
NO_COLOR/TERM=dumbis set, so the first line stays a bare, parseable number for scripts. - Natural aliases —
us,uk,euro,yen,swiss,aussie, … or any ISO 4217 code. - Robust / offline-first — stale cache still answers instantly and refreshes
in the background; unknown currencies and unreachable dates fail cleanly with a
non-zero exit; clean SIGPIPE handling (no panic when piped into
head).
fx [amount] FROM TO [date]fx 100 usd eur # 100 USD -> EUR (latest)
fx gb us # 1 GBP in USD
fx 10000 jpy eur # Japanese yen -> EUR
fx 50 GBP JPY # 50 pounds to yen
fx canadian swiss # 1 CAD to CHF
fx usd cad yesterday # rate on a past date
fx 50 usd cad 2025-05-04 # rate on a specific date
fx eur gbp 3w # 3 weeks ago
fx --refresh # force a full rates + 90-day history rebuild
fx --helpDates accept today, yesterday, YYYY-MM-DD, or a relative N{d,w,m,y}
(e.g. 7d, 2w, 6m, 1y). Amounts may contain ,/_ separators.
| Signal | Meaning |
|---|---|
↑ ... near 90d high |
FROM is near its 90-day high against TO |
↓ ... near 90d low |
FROM is near its 90-day low against TO |
→ ... mid 90d range |
Rate sits mid-range |
Rates come from the free fawazahmed0 currency-api
(EUR-based daily snapshots, no API key), cached under ~/.cache/fx/:
history.tsv— one row per day (YYYY-MM-DD+ all rates), newest-first. This single file backs both the current conversion (top row) and the 90-day anomaly window. Capped at 120 rows; written atomically..refresh_stamp— throttles background refreshes.
The hot path is pure local I/O: read the cache file, scan the rows it needs,
print. Two network cases are detached, so the foreground does not wait: the first
run (which seeds 90 days in the background) and advancing to a new day. One case
does block, by necessity: asking for a historical date that is not in the cache
fetches that window synchronously, so fx 50 usd cad 2025-05-04 takes roughly
0.2 s the first time and about 2 ms afterwards. Conversions pivot through EUR:
amount / rate(FROM) * rate(TO). jsDelivr is primary with a pages.dev
fallback CDN.
Requires a Rust toolchain (rustup.rs) and curl on PATH
(used only for the rare network fetch; every cached call is pure local I/O).
git clone https://github.com/kidsil/fx-cli.git
cd fx-cli
cargo install --path . # installs `fx` into ~/.cargo/binOr build without installing:
cargo build --release # -> target/release/fx
cargo testNo dependencies beyond the standard library, so it builds offline. The rare
network fetch for rates shells out to curl.
Dual licensed under either of
- Apache License, Version 2.0 (LICENSE-APACHE)
- MIT licence (LICENSE-MIT)
at your option. Contributions are accepted under the same dual licence.
All of them, whatever the currency-api feed carries: every ISO 4217 fiat (USD, GBP, JPY, CAD, AUD, CHF, SEK …), precious metals (XAU, XAG), and crypto (BTC, ETH, …).