Strasmore Research
Deep Dives · Matt ConnorBy Matt Connor ·

Trade Condition Codes Explained

Three platforms, three different daily highs for the same stock. Trade condition codes decide which prints update the high, the low, the close, and volume.

Trade condition codes are the reason three platforms can show three different daily highs for the same stock, with none of them wrong. Every print on the consolidated tape carries these tags, and they decide whether a print may update the last price, the day's high and low, the official open, the official close, and the consolidated volume total. A trade can be a genuine execution at a genuine price and still be locked out of nearly all of those fields.

What are trade condition codes?

A trade condition code, also called a sale condition, is a tag the reporting venue attaches to a trade report on its way to the tape. Price and size say what traded. The condition says what kind of report it is: whether it arrived on time, whether its price was computed from other executions rather than agreed at that instant, whether it happened outside regular hours, whether it covered fewer than the 100 shares of a round lot.

Attached to every code is a set of eligibility flags, and that is the part almost nobody sees. Each code is marked eligible or ineligible for the last price, for the high and low, for the open, for the close, and for consolidated volume. Those flags move independently of one another. Plenty of prints that cannot touch the high or the low still count in volume, which is why a stock's reported share count and its reported price extremes are built from overlapping but different sets of trades. A second set of flags runs alongside the first: one set governs the consolidated tape, the other governs the individual market center, since an exchange's own official close is computed only from its own prints.

Condition codes arrive in families, and only one family governs what a trade may update.

QueryCondition code families defined for US stocks
The exact SQL behind every number
SELECT
    replaceAll(type, '_', ' ') AS condition_family,
    countDistinct(id)          AS codes
FROM global_markets.stocks_condition_codes
WHERE asset_class = 'stocks'
GROUP BY condition_family
ORDER BY codes DESC
Run this yourself

The stocks dictionary holds 8 families. The largest is the sale condition family, at 40 distinct codes. Quote conditions describe the bid and the offer rather than a trade. The indicator families flag states like a short sale restriction being in force. The sale condition family is the one carrying the eligibility flags, and it is the family every number below is drawn from.

What actually rides on the tape

The panel below takes one pinned session from June 2026: every AAPL print from the 4:00 a.m. premarket open through the 8:00 p.m. end of post-market trading, grouped by the sale condition each print carried. The window is fixed in the SQL, so these figures describe that day and no other. A print can carry several codes at once and many carry none at all, which is why the columns do not sum to 100.

QueryOne AAPL session, every print grouped by its sale condition
The exact SQL behind every number
WITH
    (SELECT count()
       FROM global_markets.stocks_trades
      WHERE ticker = 'AAPL'
        AND sip_timestamp >= toDateTime('2026-06-17 08:00:00', 'UTC')
        AND sip_timestamp <  toDateTime('2026-06-18 00:00:00', 'UTC')) AS day_prints,
    (SELECT sum(size)
       FROM global_markets.stocks_trades
      WHERE ticker = 'AAPL'
        AND sip_timestamp >= toDateTime('2026-06-17 08:00:00', 'UTC')
        AND sip_timestamp <  toDateTime('2026-06-18 00:00:00', 'UTC')) AS day_shares
SELECT
    multiIf(t.code = -1,     'Regular way (no code)',
            c.code_name = '', concat('Unmapped code ', toString(t.code)),
            c.code_name)                        AS condition_name,
    round(100 * count() / day_prints, 2)        AS pct_of_prints,
    round(100 * sum(t.size) / day_shares, 2)    AS pct_of_shares
FROM
(
    SELECT
        size,
        arrayJoin(if(empty(conditions),
                     [toInt32(-1)],
                     arrayMap(x -> toInt32(x), conditions))) AS code
    FROM global_markets.stocks_trades
    WHERE ticker = 'AAPL'
      AND sip_timestamp >= toDateTime('2026-06-17 08:00:00', 'UTC')
      AND sip_timestamp <  toDateTime('2026-06-18 00:00:00', 'UTC')
) AS t
LEFT JOIN
(
    SELECT toInt32(id) AS code_id, any(name) AS code_name
    FROM global_markets.stocks_condition_codes
    WHERE asset_class = 'stocks'
      AND type = 'sale_condition'
    GROUP BY code_id
) AS c ON c.code_id = t.code
GROUP BY condition_name
ORDER BY pct_of_prints DESC
LIMIT 12
Run this yourself

The most common tag on the day was Odd Lot Trade, on 65.83% of the session's prints and 6.37% of its shares. Read the two columns against each other row by row. Where the print share towers over the share of volume, the code marks small executions. Where it runs the other way, the code marks a handful of very large ones. Counting prints and counting shares answer different questions, and the condition code is what separates them.

Why do two platforms show different daily highs?

Four kinds of print are each excluded from a different subset of fields.

  • Late reported and out of sequence. A trade agreed at 10:14 and reported at 10:31 reaches the tape tagged out of sequence, or priced off a prior reference. It counts toward volume. It cannot set the last price, and it cannot lift the day's high even when its price sits above every other trade of the session. Negotiated block trades and prints routed through a trade reporting facility from dark pool trading venues land here often.
  • Derivatively priced. An average price trade or a VWAP fill carries a price computed across many other executions rather than agreed at that moment. It is kept out of the last price and out of the extremes, since the price it shows never existed as a live quote.
  • Odd lot. Fewer than 100 shares. Odd lots are ineligible for the last price and for the high and low, while counting fully in consolidated volume. They were not reported to the consolidated tape at all until late 2013. Any volume comparison reaching back past that boundary is comparing two different definitions of the word, which our guide to average daily volume covers in detail.
  • Extended hours and form T. Premarket and post-close prints are tagged and kept out of the regular session open, high, low and close, while still counting in consolidated volume. A platform that charts them shows a different daily range from one that does not, and that is the mechanism behind after-hours and premarket trading appearing on one chart and not another.

Here is how much of a session each bucket accounts for across five household names, same pinned day.

QueryShare of a session's prints carrying price-ineligible conditions
The exact SQL behind every number
WITH
    (SELECT groupArray(toInt32(id))
       FROM global_markets.stocks_condition_codes
      WHERE asset_class = 'stocks'
        AND type = 'sale_condition'
        AND name ILIKE '%odd lot%')                       AS odd_lot_codes,
    (SELECT groupArray(toInt32(id))
       FROM global_markets.stocks_condition_codes
      WHERE asset_class = 'stocks'
        AND type = 'sale_condition'
        AND multiSearchAnyCaseInsensitive(name,
              ['form t', 'extended trading hours']))      AS extended_codes,
    (SELECT groupArray(toInt32(id))
       FROM global_markets.stocks_condition_codes
      WHERE asset_class = 'stocks'
        AND type = 'sale_condition'
        AND multiSearchAnyCaseInsensitive(name,
              ['out of sequence', 'prior reference', 'derivatively priced',
               'average price', 'price variation', 'seller'])
        AND NOT multiSearchAnyCaseInsensitive(name,
              ['form t', 'extended trading hours']))      AS late_or_derived_codes
SELECT
    ticker,
    round(100 * countIf(hasAny(conditions, odd_lot_codes))         / count(), 2) AS odd_lot_pct,
    round(100 * countIf(hasAny(conditions, late_or_derived_codes)) / count(), 2) AS late_or_derived_pct,
    round(100 * countIf(hasAny(conditions, extended_codes))        / count(), 2) AS extended_hours_pct
FROM global_markets.stocks_trades
WHERE ticker IN ('AAPL', 'KO', 'MSFT', 'NVDA', 'SPY')
  AND sip_timestamp >= toDateTime('2026-06-17 08:00:00', 'UTC')
  AND sip_timestamp <  toDateTime('2026-06-18 00:00:00', 'UTC')
GROUP BY ticker
ORDER BY odd_lot_pct DESC
Run this yourself

Odd lot prints ran from 46.8% to 85.14% of all prints across the five names. That column tracks share price closely, since a fixed dollar order buys fewer shares in a higher-priced name. NVDA topped it, with 2.4% of its prints tagged late or derivatively priced and 3.53% tagged extended hours. Every print in all three buckets is a real trade at a real price, and every one of them is barred from setting the high and the low.

A print that does not lift the high

The next panel takes the same pinned AAPL session, cuts the regular 9:30 a.m. to 4:00 p.m. window into fifteen minute buckets, and draws two highs for each bucket. One line takes the highest price of any print at all. The other takes the highest price among prints whose conditions leave them eligible to set the high.

QueryTwo versions of the same AAPL session high, fifteen minutes at a time
The exact SQL behind every number
WITH
    (SELECT groupArray(toInt32(id))
       FROM global_markets.stocks_condition_codes
      WHERE asset_class = 'stocks'
        AND type = 'sale_condition'
        AND multiSearchAnyCaseInsensitive(name,
              ['odd lot', 'form t', 'extended trading hours', 'out of sequence',
               'prior reference', 'derivatively priced', 'average price',
               'price variation', 'seller'])) AS not_high_low_codes
SELECT
    formatDateTime(toStartOfFifteenMinutes(toTimeZone(sip_timestamp, 'America/New_York')), '%H:%i') AS et_time,
    round(toFloat64(max(price)), 2)                                              AS tape_high,
    round(toFloat64(maxIf(price, NOT hasAny(conditions, not_high_low_codes))), 2) AS eligible_high
FROM global_markets.stocks_trades
WHERE ticker = 'AAPL'
  AND sip_timestamp >= toDateTime('2026-06-17 08:00:00', 'UTC')
  AND sip_timestamp <  toDateTime('2026-06-18 00:00:00', 'UTC')
  AND (toHour(toTimeZone(sip_timestamp, 'America/New_York')) * 60
       + toMinute(toTimeZone(sip_timestamp, 'America/New_York'))) >= 570
  AND (toHour(toTimeZone(sip_timestamp, 'America/New_York')) * 60
       + toMinute(toTimeZone(sip_timestamp, 'America/New_York'))) < 960
GROUP BY et_time
HAVING countIf(NOT hasAny(conditions, not_high_low_codes)) > 0
ORDER BY et_time
Run this yourself

The session opens at 09:30 with a tape high of $302.07 against an eligible high of $302.07, and runs 26 buckets to the close. The eligible line can never sit above the tape line, since eligible prints are a subset of all prints. Where the two sit on top of one another, every print in that quarter hour was allowed to set the high. Where the upper line pulls away, at least one print in that window traded above the highest eligible price, and no chart built to the eligibility rules will ever show it.

That gap is the honest answer to the discrepancy. A source that takes the maximum price of every print reports one high. A source that applies the eligibility flags reports another. A source that also drops the premarket and post-close sessions reports a third. All three read the same tape.

How solid is the eligibility layer?

Thin. The flags are reference data, and reference data gets corrected. One commercial market data provider shipped a fix to its own condition dictionary that reclassified a single sale condition's eligibility to set the official open and the official close. No trade changed. The tape for every affected day was identical before the fix and after it. What changed was one flag in a lookup table, and with it, the official open and close that every chart and backtest downstream of that dictionary had been printing.

Treat any single vendor's OHLC as one reading of the tape rather than a fact about it. When two sources disagree, ask which eligibility rules each of them applied. Delayed and consolidated quote feeds stack a second version of the same problem on top, on timing rather than on eligibility.

Where the authoritative trade condition code list lives

The tapes run under two national market system plans, the CTA Plan for NYSE-listed securities and the UTP Plan for Nasdaq-listed ones, and each publishes the sale condition specification for the tape it administers. Those specifications are the authority. Everything downstream, including the id numbers behind the panels above, is a vendor's mapping of them into its own numbering, and a code id from one provider does not necessarily mean the same thing at another. Learn the categories rather than the numbers. The categories have been stable for years. The numbers are a lookup table, and lookup tables get edited.

Data notes

The condition buckets in the panels above are built from the dictionary itself rather than from hardcoded id numbers. Each bucket is a name match inside the sale condition family (odd lot, form T and extended trading hours, out of sequence, prior reference price, derivatively priced, average price, price variation, seller). Open the SQL under any panel to read the exact patterns. Codes are not mutually exclusive and a single print can carry several, so the bucket percentages overlap and do not sum to the session. Both AAPL panels and the five-name panel cover one fixed session in June 2026, pinned in the SQL, so nothing on this page moves as new sessions arrive.

FAQ

What is a trade condition code?

It is a tag attached to a trade report on the consolidated tape describing what kind of report it is, for example late reported, derivatively priced, odd lot or extended hours. Each code carries flags marking whether that trade may update the last price, the high and low, the open, the close, and consolidated volume.

Why do two websites show different daily highs for the same stock?

Each applies a different set of eligibility rules to the same prints. One may take the highest price of any trade reported, another may exclude prints whose condition codes bar them from setting the high, and a third may also drop the premarket and post-close sessions. All of them are reading the same tape.

Do odd lot trades count toward volume?

Yes. An odd lot, meaning fewer than 100 shares, counts in consolidated volume while staying ineligible to set the last price or the day's high and low. Odd lots were not reported to the consolidated tape at all before late 2013, so long-run volume comparisons cross a definition change at that point.

Do after hours trades count in the daily high and low?

Not in the regular session's high and low. Premarket and post-close prints carry an extended hours or form T condition that keeps them out of the regular session open, high, low and close, while they still count in the day's consolidated volume.

Where is the official list of sale condition codes?

The CTA Plan and the UTP Plan publish the sale condition specifications for the tapes they administer, and those documents are the authority. Vendor feeds renumber the conditions into their own id space, so a numeric code from one provider does not necessarily match the same number at another.


Every panel here ships with the SQL that produced it. Open one, swap the ticker or the date, and run the same condition breakdown for any session on the Strasmore terminal.

#market data#trade conditions#consolidated tape#volume#ohlc