Skip to content
← All work

06 · Portfolio construction · Quant

JSE Top-40 Portfolio Optimiser Lab

Seven ways to build a JSE equity portfolio, walk-forward from 2010 to 2026 with costs and position caps, then a Deflated Sharpe to ask whether the winner is skill or a lucky draw out of seven.

Mean-variance return p.a., net of costs
15.88%
Mean-variance Sharpe (rf 7%) vs 0.075 for STX40
0.387
Deflated Sharpe, 7 strategies tested
0.660
Mean-variance annual cost drag
1.57%

Walk-forward equity, seven methods vs STX40

Month-end values from reports/backtest_results.pkl and benchmark_stx40.csv. Toggle to Sharpe vs Deflated Sharpe from metrics.csv.

Log scale. Evaluation window 2010-12-31 to 2026-10-30, R1m start. STX40 (dashed) is the index from benchmark_stx40.csv, before costs.

Problem

Every optimiser looks good in-sample. The real question for a South African equity book is which construction method still wins out of sample, after costs and turnover, and whether the best backtest is just the luckiest of several tries.

Approach

  1. 01JSE Top-40 constituents from yfinance. 35 tickers are configured and 31 load with usable history. An unbalanced panel: a stock becomes eligible once it has 252 days of history, so the first allocation is 2010-12-31 and the evaluation runs to 2026-10-30.
  2. 02A cleaner for yfinance's cents-to-rand unit jumps. STX40 jumped from about 84 to about 8,500 on 2025-04-25 and back four days later. Fixing those brought the benchmark's annualized vol from over 2,400% to 17.9%.
  3. 03Seven methods: equal weight, min variance (sample and Ledoit-Wolf), max-Sharpe mean-variance in cvxpy, risk parity (ERC), HRP and Black-Litterman. All long-only with a 15% cap per name (Reg 28 inspired), a 1% floor and at most 30 holdings.
  4. 04Monthly rebalance on an expanding window, R1m start, 30 bps charged on both sides of every trade. Deflated Sharpe (Bailey and López de Prado, 2014) asks whether a Sharpe beats the best one you would expect by luck from seven tries. A cost sweep runs from 10 to 50 bps, and pytest runs in CI on Python 3.9 to 3.11.

Results

Mean-variance: 15.88% a year at 22.96% vol, Sharpe 0.387, Deflated Sharpe 0.660, max drawdown 40.40%. R1m became about R10.6m. STX40 did 8.34% at 17.88% vol, Sharpe 0.075, and about R3.6m before costs.

Equal weight (10.33%), risk parity (9.90%), Black-Litterman (9.61%) and both min-variance versions beat the index on return and Sharpe with far less turnover. HRP came last at 8.18% with the deepest drawdown, 49.72%.

The catch is turnover. Mean-variance trades 21.72% of the book a month, which costs 1.57% a year, against 3.25% and 0.23% for 1/N. It also holds at most 8 names. A Deflated Sharpe of 0.660 is the best of the seven but well short of the usual 0.95 bar, so it is promising, not proven.

Next

Point-in-time Top-40 membership and delisted names to remove survivorship and look-ahead bias, and market impact for the less liquid names.