Home Multi-Currency How Currency Conversion Works

How Currency Conversion Works

Last updated on May 03, 2026

The conversion rule

Whenever a transaction's transactional currency differs from your functional currency, BitBooks converts it. The rule is simple:

Functional value = Transactional amount × Exchange rate at the transaction's date

The rate is "pinned" to the transaction. It's stored permanently with the entry. Future rate changes don't affect the historical conversion.


A worked example

Functional currency: USD.

You receive a transaction:

  • Transactional currency: EUR
  • Transactional amount: €1,000
  • Date: 2026-04-15
  • EUR/USD rate at 2026-04-15: 1.08

Conversion:

Functional value = €1,000 × 1.08 = $1,080

The transaction is stored with both:

  • €1,000 (transactional)
  • $1,080 (functional)

Reports default to the functional view ($1,080). The detail view shows both.


Where rates come from

For fiat-to-fiat conversions: Open Exchange Rates by default.

For Bitcoin pricing: CoinGecko by default.

You can change providers in Admin → Settings, but defaults work for everyone.

See How Exchange Rates Work in BitBooks for the rate-fetching mechanics.


When the rate is pinned

The rate is locked in at one of three moments, depending on the transaction:

  1. At post time for transactions that go straight to Posted status. The rate at that moment is recorded.
  2. At creation time for Drafts. When you save a Draft, BitBooks fetches and stores the rate based on the date you set. Even if you wait 3 weeks before posting, the rate stays at the original date's rate.
  3. At rate-resolution time for transactions whose initial fetch failed. If a transaction was created with a "pending" rate, the rate is fetched later when you click Retry. The pinned rate is the one resolved at retry, not at the original create time. (This is rare and usually only matters for stale rates.)

Once pinned, the rate doesn't change. Subsequent market movements don't affect historical entries.


The "rate timestamp"

Each line of a journal entry has a rateTimestamp: the moment whose rate was used. Usually equal to the transaction's date and time.

For day-bucket rates (most fiat): timestamp 2026-04-15T00:00:00Z gives the rate for April 15, regardless of the actual hour.

For 5-minute-bucket rates (BTC): timestamp 2026-04-15T14:23:00Z gives the rate for that 5-minute window.

If you want to verify the rate used on a transaction, click into the entry's detail and look at the rateTimestamp and exchange rate fields.


Multi-currency journal entries

A journal entry can have lines in different currencies. The journal entry has a header currency (the default for new lines) and individual line currencies that can override.

For balance: the functional totals of debits and credits must equal. So a line of 0.1 BTC might balance against a line of $9,000 USD if 0.1 BTC happens to equal $9,000 in functional terms at that date.

The transactional totals don't have to balance. (You can't add 0.1 BTC and $9,000 directly; they're different units.) The functional totals are what BitBooks checks.

Multi-currency journal entry showing BTC and USD lines with the functional total at the bottom in green ("Balanced")


Manual rate override

If the auto-fetched rate is wrong (you got a slightly different rate at the actual point of trade, e.g., an exchange's bid/ask spread), override:

  1. In a journal entry, click into the rate field next to a non-functional currency line
  2. Enter the rate you actually used
  3. Save

The line's stored rate is yours. The conversion uses your value. The override is recorded for audit so anyone reading later can see "manual rate used" and what it was.

For quick everyday transactions, don't bother. Use the auto rate. Manual override is for the cases where exact accuracy matters.


Conversion in reports

Reports compute their numbers from the functional values stored on each entry's lines. The reporting currency view (if set) re-translates from functional to reporting at the report's reference date.

So a P&L for January 2026 in USD-functional shows numbers as the entries had them at the time. If you have a reporting currency of EUR, you can toggle to EUR and the report re-translates using the USD/EUR rate at January 31.

This is a two-step translation: transactional → functional (at transaction time, locked) → reporting (at report time, recomputed).


Conversion and gain/loss

Conversion itself doesn't create gain/loss. The conversion is just a way to express the same transaction in a different currency.

Gains and losses arise from:

  • Holding non-functional assets that change value (you hold BTC, BTC's price moves, you have unrealized gain/loss)
  • Settling at a different rate (you owe €1,000 booked at $1,080, you pay when EUR/USD rate has moved to 1.10, the actual cost is $1,100, you have a $20 FX loss)

Both are handled by Tracking Bitcoin Value Changes (FX Revaluation) and similar mechanisms for foreign currency exposure.


Common questions

"My converted amount looks wrong by a few cents."

Likely a rate-bucket alignment issue or a rounding artifact. Each line is computed independently; aggregating can produce small rounding differences from "the obvious answer." This is normal accounting behavior, not a bug.

"Can I see what rate BitBooks used on a specific transaction?"

Yes. Open the transaction's detail page (or click through to its underlying journal entry). The line shows the rate, the rate source provider, and the rate timestamp.

"Why does my report say a total in USD when the transactions were all in BTC?"

If your functional currency is USD, the report displays in USD using the conversion. The underlying transactions are still in BTC. Click any number to drill into the underlying entries to see both views.


Where to go next