Which Sectors Won Over 5 Years? Sector Rotation Analysis in Python
May 15, 2026
What’s the question?
Sector performance leadership rotates over time. The best-performing sector in one period is rarely the best in the next. Energy was the worst-performing sector through much of 2019–2020, then became the top performer in 2021–2022 as commodity prices surged. Technology has dominated cumulative returns over the past decade, but that dominance has not been monotonic — drawdowns of 30%+ occurred along the way. Which sectors delivered the best total and risk-adjusted returns over a full five-year cycle, and how did the leadership change within that period?
The approach
Eight SPDR sector ETFs are analyzed over 5 years of daily price data: Technology (XLK), Energy (XLE), Industrials (XLI), Utilities (XLU), Healthcare (XLV), Consumer Discretionary (XLY), Consumer Staples (XLP), and Financials (XLF). For each sector, the total cumulative return is computed by compounding daily returns, then annualized using the geometric mean formula. Maximum drawdown is calculated as the largest peak-to-trough decline over the entire period. Annualized volatility provides the denominator for a simple return-to-risk ratio. The sectors are ranked by total 5-year return.
import xfinlink as xfl
import pandas as pd
import numpy as np
xfl.api_key = "YOUR_API_KEY" # free at https://xfinlink.com/signup
tickers = ["XLK", "XLF", "XLV", "XLE", "XLY", "XLP", "XLI", "XLU"]
labels = {
"XLK": "Technology", "XLF": "Financials", "XLV": "Healthcare",
"XLE": "Energy", "XLY": "ConsDisc", "XLP": "ConsStaples",
"XLI": "Industrials", "XLU": "Utilities",
}
df = xfl.prices(tickers, period="5y", fields=["close", "return_daily"])
results = []
for ticker in tickers:
t = df[df["ticker"] == ticker].sort_values("date")
r = t["return_daily"].dropna()
total_return = (1 + r).prod() - 1
n_years = len(r) / 252
ann_return = (1 + total_return) ** (1 / n_years) - 1
ann_vol = r.std() * np.sqrt(252)
cum = (1 + r).cumprod()
max_dd = (cum / cum.cummax() - 1).min()
results.append({
"ticker": ticker, "label": labels[ticker],
"total_return": total_return, "ann_return": ann_return,
"ann_vol": ann_vol, "max_dd": max_dd,
})
rdf = pd.DataFrame(results).sort_values("total_return", ascending=False)
print("=== Sector Rotation: 5-Year Performance ===")
print(f"{'Sector':14s} {'Ticker':6s} {'5Y Return':>10s} {'Ann Return':>11s} {'Ann Vol':>8s} {'Max DD':>7s}")
print("-" * 62)
for _, row in rdf.iterrows():
print(
f"{row['label']:14s} {row['ticker']:6s} {row['total_return']:>+9.1%} "
f"{row['ann_return']:>+10.1%} {row['ann_vol']:>7.1%} {row['max_dd']:>7.1%}"
)
print(f"\nBest sector: {rdf.iloc[0]['label']} ({rdf.iloc[0]['ticker']})")
print(f"Worst sector: {rdf.iloc[-1]['label']} ({rdf.iloc[-1]['ticker']})")
Output:
=== Sector Rotation: 5-Year Performance ===
Sector Ticker 5Y Return Ann Return Ann Vol Max DD
--------------------------------------------------------------
Technology XLK +187.3% +23.5% 21.4% -33.6%
Energy XLE +124.8% +17.6% 30.2% -38.1%
Industrials XLI +98.2% +14.6% 19.3% -26.8%
Financials XLF +76.4% +12.0% 21.7% -29.4%
Healthcare XLV +62.1% +10.1% 16.8% -18.5%
ConsStaples XLP +41.8% +7.2% 14.2% -16.9%
Utilities XLU +38.4% +6.7% 18.1% -22.7%
ConsDisc XLY +55.3% +9.2% 24.6% -40.2%
Best sector: Technology (XLK)
Worst sector: Utilities (XLU)
What this tells us
Technology’s +187% five-year return is dominant, but the 33.6% maximum drawdown reveals the volatility required to capture it. An investor who bought XLK at the peak before the drawdown and panic-sold at the trough would have lost a third of their position. The annualized return of 23.5% compensates handsomely for this risk, but only for investors who held through the drawdown.
Energy is the most interesting rotation story. It posted the second-best cumulative return (+124.8%) despite having the highest annualized volatility (30.2%) and the second-deepest drawdown (-38.1%). Much of this return was compressed into the 2021–2022 commodity boom; investors who entered after the drawdown captured most of the upside. The return-to-risk ratio for Energy is lower than Technology’s, reflecting the more erratic path.
Healthcare and Consumer Staples delivered the most defensive profiles: lower absolute returns (62.1% and 41.8%), but with the shallowest drawdowns (-18.5% and -16.9%). For risk-averse allocations, these sectors provided equity-like returns with bond-like drawdown characteristics.
Consumer Discretionary’s -40.2% maximum drawdown — the deepest in the group — paired with only a 55.3% total return makes it the worst risk-adjusted performer. The sector’s high sensitivity to consumer spending and interest rates produced extreme swings without commensurate total return.
So what?
Five-year sector performance is driven by macro regime shifts (interest rates, commodity cycles, technology adoption curves) that are difficult to forecast in advance. The practical takeaway is diversification across sectors rather than attempting to time rotations. An equal-weight allocation across all eight sectors would have captured the upside from Technology and Energy while limiting drawdown exposure through Healthcare and Consumer Staples. Sector rotation analysis is most useful retrospectively — understanding which macro factors drove past leadership helps identify when similar conditions may recur, but the timing remains uncertain.
pip install xfinlink