Sector Mean Reversion on German Stocks (XETRA): 2.58% CAGR, -2.61% vs DAX
Sector mean reversion on XETRA large caps from 2000 to 2025: 2.58% CAGR vs the DAX's 5.19%. The strategy fires every quarter and still loses by -2.61% annually, at the same volatility and a deeper drawdown. Germany is the only market in the study that fails locally.
We tested sector mean reversion on XETRA large caps from 2000 to 2025. The strategy ran cleanly: invested in all 104 quarters, an average of 68 stocks per period, the signal firing every quarter without interruption. The result was 2.58% annualized in EUR against the DAX's 5.19%. A -2.61% annual gap over 26 years.
Contents
- Method
- What is Sector Mean Reversion?
- The Screen
- What We Found
- 26 years. -2.61% annual gap vs the DAX.
- Year-by-year returns
- 2002: The deepest year
- 2009 and 2016: The two years it worked
- 2006-2007 and 2010: Missing the recovery
- 2022-2025: Still behind
- Backtest Methodology
- Limitations
- Takeaway
- Part of a Series
- References
- Run This Screen Yourself
Germany is the only market in this 13-exchange study where sector mean reversion fails against its own local index, and it isn't close. Over 26 years the strategy turned EUR 10,000 into EUR 19,386 while the DAX turned it into EUR 37,254. The problem is structural: the sectors it keeps selecting don't mean-revert in Germany the way they do elsewhere. Consumer Defensive showed up in 31 of 104 quarters. The strategy kept finding "cheap" defensives that stayed cheap.
Data: FMP financial data warehouse, 2000-2025. Updated August 2026.
Method
Data source: Ceta Research (FMP financial data warehouse) Universe: XETRA (Frankfurt Stock Exchange), market cap > €500M Period: 2000-2025 (26 years, 104 quarterly periods) Rebalancing: Quarterly (January, April, July, October), equal weight all qualifying stocks in selected sectors Execution: Entry at the next available close after the signal date Benchmark: DAX (^GDAXI) Cash rule: Hold cash if fewer than 5 sectors qualify, or fewer than 10 stocks pass the filters Transaction costs: Size-tiered model, applied to every position
What is Sector Mean Reversion?
The idea is straightforward. At each quarterly rebalance, rank all sectors by their equal-weighted 12-month trailing return. Buy every stock in the bottom 2. Re-rank and rotate each quarter.
Moskowitz and Grinblatt (1999) established the part this strategy leans on: much of the momentum anomaly operates at the industry level, so sector membership is where the signal lives. What they documented at a 12-month horizon was continuation, not reversal, so this strategy runs against their result rather than following from it. The contrarian premise is a separate claim: sectors that underperform for a full year carry suppressed valuations and depressed sentiment, and both tend to normalize. Germany is the market where that premise fails.
On US stocks, this works reliably. Beaten-down sectors recover as the dislocations that caused the underperformance resolve. The key word is "dislocation." The theory depends on the underperformance being cyclical, not structural.
Germany breaks that assumption. XETRA's largest sectors are export-led manufacturers: Volkswagen, BASF, Siemens, Deutsche Bank, Munich Re. When these sectors fall, it's rarely a sentiment dislocation. It's China slowing down. It's an energy crisis. It's a collapse in global trade. These are structural headwinds that the strategy can't distinguish from temporary weakness. The signal fires. The sector doesn't recover. The next quarter, it fires again.
The sectors that show up most often in XETRA's bottom two:
| Sector | Quarters Selected (of 104) |
|---|---|
| Consumer Defensive | 31 (29%) |
| Basic Materials | 28 (26%) |
| Real Estate | 26 (25%) |
| Communication Services | 25 (24%) |
| Utilities | 25 (24%) |
| Technology | 20 (19%) |
| Energy | 19 (18%) |
| Financial Services | 14 (13%) |
| Healthcare | 8 (7%) |
| Consumer Cyclical | 6 (5%) |
| Industrials | 6 (5%) |
Consumer Defensive at 29% is striking. In the US, Consumer Defensive rarely falls hard enough to hit the bottom two, appearing in 12 of 104 quarters. In Germany it does, repeatedly, and then stays there. That's a symptom of a broader problem: XETRA's defensive sectors have had persistent structural headwinds (margin pressure, weak domestic demand, Euro-driven competitiveness constraints) that the mean reversion signal mistakes for temporary underperformance.
The Screen
The screen below runs live. It ranks sectors by their current 12-month equal-weighted return across XETRA large caps. The bottom rows are what the backtest would buy today.
WITH prices AS (
SELECT e.symbol, e.adjClose, CAST(e.date AS DATE) AS trade_date
FROM stock_eod e
JOIN profile p ON e.symbol = p.symbol
WHERE p.sector IS NOT NULL AND p.sector != ''
AND p.marketCap > 500000000
AND p.exchange IN ('XETRA')
AND CAST(e.date AS DATE) >= CURRENT_DATE - INTERVAL '400' DAY
AND e.adjClose IS NOT NULL AND e.adjClose > 0
),
recent AS (
SELECT symbol, adjClose AS recent_price
FROM prices
QUALIFY ROW_NUMBER() OVER (PARTITION BY symbol ORDER BY trade_date DESC) = 1
),
year_ago AS (
SELECT symbol, adjClose AS old_price
FROM prices
WHERE trade_date <= CURRENT_DATE - INTERVAL '252' DAY
QUALIFY ROW_NUMBER() OVER (PARTITION BY symbol ORDER BY trade_date DESC) = 1
),
stock_returns AS (
SELECT r.symbol, pr.sector, (r.recent_price / ya.old_price - 1) * 100 AS return_12m
FROM recent r
JOIN year_ago ya ON r.symbol = ya.symbol
JOIN profile pr ON r.symbol = pr.symbol
WHERE ya.old_price > 0 AND r.recent_price > 0
AND (r.recent_price / ya.old_price - 1) BETWEEN -0.99 AND 5.0
)
SELECT pr.sector,
ROUND(AVG(sr.return_12m), 2) AS avg_return_12m_pct,
COUNT(DISTINCT sr.symbol) AS n_stocks,
ROW_NUMBER() OVER (ORDER BY AVG(sr.return_12m) ASC) AS rank_worst
FROM stock_returns sr
JOIN profile pr ON sr.symbol = pr.symbol
GROUP BY pr.sector
HAVING COUNT(DISTINCT sr.symbol) >= 5
ORDER BY avg_return_12m_pct ASC
What We Found
Germany's sector rotation ran without interruption. Every one of the 104 quarters was invested. Consistent signal, consistently behind the local benchmark.

26 years. -2.61% annual gap vs the DAX.
| Metric | Strategy | DAX |
|---|---|---|
| CAGR | 2.58% | 5.19% |
| Excess vs DAX | -2.61% | |
| Total Return | 93.86% | 272.54% |
| Volatility | 22.21% | 22.13% |
| Max Drawdown | -67.33% | -65.15% |
| Sharpe Ratio | 0.026 | 0.144 |
| Sortino Ratio | 0.036 | 0.206 |
| Calmar Ratio | 0.038 | 0.080 |
| Up Capture | 74.27% | |
| Down Capture | 78.24% | |
| Beta | 0.840 | |
| Alpha (Jensen) | -2.10% | |
| Win Rate vs DAX (quarters) | 40.38% | |
| Avg Stocks per Period | 67.9 | |
| Cash Periods | 0 of 104 |
EUR 10,000 becomes EUR 19,386. The same money in a DAX tracker becomes EUR 37,254. Over 26 years the strategy gave up roughly half the ending wealth.
The capture ratios explain the shape. Down capture of 78.24% is a genuine positive: the strategy absorbs less downside than the DAX in bad years. But up capture is 74.27%, so it gives back more than it saves when markets rise. You protect on the downside and underparticipate on the upside by a similar amount, which nets out to a portfolio that runs at 84% of the index's beta and compounds at half its rate.
The volatility number is the one that removes any defence of the strategy here. At 22.21% it's essentially identical to the DAX's 22.13%, and the max drawdown (-67.33%) is deeper than the index's (-65.15%). You take the same risk, and you get half the return. Jensen's alpha is -2.10%.
The win rate of 40.38% means the strategy beat the DAX in 42 of 104 quarters. On calendar years it's 10 of 26, the worst in the study.
Year-by-year returns

| Year | Strategy | DAX | Excess |
|---|---|---|---|
| 2000 | +3.24% | -4.51% | +7.75% |
| 2001 | -15.49% | -17.84% | +2.35% |
| 2002 | -49.73% | -39.92% | -9.82% |
| 2003 | +22.89% | +29.42% | -6.53% |
| 2004 | +8.24% | +6.79% | +1.44% |
| 2005 | +26.77% | +26.99% | -0.23% |
| 2006 | +9.56% | +22.59% | -13.03% |
| 2007 | -1.11% | +18.98% | -20.09% |
| 2008 | -45.00% | -37.44% | -7.56% |
| 2009 | +44.38% | +21.62% | +22.76% |
| 2010 | -0.41% | +15.57% | -15.97% |
| 2011 | -9.36% | -13.08% | +3.72% |
| 2012 | +15.18% | +28.03% | -12.86% |
| 2013 | +25.07% | +20.84% | +4.23% |
| 2014 | +5.47% | +3.88% | +1.59% |
| 2015 | +3.41% | +5.31% | -1.90% |
| 2016 | +35.38% | +12.79% | +22.60% |
| 2017 | +7.09% | +10.98% | -3.89% |
| 2018 | -20.17% | -17.80% | -2.37% |
| 2019 | +32.63% | +26.52% | +6.11% |
| 2020 | +10.82% | +2.55% | +8.28% |
| 2021 | +12.60% | +16.71% | -4.11% |
| 2022 | -22.84% | -12.18% | -10.66% |
| 2023 | +15.22% | +19.19% | -3.98% |
| 2024 | +6.27% | +19.41% | -13.14% |
| 2025 | +18.71% | +22.55% | -3.84% |
2002: The deepest year
2002 was -49.73% against a DAX that fell -39.92%. The tech bubble's unwind hit XETRA hard. German industrial and financial stocks that had been caught up in the late-1990s expansion cycle collapsed as global demand evaporated. The strategy bought beaten-down sectors throughout 2001 and into 2002, and those sectors kept falling.
The 2003 partial recovery (+22.89%) still trailed the DAX (+29.42%). Germany didn't catch the post-crash bounce as cleanly.
2009 and 2016: The two years it worked
2009 (+44.38% vs DAX +21.62%) and 2016 (+35.38% vs +12.79%) are the only years with more than 20 points of outperformance. Both follow the same script: a global industrial or commodity cycle turns, and the beaten-down German cyclicals the strategy was already holding reprice off multi-year lows.
Two good years in 26 is the whole case for the strategy in Germany, and it isn't enough.
2006-2007 and 2010: Missing the recovery
The damage isn't concentrated in crashes. It's in the recoveries the strategy sat out. 2007 was -1.11% while the DAX gained 18.98%, a -20.09% gap and the worst single year in the record. 2010 was -0.41% against +15.57%. 2006 was +9.56% against +22.59%.
In each case the DAX was lifted by its export-led large caps during a global upswing, and the strategy was holding whichever German sectors had lagged the previous 12 months. Those sectors lagged for a reason, and the reason didn't resolve inside a quarter.
2022-2025: Still behind
The recent record continues the pattern. 2022 was -22.84% against the DAX's -12.18%, so the downside protection that shows up in the average capture ratio wasn't there when it mattered most recently. 2024 was +6.27% against +19.41%. Across the last four years the strategy trailed the DAX in all four.
Backtest Methodology
| Parameter | Choice |
|---|---|
| Universe | XETRA (Frankfurt), market cap > €500M |
| Signal | Bottom 2 sectors by equal-weighted 12-month trailing return |
| Portfolio | All qualifying stocks in selected sectors, equal weight |
| Rebalancing | Quarterly (January, April, July, October) |
| Cash rule | Hold cash if < 5 sectors qualify or < 10 stocks pass filters |
| Execution | Next available close after the signal date |
| Transaction costs | Size-tiered model |
| Benchmark | DAX (^GDAXI) |
| Period | 2000-2025 (26 years, 104 quarters) |
| Data | Ceta Research (FMP financial data warehouse) |
Limitations
Structural vs cyclical weakness. The core assumption of sector mean reversion is that underperformance is temporary. XETRA's most-selected sectors, Consumer Defensive, Utilities, and Energy, have faced persistent structural pressures over the study period: Euro-currency competitiveness constraints, Germany's energy transition costs, and a domestic economy structurally slower than the US post-2010. The strategy can't distinguish a cheap sector from a broken one.
EUR returns vs DAX benchmark. All XETRA returns are in EUR. The DAX is also EUR-denominated, so there's no currency mismatch vs the local benchmark. For global investors comparing in USD, EUR/USD movements add a separate layer of performance drag.
Down capture of 78.24%. The strategy falls less than the DAX during down markets, which is a genuine feature. But the 74.27% up capture means it gives back slightly more than it saves. The asymmetry runs the wrong way.
Calmar of 0.038. You're taking on a -67.33% max drawdown to earn 2.58% annualized. That's not a viable risk budget for any investor, regardless of the local benchmark comparison.
Same risk, half the return. Volatility is 22.21% against the DAX's 22.13% and the max drawdown is deeper (-67.33% vs -65.15%). This isn't a case of trading return for safety. There's no compensation on either axis.
Costs are modelled, not measured. A size-tiered cost model is applied to every position, but bid-ask spread and market impact aren't. With 68 stocks and quarterly rebalancing, real-world execution would reduce already thin returns further.
Listed, not domiciled. The universe is every company listed on XETRA, which includes foreign secondary listings. Restricting to German-domiciled companies cuts the average holding from 68 stocks to 30 and moves the result to 2.81% CAGR and -2.38% excess. The conclusion doesn't change either way.
Survivorship bias. Exchange membership uses current profiles, not historical. Delisted companies aren't tracked over time.
Takeaway
Germany is the one market in this study where sector mean reversion fails against its own local benchmark, and the failure is decisive rather than marginal. The -2.61% annual gap vs the DAX over 26 years compounds into roughly half the ending wealth. The strategy ran cleanly: all 104 quarters invested, 68 stocks on average, clear signal throughout. It just didn't work.
What it kept buying were sectors that, in Germany specifically, don't mean-revert reliably. Consumer Defensive in 29% of quarters. Basic Materials in 26%. These aren't sentiment dislocations. They're sectors facing structural headwinds that a 12-month trailing return signal will keep identifying as cheap.
The result is 2.58% CAGR in EUR over 26 years against the DAX's 5.19%. Against a global benchmark like SPY (8.02%), the gap is wider still, which reflects Germany's broader structural lag: export dependence, energy costs, and a market weighted toward old-economy sectors with no equivalent of US tech-driven multiple expansion. But the local comparison is the one that matters here, and the local comparison is the one the strategy loses.
The two years where Germany worked were 2009 and 2016, when a global industrial cycle turned and the beaten-down cyclicals the strategy held repriced off multi-year lows. That's mean reversion doing what it's supposed to do. Two years out of 26 isn't a strategy.
Part of a Series
This analysis is part of our Sector Mean Reversion global exchange comparison. We tested the same strategy across multiple exchanges:
- Sector Mean Reversion on US Stocks (NYSE + NASDAQ + AMEX)
- Sector Mean Reversion on Indian Stocks (NSE)
- Sector Mean Reversion on Korean Stocks (KSC)
- Sector Mean Reversion on Taiwanese Stocks (TAI + TWO)
- Sector Mean Reversion on Swedish Stocks (STO)
- Sector Mean Reversion: Global Exchange Comparison
References
- Moskowitz, T. & Grinblatt, M. (1999). "Do Industries Explain Momentum?" Journal of Finance, 54(4), 1249-1290.
Run This Screen Yourself
Via Python:
import requests, time
API_KEY = "your_api_key" # get one at cetaresearch.com
BASE = "https://tradingstudio.finance/api/v1"
query = """
WITH prices AS (
SELECT e.symbol, e.adjClose, CAST(e.date AS DATE) AS trade_date
FROM stock_eod e
JOIN profile p ON e.symbol = p.symbol
WHERE p.sector IS NOT NULL AND p.sector != ''
AND p.marketCap > 500000000
AND p.exchange IN ('XETRA')
AND CAST(e.date AS DATE) >= CURRENT_DATE - INTERVAL '400' DAY
AND e.adjClose IS NOT NULL AND e.adjClose > 0
),
recent AS (
SELECT symbol, adjClose AS recent_price
FROM prices
QUALIFY ROW_NUMBER() OVER (PARTITION BY symbol ORDER BY trade_date DESC) = 1
),
year_ago AS (
SELECT symbol, adjClose AS old_price
FROM prices
WHERE trade_date <= CURRENT_DATE - INTERVAL '252' DAY
QUALIFY ROW_NUMBER() OVER (PARTITION BY symbol ORDER BY trade_date DESC) = 1
),
stock_returns AS (
SELECT r.symbol, pr.sector,
(r.recent_price / ya.old_price - 1) * 100 AS return_12m
FROM recent r
JOIN year_ago ya ON r.symbol = ya.symbol
JOIN profile pr ON r.symbol = pr.symbol
WHERE ya.old_price > 0 AND r.recent_price > 0
AND (r.recent_price / ya.old_price - 1) BETWEEN -0.99 AND 5.0
)
SELECT pr.sector,
ROUND(AVG(sr.return_12m), 2) AS avg_return_12m_pct,
COUNT(DISTINCT sr.symbol) AS n_stocks,
ROW_NUMBER() OVER (ORDER BY AVG(sr.return_12m) ASC) AS rank_worst
FROM stock_returns sr
JOIN profile pr ON sr.symbol = pr.symbol
GROUP BY pr.sector
HAVING COUNT(DISTINCT sr.symbol) >= 5
ORDER BY avg_return_12m_pct ASC
"""
resp = requests.post(f"{BASE}/data-explorer/execute", headers={
"X-API-Key": API_KEY, "Content-Type": "application/json"
}, json={
"query": query,
"options": {"format": "json", "limit": 100},
"resources": {"memoryMb": 16384, "threads": 6}
})
task_id = resp.json()["taskId"]
while True:
result = requests.get(f"{BASE}/tasks/data-query/{task_id}",
headers={"X-API-Key": API_KEY}).json()
if result["status"] in ("completed", "failed"):
break
time.sleep(2)
print("Sector rankings (worst to best, 12-month return):")
for r in result["result"]["rows"]:
flag = " <-- BUY" if r["rank_worst"] <= 2 else ""
print(f"#{r['rank_worst']} {r['sector']:30s} {r['avg_return_12m_pct']:+.1f}% ({r['n_stocks']} stocks){flag}")
Get your API key at cetaresearch.com. The full backtest code (Python + DuckDB) is on GitHub.
Data: Ceta Research, FMP financial data warehouse. Universe: XETRA (Frankfurt Stock Exchange), market cap > €500M. Returns in EUR. Quarterly rebalance, equal weight, 2000-2025. Benchmark: DAX (^GDAXI). Past performance does not guarantee future results.
Past performance does not guarantee future results. This is educational content, not investment advice.