Skip to the content.

← All indicators

zeonta.abdi_ranaldo_spread() — Bid-ask spread from close vs. the high-low midrange (Abdi & Ranaldo, 2017).

What it measures

Abdi & Ranaldo (2017) combine corwin_schultz_spread’s insight (a bar’s high-low midrange is a better estimate of the efficient price than its close, because the bid-ask half-spreads on either side of the range cancel) with roll_spread’s autocovariance construction, but applied to midrange-to-close-to-midrange instead of close-to-close. The result is a two-day, close/high/low-only spread estimator that behaves better than either building block alone on daily data.

Formula

eta[t] = (ln(High[t]) + ln(Low[t])) / 2; S[t] = sqrt(max((ln(Close[t-1]) - eta[t-1]) x (ln(Close[t-1]) - eta[t]), 0))

Parameters

Required inputs: high, low, close

None.

Returns

Column
AR

Usage

Examples run against the 300-bar OHLCV fixture in tests/data/ohlcv.csv, loaded as df. The output shown is the real output.

import pandas as pd
import zeonta

df = pd.read_csv('tests/data/ohlcv.csv', parse_dates=['date']).set_index('date')
zeonta.abdi_ranaldo_spread(df['high'], df['low'], df['close']).tail(3)
date
2024-10-25    0.0
2024-10-26    0.0
2024-10-27    0.0
Name: AR, dtype: float64

Accessor form: df.zta.abdi_ranaldo_spread(...)

How to read it

Read AR as a fraction of price — 0.01 is a 1% quoted spread, the same convention as corwin_schultz_spread. It is a liquidity-cost estimate, not a volatility measure: a wider AR means more of the price is spent just crossing the spread on a round trip.

Pitfalls

The raw product inside the square root can come out negative, the same known limitation corwin_schultz_spread and roll_spread both have; the paper’s own remedy, applied here, is to floor each single two-day estimate at zero rather than leave it negative or turn the whole bar into NaN. Only the first bar (no earlier bar to pair against) is genuinely undefined, and is NaN.

Reference

Formula source: https://doi.org/10.1093/rfs/hhx084