Back to Research
Execution & Costs August 14, 2026 • 19 min read

Estimating the Bid-Ask Spread from OHLC Data: Corwin-Schultz and Abdi-Ranaldo

The bid-ask spread is the most basic cost in trading — and it is invisible in the most common dataset in trading. If all you have is daily open, high, low, and close, the spread is nowhere to be seen. Yet it left fingerprints in those bars, and two well-known estimators know how to read them. This is the complete guide to recovering the spread from OHLC data.

Panoramic technical representation of Corwin-Schultz and Abdi-Ranaldo Bid-Ask Spread Estimators over OHLC candles

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, lowVolatility scales with time, the spread doesn't — isolate the part of the range that doesn't scale
Abdi-Ranaldo (2017)Close, high, lowCloses 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.

For more on execution analytics and open-source tooling, visit our official resources:

🧩 Open Source 💻 orderflow-metrics on GitHub 📚 More Research