Base currency and quote currency are the two halves of every FX price, and of every commodity price quoted in dollars, and reading them in the wrong order is the most common error in market data code. A pair is written base/quote. The base is the thing being priced, always one unit of it. The quote is the currency that one unit of the base costs. EUR/USD at 1.08 means one euro costs 1.08 US dollars. XAU/USD at 2,400 means one troy ounce of gold costs 2,400 dollars. That is the whole definition. The rest of this post applies it: the direction of a move, bid and ask, the symbol string in a feed, inverse rates, cross rates, and the bugs that follow from getting the order wrong.
What the number means in each direction#
When EUR/USD moves from 1.08 to 1.10, one euro now costs more dollars. The base strengthened against the quote. When it falls to 1.06, the base weakened. The number says nothing about the euro against any other currency, only against the dollar.
Now take USD/JPY at 150. The dollar is the base here, so one dollar costs 150 yen. A rising USD/JPY is the dollar strengthening against the yen. Notice what that does to a dashboard: "the dollar went up" is a rising number on USD/JPY and a falling number on EUR/USD. Any code that colors a rate green or red needs to know which side of the pair the currency it cares about sits on.
Commodities are simpler in one respect. Every commodity symbol SiftingIO publishes has USD as the quote: XAUUSD for gold, XAGUSD for silver, WTIUSD and UKOUSD for the two crude oil benchmarks, NATGAS for natural gas. The base is one unit of the commodity, an ounce or a barrel, and the price is dollars per unit. A rising number always means the commodity got more expensive in dollar terms. That is exactly why a table mixing commodities with USD/JPY needs a per-symbol direction flag.
To read a rate the other way round, invert it. EUR per USD is 1 / 1.08, which is 0.9259. The general rule is rate(A/B) = 1 / rate(B/A). Keep full precision through the inversion and round only when you display the result. Inverting a rounded number and inverting it back does not return the original.
Bid and ask in base and quote currency terms#
A live quote has two sides. Take EUR/USD bid 1.08010, ask 1.08025. Both numbers are in the quote currency per one unit of the base. The bid is what the quoting side will pay, in dollars, for one euro. The ask is what it charges, in dollars, to hand over one euro. The ask is always the higher number.
The spread is ask minus bid, 0.00015 here, or 1.5 pips. It is the round-trip cost of converting and converting back with no move in the market. It's also a data signal. A spread that suddenly widens on a major pair usually means a source is thin or stale at that moment. SiftingIO's price is a consensus across multiple independent venues rather than one venue's print, so a widening spread there reflects the market, but the same check applies to any single-source feed you hold alongside it.
Inverting a two-sided quote is where most bugs live. The inverse of the bid is the new ask, and the inverse of the ask is the new bid. So USD/EUR is bid 1 / 1.08025 = 0.92571 and ask 1 / 1.08010 = 0.92584. Invert each side in place instead and the bid ends up above the ask, a negative spread and a sure sign of a flipped convention. A mid price, (bid + ask) / 2, removes the spread from a time series and is what most reference and valuation work uses.
Symbols in the FX and commodity data feed#
In an API the slash disappears. An FX pair is a six-character string, EURUSD, built from two ISO 4217 codes. The first three characters are the base, the last three are the quote, and the price is always quote units per one base unit. Commodity symbols follow the same shape with a commodity code in the base position: XAUUSD, XAGUSD, WTIUSD, UKOUSD. NATGAS is the one to watch, since it carries no USD suffix at all. A parser that takes the last three characters of every symbol as the quote currency reads "GAS" as a currency code. Keep an explicit symbol table instead of deriving the quote from the string.
Fetching the live two-sided quote is one call per symbol, and the commodity call has the same shape with a different venue segment:
curl -H "X-API-Key: $SIFTING_KEY" \
"https://api.sifting.io/v1/last/quote/forex/EURUSD"
curl -H "X-API-Key: $SIFTING_KEY" \
"https://api.sifting.io/v1/last/quote/commodities/XAUUSD"
The convention matters most where you store or join data. Suppose one source gives you USDEUR and another gives you EURUSD. Both are valid pairs, both are numbers near 1, and a join on date will line them up and hand you rates that differ by roughly 17 percent with no error anywhere. The fix is to pick one canonical direction per symbol at ingest, invert anything arriving the other way round, and record which direction the source used in its own column so the transform can be audited later.
Historical bars follow the same rule. A daily EURUSD bar from the history endpoint has open, high, low and close all in USD per EUR:
curl -H "X-API-Key: $SIFTING_KEY" \
"https://api.sifting.io/v1/hist/forex/EURUSD/bars?interval=1d"
Intervals run from one minute through monthly. Bar timestamps are UTC, so a daily bar is a UTC day and won't line up with a file cut at a New York or London close without shifting. The volume field is always 0 on forex bars. Spot FX has no central tape to count from, so the field exists for schema consistency across asset classes and carries no information. Don't filter forex bars on volume greater than zero and don't compute VWAP from them. If you invert a bar, the extremes swap: the new high is 1 / old low and the new low is 1 / old high. Inverting each field in place produces a bar whose high sits below its low.
Cross rates from two pairs#
When a pair isn't quoted directly, or you want to check a quoted one, compute it from two pairs that share a currency. Treat the codes like fraction units and cancel the common one. EUR/USD times USD/JPY gives EUR/JPY, because USD is the quote in the first and the base in the second. EUR/USD divided by GBP/USD gives EUR/GBP, because USD is the quote in both. The same arithmetic prices gold in another currency: XAU/USD divided by EUR/USD gives XAU/EUR, euros per ounce.
def invert(bid, ask):
# USD per EUR to EUR per USD; the two sides swap roles
return 1 / ask, 1 / bid
def cross_multiply(eurusd, usdjpy):
# EUR/USD x USD/JPY = EUR/JPY (USD cancels)
return eurusd * usdjpy
def cross_divide(xauusd, eurusd):
# XAU/USD / EUR/USD = XAU/EUR (both quoted in USD)
return xauusd / eurusd
print(invert(1.08010, 1.08025)) # (0.925711..., 0.925840...)
print(cross_multiply(1.08, 150.00)) # 162.0
print(cross_divide(2400.0, 1.08)) # 2222.22...
Two things to keep straight. The two inputs have to carry the same timestamp, or the cross is a blend of two different moments. And a computed EURJPY will differ slightly from a directly quoted EURJPY, because the direct pair is priced on its own. Where a direct quote exists, use it as the reference and the computed cross as the check. For two-sided crosses, multiply bid by bid and ask by ask. When dividing, the bid is the first pair's bid over the second pair's ask, and the ask is the first pair's ask over the second pair's bid.
Common pitfalls#
Flipped inversion of bid and ask. Covered above, and worth a guard: after any transform, assert that bid is less than or equal to ask before writing the row. A negative spread is never real data.
Mixed quoting conventions across sources. Some datasets quote every currency as US dollars per one foreign unit, others as foreign units per one dollar. USD/JPY at 150 in one file is 0.00667 in the other. Magnitude alone won't catch this for pairs near parity, so keep a table of the canonical direction for each symbol and test every incoming source against it.
Precision and display. Most pairs are quoted to five decimals, yen pairs to three, gold to two. A formatter that assumes five decimals prints USDJPY as 150.12300 and makes a one-pip move look like a hundred. Store the raw value, pick the display precision from the symbol, and round once, at display time.
Once the direction of every symbol is written down and checked at ingest, the rest of FX and commodity data work is arithmetic. Read the docs for the full quote and bar schemas.



