1. The cost you can't see
Ask any trader for the single most basic cost of doing business and the answer is the bid-ask spread: the toll paid every time you cross the market, buying at the ask and selling at the bid. Measuring it sounds trivial — until you look at the data you actually have. Reading the spread directly requires tick-level quote data, which is expensive, frequently unavailable, and for long historical windows or thinly-covered instruments may simply not exist. What you usually have instead is bars: open, high, low, close, going back years.
So the field ends up in a strange place — the most fundamental cost in trading is the one you can't see in the most abundant market data there is. Estimating the spread from OHLC bars closes that gap.
2. Why it matters for a backtest
This is not an academic curiosity. A strategy that looks profitable on paper can turn flat or negative once real spreads are subtracted — the same reason alpha so often evaporates on contact with a live market, which we cover in the illusion of backtesting. If you cannot estimate the spread over your test window, you cannot honestly judge whether an edge survives its own transaction costs. "Assume five basis points" is a guess, not a measurement.
Spread estimation slots directly into a full transaction cost analysis: it supplies the crossing cost when finer data is missing, and it can be cross-checked against the spread you measure directly wherever tick data does exist. It is the low-cost, always-available layer of the cost stack.
3. The spread leaves fingerprints
The key insight is that the spread does not disappear just because you only kept daily bars. It is baked into the relationship between the high, the low, and the close. The high of a session tends to be a trade executed at the ask; the low tends to be a trade at the bid. So the observed range is inflated by the bid-ask bounce on top of genuine volatility — and that contamination is exactly what the estimators exploit.
Two estimators dominate practice, each reading a different fingerprint.
4. Corwin-Schultz (2012)
Corwin and Schultz start from a clean observation: a day's high-low range reflects two things — fundamental volatility and the bid-ask bounce — and the two scale differently over time. Volatility grows with the length of the interval; the spread does not. Compare the range of a single day to the range of two days combined, and the component that fails to scale with time is the spread.
Formally, for two consecutive bars you form β (the sum of squared single-day log high-low ranges) and γ (the squared log range over the combined two-day window), then:
α = ( √(2β) − √β ) / (3 − 2√2) − √( γ / (3 − 2√2) )
S = 2 ( eα − 1 ) / ( 1 + eα )
The result S is a proportional spread — a fraction of price. Averaged across many consecutive pairs, it is a robust estimate from nothing but daily highs and lows. Per-pair negative values (noise when the true spread is tiny) are conventionally floored at zero.
5. Abdi-Ranaldo (2017)
Abdi and Ranaldo attack the same problem from a different angle, using the close as well as the range. Define the log mid-range of each bar as η = (log high + log low) / 2. When a real spread exists, closes sit systematically off the true mid — at the bid or the ask — so the covariance between each close and the surrounding mid-ranges recovers the spread:
S = √( max( 4 · E[ (ct − ηt)(ct − ηt+1) ], 0 ) )
where ct is the log close. The whole input is close, high, and low — no ticks, no quotes. Like Corwin-Schultz, the estimate is proportional and floored at zero when the covariance turns negative (which happens on strongly trending, low-spread series).
| Estimator | Inputs | Core idea |
|---|---|---|
| Corwin-Schultz (2012) | High, low | Volatility scales with time, the spread doesn't — isolate the part of the range that doesn't scale |
| Abdi-Ranaldo (2017) | Close, high, low | Closes sit off the high-low mid at bid/ask; their covariance recovers the spread |
6. Reading the estimates honestly
These are estimators, and it pays to treat them as such. Any single observation is noisy; their value emerges in aggregate, averaged over many bars where the noise cancels and the signal remains. Both can return a small negative number when the true spread is near zero, which is why the standard convention floors them at zero. And they estimate the effective spread that trading actually reveals — closely related to the effective spread measured directly in TCA — not a quoted number frozen on a screen.
They are also complementary to the rest of the microstructure toolkit. Where VPIN gauges the toxicity of the flow and markouts measure adverse selection after the fact, spread estimators give you the crossing cost up front — the three together sketch the true cost of liquidity from three angles.
7. Which one, and when
In practice, run both and compare. Corwin-Schultz needs only highs and lows, which makes it usable on the widest range of historical data; Abdi-Ranaldo brings the close into play and is often more stable on daily equity and crypto series. Neither is a single-day oracle — feed them a window of bars, average, and read the aggregate. Sampling the market by activity rather than clock time, via information-driven bars, can further stabilise the inputs before you estimate. And wherever you can reconstruct a true book from order book data, use these estimators as the cross-check for the long stretches where that finer data isn't available.
8. Computing it
Neither estimator needs a heavyweight stack. Our open-source orderflow-metrics library ships both, dependency-free, in TypeScript and Python:
import { corwinSchultz, abdiRanaldo } from "orderflow-metrics";
const bars = [
{ high: 10.20, low: 9.80, close: 10.18 },
{ high: 10.25, low: 9.85, close: 9.88 },
{ high: 10.30, low: 9.90, close: 10.27 },
];
corwinSchultz(bars); // proportional spread from the two-day high-low range
abdiRanaldo(bars); // proportional spread from close vs high-low mid-range
The same functions exist in the Python distribution in snake_case (corwin_schultz, abdi_ranaldo). Point them at a stream of OHLC bars, average the output over your window, and you have a spread estimate you can subtract from any backtest — part of the broader market microstructure tooling we build in the open.
9. Conclusion
The costs you can't see are the ones that decide whether a strategy lives or dies. The bid-ask spread is the most basic of them, and the most commonly ignored simply because the data that would reveal it directly is so often missing. Corwin-Schultz and Abdi-Ranaldo turn the cheapest, most abundant market data — daily highs, lows, and closes — into an honest estimate of that cost. Read them in aggregate, floor them at zero, cross-check them where you can, and the invisible toll becomes a number you can actually put in your P&L. Explore the rest of the toolkit in our quantitative research library, or read the implementation in our open-source metrics.