Support

Compare on-exchange and off-exchange trade volume

Overview

In this example, we will stream live data and compare the volume of trades on the different venues that compose the Nasdaq Basic with NLS Plus (XNAS.BASIC) dataset.

The following venues are included in this dataset:

  • Nasdaq (XNAS)
  • Nasdaq BX (XBOS)
  • Nasdaq PSX (XPSX)
  • FINRA/Nasdaq TRF Carteret (FINN)
  • FINRA/Nasdaq TRF Chicago (FINC)

The FINRA/Nasdaq TRFs aggregate data from most of the 30 ATSs and account for approximately 45% to 49% of the average daily volume (ADV) in all exchange-listed securities.

As of January 2025, XNAS.BASIC represented about 63% of ADV, which includes both on-exchange and off-exchange trades.

Trades schema

We will use the trades schema for this example. NLS Plus reports the size, price, and exchange/TRF for every trade. The trade aggressor side is not available on this dataset, so the side field will always be set to N.

The side field is populated on our depth-of-book feeds, such as Nasdaq TotalView-ITCH (XNAS.ITCH).

Info
Info

Real-time XNAS.BASIC data is included with a Plus or Unlimited subscription through our Databento US Equities service. Visit our pricing page for more information.

Example

import databento as db

# First, create the respective clients
historical_client = db.Historical(key="$YOUR_API_KEY")
live_client = db.Live(key="$YOUR_API_KEY")

# Next, we will subscribe to the trades schema for the designated symbol
symbol = "TSLA"

live_client.subscribe(
    dataset="XNAS.BASIC",
    schema="trades",
    symbols=symbol,
)

# We will stream live data to file for further use
live_client.add_stream(f"{symbol}_trades.dbn")
live_client.start()

# Let the connection run for 15 seconds. All trades in this time will be saved
# to the file above
live_client.block_for_close(timeout=15)

# Now we will open the file for our analysis and create a Pandas DataFrame
dbn_store = db.DBNStore.from_file(f"{symbol}_trades.dbn")
df = dbn_store.to_df(schema="trades")

# Calculate statistics by venue
venue_df = df.groupby("publisher_id", as_index=False)["size"].agg(["sum", "mean"])

venue_df["mean"] = venue_df["mean"].round().astype(int)
total_volume = venue_df["sum"].sum()
venue_df["percentage"] = (venue_df["sum"] / total_volume * 100).round(2)

# Clean up for readability
publishers = historical_client.metadata.list_publishers()
publisher_venues = {x["publisher_id"]: x["description"].partition("-")[2] for x in publishers}
venue_df["publisher_id"] = venue_df["publisher_id"].map(publisher_venues)

venue_df = venue_df.sort_values("sum", ascending=False)
venue_df.columns = ["Venue", "Total Volume", "Average Size", "% of Dataset Total"]

print(venue_df)

Result

Info
Info

If you do not see any output, it could be because the markets are closed. See the start parameter in Live.subscribe for using intraday replay.

                        Venue  Total Volume  Average Size  % of Dataset Total
1   FINRA/Nasdaq TRF Carteret         86591            56               68.93
0                      Nasdaq         36218            48               28.83
2    FINRA/Nasdaq TRF Chicago          2408            51                1.92
3                   Nasdaq BX           348            58                0.28
4                  Nasdaq PSX            57            11                0.05