← All posts

Trading Days vs Calendar Days: Why 252 Is Only an Average

US equity years held 248 to 254 trading days from 1996 to 2025 and hit exactly 252 in only 17 of the 30. How to count sessions instead of assuming.

A US equity year holds 252 trading days on average and hardly ever exactly that. Counting the sessions in thirty years of daily bars, 1996 through 2025, the yearly total ran from 248 in 2001 to 254 in 1996 and landed on exactly 252 in 17 of the 30 years. The number 252 is a modelling convention rather than a measurement, and it misleads in two different ways: when it scales a quantity for a year that did not hold 252 sessions, and when it is used to count rows.

How Many Trading Days Are in a Year?

The arithmetic is short. Start with the weekdays in the calendar year, which is 260, 261 or 262 depending on where 1 January falls and whether February has an extra day. Subtract the holidays the exchange observes on a weekday. Subtract anything unscheduled, which is rare and memorable. What remains is the session count, and the only reliable way to get it for a past year is to count the rows.

import numpy as np
import xfinlink as xfl

xfl.set_api_key("YOUR_API_KEY")  # free at https://xfinlink.com/signup

sessions = xfl.prices("SPY", start="1996-01-01", end="2025-12-31", fields=["close"])["date"]

per_year = sessions.groupby(sessions.dt.year).size()
weekdays = {y: int(np.busday_count(f"{y}-01-01", f"{y + 1}-01-01")) for y in per_year.index}

print(per_year.mean(), per_year.min(), per_year.max())

Full script, including the cross-checks and the chart: trading-days-vs-calendar-days.py

US equity trading days per calendar year from 1996 to 2025, against the weekday count and the 252 convention
US equity sessions per calendar year, 1996-01-01 to 2025-12-31
Source: daily bars, one row per session. Weekdays = Monday to Friday.

Year   Sessions  Weekdays   Gap   Year   Sessions  Weekdays   Gap
1996        254       262     8   2011        252       260     8
1997        253       261     8   2012        250       261    11
1998        252       261     9   2013        252       261     9
1999        252       261     9   2014        252       261     9
2000        252       260     8   2015        252       261     9
2001        248       261    13   2016        252       261     9
2002        252       261     9   2017        251       260     9
2003        252       261     9   2018        251       261    10
2004        252       262    10   2019        252       261     9
2005        252       260     8   2020        253       262     9
2006        251       260     9   2021        252       261     9
2007        251       261    10   2022        251       260     9
2008        253       262     9   2023        250       260    10
2009        252       261     9   2024        252       262    10
2010        252       261     9   2025        250       261    11

Sessions per year: mean 251.7, median 252, min 248 (2001), max 254 (1996)
Years landing on exactly 252: 17 of 30
Weekdays per year: mean 260.9, min 260, max 262
Window totals: 7,550 sessions against 7,828 weekdays, a difference of 278 days over 30 years

Same session dates across three long-lived listings
  KO      7550 sessions   dates absent vs SPY: 0   dates not in SPY: 0
  JNJ     7550 sessions   dates absent vs SPY: 0   dates not in SPY: 0
  IBM     7550 sessions   dates absent vs SPY: 0   dates not in SPY: 0

The observed-holiday list is not fixed
  Sessions on the third Monday of January: 1996, 1997
  No session on the 19 June observance: 2022, 2023, 2024, 2025

2026 scheduled: 261 weekdays minus 10 published holidays = 251 sessions

Day after Thanksgiving (an early close), SPY volume against that year's median session
  2015 0.33x   2016 0.41x   2017 0.43x   2018 0.52x   2019 0.56x   2020 0.38x
  2021 1.73x   2022 0.35x   2023 0.38x   2024 0.57x   2025 0.72x
  Below the year's median in 10 of 11 years; mean 0.58x

What the gap does to an annualisation factor
  sqrt(251.7) = 15.864    sqrt(260.9) = 16.153
  15% annualised on the session count becomes 15.27% on the weekday count  (+0.27%)
  20% annualised on the session count becomes 20.36% on the weekday count  (+0.36%)
  30% annualised on the session count becomes 30.55% on the weekday count  (+0.55%)

Coca-Cola, Johnson & Johnson and IBM return the same 7,550 dates as SPY, with no date present in one series and absent from another, so the calendar above is a property of the market rather than of one listing.

Why the Count Moves from Year to Year

Four separate mechanisms push it around, and they do not cancel.

The first is where the fixed-date holidays fall. New Year’s Day, Independence Day and Christmas move across the week, and a Saturday date can remove the holiday altogether. The New York Stock Exchange states the rule plainly in the footnote to its own calendar: “Because the holiday falls on Saturday, January 1, 2028, no New Year’s Day holiday is observed” (nyse.com/trade/hours-calendars, checked 14 September 2026). The data agrees for the earlier instances of the same alignment. Friday 31 December 1999, and the same date in 2004, 2010 and 2021, are all full sessions in the series, which is why 2000, 2005, 2011 and 2022 each carry only 260 weekdays yet still reach 252, 252, 252 and 251 sessions.

The second is that the list of observed holidays is itself not fixed. The series contains a session on the third Monday of January in 1996 and 1997 and in no year after that. The June observance disappears from 2022 onward. The exchange’s current list runs to ten names, Martin Luther King, Jr. Day and Juneteenth National Independence Day included (nyse.com/trade/hours-calendars, checked 14 September 2026). A study spanning 1996 to today is therefore counting against a moving definition, and an annualisation factor calibrated on recent years is slightly wrong for the older part of the same sample.

Third, closures happen. The four sessions from 11 to 14 September 2001 are absent from the series and produce the lowest count in the window at 248. Two more are missing on 29 and 30 October 2012, when Hurricane Sandy shut the market. Single days are gone on 11 June 2004, 2 January 2007, 5 December 2018 and 9 January 2025, each a national day of mourning, which is why those four years sit one session below the arithmetic their weekday count implies.

Fourth, and least interesting, the calendar year itself is 260, 261 or 262 weekdays long.

What a Business-Day Date Range Misses

Generating dates with a rule is the common shortcut, and the standard rule knows about weekends only. The pandas offset alias table defines B as “business day frequency” and C as “custom business day frequency”, and the explanation of the second makes the boundary explicit: “The CDay or CustomBusinessDay class provides a parametric BusinessDay class which can be used to create customized business day calendars which account for local holidays and local weekend conventions” (pandas.pydata.org, checked 14 September 2026). The plain business-day range accounts for none.

Applied to 2025 that produces 261 dates against 250 actual sessions. Reindexing a price series onto those dates and forward-filling inserts eleven flat rows carrying a zero return, each one sitting on a day the market was shut. Reindexing without filling inserts eleven gaps that every downstream calculation then has to handle. Across the full thirty years the excess runs to 278 days, a little over nine a year, and it lands on holidays rather than scattering evenly.

The maintainers of pandas_market_calendars built a package for this, and their own description of the problem is accurate: pandas “includes excellent functionality for generating sequences of dates and capabilities for custom holiday calendars, but as an explicit design choice it does not include the actual holiday calendars for specific exchanges or OTC markets” (pandas-market-calendars.readthedocs.io, checked 14 September 2026). The same page describes the package as providing “access to over 50+ unique exchange calendars for global equity and futures markets”, and for anyone who must know in advance whether a future date is a session, that is the right tool.

For historical work the reconstruction is unnecessary. A daily series returns one row per session, so the index of the frame that comes back already is the exchange calendar for the period requested. Counting len(df) answers the question that a generated date range only approximates.

Which Unit of Time Should You Count In?

UnitWhat it countsTypical yearUse it for
Calendar daysEvery day365 or 366Interest accrual, filing deadlines, holding periods for tax
WeekdaysMonday to Friday260 to 262Nothing in market data
Business-day rangeWeekdays, by rule260 to 262Scheduling, where a wrong day is harmless
Exchange sessionsDays the market opened248 to 254Returns, volatility, lookbacks, event windows
The 252 conventionAn agreed constant252Quoting an annualised figure so it compares to others

The last row is not a mistake. A volatility figure annualised on 252 compares against every other figure annualised on 252, and swapping in the true count for each year produces numbers that agree with nobody. The constant only becomes an error when it is used to count rows rather than to scale a quantity.

Where the Difference Actually Costs Something

For annualising volatility the gap is small and worth knowing. The square root of 251.7 is 15.864 against 16.153 for the weekday count, so a 20 percent annualised volatility computed on the wrong factor is quoted as 20.36 percent. That is noise next to the estimation error on the volatility itself. Whether volatility scales with the square root of time at all is tested in a variance ratio study.

Row counting is where it turns expensive. A lookback defined as “the last 252 rows” reaches back exactly one year in a 252-session year and further than that in every short one: 2001 held 248 sessions, 2012, 2023 and 2025 held 250 each. Rebalance on that rule at a fixed monthly date and the formation window drifts against the calendar, which matters for anything compared against a published series. An event window of 20 sessions around an announcement spans a different number of calendar days depending on where the holidays sit, so measuring the window in trading days and reporting it in calendar days is a common and avoidable confusion. The same discipline underlies running an event study in Python and the broader question of what data a backtest requires.

The practical rule is short. Scale with 252 when quoting a number for comparison. Count with the rows when selecting a window, and let the data supply the calendar.

FAQ

How many trading days are in 2026? 251 scheduled sessions: 261 weekdays minus the ten holidays the exchange publishes for the year. Two of those 251 are shortened rather than full. The NYSE calendar states that “Each market will close early at 1:00 p.m. (1:15 p.m. for eligible options) on Friday, November 27, 2026” and again on “Thursday, December 24, 2026” (nyse.com/trade/hours-calendars, checked 14 September 2026).

Does a half day count as a trading day? Yes. An early close is a session and produces one daily bar like any other, so nothing in a daily series marks it. Volume does. SPY traded at 0.58 times the year’s median session volume on the day after Thanksgiving, averaged over 2015 to 2025, and below the median in 10 of those 11 years. The exception is 2021, at 1.73 times: a shortened session is still a session, and it absorbs whatever arrives during it.

Is 252 wrong, then? No, it is a convention that is right about 57 percent of the time and never off by more than four days in the last thirty years. Use it for annualising. Do not use it to decide how many rows make a year, and do not assume two data sources counted the same sessions before comparing their volatility figures. Related reading on the arithmetic of annualising: log returns vs simple returns.

Where do the sessions come from? Daily bars in the xfinlink API carry one row per session, so the session calendar for any window is the frame that comes back. A free key covers the most recent twelve months, which is enough to count a year and check it against the exchange calendar; longer histories go with the paid plans listed on pricing.

Built with xfinlink — free financial data API for Python.