Payments & ledgers
Currency exchange - a rate that is only true for a few seconds
Why a quoted FX rate is a short-lived promise with an expiry, and how rounding is a ledger decision.
What this board gets wrong on purpose
The tension
EVERY QUOTE IS A FREE 30-SECOND OPTION, AND THE BOARD ONLY PRICES IT FOR A CALM MARKET. The customer sees a frozen rate and may execute at any point before expiry - so a customer who watches the market can request a quote, wait, and accept only if the mid has moved against the house. The 35 bp spread covers that comfortably on a normal day: EUR/USD moves about 1 bp in 30 seconds, one thirty-fifth of the margin. It does not cover a gap. When a central bank surprises the market a pair can move 2% in under a second (EUR/CHF moved 30% in minutes in January 2015), and every live quote for that pair becomes a guaranteed loss the instant it is accepted. The aggregator rejects STALE rates but nothing on this board watches VOLATILITY: there is no breaker that widens spreads or withdraws outstanding quotes when the mid moves more than the spread within a quote lifetime, and the hedge only runs once the net position crosses $250,000 - so the loss is booked before treasury has even been told it exists.
Practise this as an interview
Take Currency exchange - a rate that is only true for a few seconds as a timed system design interview on a blank board - on your own, with the requirements to check yourself against, or with a candidate you invite.
Start here
Why this design
Any product that lets money cross a border - a wallet, a remittance app, a marketplace paying sellers abroad, a card spending in a foreign currency - has to answer "how much will I get?" with a number it will honour. The naive design reads a rate at the moment of execution and multiplies; the customer sees one number and receives another. Interviewers use this question to see whether you model the quote as a stored, expiring promise, whether you know a cross-currency movement must balance per currency, and whether you can say exactly where a rounding fraction goes. Treasury teams care about the next layer: every executed quote leaves the company holding currency risk until it is hedged.
What you will learn
- Why a quote is a row with a frozen rate, both amounts already rounded, and an expires_at - and why execution spends a quote id, never "the current rate".
- How a mid rate is built from three feeds, and why staleness is a reject, not a warning.
- How the spread and the rounding rule are applied in that order, in integers, per currency’s minor units (USD 2, JPY 0, KWD 3).
- Why a conversion is four ledger entries in two currencies through FX position accounts, and each currency sums to zero.
- How the company’s resulting exposure flows to treasury and is netted before it is hedged.
- The honest weakness: a 30-second quote is a free option, and nothing here withdraws quotes when the market gaps.
How to read this board
- Diagram, left to right: RATES (a number that decays), QUOTE (a promise with an expiry), EXECUTE (only a live quote can be spent), TREASURY (the risk you kept). Press play for the walkthrough.
- Below it, RECONCILIATION: quoted vs executed vs hedged, and where rounding went.
- Then the DATA MODEL: five tables. Note conversions is UNIQUE on quote_id.
- Pages 1-3 · Requirements, Capacity and Storage: 5 M quotes a day, 1,170 ticks a second in.
- Page 4 · Availability - why a stale rate stops quoting rather than guessing.
- Page 5 · How it works - 1,000 USD to EUR end to end, then Page 6 · Deployment.
1 · Requirements
The service quotes and executes currency conversions for customers holding balances in several currencies. It does not set the market price - it buys rates from liquidity providers - but it sets its own spread, owns the rounding rule, and holds the resulting position until treasury covers it.
Ask these before designing
- Firm quotes or indicative? A firm quote is honoured if accepted before expiry. An indicative one is re-priced at execution. Firm is what customers expect and what this board does; it is also what creates the free option.
- How long is a quote alive? 30 s is common for retail. Longer helps conversion rate and costs margin; shorter annoys people on slow phones.
- Which side is the customer fixing? "Send exactly 1,000 USD" and "receive exactly 900 EUR" are different calculations and round on different sides.
- Which way does rounding go, and where does the remainder land? Half-even on the received amount, with the difference absorbed by the FX position account, is one defensible answer. "Always in our favour" is another, and is a regulatory conversation.
- Do we hold the risk or pass it through? Matching every conversion one-for-one with a provider trade removes risk and adds 200 ms and a fee to each. Netting and hedging above a threshold is cheaper and leaves the company exposed in between.
Functional
- Build a mid rate per pair from multiple providers; reject a pair whose freshest good rate is older than 2 s.
- Issue a firm quote: sell amount, buy amount, all-in rate, expiry.
- Execute a conversion against a quote id, once, before expiry, by the customer who requested it.
- Post the conversion to the ledger as balanced entries per currency.
- Track net position per currency and hedge it above a threshold.
- Reconcile daily: quoted margin vs realised margin vs hedge cost.
Non-functional
- Quote p99 under 80 ms; execution p99 under 200 ms.
- A rate older than 2 s is never quoted. Better to refuse than to quote a number the market has left.
- Every executed conversion is reproducible from the stored quote - rate, rounding, amounts - years later.
- Integer minor units in the ledger, always; rates stored as NUMERIC(18,8), never floats.
Out of scope
Payment rails, KYC, card-scheme FX, the provider contracts and treasury’s hedging strategy itself.
The rest of this board
Available on Tier Slate
This page publishes the question. The answer — 5 more written pages, an 8-step narrated walkthrough and a 5-table schema — is the board itself, and it opens in Tierslate.
- 5written pages
- 8walkthrough steps
- 5tables30 columns
5 pages behind this one
- Capacity estimation
- Storage estimation
- Availability
- How it works
- Deployment plan
More boards
Systems where losing a write is losing money.
- Double-entry ledger - the balance is not a column
- Fraud detection - a decision that must fit in 120 milliseconds
- Payment gateway - what a timeout means when the card may already be charged
- Matching engine - price-time priority, and why the clock cannot decide who was first
- Wallet transfers - the state machine, the hold that expires, and the reversal that cannot
- OTP verification - six digits and a clock
- Subscription billing - the invoice is computed, not typed
- Loyalty points - a currency you print yourself