Owner Earnings Yield: Buffett's Metric Backtested on 25 Years of US Stock Data
We screened for stocks with high Owner Earnings yield, strong profitability, and reasonable size across all US exchanges from 2000 to 2025. The strategy returned 12.25% annually vs 7.85% for the S&P 500. Zero cash periods, 23 stocks per rebalance on average, and a down capture ratio of 33.1%. The portfolio captured almost all of the market's upside while absorbing less than a third of its losses.
Contents
- Method
- What is Owner Earnings?
- The Signal
- The Screen (SQL)
- What We Found
- 25 years of data. 4.4% annual alpha over the S&P 500.
- Year-by-year returns
- The early 2000s: OE Yield's golden era
- The 2019 anomaly
- Where OE Yield struggles: post-2017 growth dominance
- Backtest Methodology
- Limitations
- Takeaway
- Part of a Series
- References
- Run This Screen Yourself
Method
Data source: Ceta Research (FMP financial data warehouse) Universe: All US exchanges (NYSE, NASDAQ, AMEX), market cap > $1B USD Period: 2000-2025 (25 years, 25 annual periods) Rebalancing: Annual (July, after FY filings + 45-day lag), equal weight top 30 Benchmark: S&P 500 Total Return (SPY) Execution: Next-day close (market-on-close after signal date) Cash rule: Hold cash if fewer than 10 stocks qualify Transaction costs: Size-tiered model (0.1-0.5% one-way based on market cap)
Owner Earnings computed from raw financial statements at each rebalance: Net Income + D&A - min(|Capex|, D&A). The min() function proxies maintenance capex. If a company spends less on capex than its depreciation, all capex is maintenance. If it spends more, the excess is treated as growth investment and excluded.
What is Owner Earnings?
Warren Buffett introduced Owner Earnings in his 1986 Berkshire Hathaway letter. He wanted a metric that answers one question: what does this business actually earn for its owners?
Owner Earnings = Net Income + D&A - Maintenance Capex
Standard free cash flow deducts all capital expenditure. Every dollar spent building a new factory gets the same treatment as a dollar replacing a broken machine. Owner Earnings only deducts the maintenance portion. It doesn't penalize companies for investing in growth.
Consider two companies with identical operations generating $500M in net income:
- Company A spends $200M on maintenance. Owner Earnings: $300M. FCF: $300M. Same.
- Company B spends $200M on maintenance and $400M building new capacity. Owner Earnings: $300M. FCF: -$100M. Dramatically different.
FCF makes Company B look like a money-losing operation. Owner Earnings shows both businesses have identical earning power from existing operations.
OE Yield = Owner Earnings / Market Cap
Higher yield means you're paying less per dollar of owner earnings. We screen for the cheapest stocks on this metric.
The Signal
| Filter | Threshold | Rationale |
|---|---|---|
| OE Yield | > 5% | Less than 20x owner earnings |
| OE Yield | < 50% | Removes data artifacts |
| ROE | > 10% | Solid returns on shareholder equity |
| Operating Margin | > 10% | Genuine pricing power |
| Market Cap | > $1B USD | Liquidity filter |
Top 30 by highest OE yield, equal weight. Annual rebalance in July (after FY filings become available, with a 45-day lag to prevent look-ahead bias).
The signal is deliberately simple. Four quality/value filters plus a size gate. Unlike our FCF Yield strategy, there's no interest coverage filter. This means more stocks qualify, particularly industrial and consumer companies that carry moderate debt but generate strong owner earnings.
The Screen (SQL)
WITH income_latest AS (
SELECT symbol, netIncome, depreciationAndAmortization,
ROW_NUMBER() OVER (PARTITION BY symbol ORDER BY date DESC) AS rn
FROM income_statement
WHERE period = 'FY'
AND netIncome IS NOT NULL
AND depreciationAndAmortization IS NOT NULL
),
cashflow_latest AS (
SELECT symbol, capitalExpenditure,
ROW_NUMBER() OVER (PARTITION BY symbol ORDER BY date DESC) AS rn
FROM cash_flow_statement
WHERE period = 'FY'
AND capitalExpenditure IS NOT NULL
),
calculated AS (
SELECT
i.symbol,
i.netIncome + i.depreciationAndAmortization
- LEAST(ABS(c.capitalExpenditure), i.depreciationAndAmortization)
AS owner_earnings
FROM income_latest i
JOIN cashflow_latest c ON i.symbol = c.symbol AND c.rn = 1
WHERE i.rn = 1
)
SELECT
calc.symbol,
p.companyName,
ROUND(calc.owner_earnings / k.marketCap * 100, 2) AS oe_yield_pct,
ROUND(k.freeCashFlowYieldTTM * 100, 2) AS fcf_yield_pct,
ROUND(k.returnOnEquityTTM * 100, 1) AS roe_pct,
ROUND(f.operatingProfitMarginTTM * 100, 1) AS op_margin_pct,
ROUND(k.marketCap / 1e9, 1) AS market_cap_billions
FROM calculated calc
JOIN key_metrics_ttm k ON calc.symbol = k.symbol
JOIN financial_ratios_ttm f ON calc.symbol = f.symbol
JOIN profile p ON calc.symbol = p.symbol
WHERE calc.owner_earnings > 0
AND calc.owner_earnings / k.marketCap > 0.05
AND calc.owner_earnings / k.marketCap < 0.50
AND k.returnOnEquityTTM > 0.10
AND f.operatingProfitMarginTTM > 0.10
AND k.marketCap > 1e9
AND p.exchange IN ('NYSE', 'NASDAQ', 'AMEX')
ORDER BY calc.owner_earnings / k.marketCap DESC
LIMIT 30
Run this query on Ceta Research
What We Found

25 years of data. 4.4% annual alpha over the S&P 500.
| Metric | OE Yield (US) | S&P 500 |
|---|---|---|
| CAGR | 12.25% | 7.85% |
| Total Return | 1,698% | 562% |
| Volatility | 23.8% | 16.6% |
| Max Drawdown | -41.1% | -38.0% |
| Sharpe Ratio | 0.432 | 0.352 |
| Sortino Ratio | 0.943 | 0.628 |
| Win Rate (annual) | 56% | - |
| Avg Stocks per Period | 23.2 | - |
| Cash Periods | 0 of 25 | - |
OE Yield turned $10,000 into $179,750. The S&P 500 turned it into $66,170. The strategy was fully invested every single period.
The standout metric is down capture: 33.1%. The portfolio captured only 33 cents of every dollar the market lost. Up capture was 125%, meaning it participated in rallies and then some. That combination, more upside with less downside, drives the 4.4% excess CAGR.
Year-by-year returns

| Year | OE Yield | S&P 500 | Excess |
|---|---|---|---|
| 2000 | +26.3% | -14.8% | +41.1% |
| 2001 | +6.3% | -22.4% | +28.8% |
| 2002 | +14.2% | +6.9% | +7.3% |
| 2003 | +29.7% | +14.9% | +14.8% |
| 2004 | +25.6% | +8.9% | +16.7% |
| 2005 | +33.5% | +8.0% | +25.5% |
| 2006 | +25.0% | +20.9% | +4.0% |
| 2007 | -15.9% | -15.2% | -0.7% |
| 2008 | -29.9% | -26.9% | -3.0% |
| 2009 | +13.8% | +16.0% | -2.2% |
| 2010 | +27.1% | +33.5% | -6.4% |
| 2011 | +7.3% | +4.2% | +3.1% |
| 2012 | +43.4% | +20.7% | +22.7% |
| 2013 | +33.8% | +24.7% | +9.0% |
| 2014 | +8.4% | +7.2% | +1.2% |
| 2015 | -4.8% | +2.7% | -7.5% |
| 2016 | +37.5% | +18.6% | +18.9% |
| 2017 | +6.8% | +14.3% | -7.6% |
| 2018 | +0.2% | +11.2% | -11.0% |
| 2019 | -33.0% | +7.4% | -40.4% |
| 2020 | +74.6% | +41.0% | +33.6% |
| 2021 | -16.6% | -10.7% | -5.9% |
| 2022 | +12.0% | +18.1% | -6.1% |
| 2023 | +16.8% | +25.4% | -8.6% |
| 2024 | +26.7% | +14.4% | +12.3% |
The early 2000s: OE Yield's golden era
| Year | OE Yield | S&P 500 | Excess |
|---|---|---|---|
| 2000 | +26.3% | -14.8% | +41.1% |
| 2001 | +6.3% | -22.4% | +28.8% |
| 2002 | +14.2% | +6.9% | +7.3% |
| Cumulative | +53.3% | -30.3% | +83.6% |
While the market cratered during the dot-com bust, OE Yield gained 53% over three years. The screen naturally avoids overvalued growth stocks. Companies trading at less than 20x owner earnings with 10%+ ROE and margins aren't dot-com highfliers. They're profitable, established businesses that happened to be cheap.
2003-2006 continued the run. The portfolio gained 29.7%, 25.6%, 33.5%, and 25.0% in four consecutive years. Value stocks dominated this era, and OE Yield was positioned perfectly.
The 2019 anomaly
The worst year was 2019: -33.0% while SPY gained 7.4%. That's a 40-point gap, the strategy's widest annual underperformance. This July 2019 to July 2020 period covers the start of the COVID crash. Small-cap value stocks were hit hard in late February and March 2020.
The bounce was equally dramatic. The 2020 period (July 2020 to July 2021) returned +74.6%, the strategy's single best year. The stocks that were beaten down in the crash recovered violently. Over the two periods combined, OE Yield returned +16.1% vs SPY's +51.4%. The strategy recovered but gave up ground to the benchmark in net terms.
Where OE Yield struggles: post-2017 growth dominance
From 2017 to 2023, OE Yield underperformed SPY in 5 of 7 years. Total return over those 7 years: OE Yield +57%, SPY +130%. The gap is growth-driven. Mega-cap tech companies with high P/E ratios and massive growth capex don't pass the OE Yield screen. NVIDIA, Microsoft, and Apple all fail multiple filters during their biggest runs.
This is the tradeoff. You get 4.4% annual alpha over 25 years, but you have to sit through extended periods of underperformance when growth leads the market.
Backtest Methodology
| Parameter | Choice |
|---|---|
| Universe | NYSE + NASDAQ + AMEX, Market Cap > $1B USD |
| Signal | OE Yield 5-50%, ROE > 10%, OPM > 10% |
| Portfolio | Top 30 by OE Yield, equal weight |
| Rebalancing | Annual (July) |
| Cash rule | Hold cash if < 10 qualify |
| Benchmark | S&P 500 Total Return (SPY) |
| Execution | Next-day close (market-on-close) |
| Period | 2000-2025 (25 annual periods) |
| Data | Point-in-time (45-day lag for FY filings) |
| Transaction costs | Size-tiered (0.1-0.5% one-way by market cap) |
Full methodology details: backtests/METHODOLOGY.md
Limitations
Annual rebalancing only. We test July-to-July periods using the most recent FY filing. Quarterly or monthly rebalancing with TTM data might produce different results. We chose annual to match the data source (FY filings) and reduce turnover.
Maintenance capex is estimated. We use min(|Capex|, D&A) as a proxy. Companies with recently purchased assets have high depreciation but may need little maintenance. Companies that defer maintenance look artificially profitable. The proxy is standard in the literature but imperfect.
Recent underperformance. OE Yield trailed SPY from 2017-2023. Growth-dominated markets are the strategy's blind spot. Factor strategies are cyclical, and this one requires patience through multi-year stretches of relative weakness.
Survivorship bias. Exchange membership uses current company profiles. Delisted companies, M&A targets, and bankruptcies aren't fully captured in the historical universe.
Concentrated portfolio. 23 stocks on average. Higher concentration means more stock-specific risk than a broad index.
Benchmark currency mismatch. Returns and benchmark are both in USD for US exchanges. No adjustment needed. For regional blogs, currency differences apply.
Takeaway
OE Yield works on US stocks. 12.25% CAGR over 25 years, 4.4% alpha above the S&P 500, zero cash periods. The portfolio always found at least 10 qualifying stocks from the US universe.
The strategy's edge is asymmetric. 125% up capture, 33% down capture. It participates fully in rallies and absorbs a third of the losses. That asymmetry, not raw stock picking, drives the long-term alpha.
The cost is clear: multi-year stretches of underperformance during growth-led markets. 2017-2023 was tough. The next growth cycle will produce another dry spell. OE Yield rewards investors who can hold through those periods. The 25-year record shows the math works if you give it time.
Part of a Series
This analysis is part of our Owner Earnings Yield global exchange comparison. We tested the same screen on 13 exchanges worldwide: - OE Yield on Indian Stocks (BSE + NSE) - 17.2% CAGR, +5.1% vs Sensex - OE Yield on Hong Kong Stocks (HKSE) - 10.4% CAGR, +8.8% vs Hang Seng - OE Yield on Swedish Stocks (STO) - 9.6% CAGR, +7.0% vs OMX Stockholm 30 - OE Yield on Chinese Stocks (SHZ + SHH) - 9.6% CAGR, +7.2% vs SSE Composite - OE Yield: 13-Exchange Global Comparison - full comparison table
References
- Buffett, W. (1986). "Berkshire Hathaway Annual Letter to Shareholders."
- Greenwald, B., Kahn, J., Sonkin, P. & van Biema, M. (2001). Value Investing: From Graham to Buffett and Beyond. John Wiley & Sons.
- Greenblatt, J. (2006). The Little Book That Beats the Market. John Wiley & Sons.
Run This Screen Yourself
Via web UI: Run the Owner Earnings Yield screen on Ceta Research. The query is pre-loaded. Hit "Run" and see what passes today.
Via Python:
import requests, time
API_KEY = "your_api_key" # get one at cetaresearch.com
BASE = "https://tradingstudio.finance/api/v1"
resp = requests.post(f"{BASE}/data-explorer/execute", headers={
"X-API-Key": API_KEY, "Content-Type": "application/json"
}, json={
"query": """
WITH income_latest AS (
SELECT symbol, netIncome, depreciationAndAmortization,
ROW_NUMBER() OVER (PARTITION BY symbol ORDER BY date DESC) AS rn
FROM income_statement WHERE period = 'FY'
AND netIncome IS NOT NULL AND depreciationAndAmortization IS NOT NULL
),
cashflow_latest AS (
SELECT symbol, capitalExpenditure,
ROW_NUMBER() OVER (PARTITION BY symbol ORDER BY date DESC) AS rn
FROM cash_flow_statement WHERE period = 'FY' AND capitalExpenditure IS NOT NULL
),
calculated AS (
SELECT i.symbol,
i.netIncome + i.depreciationAndAmortization
- LEAST(ABS(c.capitalExpenditure), i.depreciationAndAmortization) AS owner_earnings
FROM income_latest i JOIN cashflow_latest c ON i.symbol = c.symbol AND c.rn = 1
WHERE i.rn = 1
)
SELECT calc.symbol, p.companyName,
ROUND(calc.owner_earnings / k.marketCap * 100, 2) AS oe_yield_pct,
ROUND(k.returnOnEquityTTM * 100, 1) AS roe_pct,
ROUND(f.operatingProfitMarginTTM * 100, 1) AS op_margin_pct,
ROUND(k.marketCap / 1e9, 1) AS market_cap_billions
FROM calculated calc
JOIN key_metrics_ttm k ON calc.symbol = k.symbol
JOIN financial_ratios_ttm f ON calc.symbol = f.symbol
JOIN profile p ON calc.symbol = p.symbol
WHERE calc.owner_earnings > 0
AND calc.owner_earnings / k.marketCap > 0.05
AND calc.owner_earnings / k.marketCap < 0.50
AND k.returnOnEquityTTM > 0.10
AND f.operatingProfitMarginTTM > 0.10
AND k.marketCap > 1e9
AND p.exchange IN ('NYSE', 'NASDAQ', 'AMEX')
ORDER BY calc.owner_earnings / k.marketCap DESC
LIMIT 30
""",
"options": {"format": "json", "limit": 100}
})
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)
for r in result["result"]["rows"][:10]:
print(f"{r['symbol']:8s} OE Yield={r['oe_yield_pct']}% ROE={r['roe_pct']}% MCap=${r['market_cap_billions']}B")
Get your API key at cetaresearch.com. The full backtest code (Python + DuckDB) is on GitHub.
Data: Ceta Research, FMP financial data warehouse. Universe: NYSE + NASDAQ + AMEX. Annual rebalance (July), equal weight top 30, 2000-2025. Transaction costs included.