Price-to-Sales Below 1 in South Africa: Beats the S&P 500, But Do the
We screened JSE-listed stocks for low price-to-sales ratios with qualifying margins and profitability, then backtested the resulting portfolio from 2000 to 2025. The strategy returned 9.71% annually vs 8.01% for the S&P 500. That's +1.70% annual outperformance. But the Sharpe ratio of 0.031 looks alarming on first glance, and the first six years were entirely in cash.
Contents
- Method
- The Screen
- JSE P/S Screen (SQL)
- What We Found
- The headline numbers
- The Sharpe ratio explanation
- Cash periods: 2000-2005 in the dark
- Year-by-year returns
- 2008: the JSE held up far better than the S&P 500
- 2009: the near-double
- 2017-2020: four years of underperformance
- 2022: commodity boom, clear advantage
- Crisis performance summary
- Backtest Methodology
- Limitations
- Takeaway
- Part of a Series
- References
- Run This Screen Yourself
Here's what actually happened, and why the Sharpe isn't what it appears to be.
Method
- Data source: Ceta Research (FMP financial data warehouse)
- Universe: JNB (Johannesburg Stock Exchange), market cap > ZAR 1B
- Period: 2000-2025 (25 years total; effectively 20 years invested from 2006)
- Rebalancing: Quarterly (January, April, July, October), equal weight, top 30 by lowest P/S
- Benchmark: S&P 500 Total Return (SPY)
- Cash rule: Hold cash if fewer than 10 stocks qualify
Popularized by Kenneth Fisher in Super Stocks (1984). 45-day lag on financial data to prevent look-ahead bias.
The Screen
Signal filters:
| Criterion | Metric | Threshold |
|---|---|---|
| Cheap relative to revenue | Price-to-Sales | < 1.0 |
| Business quality | Gross Margin | > 20% |
| Operational efficiency | Operating Margin | > 5% |
| Capital returns | ROE | > 10% |
Size filter: Market cap > ZAR 1B
Selection: Top 30 by lowest P/S among qualifying stocks.
JSE P/S Screen (SQL)
SELECT
f.symbol,
p.companyName,
p.exchange,
p.sector,
ROUND(f.priceToSalesRatioTTM, 3) AS ps_ratio,
ROUND(f.grossProfitMarginTTM * 100, 1) AS gross_margin_pct,
ROUND(f.operatingProfitMarginTTM * 100, 1) AS op_margin_pct,
ROUND(k.returnOnEquityTTM * 100, 1) AS roe_pct,
ROUND(k.marketCap / 1e9, 2) AS mktcap_b
FROM financial_ratios_ttm f
JOIN key_metrics_ttm k ON f.symbol = k.symbol
JOIN profile p ON f.symbol = p.symbol
WHERE f.priceToSalesRatioTTM > 0
AND f.priceToSalesRatioTTM < 1
AND f.grossProfitMarginTTM > 0.20
AND f.operatingProfitMarginTTM > 0.05
AND k.returnOnEquityTTM > 0.10
AND k.marketCap > 1000000000
AND p.exchange IN ('JNB')
QUALIFY ROW_NUMBER() OVER (PARTITION BY f.symbol ORDER BY f.priceToSalesRatioTTM ASC) = 1
ORDER BY f.priceToSalesRatioTTM ASC
LIMIT 30
Run this query on Ceta Research
What We Found

The headline numbers
| Metric | P/S Screen (JNB) | S&P 500 |
|---|---|---|
| CAGR | 9.71% | 8.01% |
| Excess Return | +1.70% | — |
| Max Drawdown | -45.39% | -45.53% |
| Sharpe Ratio | 0.031 | 0.354 |
| Avg Stocks per Period | 23.6 (when invested) | — |
| Cash Periods | 24 of 100 | — |
That Sharpe of 0.031 demands an explanation. On its face, it looks like the strategy barely generates excess returns over risk. It doesn't mean that.
The Sharpe ratio explanation
The Sharpe formula is: (Portfolio Return - Risk-Free Rate) / Volatility
South Africa's risk-free rate is approximately 9%, reflecting local government bond yields. The portfolio returned 9.71% CAGR. Plug those numbers in: the numerator is 0.71%. Divide by the strategy's volatility and you get a near-zero Sharpe.
This isn't a strategy failure. It's a local rates context issue. A South African investor allocating capital domestically faces a 9% hurdle rate from government bonds. Against that benchmark, the P/S screen generates modest excess returns on a risk-adjusted basis. That's the honest conclusion: this screen outperforms the S&P 500 globally, but it doesn't clearly beat domestic South African fixed income on a risk-adjusted basis.
The +1.70% annual outperformance over SPY is genuine global outperformance. The Sharpe of 0.031 is a South African domestic investor problem, not a global comparison problem.
Cash periods: 2000-2005 in the dark
The first 24 quarterly periods (2000-2005) were entirely in cash. FY financial data for JSE-listed companies wasn't available in sufficient coverage until around 2006. The screen couldn't find 10 qualifying stocks during those years, so the cash rule triggered every quarter.
This matters for the CAGR calculation. The 9.71% CAGR covers all 25 years including the six cash years. From 2006 onward (when the strategy was actually invested), the returns are materially higher.
Year-by-year returns

| Year | JNB P/S | S&P 500 | Excess |
|---|---|---|---|
| 2000 | 0.0% (cash) | -10.5% | — |
| 2001 | 0.0% (cash) | -9.17% | — |
| 2002 | 0.0% (cash) | -19.92% | — |
| 2003 | 0.0% (cash) | +24.12% | — |
| 2004 | 0.0% (cash) | +10.24% | — |
| 2005 | 0.0% (cash) | +7.17% | — |
| 2006 | +24.69% | +13.65% | +11.04% |
| 2007 | +25.18% | +4.40% | +20.78% |
| 2008 | -19.58% | -34.31% | +14.73% |
| 2009 | +99.91% | +24.73% | +75.18% |
| 2010 | -6.68% | +14.31% | -20.99% |
| 2011 | +30.01% | +2.46% | +27.55% |
| 2012 | +33.16% | +17.09% | +16.07% |
| 2013 | +36.62% | +27.77% | +8.85% |
| 2014 | +7.21% | +14.50% | -7.29% |
| 2015 | -8.49% | -0.12% | -8.37% |
| 2016 | +37.15% | +14.45% | +22.70% |
| 2017 | +2.36% | +21.64% | -19.28% |
| 2018 | -7.63% | -5.15% | -2.48% |
| 2019 | -1.97% | +32.31% | -34.28% |
| 2020 | -10.82% | +15.64% | -26.46% |
| 2021 | +35.17% | +31.26% | +3.91% |
| 2022 | +4.94% | -18.99% | +23.93% |
| 2023 | +5.35% | +26.00% | -20.65% |
| 2024 | +14.43% | +25.28% | -10.85% |
| 2025 | +3.35% | +15.34% | -11.99% |
2008: the JSE held up far better than the S&P 500
While the S&P 500 lost -34.31% in 2008, this screen returned -19.58%. That's a 14-point advantage in the worst year of the financial crisis. JSE-listed companies with P/S below 1 and positive operating margins were less exposed to the credit market destruction that drove US financials down. South African banks, while not immune, didn't hold the same toxic mortgage exposure as US counterparts.
2009: the near-double
The portfolio gained +99.91% in 2009, the largest single-year return in the dataset. The combination of a deeply depressed starting point (stocks that survived 2008 with real earnings) and a commodity supercycle revival produced an extraordinary recovery year. The JSE's resource-heavy composition amplified the rebound. Low P/S stocks that hadn't de-rated further than fundamentals justified snapped back hard.
2017-2020: four years of underperformance
The strategy underperformed in four consecutive years from 2017 to 2020. South Africa's political uncertainty (land reform debate, Zuma-era policy drift, cabinet reshuffles), combined with weak commodity prices and currency pressure, dragged the equity market. The rand weakened, reducing USD returns for foreign investors. The P/S screen couldn't offset structural macro headwinds at the country level.
This is the weak point of any single-country strategy: concentrated political and currency risk doesn't diversify away.
2022: commodity boom, clear advantage
The strategy returned +4.94% in 2022 while the S&P 500 fell -18.99%. A 24-point gap. South Africa's commodity exposure (gold, platinum group metals, iron ore, coal) turned into a tailwind when global inflation drove raw material prices higher. The same sectors that suffered in 2019-2020 recovered sharply. This is the cyclical flip side.
Crisis performance summary
| Event | JNB P/S | S&P 500 | Gap |
|---|---|---|---|
| Financial Crisis (2008) | -19.58% | -34.31% | +14.73% |
| COVID (2020) | -10.82% | +15.64% | -26.46% |
| 2022 Inflation Bear | +4.94% | -18.99% | +23.93% |
The screen protects well against financial-sector crises (2008, 2022) but suffers during growth-driven recoveries (COVID 2020) where US tech dominance is the main return driver.
Backtest Methodology
| Parameter | Choice |
|---|---|
| Universe | JNB (Johannesburg), Market Cap > ZAR 1B |
| Signal | P/S < 1.0, Gross Margin > 20%, Operating Margin > 5%, ROE > 10% |
| Portfolio | Top 30 by lowest P/S, equal weight |
| Rebalancing | Quarterly (January, April, July, October) |
| Cash rule | Hold cash if < 10 qualify |
| Benchmark | S&P 500 Total Return (SPY) |
| Period | 2000-2025 (25 years, 100 periods; invested from 2006) |
| Data | Point-in-time (45-day lag for financial statements) |
Limitations
Sharpe is misleading without local context. The 0.031 Sharpe is calculated against a 9% South African risk-free rate. Against a global or US risk-free rate, the Sharpe would be substantially higher. Always interpret Sharpe ratios with the local rate context in mind.
Currency risk is significant. Returns are in ZAR. The rand has depreciated meaningfully against the USD and EUR over 25 years. A foreign investor's realized returns in home currency could be materially lower than the ZAR-denominated CAGR.
Six years in cash. The 2000-2005 data gap is a real limitation. The 9.71% CAGR includes those cash years, which compresses the apparent return. The strategy's actual performance when invested is better than the headline number implies.
Political and policy risk. South Africa carries elevated country-specific risk (currency controls, land reform uncertainty, infrastructure challenges). These aren't priced into a simple backtest.
Commodity concentration. The JSE is heavily weighted toward mining and resources. The P/S screen will tend to pull from these sectors. Sector concentration is persistent.
Survivorship bias. Exchange membership uses current profiles, not historical. Delistings aren't tracked.
Transaction costs not included. With quarterly rebalancing and roughly 24 stocks, cost drag is approximately 0.3-0.5% annually.
Takeaway
The South Africa P/S screen beats the S&P 500 by +1.70% annually over 25 years. That's a real result, especially given the six cash years dragging on the CAGR. From 2006 onward the performance is stronger.
The Sharpe of 0.031 is honest about one thing: against a 9% domestic risk-free rate, the strategy doesn't generate impressive risk-adjusted returns for a South African investor. They could have held government bonds at similar after-volatility returns. For an international investor using the S&P 500 as the comparison benchmark, the picture is different.
The most interesting feature of this dataset is 2008 vs 2009. The screen lost -19.58% in 2008 (vs -34.31% for SPY) and then gained +99.91% in 2009. If you stayed in, the round-trip was exceptional. That's what this strategy does in commodity-heavy emerging markets: it falls less in financial crises, then recovers hard when the real economy comes back.
The currency and political risk are real and not going away. Any foreign investor allocating to JSE needs to factor in ZAR exposure and a higher required return than they'd demand from a developed market.
Part of a Series
This analysis is part of our P/S value screen global exchange comparison. We tested the same four-factor screen across 13 exchanges worldwide: - P/S Value Screen on US Stocks - P/S Value Screen on Indian Stocks - P/S Value Screen on Canadian Stocks - P/S Value Screen on Swedish Stocks - P/S Value Screen on German Stocks (XETRA) - P/S Value Screen on Japanese Stocks - P/S Value Screen: 13-Exchange Global Comparison
References
- Fisher, K. (1984). Super Stocks. Dow Jones-Irwin.
- Barbee, W., Mukherji, S. & Raines, G. (1996). "Do Sales-Price and Debt-Equity Explain Stock Returns Better than Book-Market and Firm Size?" Financial Analysts Journal, 52(2), 56-60.
- Gray, W. & Vogel, J. (2012). "Analyzing Valuation Measures: A Performance Horse Race over the Past 40 Years." Journal of Portfolio Management, 39(1), 112-121.
- Novy-Marx, R. (2013). "The Other Side of Value: The Gross Profitability Premium." Journal of Financial Economics, 108(1), 1-28.
Run This Screen Yourself
Via web UI: Run the P/S 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": """
SELECT
f.symbol,
p.companyName,
ROUND(f.priceToSalesRatioTTM, 3) AS ps_ratio,
ROUND(f.grossProfitMarginTTM * 100, 1) AS gross_margin_pct,
ROUND(f.operatingProfitMarginTTM * 100, 1) AS op_margin_pct,
ROUND(k.returnOnEquityTTM * 100, 1) AS roe_pct,
ROUND(k.marketCap / 1e9, 2) AS mktcap_b
FROM financial_ratios_ttm f
JOIN key_metrics_ttm k ON f.symbol = k.symbol
JOIN profile p ON f.symbol = p.symbol
WHERE f.priceToSalesRatioTTM > 0
AND f.priceToSalesRatioTTM < 1
AND f.grossProfitMarginTTM > 0.20
AND f.operatingProfitMarginTTM > 0.05
AND k.returnOnEquityTTM > 0.10
AND k.marketCap > 1000000000
AND p.exchange IN ('JNB')
QUALIFY ROW_NUMBER() OVER (
PARTITION BY f.symbol ORDER BY f.priceToSalesRatioTTM ASC
) = 1
ORDER BY f.priceToSalesRatioTTM ASC
LIMIT 30
""",
"options": {"format": "json", "limit": 30}
})
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"]:
print(f"{r['symbol']:10s} P/S={r['ps_ratio']:.3f} GM={r['gross_margin_pct']:.1f}% ROE={r['roe_pct']:.1f}%")
Get your API key at cetaresearch.com. The full backtest code (Python + DuckDB) is on GitHub.
Data: Ceta Research, FMP financial data warehouse. Universe: JNB (Johannesburg). Returns in ZAR. Quarterly rebalance, equal weight, 2006-2025 invested.