Strasmore Research
Deep Dives Matt ConnorBy Matt Connor · Updated 2026-07-26

Feb 24, 2022: War at the Open, Green by Close

February 24, 2022: Russia invaded Ukraine and US stocks gapped down, then closed green. The full reversal, plus oil, gold, defense, bonds and the aftermath.

In the early hours of February 24, 2022, Russia launched its full-scale invasion of Ukraine, and the US stock market produced one of the most-studied sessions of the decade. Index futures fell hard overnight, the Nasdaq-100 ETF QQQ opened -3.2% down, and closed +3.4%. This page receipts the whole event: the reversal minute by minute, what the S&P 500 and Dow did, where oil, gold, defense stocks, Treasuries and volatility went, and what the weeks after looked like. Every number is a stored query; expand any panel for the SQL.

Before the open: how the night unfolded

The invasion did not come out of nowhere. Through late 2021 and early 2022 Russia massed troops near Ukraine's border, Western estimates reported at the time ran well above 100,000, and markets had been trading the risk for weeks. On Monday, February 21, Moscow recognized the two self-declared separatist republics in eastern Ukraine; US markets were closed for the Washington's Birthday holiday, and the first session able to price the news was Tuesday the 22nd, a red one. The first tranche of Western sanctions landed that same Tuesday, alongside Germany's halt of the Nord Stream 2 pipeline certification.

Then, shortly before 6 a.m. Moscow time on Thursday, February 24, around 10 p.m. Wednesday evening in New York, Russia's president announced the operation, and explosions were reported across Ukrainian cities within the hour. US index futures, which trade through the night, sold off immediately. By sunrise the only open question was how far below Wednesday's close the 9:30 a.m. opening auction would print.

The day, on one row

One row holds the whole answer, prior close, gap, low, high, close, and volume:

QueryQQQ on February 24, 2022: the invasion-day reversal, receipted
prior_closerth_opengap_pctrth_lowlow_etrth_highrth_closeday_change_pctlow_vs_prior_pcttrough_to_close_pctclose_to_high_pctopen_above_low_pctday_shares_mrth_minute_bars
329.41318.84-3.2318.2609:30341.04340.653.4-3.470.110.18128390
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(argMaxIf(close, window_start, (toHour(toTimeZone(window_start, 'America/New_York')) * 60 + toMinute(toTimeZone(window_start, 'America/New_York'))) BETWEEN 570 AND 959)) / minIf(toFloat64(low), (toHour(toTimeZone(window_start, 'America/New_York')) * 60 + toMinute(toTimeZone(window_start, 'America/New_York'))) BETWEEN 570 AND 959) - 1) * 100, 1) AS trough_to_close_pct,
    round((maxIf(toFloat64(high), (toHour(toTimeZone(window_start, 'America/New_York')) * 60 + toMinute(toTimeZone(window_start, 'America/New_York'))) BETWEEN 570 AND 959) / toFloat64(argMaxIf(close, window_start, (toHour(toTimeZone(window_start, 'America/New_York')) * 60 + toMinute(toTimeZone(window_start, 'America/New_York'))) BETWEEN 570 AND 959)) - 1) * 100, 2) AS close_to_high_pct,
    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)) / minIf(toFloat64(low), (toHour(toTimeZone(window_start, 'America/New_York')) * 60 + toMinute(toTimeZone(window_start, 'America/New_York'))) BETWEEN 570 AND 959) - 1) * 100, 2) AS open_above_low_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')
Run this yourself

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 for six and a half hours and finished at $340.65: +3.4% on the day, a 7% swing trough-to-close on 128 million shares. And the close was no fade-into-the-bell: it landed just 0.11% under the session high of $341.04, meaning the day ended essentially at its top. Anyone who sold in that first minute of trading sold within 0.18% of the session's ultimate low, the actual bottom tick printed moments after the opening cross, not at it (the auction print was $318.84; the low, $318.26).

The reversal, bucket by bucket

Half-hour buckets show the shape of the climb:

QueryQQQ by half-hour: February 24, 2022 regular session
et_timebucket_closebucket_lowshares_m
09:30325.41318.2618.6
10:00326.04323.1113.8
10:30328.14324.69.5
11:00329.12327.38
11:30330.29327.66.1
12:00328.03326.936
12:30328.18327.164.7
13:00328.8327.765.1
13:30331.72328.777.6
14:00333.3330.77.6
14:30335.94333.298.1
15:00336.89335.487.3
15:30340.65336.8215
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_time
Run this yourself

The staircase was not perfectly clean, the 12:00 ET bucket closed at $328.03, under the 11:30 close of $330.29, but the direction never seriously flipped, and the final bucket closed at $340.65, the top of the table. Volume traced the usual U: 18.6 million shares in the opening half hour and 15 million in the last, against 4.7 million around lunch. It is 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.

Not just the Nasdaq: the whole market reversed

A one-index story would leave the obvious question open: was this a growth-stock quirk or a market-wide reversal? The panel below puts eleven tickers on one yardstick, the three major index ETFs, then the assets a geopolitical-shock playbook reaches for: energy, gold, long Treasuries, a volatility ETF, and four defense contractors.

QueryEleven tickers on February 24, 2022: gap, intraday extremes, close (% vs prior close)
tickerprior_closegap_pctlow_vs_prior_pcthigh_vs_prior_pctday_change_pct
QQQ329.41-3.2-3.43.53.4
SPY421.97-2.6-2.71.61.5
DIA331.44-2.6-2.60.40.3
XLE67.642.1-3.42.2-0.9
GLD178.282.4-1.72.4-0.6
TLT136.621.8-0.320.2
VIXY20.1711.1-3.811.2-3
LMT389.142.3-0.42.51.7
NOC386.12.514.42.4
RTX92.270.8-3.22.62.1
GD216.250.2-2.21.21.1
The exact SQL behind every number
SELECT
    ticker,
    round(prior_close, 2) AS prior_close,
    round((rth_open / prior_close - 1) * 100, 1) AS gap_pct,
    round((rth_low / prior_close - 1) * 100, 1) AS low_vs_prior_pct,
    round((rth_high / prior_close - 1) * 100, 1) AS high_vs_prior_pct,
    round((rth_close / prior_close - 1) * 100, 1) AS day_change_pct
FROM (
    SELECT
        ticker,
        argMaxIf(toFloat64(close), window_start, (toHour(toTimeZone(window_start, 'America/New_York')) * 60 + toMinute(toTimeZone(window_start, 'America/New_York'))) BETWEEN 570 AND 959 AND toDate(toTimeZone(window_start, 'America/New_York')) = toDate('2022-02-23')) AS prior_close,
        argMinIf(toFloat64(open), window_start, (toHour(toTimeZone(window_start, 'America/New_York')) * 60 + toMinute(toTimeZone(window_start, 'America/New_York'))) BETWEEN 570 AND 959 AND toDate(toTimeZone(window_start, 'America/New_York')) = toDate('2022-02-24')) AS rth_open,
        minIf(toFloat64(low), (toHour(toTimeZone(window_start, 'America/New_York')) * 60 + toMinute(toTimeZone(window_start, 'America/New_York'))) BETWEEN 570 AND 959 AND toDate(toTimeZone(window_start, 'America/New_York')) = toDate('2022-02-24')) AS rth_low,
        maxIf(toFloat64(high), (toHour(toTimeZone(window_start, 'America/New_York')) * 60 + toMinute(toTimeZone(window_start, 'America/New_York'))) BETWEEN 570 AND 959 AND toDate(toTimeZone(window_start, 'America/New_York')) = toDate('2022-02-24')) AS rth_high,
        argMaxIf(toFloat64(close), window_start, (toHour(toTimeZone(window_start, 'America/New_York')) * 60 + toMinute(toTimeZone(window_start, 'America/New_York'))) BETWEEN 570 AND 959 AND toDate(toTimeZone(window_start, 'America/New_York')) = toDate('2022-02-24')) AS rth_close
    FROM global_markets.delayed_stocks_minute_aggs
    WHERE ticker IN ('QQQ', 'SPY', 'DIA', 'XLE', 'GLD', 'TLT', 'VIXY', 'LMT', 'NOC', 'RTX', 'GD')
      AND window_start >= toDateTime('2022-02-23 00:00:00') AND window_start < toDateTime('2022-02-24 23:59:00')
    GROUP BY ticker
)
ORDER BY indexOf(['QQQ', 'SPY', 'DIA', 'XLE', 'GLD', 'TLT', 'VIXY', 'LMT', 'NOC', 'RTX', 'GD'], ticker)
Run this yourself

The reversal was market-wide, but its size didn't simply track the depth of the morning's gap: QQQ gapped -3.2% at the open and closed +3.4%. SPY and DIA gapped by almost the same amount, -2.6% and -2.6% respectively, yet SPY closed +1.5% against DIA's +0.3%, barely green. The growth-heavy index reversed hardest; the industrial-heavy Dow, least, composition, not gap size, separates SPY from DIA.

Where the money went: oil, gold, defense, volatility

Now read the lower rows of the same panel, the "war trade". Energy opened at its highs and closed red: XLE gapped +2.1%, traded as deep as -3.4% below the prior close intraday, and finished -0.9%. Gold made the same round trip: GLD opened +2.4% and closed -0.6%. VIXY, an ETF holding short-term VIX futures, the closest tradable proxy for the volatility index, gapped +11.1% at the open and closed -3%, below the prior day's close. The classic fear assets peaked exactly when the headlines were worst and faded alongside the equity recovery: the equity reversal, inverted.

The defense contractors were the exception that held. All four closed green, LMT +1.7%, NOC +2.4%, RTX +2.1%, GD +1.1%, and Northrop Grumman never printed below Wednesday's close all session (its intraday low was +1%), the only one of the four that didn't touch red.

Treasuries: the safe-haven bid came later

A falling Treasury yield means bond prices are being bid up, the textbook safe-haven flow. On invasion day itself, that flow barely registered at the close:

Query2-year and 10-year Treasury yields, February 18 – March 8, 2022 (%)
sessionyield_2_yearyield_10_year
2022-02-181.471.92
2022-02-221.561.94
2022-02-231.581.99
2022-02-241.541.96
2022-02-251.551.97
2022-02-281.441.83
2022-03-011.311.72
2022-03-021.51.86
2022-03-031.531.86
2022-03-041.51.74
2022-03-071.551.78
2022-03-081.631.86
The exact SQL behind every number
SELECT
    toString(date) AS session,
    round(yield_2_year, 2) AS yield_2_year,
    round(yield_10_year, 2) AS yield_10_year
FROM global_markets.treasury_yields
WHERE date BETWEEN '2022-02-18' AND '2022-03-08'
ORDER BY date
Run this yourself

The 10-year yield closed at 1.96% on February 24, just under the prior day's 1.99%. The real flight arrived the following week: by 2022-03-01 the 10-year printed 1.72%, the lowest close in this window, and the 2-year fell from 1.58% to 1.31% over the same stretch. TLT, the long-Treasury ETF in the panel above, told the intraday version of the same story: a +1.8% gap at the open that faded to +0.2% by the close.

The days around it

Three weeks of daily closes put the reversal in context, and quietly settle whether it held:

QueryQQQ daily closes, February 22 – March 14, 2022
sessionclose_usdchange_pctshares_m
2022-02-22338.09-185.1
2022-02-23329.41-2.685.1
2022-02-24340.653.4128.5
2022-02-25345.871.577.6
2022-02-28347.150.476.4
2022-03-01341.56-1.666.8
2022-03-02347.181.669.8
2022-03-03342.26-1.470.6
2022-03-04337.35-1.475.8
2022-03-07324.83-3.789.2
2022-03-08323.28-0.5115.3
2022-03-09335.053.663.8
2022-03-10331.42-1.168.7
2022-03-11324.34-2.165.6
2022-03-14318.21-1.978.9
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-15 23:59:00')
        GROUP BY et_date
    )
)
WHERE et_date >= toDate('2022-02-22') AND et_date <= toDate('2022-03-14')
ORDER BY et_date
Run this yourself

The sessions before the invasion were red (-1% and -2.6% on February 22–23) as the buildup dominated headlines, and the invasion day itself closed +3.4%, followed by another +1.5% the next day. Markets falling on the threat and rallying on the event is a pattern old enough to have a Wall Street proverb attached, and this tape is its cleanest modern receipt. But keep reading the table: it ends at $318.21 on 2022-03-14, below where the invasion morning's panic printed.

Did the rally hold?

No, and now the claim has receipts:

QueryAfter the reversal: when QQQ broke the invasion-day low, and how far it fell
invasion_closeinvasion_lowfirst_close_below_invasion_lowcalendar_days_to_new_lowmarch_low_closemarch_low_datemarch_low_vs_invasion_close_pctyear_low_closeyear_low_dateyear_low_vs_invasion_close_pct
340.65318.262022-03-1418318.212022-03-14-6.6260.022022-12-28-23.7
The exact SQL behind every number
WITH
    daily AS (
        SELECT
            toDate(toTimeZone(window_start, 'America/New_York')) AS d,
            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 c
        FROM global_markets.delayed_stocks_minute_aggs
        WHERE ticker = 'QQQ'
          AND window_start >= toDateTime('2022-02-24 04:00:00') AND window_start < toDateTime('2023-01-01 00:00:00')
        GROUP BY d
    ),
    (SELECT c FROM daily WHERE d = toDate('2022-02-24')) AS invasion_close,
    (
        SELECT round(minIf(toFloat64(low), (toHour(toTimeZone(window_start, 'America/New_York')) * 60 + toMinute(toTimeZone(window_start, 'America/New_York'))) BETWEEN 570 AND 959), 2)
        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')
    ) AS invasion_low
SELECT
    invasion_close,
    invasion_low,
    toString(minIf(d, c < invasion_low AND d > toDate('2022-02-24'))) AS first_close_below_invasion_low,
    dateDiff('day', toDate('2022-02-24'), minIf(d, c < invasion_low AND d > toDate('2022-02-24'))) AS calendar_days_to_new_low,
    round(minIf(c, d BETWEEN toDate('2022-02-25') AND toDate('2022-03-31')), 2) AS march_low_close,
    toString(argMinIf(d, (c, d), d BETWEEN toDate('2022-02-25') AND toDate('2022-03-31'))) AS march_low_date,
    round((minIf(c, d BETWEEN toDate('2022-02-25') AND toDate('2022-03-31')) / invasion_close - 1) * 100, 1) AS march_low_vs_invasion_close_pct,
    round(minIf(c, d > toDate('2022-02-24')), 2) AS year_low_close,
    toString(argMinIf(d, (c, d), d > toDate('2022-02-24'))) AS year_low_date,
    round((minIf(c, d > toDate('2022-02-24')) / invasion_close - 1) * 100, 1) AS year_low_vs_invasion_close_pct
FROM daily
Run this yourself

18 calendar days after the invasion, on 2022-03-14, QQQ closed below the $318.26 panic low from the invasion morning's opening minute. That March low close of $318.21 sat -6.6% under the invasion-day close, and the year kept going: by 2022-12-28, QQQ closed at $260.02, -23.7% below where invasion day ended. The February 24 reversal was real, violent, and local. The rate-hike cycle, not the war, set 2022's direction, the same lesson the March 2020 crash taught in the opposite direction.

How rare is a reversal this size?

Scanning every QQQ session from 2003 through the end of 2025 finds exactly 2 sessions that traded at least 3% below the prior close intraday and still finished at least 1% above it:

QueryQQQ sessions since 2003: intraday low ≤ −3% vs prior close AND close ≥ +1%
sessionlow_vs_prior_pctclose_vs_prior_pcttrough_to_close_pct
2022-02-24-3.43.47
2022-10-13-3.22.35.7
The exact SQL behind every number
SELECT
    toString(d) AS session,
    low_vs_prior_pct,
    close_vs_prior_pct,
    trough_to_close_pct
FROM (
    SELECT
        d,
        round((lo / prev_c - 1) * 100, 1) AS low_vs_prior_pct,
        round((c / prev_c - 1) * 100, 1) AS close_vs_prior_pct,
        round((c / lo - 1) * 100, 1) AS trough_to_close_pct
    FROM (
        SELECT d, c, lo,
               lagInFrame(c) OVER (ORDER BY d ASC ROWS BETWEEN 1 PRECEDING AND CURRENT ROW) AS prev_c
        FROM (
            SELECT
                toDate(toTimeZone(window_start, 'America/New_York')) AS d,
                argMaxIf(toFloat64(close), window_start, (toHour(toTimeZone(window_start, 'America/New_York')) * 60 + toMinute(toTimeZone(window_start, 'America/New_York'))) BETWEEN 570 AND 959) AS c,
                minIf(toFloat64(low), (toHour(toTimeZone(window_start, 'America/New_York')) * 60 + toMinute(toTimeZone(window_start, 'America/New_York'))) BETWEEN 570 AND 959) AS lo
            FROM global_markets.delayed_stocks_minute_aggs
            WHERE ticker = 'QQQ'
              AND window_start >= toDateTime('2003-01-01 00:00:00') AND window_start < toDateTime('2026-01-01 00:00:00')
            GROUP BY d
        )
    )
    WHERE prev_c > 0 AND isFinite(prev_c)
)
WHERE low_vs_prior_pct <= -3 AND close_vs_prior_pct >= 1
ORDER BY session
Run this yourself

February 24, 2022 is one. The other is 2022-10-13, the session that opened an hour after a hotter-than-expected inflation report, another gap-down-close-green day. The invasion-day reversal was the larger of the two: a 7% trough-to-close swing against 5.7%. Twenty-three years of tape, and both prints landed in the same year.

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: 128.5 million shares made it the heaviest session in the three-week table above, 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 QQQ opened -3.2% down with its low in the opening minute, then climbed all session to close +3.4%. The S&P 500 ETF SPY closed +1.5% and the Dow ETF DIA +0.3%.

Why did stocks go up when Russia invaded Ukraine?

This page doesn't assign motives; the tape records the sequence, heavy selling pressure was fully expressed at the 9:30 open, and buying dominated every subsequent hour. The falling-on-threat, rallying-on-event pattern has many historical parallels.

What did oil and gold do when Russia invaded Ukraine?

Both spiked at the open and faded: the energy ETF XLE gapped +2.1% and closed -0.9%, and the gold ETF GLD gapped +2.4% and closed -0.6%. Defense stocks held their gains: all four majors in the panel above closed green.

Did the rally after the invasion last?

No. QQQ closed below its invasion-day panic low 18 calendar days later, on 2022-03-14, and by its 2022 low on 2022-12-28 it stood -23.7% below the invasion-day close.

How rare is a reversal like February 24, 2022?

Very. From 2003 through the end of 2025, only 2 QQQ sessions traded 3% or more below the prior close intraday and still closed at least 1% higher, this one, and 2022-10-13.


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.

#market history#geopolitics#reversals#qqq