Support

Analyze auction imbalance dynamics

The closing auction for US Equities is one of the most important events of the trading day. With nearly 10% of average daily volume transacting on the close, the dynamics around price discovery are critical, as these closing prices are used in various use cases, such as benchmark pricing for index funds. While different exchanges share many common behaviors during the closing auction, there are some microstructure differences, such as accepted order types, that can affect the price discovery mechanism.

Overview

We'll use the historical client to request data for the closing auction. The imbalance schema provides auction imbalance data such as reference price, paired quantity, and auction status. We'll also use the OHLCV-1m schema to look at the price and volume transacting on the limit order book.

We'll compare the closing auction process for SPY on NYSE Arca and QQQ on Nasdaq. Due to differences in the data disseminated by the exchange, we'll use different fields for Nasdaq and NYSE.

For both venues, the ref_price field represents the price at which imbalance shares are being calculated. This price is generally within the current BBO on the limit order book.

For Nasdaq, we'll look at the cont_book_clr_price field, which represents the price at which auction and continuous orders would transact.

For NYSE Arca, we'll look at the ind_match_price field instead. This field represents the price at which the highest number of shares would trade, subject to auction collars.

Example

import datetime as dt
from zoneinfo import ZoneInfo
import databento as db
import pandas as pd
from matplotlib import dates as mdates
from matplotlib import pyplot as plt

def get_imbalance_df(
    client: db.Historical,
    dataset: str,
    symbol: str,
    start: dt.datetime,
    end: dt.datetime,
) -> pd.DataFrame:
    """Get imbalance schema data"""
    imbalance_df = client.timeseries.get_range(
        dataset=dataset,
        symbols=symbol,
        schema="imbalance",
        start=start,
        end=end,
    ).to_df(tz=start.tzinfo)

    return imbalance_df

def get_ohlcv_df(
    client: db.Historical,
    dataset: str,
    symbol: str,
    start: dt.datetime,
    end: dt.datetime,
) -> pd.DataFrame:
    """Get OHLCV schema data"""
    ohlcv_df = client.timeseries.get_range(
        dataset=dataset,
        symbols=symbol,
        schema="ohlcv-1s",
        start=start,
        end=end,
    ).to_df(tz=start.tzinfo)

    return ohlcv_df


def plot_closing_auction(
    venue: str,
    symbol: str,
    imbalance_df: pd.DataFrame,
    ohlcv_df: pd.DataFrame,
    start_time: dt.time,
    end_time: dt.time,
) -> None:
    imbalance_df = imbalance_df.between_time(start_time, end_time)

    ohlcv_df = ohlcv_df.between_time(start_time, end_time)
    ohlcv_df["cumulative_volume"] = ohlcv_df["volume"].cumsum()

    # Create the subplots
    _, (ax1, ax2) = plt.subplots(
        2, 1,
        figsize=(14, 9),
        sharex=True,
        gridspec_kw={"height_ratios": [3, 1]},
    )

    # First subplot will display prices
    ax1.plot(
        imbalance_df.index,
        imbalance_df["ref_price"],
        label="Reference Price",
        drawstyle="steps-pre",
    )

    ax1.plot(
        ohlcv_df.index,
        ohlcv_df["close"],
        label="Last Price",
        drawstyle="steps-post",
    )

    ax1.plot(
        imbalance_df.index,
        imbalance_df["uncrossing_price"],
        label="Theoretical Uncrossing Price",
        drawstyle="steps-post",
    )

    ax1.set_ylabel("Price")
    ax1.grid(True)
    ax1.legend(loc="best")
    ax1.set_title(f"{venue} Closing Auction - {symbol}")

    # Second subplot will display traded volume and auction paired quantity
    ax2.plot(
        ohlcv_df.index,
        ohlcv_df["cumulative_volume"],
        label="Cumulative Volume",
        drawstyle="steps-post",
    )

    ax2.plot(
        imbalance_df.index,
        imbalance_df["paired_qty"],
        label="Paired Quantity",
        drawstyle="steps-post",
    )

    ax2.set_xlabel("Time (ET)")
    ax2.set_ylabel("Volume")
    ax2.grid(True)
    ax2.legend(loc="best")
    ax2.xaxis.set_major_formatter(mdates.DateFormatter("%H:%M:%S", tz=start_time.tzinfo))
    plt.xticks(rotation=45)

    plt.show()


# Set parameters
tz = ZoneInfo("America/New_York")
start = dt.datetime(2025, 11, 5, 15, 58, 0, tzinfo=tz)
end = dt.datetime(2025, 11, 5, 16, 0, 1, tzinfo=tz)

client = db.Historical("YOUR_API_KEY")

# First, we'll look at SPY data on NYSE Arca (ARCX.PILLAR)
arcx_symbol = "SPY"
arcx_dataset = "ARCX.PILLAR"

arcx_ohlcv_df = get_ohlcv_df(client, arcx_dataset, arcx_symbol, start, end)
arcx_imbalance_df = get_imbalance_df(client, arcx_dataset, arcx_symbol, start, end)
arcx_imbalance_df = arcx_imbalance_df.rename(columns={"ind_match_price": "uncrossing_price"})
arcx_imbalance_df = arcx_imbalance_df[["ref_price", "uncrossing_price", "paired_qty", "symbol"]]

print(arcx_imbalance_df)

plot_closing_auction(
    "ARCX",
    arcx_symbol,
    arcx_imbalance_df,
    arcx_ohlcv_df,
    dt.time(15, 58, 0, tzinfo=tz),
    dt.time(16, 0, 0, tzinfo=tz),
)

# Next, we'll look at QQQ data on Nasdaq (XNAS.ITCH)
xnas_symbol = "QQQ"
xnas_dataset = "XNAS.ITCH"

xnas_ohlcv_df = get_ohlcv_df(client, xnas_dataset, xnas_symbol, start, end)
xnas_imbalance_df = get_imbalance_df(client, xnas_dataset, xnas_symbol, start, end)
xnas_imbalance_df = xnas_imbalance_df.rename(columns={"cont_book_clr_price": "uncrossing_price"})
xnas_imbalance_df = xnas_imbalance_df[["ref_price", "uncrossing_price", "paired_qty", "symbol"]]

print(xnas_imbalance_df)

plot_closing_auction(
    "XNAS",
    xnas_symbol,
    xnas_imbalance_df,
    xnas_ohlcv_df,
    dt.time(15, 58, 0, tzinfo=tz),
    dt.time(16, 0, 0, tzinfo=tz),
)

Results

First, we'll take a look at the closing auction for SPY on NYSE Arca.

See also
See also

See the NYSE Arca auction documentation for more information.

                                     ref_price  uncrossing_price  paired_qty symbol
ts_recv
2025-11-05 15:58:00.061423353-05:00    677.715            684.49      951885    SPY
2025-11-05 15:58:01.055768239-05:00    677.760            676.22     1193528    SPY
2025-11-05 15:58:02.054678442-05:00    677.760            676.50     1194940    SPY
2025-11-05 15:58:03.056152198-05:00    677.760            676.50     1195106    SPY
2025-11-05 15:58:04.054268311-05:00    677.720            676.18     1200714    SPY
...                                        ...               ...         ...    ...
2025-11-05 15:59:55.052843213-05:00    677.670            677.81     1401805    SPY
2025-11-05 15:59:56.050469422-05:00    677.600            677.76     1402565    SPY
2025-11-05 15:59:57.051653917-05:00    677.590            677.72     1402565    SPY
2025-11-05 15:59:58.052726733-05:00    677.530            677.68     1403065    SPY
2025-11-05 15:59:59.050924091-05:00    677.470            677.60     1403218    SPY

NYSE Arca Auction imbalance

Next, we'll look at the closing auction for QQQ on Nasdaq. You'll notice that in both examples, these theoretical uncrossing prices will converge towards the last traded price as we approach the close.

See also
See also

See the Nasdaq Closing Cross documentation for more information.

                                     ref_price  uncrossing_price  paired_qty symbol
ts_recv
2025-11-05 15:58:00.060035531-05:00     623.49            621.42      677169    QQQ
2025-11-05 15:58:01.019052477-05:00     623.52            622.10      677696    QQQ
2025-11-05 15:58:02.055023604-05:00     623.52            622.06      678796    QQQ
2025-11-05 15:58:03.010276615-05:00     623.54            622.20      678836    QQQ
2025-11-05 15:58:04.040411490-05:00     623.47            622.00      699336    QQQ
...                                        ...               ...         ...    ...
2025-11-05 15:59:55.008542818-05:00     623.43            623.43      827157    QQQ
2025-11-05 15:59:56.099171134-05:00     623.37            623.37      826774    QQQ
2025-11-05 15:59:57.059011900-05:00     623.36            623.36      826774    QQQ
2025-11-05 15:59:58.025345576-05:00     623.27            623.27      826393    QQQ
2025-11-05 15:59:59.032786745-05:00     623.31            623.31      826393    QQQ

Nasdaq Auction imbalance