Currencies and FX
In short
Section titled “In short”- Send every amount as a whole number of minor units: pesewas for cedis, cents for dollars. GHS 4,950.00 is
495000. - Some currencies have no minor unit. The West and Central African CFA francs (XOF, XAF) are counted in whole francs: XOF 50,000 is
50000. - SpeculaPro converts every transaction into your base currency using the rates you load, with exact arithmetic, and keeps both amounts and the rate used.
Why minor units
Section titled “Why minor units”Decimal amounts such as 4950.00 are easy to get subtly wrong: a computer storing 0.1 + 0.2 doesn’t get exactly 0.3. Whole numbers are exact. Every regulator-facing figure SpeculaPro produces can be reproduced to the last pesewa.
How many decimals each currency has
Section titled “How many decimals each currency has”| Currency | Decimals | Amount | You send |
|---|---|---|---|
| GHS Ghana cedi | 2 | GHS 4,950.00 | 495000 |
| NGN Nigerian naira | 2 | NGN 250,000.00 | 25000000 |
| USD US dollar | 2 | USD 120.00 | 12000 |
| EUR euro | 2 | EUR 75.50 | 7550 |
| XOF West African CFA franc | 0 | XOF 50,000 | 50000 |
| XAF Central African CFA franc | 0 | XAF 50,000 | 50000 |
| KWD Kuwaiti dinar | 3 | KWD 12.500 | 12500 |
SpeculaPro knows the currencies whose minor units differ from 2 (including XOF, XAF, GNF, RWF, UGX, JPY with 0, and BHD, KWD, OMR, TND with 3). Every other currency is treated as having 2 decimals.
How conversion works
Section titled “How conversion works”For each transaction SpeculaPro takes:
- your amount and currency (
amount_minor,currency), - your workspace’s base currency,
- the rate for that day: units of base per 1 unit of the foreign currency (see FX rates).
It multiplies, adjusts for the difference in decimals between the two currencies, and rounds to the nearest minor unit (halves round away from zero). The arithmetic is exact: no floating-point error is introduced at any step.
Worked example: US dollars into cedis
Section titled “Worked example: US dollars into cedis”| Transaction | USD 120.00 → amount_minor: 12000, currency: "USD" |
| Rate loaded | {"quote": "USD", "rate": "15.25"}: 1 USD = 15.25 GHS |
| Calculation | 12,000 cents × 15.25 = 183,000 pesewas (USD and GHS both have 2 decimals) |
| Result | GHS 1,830.00 |
Worked example: CFA francs into cedis
Section titled “Worked example: CFA francs into cedis”| Transaction | XOF 50,000 → amount_minor: 50000, currency: "XOF" |
| Rate loaded | {"quote": "XOF", "rate": "0.02685"}: 1 XOF = 0.02685 GHS |
| Calculation | 50,000 × 0.02685 = 1,342.5 cedis. GHS has 2 more decimals than XOF, so × 100 = 134,250 pesewas |
| Result | GHS 1,342.50 |
What is stored
Section titled “What is stored”Each transaction keeps all of these, so any converted amount can be checked later:
| Stored | Example (USD case) |
|---|---|
| Original amount and currency | 12000 USD |
| Base amount and currency | 183000 GHS |
| Rate used | 15.25 |
Transactions in your base currency
Section titled “Transactions in your base currency”No rate is needed: the rate is recorded as 1 and the base amount equals the original amount.
When conversion fails
Section titled “When conversion fails”The transaction is rejected, never converted with a guess:
- No rate on or before the transaction date:
no FX rate for USD→GHS on or before 2026-10-09; load rates via /api/v1/ingest/fx-rates. - Rate too old: more than 8 days (with the default settings) older than the transaction:
latest FX rate for USD→GHS is from 2026-09-28, too old for 2026-10-09.
Load the missing rates, wait a few minutes (servers cache rates for up to 5 minutes), and resend the rejected transactions with the same external_id.