ntro.capabilities.fx converts a monetary amount from one currency to
another at the exchange rate for a given value date. Use it from an
activity when a runbook handles multi-currency figures — e.g. normalising
invoices to a reporting currency before export.
It complements CurrencyCode, which only validates ISO-4217
codes; fx is what actually converts.
Convert
onis the value date. Pass it for a historical rate (typically the document/transaction date); omit it for the latest available rate.- Same-currency conversions are a local no-op (
rate=1,source="identity") — no network call. - The converted
amountis quantised to 2 decimal places; the rawrateis returned at full precision so callers can re-round (minor-unit-aware rounding, e.g. JPY = 0dp, is a future refinement). - Raises
fx.FxErroron an unsupported currency pair or a feed failure.
fx.rate(from_ccy, to_ccy, on=...) returns an FxRate
(rate, rate_date, source).
Rates + caching
Rates come from a free, key-less dated feed (frankfurter.dev, backed by the ECB reference rates), keyed by the value date. Rates for a past date are immutable, so resolution is write-once-read-many across two layers — the external feed is hit at most once per(base, quote, date):
| Layer | Where | Lifetime |
|---|---|---|
| L1 | in-process dict in the capability | the worker process |
| L2 | system.fx_rates in the tenant data plane | durable; shared across replicas |
tenant_slug to enable the persisted cache.
The cache is not the audit record. Stamp the applied rate on the
consuming row — e.g.
ExpenseRow.fx_rate / fx_rate_date / source — so the
figure stays reproducible even if the cache is cleared. The two-layer cache is
purely a performance + rate-limit concern.Return shapes
Related
Typing
CurrencyCode — the ISO-4217 validator the amounts carry.expenses
Where converted amounts land + the audit-stamp pattern.
Data capability
The tenant data plane the L2 cache (
system.fx_rates) is written through.