vxx_yearly
Answered against 22 years of US equities and 12 years of US options data and published with the query that produced it. This result is stored as of 2026-09-22, from what-is-vxx-etn.
| year | change_pct | sessions |
|---|---|---|
| 2019 | -66.8 | 188 |
| 2020 | 15.7 | 253 |
| 2021 | -93.7 | 252 |
| 2022 | -21.1 | 251 |
| 2023 | -93.1 | 250 |
| 2024 | -81.6 | 252 |
| 2025 | -43.5 | 250 |
| 2026 | -32.9 | 181 |
- Rows × columns
- 8 × 3
- Computed
- Completeness
- No missing values
- Source
- US exchange, SIP and OPRA market data
- Licence
- Strasmore terms · free, no signup
What each column holds
| Column | Type | Range | Notes |
|---|---|---|---|
year |
text | 8 distinct values (2019, 2020, 2021…) | |
change_pct |
number | -93.7 to 15.7 | percent |
sessions |
number | 181 to 253 |
Computed from Strasmore's warehouse of US exchange, SIP and OPRA market data. Equity prices are delayed; options greeks and implied volatility are end-of-day. This result is stored, not recomputed on load — it is exactly the numbers that were returned on , and the query below is what returned them.
Run it yourself
This is the exact query behind the result above. Change a ticker, a date or a column and run it against the warehouse — no account, no key. The no-signup tier is smaller than the one this page was computed on; a query that reaches past it comes back saying which plan runs it.
WITH splits AS
(
SELECT
groupArray(execution_date) AS split_dates,
groupArray(price_factor) AS price_factors
FROM
(
SELECT
execution_date,
toFloat64(any(split_from)) / toFloat64(any(split_to)) AS price_factor
FROM global_markets.stocks_splits
WHERE ticker = 'VXX'
GROUP BY execution_date
)
),
daily AS
(
SELECT
a.date AS date,
toFloat64(a.close) * arrayProduct(arrayMap((d, f) -> if(d > a.date, f, 1.0), s.split_dates, s.price_factors)) AS adj_close
FROM global_markets.stocks_daily_aggs AS a
CROSS JOIN splits AS s
WHERE a.ticker = 'VXX'
AND a.date >= toDate('2019-01-01')
)
SELECT
toString(toYear(date)) AS year,
round(100 * (argMax(adj_close, date) / argMin(adj_close, date) - 1), 1) AS change_pct,
count() AS sessions
FROM daily
GROUP BY year
ORDER BY year