FX rates¶
Keep prices.bean filled with official rates, so every foreign amount
converts to CHF the way the tax authorities accept. For VAT, a foreign amount
is converted at the rate the ESTV publishes, either the monthly average or
the daily selling rate, and you keep your choice for at least a tax
period.1 quints uses the daily rate and fetches it from the BAZG at full
precision.
Applies if
- Legal form: any.
- VAT status: any. The rates matter for VAT once you're registered, and for the year-end valuation always.
- Currencies: any the BAZG publishes, or any other beanprice source.
Keep rates current¶
quints prices sync # fill gaps and extend to today
quints prices sync --from 2026-01-01 # repair mode: re-check every day
Needs network. Every run extends each currency forward to today and
fills interior gaps — days missing anywhere in the file's history are
fetched too. Days the source has no rate for (weekends, holidays) are
checked once and recorded in a ; quints: verified … comment in the file
header, so they are never re-fetched. A day only counts as checked once a
newer rate is in the file: today, before the source publishes, is asked
again next time. The scan never reaches back more than ten years, the time
books are kept,3 so a mistyped year in an old line can't start a
century of requests. The file is written as rates arrive:
interrupt a long backfill and the next run resumes where it left off.
--from ignores the verified record and re-checks the whole range.
The BAZG API has no bulk endpoint — the fetch is one request per calendar
day per currency, so a first backfill takes a while; a live per-currency
progress bar shows how far along it is. Rates already in the file are never
touched — manual overrides survive a sync, with any ; comment on their
line. The file is replaced in one step, so a crash mid-write leaves the
old file. A line below the first price that isn't a price (another
directive, a stray note) is refused before anything is fetched, because
the rewrite would drop it.
Which currencies to fetch, and from which source, comes from the ledger —
the same commodity price: metadata bean-price reads, with the same
syntax, so quints and the standard beancount tooling always agree on where
rates come from:
Any beanprice source works —
bundled ones by short name (ecbrates/EUR-CHF), third-party ones by module
path — whether it serves bulk series or single days; quints drives it
day-by-day when it has to. The full bean-price syntax is supported: fallback
chains ("CHF:beanprice_bazg/EUR,ecbrates/EUR-CHF" — the first source that
answers wins), inverted pairs (^ prefix stores the reciprocal rate),
several quote currencies separated by spaces, and price: "" to opt a
commodity out. quints additionally refuses rates quoted in the wrong
currency, so a wrong ticker fails loudly instead of corrupting the file.
A price: value quints can't parse is listed, the other currencies still
sync, and the run then exits 1, so a currency never goes unfetched quietly.
When the ledger declares no priced commodities (or you run outside a
ledger), a [prices] section in quints.toml supplies the plan instead —
and --source MODULE forces a specific module either way:
[prices]
source = "ecbrates" # ECB reference rates, "BASE-SYMBOL" tickers
currencies = ["EUR"]
tickers = { EUR = "EUR-CHF" } # 1 EUR = x CHF
Why quints prices sync and not bean-price?¶
Both read the same commodity metadata and drive the same sources —
bean-price works fine against a quints ledger, and quints' own
beanprice-bazg source is tested against it. The difference is what happens
around the fetch:
- The file is maintained, not just appended to.
bean-priceprints directives for you to paste; sync ownsprices.bean— sorted, one entry per day, weekend echoes de-duplicated, manual overrides left alone. - Gaps heal themselves.
bean-pricefetches the dates you ask for. Every sync run re-checks the whole history and fills any missing day, so a stretch lost to a laptop shutdown or a source outage doesn't stay missing silently. Days the source genuinely has no rate for (weekends, holidays) are recorded in the file header and checked exactly once. - Interrupts are cheap. Rates are written as they arrive; killing a 2-year backfill loses at most one window, and the next run resumes.
- Full precision.
bean-price --updaterounds through the ledger'sdisplay_precision; sync writes the raw source rate (BAZG publishes five decimals), which is what the ESTV-accepted daily rate actually is. - Daily rates, by day. quints converts at the official rate of the transaction day (the daily option of Art. 45 MWSTV), not the latest quote — so the unit of work here is "keep a complete daily series current", which is a loop and a file format, not a one-shot fetch.
Year-end revaluation¶
Prints the year-end revaluation transaction:2 unrealized FX
differences on non-CHF balances, booked against the configured
fx_gain/fx_loss accounts. Review it, paste it into books/<year>.bean.
Like everything in quints, it's printed for review — never written into your
books directly.
It is one step of the year-end sequence — quints close check says whether
it is still outstanding; see Close the year.
For VAT amounts on foreign invoices, use quints vat convert
instead of manual conversion.
-
Art. 45 MWSTV, SR 641.201. Abs. 3: "nach dem von der ESTV veröffentlichten Wechselkurs, wobei wahlweise der Monatsmittelkurs oder der Tageskurs für den Verkauf von Devisen verwendet werden kann"; Abs. 5: the choice is kept "während mindestens einer Steuerperiode". Input tax follows the same rule (Art. 58 MWSTV). MWST-Info 07 Steuerbemessung und Steuersätze, Ziff. 1.3.2; ESTV, Fremdwährungskurse; the daily rates: BAZG. ↩
-
Valuation: Art. 960 ff. OR; the currency of the accounts and the disclosure of the rates used: Art. 958d Abs. 3 OR. SR 220. ↩
-
Art. 958f Abs. 1 OR: "Die Geschäftsbücher und die Buchungsbelege … sind während zehn Jahren aufzubewahren." fedlex. ↩