Ticker League

Historical returns heatmap — methodology

How TickerLeague computes the monthly, quarterly, and yearly returns shown in the seasonality heatmap, including which return type applies to each instrument, what we exclude, and why.

What this page covers

The historical returns heatmap shows period-by-period returns for a single instrument — monthly, quarterly, and yearly — across every year of available price history. Below each table we summarize behavior by period with an Average, a Median, and a Win Rate.

The same widget is used for stocks, ETFs, indices, cryptocurrencies, foreign exchange pairs, and commodities. The math is the same in every case; the interpretation of the resulting number depends on the instrument type — see return type by instrument below.

How each period return is computed

Each cell in the heatmap is a close-to-close return: we take the closing price on the last trading day of the period and divide it by the closing price on the last trading day of the prior period, then subtract one.

returnt = (closelast day of period t ÷ closelast day of period t−1) − 1

Concretely: the March 2024 monthly return is the close on the last trading day of March 2024 divided by the close on the last trading day of February 2024, minus one. The 2024 yearly return is the close on the last trading day of 2024 divided by the close on the last trading day of 2023, minus one. Quarters work the same way.

This is the convention used by Bloomberg, FactSet, CRSP (the academic gold standard), and Stock Trader's Almanac. It produces returns that are comparable across periods because every period boundary is anchored to a real trading day's close.

Return type by instrument

The formula above measures price change between two closes. What that price change represents depends on the price series we read — and on the economics of the instrument itself.

  • Stocks and ETFs — total return. Prices are dividend-adjusted: cash dividends are treated as if they were reinvested in additional shares on the ex-date, and stock splits are normalized retroactively. A stock that fell 2% in price but paid a 3% dividend that period shows a +1% return, not −2%.
  • Indices — price return. Returns reflect the change in the index value as quoted. They capture price change of the constituent stocks but do not include the dividends those constituents pay. The S&P 500 (SPX) typically yields ~1.5% per year in dividends; that yield is missing from the SPX numbers shown here. For a total-return number, look up the total-return version of the same index (for the S&P 500 that is the S&P 500 Total Return Index, ticker SP500TR).
  • Cryptocurrencies — spot price return. Returns reflect change in the spot price of the coin or token. Staking rewards, lending interest, and other on-chain yields are not included.
  • Foreign exchange — spot exchange-rate return. Returns reflect change in the spot exchange rate of the pair. They do not include the interest-rate differential between the two currencies (carry). A position held in EURUSD will accrue or pay interest based on the difference in policy rates; that effect is absent from the returns shown here.
  • Commodities — spot price return. Returns reflect change in the spot price of the underlying physical commodity. They do not include futures roll yield (positive when the curve is backwardated, negative when in contango) or the cost of physically holding the commodity (storage, insurance, financing). For commodities held via futures or ETFs, total return can differ materially from spot return shown here.

Summary statistics — Average, Median, Win Rate

Below the per-year rows, the heatmap shows three summary rows for each period (each month, each quarter, or for the full year):

  • Average — arithmetic mean of returns for that period across all complete years on file. We use a simple arithmetic mean rather than a geometric mean because the cells are themselves period returns and we want the typical magnitude, not a compounded growth rate.
  • Median — middle return for that period across all complete years. Less sensitive to a single extreme year than the average.
  • Win Rate — share of complete years for which the return for that period was strictly positive (greater than 0%). A January Win Rate of 70% means January closed up in 70% of the complete Januaries on file.

What is excluded from the summary rows, and why

Two specific periods are excluded from Average, Median, and Win Rate even though they may still appear as cells in the table:

  • The current in-progress period. If today is mid-March, the March monthly cell reflects price change so far, not a completed month. A partial period is not directly comparable to a full one, and folding it into an average would bias the historical statistic toward whatever the market happens to be doing right now. We display the partial cell because users find it useful as a live reference, but exclude it from the statistics.
  • The very first period in the price history. The formula above requires a prior-period close to compare against. For the first month (or quarter, or year) in our price history, no such close exists — the return is undefined and the cell is left blank. This matters most for recent IPOs and for crypto or commodities that started trading mid-period: their first cell is empty rather than showing a partial-period return that would not be comparable to subsequent rows. This convention is used by CRSP and by Bloomberg/FactSet for the same reason.

Color scale

Cell color encodes signed magnitude — green for positive, red for negative, neutral for values close to zero. The intensity scale auto-fits each table: we set the saturation cap at roughly the 90th percentile of absolute values in that table, clamped to a reasonable floor and ceiling per granularity (e.g., monthly returns saturate at smaller magnitudes than yearly ones). This keeps a single outlier from washing out the rest of the cells while still letting extreme values saturate at the strongest color bucket.

For Win Rate cells the color encodes distance from 50%: deeper green well above 50%, deeper red well below.

Data sources and freshness

  • End-of-day price bars are sourced daily from a third-party market data provider, covering the instrument's primary listing.
  • For stocks and ETFs, prices are pulled from the dividend-adjusted history (total-return prices), so per-period returns implicitly include reinvested cash dividends and split adjustments.
  • For indices, crypto, FX, and commodities, prices are pulled from the unadjusted history (the value as it was quoted that day).
  • Tables are refreshed after each trading day's close. The current in-progress period updates as new daily bars arrive but, as noted above, is not folded into the summary statistics until it completes.

Known limits

  • Backward-looking only. Seasonality describes what historically happened in a given month/quarter/year. It is not a forecast. A January with a 70% Win Rate across 30 years still has a 30% chance of being negative.
  • Sample size matters. A 5-year window contains only 5 Januaries. Averages computed on small samples can move materially with one outlier year. The longer the available history, the more stable the statistic.
  • Survivorship. Statistics are computed only for instruments that exist today and have a continuous price record. Stocks that delisted, indices that were discontinued, or tokens that died are not represented.
  • Index dividends, FX carry, commodity roll yield. See return type by instrument above. These structural components of total return are not reflected in the numbers shown for indices, FX pairs, or commodities.
  • No transaction costs or taxes. Returns are gross of spreads, commissions, fees, and any tax treatment of dividends or capital gains.

Want to see this in action? Browse companies and open the "Seasonality" tile on any ticker, or jump straight to S&P 500 seasonality.