Seasonality and Calendar Anomalies
Seasonality and calendar anomalies are recurring, calendar-linked patterns in returns or volatility — the turn-of-month drift, the Monday effect, the lunchtime liquidity lull, the pre-holiday ramp. They are seductive because they are trivially easy to find and trivially easy to fool yourself with. This guide treats calendar effects as a hypothesis-testing problem rather than a folklore collection: how to formulate them, how to test them while accounting for the dozens of variants you silently tried, and why the honest base rate is that most published calendar anomalies vanish out-of-sample, shrink under transaction costs, or were never significant once you correct for multiple testing.
The four families worth knowing
Calendar effects cluster into a handful of families, each with a different plausible mechanism and a different decay profile:
- Turn-of-month (TOM). Equity index returns have historically concentrated in a
window spanning roughly the last trading day of a month through the first three of the next. The usual mechanism is flow: pension contributions, payroll 401(k) buying, fund reinvestment of distributions, and index reconstitution all cluster near month boundaries.
- Day-of-week. The classic "weekend/Monday effect" — historically negative
Monday returns — is the most studied and the most decayed. Much of it was a measurement artifact of how the Friday-close-to-Monday-close window was settled and how dividends were handled.
- Intraday. The U-shaped volume and volatility curve (busy open, quiet midday,
busy close) is one of the most robust patterns in all of finance because it is driven by structural features of the trading day — overnight information release, closing auctions, and risk-transfer deadlines.
- Holiday and event-calendar. Pre-holiday positive drift, the half-day before
Thanksgiving, options expiration (OPEX) and triple-witching flows, and turn-of-year tax effects (the January small-cap effect, December tax-loss selling).
The intraday family is structural and durable. The return-based families (TOM, day-of-week, holiday drift) are weaker, more fragile, and far more contaminated by data mining. Keep that hierarchy in mind before you allocate capital to any of them.
Why calendar anomalies are a data-mining trap
Here is the core problem. A "calendar anomaly" is parameterized by a partition of the calendar and a comparison. The number of plausible partitions is enormous: days of the week (5), days of the month (~21), months (12), week-of-month, business days from month-end, distance-to-holiday, OPEX-relative days, even-vs-odd, and every intersection of these. If you scan hundreds of partitions against a single price series and report the most extreme t-statistic, you are running a giant multiple comparison and the maximum will look significant even on pure noise.
This is not a hypothetical. With 250 independent tests at the 5% level, you expect roughly 12 "significant" results by chance, and the most extreme of them will carry a t-statistic well above 3. The literature on calendar effects is, to a first approximation, a survivorship-biased sample of the noisiest partitions of a few heavily reused indices. Treat any single-anomaly p-value as essentially uninformative; what matters is the p-value adjusted for the search. This is the same discipline you need for any signal scan — see strategy significance testing and avoiding overfitting.
Testing a calendar effect properly
The minimum bar for taking a calendar effect seriously has five parts:
- Pre-register the partition and the direction before looking at the data, or
explicitly count how many partitions you searched.
- Use robust standard errors. Daily returns are heteroskedastic and mildly
autocorrelated; OLS standard errors understate uncertainty. Use Newey-West (HAC) errors.
- Adjust for the family of tests you ran (Bonferroni as a crude floor, or the
White/Hansen reality-check / SPA test for the honest version).
- Hold out time, not rows. Split chronologically; a calendar effect that exists
in 1990-2010 but not 2010-2025 has decayed and is not tradable today.
- Subtract costs. A TOM effect of 5 bps per month dies instantly if your
round-trip cost is 4 bps and you trade it monthly.
Here is a compact, honest test harness for a day-of-week effect that reports HAC-corrected statistics and a Bonferroni-adjusted threshold:
import numpy as np
import pandas as pd
import statsmodels.api as sm
def day_of_week_test(returns: pd.Series, n_partitions_searched: int = 5):
"""returns: daily simple returns indexed by DatetimeIndex."""
df = pd.DataFrame({"r": returns})
df["dow"] = returns.index.dayofweek # 0=Mon ... 4=Fri
dummies = pd.get_dummies(df["dow"], prefix="d").astype(float)
# No intercept: each coefficient is that day's mean return
X = dummies.values
model = sm.OLS(df["r"].values, X)
# Newey-West HAC errors with a 5-day lag to handle autocorr/heteroskedasticity
res = model.fit(cov_type="HAC", cov_kwds={"maxlags": 5})
out = pd.DataFrame({
"mean_ret_bps": res.params * 1e4,
"t_stat": res.tvalues,
"p_value": res.pvalues,
}, index=[f"day_{i}" for i in range(X.shape[1])])
# Bonferroni floor for the number of partitions you actually searched
out["p_bonferroni"] = np.minimum(out["p_value"] * n_partitions_searched, 1.0)
return out
# stats = day_of_week_test(spx_daily_returns, n_partitions_searched=20)
Note what the npartitionssearched argument forces you to do: be honest about the search. If you tried day-of-week, day-of-month, and month-of-year scans before landing on "Tuesdays are special," your effective number of tests is in the dozens, and the Bonferroni column is the number you should believe.
A realistic turn-of-month example
TOM is the calendar effect with the most credible economic story, so it is worth seeing how to define and test it cleanly. Define the TOM window as business days [-1, +3] around the month boundary and compare mean daily returns inside versus outside the window:
import numpy as np
import pandas as pd
from scipy import stats
def turn_of_month(returns: pd.Series, window=(-1, 3)):
idx = returns.index
# business-day position relative to month end (negative = before month end)
month = idx.to_period("M")
last_bday = pd.Series(idx, index=idx).groupby(month).transform("max")
days_from_me = np.busday_count(
idx.values.astype("datetime64[D]"),
last_bday.values.astype("datetime64[D]"),
) # 0 on last bday, positive before, negative after
pos = -days_from_me # +1 = first bday after, etc.
in_tom = (pos >= window[0]) & (pos <= window[1])
tom = returns[in_tom]
rest = returns[~in_tom]
t, p = stats.ttest_ind(tom, rest, equal_var=False)
return {
"tom_mean_bps": tom.mean() * 1e4,
"rest_mean_bps": rest.mean() * 1e4,
"tom_share_of_days": in_tom.mean(),
"welch_t": t, "welch_p": p,
}
When you run this on U.S. large-cap indices over long samples you typically find a genuine, economically meaningful gap: the TOM window captures a disproportionate share of the total monthly return while being a minority of the days. That part replicates. The trap is in the next step — turning it into a strategy. The naive "buy the close before TOM, sell at the end of TOM" trade involves a round trip every month, and the edge per trip is small relative to spread, commission, and the market impact of trading the close. The effect being real and the strategy being profitable after transaction costs are two different claims.
What survives, what doesn't
A blunt summary of the current evidence base, with the caveat that these are tendencies, not guarantees:
| Effect | Mechanism quality | OOS persistence | Tradable net of costs |
|---|---|---|---|
| Intraday U-shape (vol/volume) | Structural | High | Yes (for execution timing) |
| Turn-of-month drift | Flow-based, plausible | Moderate, decayed | Marginal |
| Pre-holiday drift | Weak, behavioral | Low | Rarely |
| Monday/weekend effect | Mostly artifact | Gone | No |
| January small-cap effect | Tax + liquidity | Decayed sharply | Rarely |
| OPEX / triple-witching flow | Structural flow | Moderate | Situational |
The pattern is consistent: effects with a structural mechanism (the trading day has a real open and close; index funds really do rebalance) persist, while effects that are purely statistical or purely behavioral tend to decay once they are published and arbitraged, or turn out to have been measurement artifacts all along. The disappearance of the Monday effect after it was documented in the 1980s is the canonical example of an anomaly that may have been partly real and was then arbitraged or recalibrated away.
Using calendar effects without fooling yourself
Calendar effects are most valuable not as standalone strategies but as conditioning variables and execution timing inputs:
- Execution, not alpha. The intraday liquidity curve is the most reliable
calendar effect, and its best use is scheduling: lean on the liquid open and close, avoid crossing the spread at the midday trough. This feeds directly into execution algorithms.
- As a feature, not a system. A TOM dummy or a days-to-holiday count can be a
weak feature inside a larger model. On its own it is too thin and too unstable to size meaningfully.
- Regime awareness. Calendar effects interact with regime. TOM flow is stronger
when retirement contributions are growing; OPEX effects depend on dealer gamma positioning. A static dummy ignores this.
- Capacity is tiny. Even where an edge survives costs, the flow windows are
crowded. The strategy capacity for "buy the TOM window" is small and you are competing with the same index funds whose flow creates the effect.
The right mental model: a calendar anomaly is a prior, not a signal. It tells you
where to look for flow or liquidity structure, and it raises or lowers your
confidence in an independent signal. It almost never justifies a position by itself.
Failure modes specific to calendar research
Beyond generic backtesting biases, calendar research has its own recurring traps:
- Boundary definition leakage. "TOM" and "OPEX" have multiple reasonable
definitions (calendar days vs business days, [-1,+3] vs [0,+2]). If you tune the boundary to maximize the backtest, you have added free parameters and inflated the result. Fix the definition first.
- Holiday-calendar look-ahead. Using the current exchange holiday calendar over
a 30-year backtest imports knowledge of holidays that shifted or were added later.
- Reused-data correlation. Testing TOM on the S&P 500, then the Dow, then the
Nasdaq is not three independent confirmations — they share 90%+ of their variance. The "replication across indices" is mostly an illusion.
- Survivorship in the literature. You only read about the calendar partitions
that worked. The thousands that didn't were never published, so the published t-stats are order statistics, not random draws.
- Cost-blind sizing. Calendar trades are high-turnover by construction (they fire
on a schedule). The hurdle from costs is therefore unusually high relative to the thin per-trade edge.
Conclusion
Seasonality and calendar anomalies are the easiest patterns in markets to discover and among the hardest to monetize. The discipline that separates signal from mirage is entirely about honesty: pre-register your partition, count every variant you searched, use HAC standard errors, adjust aggressively for the family of tests, hold out time rather than rows, and never quote a return without subtracting realistic costs. Structural effects like the intraday liquidity curve are durable and useful — mostly for execution timing rather than alpha. Return-based effects like the Monday or pre-holiday drift are mostly decayed, mostly artifacts, or mostly arbitraged. Treat every calendar pattern as a hypothesis to be falsified with a deflated Sharpe ratio and rigorous significance testing, and your base rate of being fooled will drop dramatically.
