How every number on this site was taken
Every figure here comes off a measurement rig we built and run ourselves. Bid and ask are sampled from our own accounts, continuously, and stored raw. A published figure is an aggregate over the stated window — nothing is smoothed, nothing is rounded in our favour, and nothing is copied from a broker's marketing page and dressed up as a finding.
This page is longer than a methodology note usually is, on purpose. A comparison table is only worth the method behind it, and the parts of a method that matter are the parts where it can go wrong. Those are written out below, including the mistakes we made and what they cost us.
- Instrument
- EUR/USD
- Window
- 2026-08-27 → 2026-09-25, continuous, UTC
- Sampling
- 1 second, MT5 tick stream
- Accounts
- our own, opened the same week
- Profile
- 10 lots a month
The rig
Two legs feed one store.
The API leg. Where a broker exposes a programmatic price feed, we subscribe to it directly and receive every quote the server sends, each one stamped by the server as it is produced. There is no polling interval to miss a tick between, and no chart to read numbers off. The connection is held open by a service that reconnects with backoff when the line drops and renews its own credentials before they expire, so a quiet night is a quiet night rather than a gap nobody noticed.
The terminal leg. Most brokers do not expose an API, so for those we run their own MetaTrader 5 terminal on a dedicated machine and read the tick stream out of it locally. That machine does not sleep, does not hibernate, and does not stop when it is running on battery — all three of which we had to turn off explicitly, because the defaults would have cut exactly the overnight hours this site exists to measure.
Both legs write into the same store in the same shape, so the aggregation that produces a published number is written once and tested once, rather than once per broker.
What a tick is here, and the three unit traps
A tick is a bid and an ask with a timestamp. That sounds like it cannot be got wrong. It can, and we got it wrong three times in the first fortnight — each time producing numbers that looked entirely plausible.
Pip size. A pip is 0.0001 on most currency pairs and 0.01 on yen crosses. Our first conversion derived it from the symbol's digit count with an off-by-one exponent, which scaled every published spread by a factor of a thousand. It is caught now by a conversion that is checked against known answers on five-digit and three-digit symbols before it is allowed to run, and the broker's own declared pip position is compared with ours — when the two disagree, the symbol is skipped rather than published.
Price scale belongs to the wire, not to the symbol. One feed sends every quote as an integer scaled by the same power of ten regardless of how many digits the symbol quotes. Storing the symbol's digit count alongside those integers made a yen pair read a hundred pips wide. What catches this now is not a rule but a sanity corridor on the result, plus a stored live quote in the test suite as a regression.
Two numbers in one record can be in different units. An execution price returned by an order is a plain decimal; the quotes streaming alongside it are scaled integers. An order probe therefore compares its own fill against the quote stream and, when the two disagree by more than a couple of per cent, records the result as suspect instead of printing a slippage figure.
Metals, indices and crypto have no settled pip convention, so the conversion refuses them outright rather than quietly applying the currency rule. Their ticks are still collected; only the published conversion is withheld until a convention is fixed deliberately.
Clocks
A terminal stamps each tick with the broker server's time, not UTC. One broker in this set runs three hours ahead. Mix that into a table of spread by hour of day and every row is three hours out of place while every number in it stays perfectly believable — the illiquid rollover window lands in the middle of the London session and nobody can see anything wrong.
The offset is therefore measured at every start, from fresh ticks, rather than written down as a constant: it follows the broker's own daylight-saving switch, not ours. Three guards sit around that measurement, because measuring it is the dangerous part:
- The market calendar. Outside trading hours nothing is measured and nothing is written. With the market shut, every quote a terminal holds is the Friday close, so what a naive measurement returns is not a time zone at all — it is the age of a frozen quote.
- A plausible range. No broker server sits more than fourteen hours from UTC. A reading beyond that is refused rather than used.
- Tick freshness. After the offset is subtracted, a tick we are about to store must be seconds old. This is the guard that holds whatever the offset says, and it is the one that finally closed the hole.
That third guard exists because the first two were not enough. One Saturday the rig measured a frozen Friday quote, got a figure that happened to round to exactly sixteen hours, passed the whole-hour check on the way through, and stamped a Friday price with a Saturday time. The age of a stale quote lands on a round hour roughly once an hour, so this was not bad luck — it was a hole waiting for the clock. Four rows were affected and are quarantined. A test reproducing that exact reading now runs on every change.
Spread, and why zero can be true
Spread is the ask minus the bid, converted to pips at the symbol's own scale, averaged over the hour it falls in. Ticks sometimes carry one side alone, so each side is carried forward and a spread is only computed once both have been seen.
On a raw or commission-based account the spread is genuinely zero a great deal of the time: the broker is not paid through the spread there, it is paid through a per-lot commission. Our first sanity corridor had a lower bound and would have thrown that away as a units error — it would have rejected the truth and kept nothing. The lower bound is gone. What remains is an upper bound and a refusal of negative spreads, which are impossible rather than merely surprising.
This is also why a table of raw spread alone is close to useless for deciding where to trade, and why this site leads with cost of ownership instead. A zero spread with a commission attached can cost more over a year than a wider spread with none.
Is the feed even real?
This is the question that changed the shape of the project, and it is the one most comparison sites never ask.
A demo feed is not obliged to be the market. It can be a simulator that emits a fixed spread, or one that steps between two or three values, and from a chart it is indistinguishable from a live quote. We found exactly that on one of our own accounts: across hundreds of thousands of observations, one symbol produced a single spread value, another produced two, with nothing in between. A morning's worth of comparison built on that feed was wrong, and we had already written it down before we checked.
So every series is now audited before any figure from it is published, over the whole population of observations rather than a sample. The test is not how many distinct values appear — our first attempt used that and would have condemned a genuine raw feed that sits at zero most of the time. The test is continuity: a real spread passes through neighbouring values on its way between levels; a fixture jumps. A series that fails is not published at all, and the broker it belongs to is not ranked.
We would rather show a table with fewer brokers in it than a table where one column is a simulator.
Execution: slippage and rejected orders
Spread is what a broker quotes. Execution is what you actually get, and the two part company precisely when it matters — in the seconds around a scheduled release.
Each execution probe is a market order for the smallest volume the account allows, closed immediately. The position is closed the moment it opens because an open position would accrue swap and market exposure and contaminate the next measurement; after every run the account is reconciled against the broker to confirm nothing is left open, rather than assuming the close went through.
Each probe records what was requested, what was filled, the difference in pips, the round-trip latency, and the broker's own status. Slippage is signed, and the sign is from the trader's point of view: positive is worse for you — bought higher, or sold lower, than the price asked for.
Probes run on a regular cadence through the session and tighten to a short interval in the minutes around scheduled economic releases, which are read from a public calendar with exact release times. A spaced-out grid of probes would show nothing: the whole of execution quality lives in the few minutes a day when liquidity thins, and a measurement that misses those minutes reports that every broker is identical.
Where a probe gets no execution event within its window, it is recorded as no answer — our own status, not a rejection by the broker. Mixing the two would inflate a broker's rejection rate with our own timeouts, which is the kind of error that is very comfortable to make when it favours the broker you are paid by.
Cost of ownership
The headline figure is not a spread. It is what a stated trading profile costs over a year on each account: the spread paid across the traded volume, plus the commission on that volume, plus a year of swap. The profile is printed on the page that uses it, so the number can be recomputed by anyone who disagrees with our assumptions.
Commission is taken from the broker's published schedule and verified against an actual fill where we have one — on our own FxPro account the schedule implies a per-million rate, and a real trade confirmed it to the cent. Where a schedule and a fill disagree, the fill wins and the discrepancy is noted.
Where every number comes from
Not every figure on this site is a measurement, and the ones that are not say so at the number, not in a footnote. There are three classes, and brokers are only ranked against others in the same class — a broker we measured and a broker quoted from its own price list are not competing in one column.
- 🔬 our measurementour own account, sampled on our rig
- 📊 third partypublic live spreads from a third party
- 📄 stated by the brokerthe broker's own published terms
A figure with no declared class is not printed as a number at all: the build refuses to publish it. That rule exists because an undeclared source silently reads as “we measured this”, which is the most expensive kind of quiet mistake a site like this can make.
Coverage, gaps and bad readings
A month of data with a hole in it is not a month of data. Coverage is therefore reported as the share of calendar market hours in the window that actually hold samples — not as the count of hours of the day we have ever seen, which is a number that can only go up and which hid a multi-day outage from us for exactly as long as we trusted it.
When a reading turns out to be wrong, it is flagged rather than deleted, and every query that feeds a published figure excludes flagged rows. Two reasons: a tick cannot be re-collected, so throwing one away destroys evidence permanently; and a record of what the rig did wrong is worth keeping, because the next fault usually rhymes with the last one.
An hourly watchdog compares the age of the newest row in each series against the market calendar and raises an alarm when a series goes quiet during trading hours. It found its first real outage within an hour of being switched on — a collector that had been dead for three and a half days while every service around it reported itself healthy.
What we deliberately do not measure
Measuring a withdrawal honestly means depositing and withdrawing our own money at every broker in the table. We have not done that, so the axis reads no data rather than reprinting a broker's claimed processing time as if it were a finding. The same applies to support response times.
We also do not measure anything on an account type we did not open, and we do not average across account types. A raw account and a standard account at the same broker are different products with different costs, and a comparison that mixes them is not a comparison.
- Withdrawal timenot measured in this release
- Support responsenot measured · see methodology
Where the money comes from
One broker in these tables, FxPro, pays us a commission when someone opens an account through our link. It is measured on the same instrument, in the same window, on the same kind of account as the others, and where it loses — it currently loses on cost — that is printed in the same type size as where it wins. The other brokers are named and measured, and none of them is linked.
This is the whole reason the method above is written out at this length. A site with a commercial interest in one of the names it compares has exactly one way to be worth reading, and that is to publish how the numbers were taken, in enough detail that a sceptical reader can find the places we could have cheated and check that we did not.