← All posts

AAPL vs XOM: Do Individual Stocks Have Seasonal Patterns?

AAPL's best month: July (+6.7%, 100% win rate). XOM's best: January (+8.1%). Opposite seasonal patterns.

What’s the question?

Broad market seasonality — the “sell in May” effect, the January effect, the Santa Claus rally — is well documented in academic literature and widely discussed among practitioners. But individual stocks have their own business cycles that differ from the market as a whole. Apple has a product launch calendar (September iPhone events, June WWDC), while ExxonMobil’s revenue is tied to seasonal energy demand (winter heating, summer driving). Do these company-specific cycles produce measurable monthly return patterns, and if so, do the patterns differ enough between stocks to create calendar-based diversification opportunities?

The approach

Five complete calendar years of daily price data (2021–2025) are fetched for both Apple (AAPL) and ExxonMobil (XOM). Daily returns are compounded within each calendar month to produce monthly returns, then averaged across all five instances of each month to compute the mean monthly return, win rate (percentage of years with a positive return for that month), and sample size. The five-year window provides enough data to identify persistent patterns while remaining recent enough to reflect current business dynamics. A visual bar chart scales with the magnitude of the average return, using “+” for positive months and “-” for negative months.

import xfinlink as xfl
import pandas as pd
xfl.set_api_key("your_key")  # free at https://xfinlink.com/signup
month_names = {1:"Jan",2:"Feb",3:"Mar",4:"Apr",5:"May",6:"Jun",
               7:"Jul",8:"Aug",9:"Sep",10:"Oct",11:"Nov",12:"Dec"}
for ticker in ["AAPL", "XOM"]:
    df = xfl.prices(ticker, start="2021-01-01", end="2025-12-31", fields=["return_daily"])
    df["month"] = df["date"].dt.month
    df["year"] = df["date"].dt.year
    monthly = df.groupby(["year","month"])["return_daily"].apply(lambda x: (1+x).prod()-1).reset_index()
    monthly.columns = ["year","month","return"]
    avg = monthly.groupby("month")["return"].agg(["mean","count"]).round(4)
    avg["win_rate"] = monthly.groupby("month")["return"].apply(lambda x: (x>0).mean()).round(2)
    avg.index = avg.index.map(month_names)
    print(f"=== {ticker} Monthly Seasonality (2021-2025, 5 complete years) ===")
    for m, r in avg.iterrows():
        bar = "+" * max(0, int(r["mean"]*200)) + "-" * max(0, int(-r["mean"]*200))
        print(f"  {m:3s}  avg={r['mean']:>+6.1%}  wins={r['win_rate']:.0%}  n={int(r['count'])}  {bar}")
    print()

Output:

=== AAPL Monthly Seasonality (2021-2025, 5 complete years) ===
  Jan  avg= -0.2%  wins=20%  n=5
  Feb  avg= -2.1%  wins=40%  n=5  ----
  Mar  avg= +1.0%  wins=60%  n=5  ++
  Apr  avg= -0.8%  wins=40%  n=5  -
  May  avg= +0.3%  wins=40%  n=5
  Jun  avg= +4.6%  wins=80%  n=5  +++++++++
  Jul  avg= +6.7%  wins=100%  n=5  +++++++++++++
  Aug  avg= +2.4%  wins=60%  n=5  ++++
  Sep  avg= -3.3%  wins=40%  n=5  ------
  Oct  avg= +3.9%  wins=60%  n=5  +++++++
  Nov  avg= +5.4%  wins=80%  n=5  ++++++++++
  Dec  avg= -0.1%  wins=60%  n=5

=== XOM Monthly Seasonality (2021-2025, 5 complete years) ===
  Jan  avg= +8.1%  wins=80%  n=5  ++++++++++++++++
  Feb  avg= +6.0%  wins=80%  n=5  ++++++++++++
  Mar  avg= +5.2%  wins=80%  n=5  ++++++++++
  Apr  avg= +0.9%  wins=80%  n=5  +
  May  avg= +0.2%  wins=40%  n=5
  Jun  avg= +1.2%  wins=60%  n=5  ++
  Jul  avg= +2.2%  wins=60%  n=5  ++++
  Aug  avg= +0.6%  wins=60%  n=5  +
  Sep  avg= +0.6%  wins=40%  n=5  +
  Oct  avg= +5.5%  wins=60%  n=5  +++++++++++
  Nov  avg= -0.7%  wins=60%  n=5  -
  Dec  avg= -1.3%  wins=40%  n=5  --

What this tells us

The two stocks exhibit nearly opposite seasonal profiles. Apple’s strongest months are July (+6.7%, 100% win rate across all five years) and November (+5.4%, 80% win rate). July aligns with the pre-earnings anticipation period — Apple typically reports fiscal Q3 results in late July or early August, and the run-up reflects positioning ahead of the announcement. November’s strength corresponds to holiday season optimism and early Black Friday demand signals. Apple’s worst month is September (-3.3%), which is counterintuitive given that Apple’s annual iPhone launch event occurs in September. This is a textbook example of the “buy the rumor, sell the news” dynamic: the market prices in the product launch during the summer months, and September marks the point where expectations meet (or fail to meet) reality.

ExxonMobil’s seasonal pattern is driven by energy commodity cycles. The first quarter dominates: January (+8.1%), February (+6.0%), and March (+5.2%) all show strong average returns with 80% win rates. This Q1 strength aligns with winter heating demand in the Northern Hemisphere, which supports crude oil and natural gas prices. The fourth quarter is ExxonMobil’s weakest period (November -0.7%, December -1.3%), reflecting the seasonal trough between summer driving season and winter heating demand.

The two stocks have essentially zero overlap in their strong months. Apple peaks in summer and fall; ExxonMobil peaks in winter and early spring. This complementary seasonal structure means that a portfolio holding both stocks benefits from calendar-based diversification — when one stock is in its historically weak season, the other is typically in its strong season.

So what?

Seasonal patterns in individual stocks are suggestive but not predictive with high confidence, given the small sample size (five observations per month). They are best used as a secondary input: if fundamental analysis already favors a position, seasonal tendencies can inform the timing of entry and exit. The AAPL-XOM comparison also illustrates that sector diversification provides calendar diversification as a byproduct — holding stocks with different business cycle exposures naturally smooths returns across the year. A sector breakdown of sell in May measures the winter-against-summer gap in all eleven sectors of the index, on point-in-time members.