Feb 24, 2022: War at the Open, Green by Close
February 24, 2022: Russia invaded Ukraine before the US open. The tape gapped hard down at 9:30 — and closed up. The full reversal, minute by minute.
In the early hours of February 24, 2022, Russia launched its full-scale invasion of Ukraine. US index futures fell hard overnight, and by the 9:30 open the repricing was on the tape. What happened next is the reason this session gets studied: the low of the entire day printed in the opening minute, and the market spent six and a half hours climbing out of it to close green. Every number is a stored query; expand any panel for the SQL.
The day, on one row
The exact SQL behind every number
WITH
(
SELECT argMaxIf(toFloat64(close), window_start, (toHour(toTimeZone(window_start, 'America/New_York')) * 60 + toMinute(toTimeZone(window_start, 'America/New_York'))) BETWEEN 570 AND 959)
FROM global_markets.delayed_stocks_minute_aggs
WHERE ticker = 'QQQ'
AND window_start >= toDateTime('2022-02-23 00:00:00') AND window_start < toDateTime('2022-02-24 04:00:00')
) AS prior_rth_close
SELECT
round(prior_rth_close, 2) AS prior_close,
round(toFloat64(argMinIf(open, window_start, (toHour(toTimeZone(window_start, 'America/New_York')) * 60 + toMinute(toTimeZone(window_start, 'America/New_York'))) BETWEEN 570 AND 959)), 2) AS rth_open,
round((toFloat64(argMinIf(open, window_start, (toHour(toTimeZone(window_start, 'America/New_York')) * 60 + toMinute(toTimeZone(window_start, 'America/New_York'))) BETWEEN 570 AND 959)) / prior_rth_close - 1) * 100, 1) AS gap_pct,
round(minIf(toFloat64(low), (toHour(toTimeZone(window_start, 'America/New_York')) * 60 + toMinute(toTimeZone(window_start, 'America/New_York'))) BETWEEN 570 AND 959), 2) AS rth_low,
formatDateTime(toTimeZone(argMinIf(window_start, (toFloat64(low), toInt64(toUnixTimestamp(window_start))), (toHour(toTimeZone(window_start, 'America/New_York')) * 60 + toMinute(toTimeZone(window_start, 'America/New_York'))) BETWEEN 570 AND 959), 'America/New_York'), '%H:%i') AS low_et,
round(maxIf(toFloat64(high), (toHour(toTimeZone(window_start, 'America/New_York')) * 60 + toMinute(toTimeZone(window_start, 'America/New_York'))) BETWEEN 570 AND 959), 2) AS rth_high,
round(toFloat64(argMaxIf(close, window_start, (toHour(toTimeZone(window_start, 'America/New_York')) * 60 + toMinute(toTimeZone(window_start, 'America/New_York'))) BETWEEN 570 AND 959)), 2) AS rth_close,
round((toFloat64(argMaxIf(close, window_start, (toHour(toTimeZone(window_start, 'America/New_York')) * 60 + toMinute(toTimeZone(window_start, 'America/New_York'))) BETWEEN 570 AND 959)) / prior_rth_close - 1) * 100, 1) AS day_change_pct,
round((minIf(toFloat64(low), (toHour(toTimeZone(window_start, 'America/New_York')) * 60 + toMinute(toTimeZone(window_start, 'America/New_York'))) BETWEEN 570 AND 959) / prior_rth_close - 1) * 100, 1) AS low_vs_prior_pct,
round(toFloat64(sum(volume)) / 1e6, 1) AS day_shares_m,
countIf((toHour(toTimeZone(window_start, 'America/New_York')) * 60 + toMinute(toTimeZone(window_start, 'America/New_York'))) BETWEEN 570 AND 959) AS rth_minute_bars
FROM global_markets.delayed_stocks_minute_aggs
WHERE ticker = 'QQQ'
AND window_start >= toDateTime('2022-02-24 04:00:00') AND window_start < toDateTime('2022-02-24 23:59:00')The receipt everyone should sit with: the session low of $318.26 printed at 09:30 ET — the opening minute — -3.4% below the prior close. From that first print, QQQ climbed nearly all day and finished at $340.65: +3.4% on the day, a swing of more than seven percent trough-to-close, on 128 million shares. Anyone who sold into the opening auction transacted at what turned out to be the bottom tick of the session.
The reversal, bucket by bucket
The exact SQL behind every number
SELECT
formatDateTime(toStartOfInterval(toTimeZone(window_start, 'America/New_York'), INTERVAL 30 MINUTE), '%H:%i') AS et_time,
round(toFloat64(argMax(close, window_start)), 2) AS bucket_close,
round(min(toFloat64(low)), 2) AS bucket_low,
round(toFloat64(sum(volume)) / 1e6, 1) AS shares_m
FROM global_markets.delayed_stocks_minute_aggs
WHERE ticker = 'QQQ'
AND window_start >= toDateTime('2022-02-24 04:00:00') AND window_start < toDateTime('2022-02-24 23:59:00')
AND (toHour(toTimeZone(window_start, 'America/New_York')) * 60 + toMinute(toTimeZone(window_start, 'America/New_York'))) BETWEEN 570 AND 959
GROUP BY et_time
ORDER BY et_timeEvery single half-hour bucket after the open closed higher than the one before it for most of the day — a staircase up, the mirror image of 2011's staircase down. Sessions like this are why "the market fell on the invasion" retellings mislead: the futures fell overnight; the cash session rallied from its first minute. Which one you experienced depended entirely on when your orders arrived — the theme overnight gaps measures on calmer weeks.
The days around it
The exact SQL behind every number
SELECT
toString(et_date) AS session,
close_usd,
round(if(prev_close = 0, NULL, (close_usd / prev_close - 1) * 100), 1) AS change_pct,
shares_m
FROM (
SELECT et_date, close_usd, shares_m,
lagInFrame(close_usd) OVER (ORDER BY et_date ASC ROWS BETWEEN 1 PRECEDING AND CURRENT ROW) AS prev_close
FROM (
SELECT
toDate(toTimeZone(window_start, 'America/New_York')) AS et_date,
round(argMaxIf(toFloat64(close), window_start, (toHour(toTimeZone(window_start, 'America/New_York')) * 60 + toMinute(toTimeZone(window_start, 'America/New_York'))) BETWEEN 570 AND 959), 2) AS close_usd,
round(toFloat64(sum(volume)) / 1e6, 1) AS shares_m
FROM global_markets.delayed_stocks_minute_aggs
WHERE ticker = 'QQQ'
AND window_start >= toDateTime('2022-02-18 00:00:00') AND window_start < toDateTime('2022-03-01 23:59:00')
GROUP BY et_date
)
)
WHERE et_date >= toDate('2022-02-22') AND et_date <= toDate('2022-03-01')
ORDER BY et_dateContext makes the reversal stranger: the sessions before the invasion were red (-1% and -2.6% on Feb 22-23) as the buildup dominated headlines, and the invasion day itself closed +3.4%, followed by another +1.5% the next day. The pattern — markets falling on the threat and rallying on the event — is old enough to have a Wall Street proverb attached, and this tape is its cleanest modern receipt. What the proverb omits: the rally did not hold. The index made new lows within weeks as the rate-hike cycle, not the war, set 2022's direction.
What this day teaches
Three durable mechanics live in this tape. First: by the time a widely anticipated event happens, positioning has often already happened — the two red sessions before the invasion carried the anticipation. Second: the opening auction is where accumulated overnight fear clears at a single price, and that price can be the day's extreme in either direction. Third: a violent one-day reversal tells you about that day's flows, not the next quarter's direction — the weeks after this session went to new lows anyway. Event days reward knowing the machinery; they punish confusing a session's shape with a regime's.
The volume detail confirms who was doing what: {q:day_tape:day_shares_m} million shares made it one of QQQ's heaviest sessions of that winter, and heavy volume on a trough-to-close climb means the buying was real size meeting real selling — not an empty-tape drift. Compare the same signature in March 24, 2020's rally, where conviction volume also marked the turn of a panic, and contrast it with thin bounce days that fade the following session.
Invasion day FAQ
What did the stock market do when Russia invaded Ukraine?
On the day itself — February 24, 2022 — the Nasdaq-100 ETF opened -3.2% down with its low in the opening minute, then climbed all session to close +3.4%.
Why did stocks go up on the invasion?
This page doesn't assign motives; the tape records the sequence — heavy selling pressure was fully expressed at the open, and buying dominated every subsequent hour. The falling-on-threat, rallying-on-event pattern has many historical parallels.
Did the rally last?
No. The February 24 reversal was violent but local; the index set fresh lows within weeks and 2022 became a bear-market year on the back of the rate cycle.
Every panel above is a stored, versioned query over the historical tape — expand the SQL to see each measurement. Want to feel this day instead of reading it? It is one of the playable scenarios in the Strasmore Labs trading simulator.