Data to 5 October 2026

Pre-registered research

Portfolio lab v2: pre-registered specification

Written 2026-10-02 by lane 2, before any v2 result was computed. The v1 spec (portfolio_lab_spec.md, nine portfolios) and its results log stay as they are. Parameters come from the cited papers or from common practice; none is tuned on Indian data. Any change after this date is logged under “Amendments” with the date and the reason, and results run under a changed rule say so.

The library that implements this is described in portfolio_library.md.

1. Questions

  1. Across the whole range of equity / debt / gold mixes, what did each mix deliver in India after costs and tax, and how wide is the uncertainty?
  2. Did the named portfolios from the literature (permanent, golden butterfly, All Weather, three-fund, equal thirds, endowment-style, 60/20/20) earn anything over a plain mix with the same equity share?
  3. Did factor indices beat their parent index after realistic fund costs, and how much of their record is back-tested history from before launch?
  4. Did risk-based and tactical rules beat a static mix of the same assets after costs and tax?
  5. How much of any apparent winner is luck once we account for the number of portfolios tried?

2. Pre-registered predictions (graded in the results log)

3. Assets

All total return, in rupees, on the NSE trading calendar. Levels never leave the pipeline; only rebased curves and statistics are published.

KeyAssetSourceTax classCost classFirst date
n50, n100, n500, next50Nifty 50, 100, 500, Next 50 TRIsindex_tri / asset_tri_daily (repaired against the price index)equitybroad1999 to 2003
mid100, small100Nifty Midcap 100, Smallcap 100 TRIssameequitybroad_small2003-01, 2004-01
mid150, small250Nifty Midcap 150, Smallcap 250 TRIssameequitybroad_small2005-04
multicapNifty500 Multicap 50:25:25 TRI (stands in for the total market: the Nifty Total Market TRI exists in our data only from 2026-06)sameequitybroad_small2005-04
n50ew, n100ew, n500ewEqual-weight Nifty 50, 100, 500 TRIssameequitybroad_small1995, 2003, 2005
factor indiceslisted in section 5.4sameequityfactor2005-04 (mostly back-tested)
gsec5NSE 5-year benchmark G-sec indexasset_tri_dailydebtgilt2001-09
gsec10Synthetic 10-year constant-maturity G-sec total return, built from month-end 10-year yields (IDH FMDMGSEC1011M) by the par-bond method (Swinkels 2019): buy a 10-year par bond at last month’s yield, reprice at this month’s yield with 1/12 year less maturity, plus a month’s coupon. Daily values are interpolated geometrically within the month, so its daily volatility is understated; risk statistics use monthly returns. Validated by its correlation with gsec5 and the ratio of their volatilities to their durations.deriveddebtgilt2001-05
goldDomestic gold in rupees including import duty (WGC INR; USD gold × USD/INR before 2005)asset_tri_dailygoldgold1995
cashAccrued 91-day T-bill yield, standing in for a liquid fundasset_tri_dailydebtliquid1993

There is no Indian commodity, REIT (before 2019) or inflation-linked bond total-return series long enough to use. International equity is excluded from the headline window: global index levels cannot be republished, and the INR international funds whose AMFI NAVs we could use start in 2011 at the earliest. An international window from 2011 using AMFI NAVs (costs already inside the NAV) is planned as v2.1 and will be pre-registered separately.

4. Windows

5. The library

Weights are given as asset key: percent. Unless stated, a portfolio rebalances once a year at the first session of January, back to its targets (the v1 convention). Monthly rules decide at month-end closes and trade at the next session’s close.

5.1 Static: equity and debt (ed_*, 11 portfolios)

n500 / gsec5 from 100/0 to 0/100 in steps of 10 points. The brief’s named mixes (100, 80/20, 70/30, 60/40, 50/50, 40/60, 20/80) are all in the grid, which shows the whole frontier instead of a chosen point. Citation for the 60/40 as a reference: the standard balanced benchmark (for example, Bogle 1994).

5.2 Static: equity, debt and gold (edg_*, 66 portfolios)

n500 / gsec5 / gold in 10-point steps summing to 100 (66 mixes, including the pure ones). Shown as a ternary heat map.

5.3 Named multi-asset mixes

CodeName and sourceIndian constructionSubstitutions disclosed
sixty_forty60/40n500 60, gsec5 40none
sixty_twenty_twenty60/20/20n500 60, gsec5 20, gold 20none
equal_thirdsEqual thirdsn500, gsec5, gold at 33.3 eachnone
permanentPermanent portfolio (Browne 1987, Why the Best-Laid Investment Plans Usually Go Wrong)n500 25, gsec10 25, gold 25, cash 25long bond is the synthetic 10-year G-sec (Browne used long Treasuries)
golden_butterflyGolden butterfly (Tyler, Portfolio Charts)n500 20, small250 20, gsec10 20, cash 20, gold 20small-cap value becomes Nifty Smallcap 250 (no small-cap value index); short-term Treasuries become the T-bill
all_weatherAll Weather, Indian approximation (Dalio / Bridgewater as popularised by Robbins 2014: 30 stocks, 40 long bonds, 15 intermediate bonds, 7.5 gold, 7.5 commodities)n500 30, gsec10 40, gsec5 15, gold 15commodities go into gold (no Indian commodity total-return series). This is an approximation of a published retail version, not Bridgewater’s fund
three_fundThree-fund (Bogle; Bogleheads 42/18/40)domestic version: n500 60, gsec5 40 until the international sleeve exists (v2.1)identical to 60/40 in the headline window; listed so the site can say so plainly
endowmentEndowment-style (Swensen 2005, Unconventional Success: 30 domestic, 15 developed, 5 emerging, 20 real estate, 15 TIPS, 15 Treasuries)n500 30, mid150 10, small250 10, gold 20, gsec10 15, gsec5 15the 20% foreign equity becomes Indian mid and small caps (a diversifier within India, not abroad); real estate becomes gold; TIPS become the 10-year G-sec

5.4 Pure beta, factors and blends

Beta (buy and hold, no rebalancing needed): n50, n100, n500, next50, mid150, small250, multicap, n50ew, n100ew, n500ew.

Single factors (buy and hold), each compared with its parent: momentum (mom200 Nifty200 Momentum 30, mom500 Nifty500 Momentum 50; Jegadeesh-Titman 1993, Carhart 1997), value (val200 Nifty200 Value 30, val500 Nifty500 Value 50, val50 Nifty50 Value 20 from 2009; Fama-French 1992/1993), quality (qual200 Nifty200 Quality 30, qual500 Nifty500 Quality 50; Novy-Marx 2013, Asness-Frazzini-Pedersen “Quality minus junk”), low volatility (lv100 Nifty100 Low Volatility 30, lv500 Nifty500 Low Volatility 50, lv50 Nifty Low Volatility 50; Frazzini-Pedersen 2014 “Betting against beta”), alpha (alpha50 Nifty Alpha 50), high beta (hb50 Nifty High Beta 50, the counterpart to low volatility), and NSE’s provider multi-factor indices (mqvlv Nifty500 Multifactor MQVLv 50, aqvlv Nifty Alpha Quality Value Low-Volatility 30).

Beta plus a tilt (annual rebalance): 70% n500 + 30% of one factor, for mom200, val200, qual200, lv100.

Blends (annual rebalance): factor_four = 25% each of mom200, val200, qual200, lv100; value_momentum = 50/50 val200 and mom200 (Asness, Moskowitz and Pedersen 2013); quality_lowvol = 50/50 qual200 and lv100.

Factor statistics: annualised return over the parent, tracking error, information ratio, and CAPM alpha and beta against the parent with Newey-West standard errors (12 monthly lags), each reported for the back-tested history and the live history separately.

Launch dates: NSE’s factor indices are back-tested before launch. The base date and the launch (live) date of each index used, with its source, are in Appendix A. Any history before the launch date is labelled “back-tested” on the site.

5.5 Risk-based (monthly rebalance; universe n500, gsec5, gold)

Covariances use the trailing 252 sessions of daily log returns at the decision date. Long-only, fully invested.

CodeRuleSource
one_over_n1/3 each, rebalanced monthlyDeMiguel, Garlappi and Uppal (2009)
inverse_volweights proportional to 1/σcommon practice
ercequal risk contributionMaillard, Roncalli and Teïletche (2010)
min_variancelong-only minimum variance on the Ledoit-Wolf shrunk covarianceClarke, de Silva and Thorley (2006); Ledoit and Wolf (2004)
max_diversificationmaximise (w·σ) / √(w’Σw), long-onlyChoueifaty and Coignard (2008)
hrphierarchical risk parity, single linkage on the correlation distance, recursive bisectionLópez de Prado (2016)
mv_shrinklong-only maximum Sharpe using the Ledoit-Wolf covariance and the trailing 60-month mean excess return of each assetMarkowitz (1952); Ledoit and Wolf (2004)
black_littermanBlack-Litterman with no views, reference portfolio 60/40 (n500/gsec5), δ = 2.5, τ = 0.05Black and Litterman (1992); He and Litterman (1999)

black_litterman with no views returns the reference portfolio by construction; it is included to show this, and a test checks the identity. Mean-variance with estimated means is included because DeMiguel et al. found it loses to 1/N out of sample; whether that holds here is part of the record.

5.6 Tactical (monthly decisions)

CodeRule as publishedIndian adaptation
gtaaFaber (2007): each of N sleeves is held when its month-end price is above its 10-month average, else cashsleeves n500, gsec5, gold, 1/3 each
sixty_forty_trendv1 rule: 60/40 with the equity sleeve in cash below its 10-month averageunchanged from v1
dual_momentumAntonacci (2014) GEM: the better of home and foreign equity over 12 months if it beats T-bills, else bondshome n500, “foreign” leg replaced by gold, bonds gsec5 (as v1)
paaKeller and Keuning (2016) PAA2: momentum = price / 12-month SMA − 1; n = number of risky assets with positive momentum; bond fraction = (N − n) / (N − n1) with n1 = a·N/4, a = 2, capped at 1; the rest goes equally to the top-T risky assets by momentumrisky N = 5: n50, next50, mid100, small100, gold; T = 3; safe asset gsec5
vaaKeller and Keuning (2017) VAA-G4: 13612W momentum (12·r1 + 4·r3 + 2·r6 + r12); if every offensive asset is positive, all in the best offensive; otherwise all in the best defensiveoffensive n50, mid100, small100, gold; defensive gsec5, cash
daaKeller and Keuning (2018) DAA: canaries decide the cash share (b canaries negative by 13612W → b/2 in the safe asset); the rest equally in the top T offensive by 13612Wcanaries small100 and gsec5 (the Indian stand-ins for Keller’s emerging-market and bond canaries); offensive n50, next50, mid100, gold; T = 2; safe = the better of gsec5 and cash by 13612W
baaKeller (2022) BAA-G4: if any canary is negative by 13612W, go to the top defensive by price/SMA12; else the top offensive by price/SMA12canaries n50, small100, gold, gsec5; offensive top 2 of n50, next50, mid100, small100, gold; defensive top 1 of gsec5, gsec10, cash, any asset worse than cash replaced by cash
vol_managedMoreira and Muir (2017): equity weight = c / σ²(last month’s realised variance)c = the expanding median of past monthly realised variances (no look-ahead), weight capped at 1 (no leverage), remainder in cash; equity is n500
cppiPerold and Sharpe (1988) CPPI: exposure = m × (wealth − floor)m = 3, floor starts at 80% of initial wealth and accrues at the T-bill rate, never reset; risky n500, safe cash; exposure capped at 100%
valuation_capeValuation glide: equity share moves linearly from 80% when the Sensex CAPE10 (IIMA) is at or below its expanding 20th percentile to 20% at or above the 80th; rest in gsec5uses the previous month-end CAPE (one-month publication lag); percentile needs 60 months of history
valuation_yield_gapSame glide on the Nifty 500 earnings yield minus the 10-year G-sec yield (high gap = cheap equity = more equity)same lags

5.7 Ensembles

6. Rebalancing sensitivity

On sixty_forty, sixty_twenty_twenty, equal_thirds and permanent: no rebalancing, monthly, quarterly, annual (default), and tolerance bands checked monthly (rebalance a sleeve that drifts more than 5 points or 25% of its target from target, whichever is smaller; Swedroe’s 5/25 rule).

7. Costs

Running costs are an annual drag applied daily to the asset’s NAV, by cost class and date, representing the all-in cost of the cheapest widely available index fund or ETF of the day (direct plans from January 2013):

Cost classto 20122013 to 20182019 onTrade cost each way
broad (Nifty 50, 100, 500, Next 50)0.75%0.30%0.15%0.05%
broad_small (mid, small, equal-weight, multicap)1.00%0.50%0.30%0.10%
factor1.25%0.90%0.75%0.15%
gold (ETF)1.00%0.75%0.50%0.10%
gilt (G-sec funds)0.75%0.40%0.25%0.05%
liquid0.40%0.20%0.15%0.00%

Plus stamp duty of 0.005% on purchases from 1 July 2020 and securities transaction tax on equity-fund redemptions at the rate in force (0.15% from 1 October 2004, 0.20% from 1 June 2005, 0.25% from 1 June 2006, 0.001% from 1 June 2013). No exit loads (index funds and ETFs held beyond their exit-load period; the monthly tactical rules would pay exit loads on some funds, which is stated as a caveat).

Cost audit (before results): the schedule is checked against the observed tracking difference (index TRI return minus fund NAV return) of the oldest index funds in AMFI’s NAV history, by calendar year, from 2006. If the observed median for a class and era differs from the schedule by more than 0.25 points a year and at least three funds support it, the observed value replaces the schedule, and the change is logged under Amendments before any portfolio result is read.

8. Tax

The engine in lab/tax.py (rules in portfolio_library.md). The dated rules are in Appendix B. Headline after-tax results: resident individual, 30% slab plus cess, no surcharge (taxable income assumed under the surcharge threshold), equity exemption off, losses set off and carried forward, tax paid each financial year from the portfolio, and full liquidation at the end of the window. Sensitivity: exemption on (for a portfolio of Rs 10 lakh at the start), and a 20% slab investor.

9. Evaluation

For every portfolio on the headline window: pre-tax and after-tax CAGR; real after-tax CAGR (CPI-deflated); volatility, Sharpe and Sortino (monthly returns, over the T-bill); maximum drawdown with its peak, trough and recovery dates; the five worst drawdowns; time under water; Ulcer index; worst calendar year; worst 12 months, worst 3 years and worst real 5 years; rolling 3, 5 and 10-year CAGR (minimum, quartiles, maximum); calendar-year returns; tracking error and beta against 60/40; turnover a year; cost drag and tax drag in CAGR points; returns by regime (section 4); and the CAGR for every start month to the end of the window (start-date sensitivity).

“What would have hurt”: for each portfolio, the worst outcome a reader would have lived through: the deepest drawdown and how long it took to recover, the longest stretch it trailed 60/40, and the worst 5-year real return.

10. Multiple testing and robustness

11. Outputs

Derived tables in .cache/derived/lab_v2_*.parquet and bundles under lab/v2/*, documented in FRONTEND_HANDOFF.md (lane 2 section).

12. Known limits

Appendix A. Factor index launch dates

Filled 2026-10-02, before the first run, from NSE Indices factsheets (the header gives launch date, base date and base value; every base value is 1000) and the combined methodology document (niftyindices.com/Methodology/Method_NIFTY_Equity_Indices.pdf, September 2026 edition). FS/ is niftyindices.com/Factsheet/; arch/ is nsearchives.nseindia.com/content/indices/. Anything before the launch date is a back-test and is labelled so.

KeyIndexBase dateLaunch (live) dateSource
mom200Nifty200 Momentum 302005-04-012020-08-25FS/Factsheet_Nifty200_Momentum30.pdf
mom500Nifty500 Momentum 502005-04-012024-06-04FS/FactsheetNifty500Momentum50.pdf
val200Nifty200 Value 302005-04-012024-06-12FS/Factsheet_Nifty200_Value30.pdf
val500Nifty500 Value 502005-04-012018-10-24FS/FactsheetNIFTY500Value50.pdf
val50Nifty50 Value 202009-01-012014-03-28FS/Nifty50_Value20.pdf
qual200Nifty200 Quality 302005-04-012018-04-17FS/Factsheet_NIFTY200_Quality30.pdf
qual500Nifty500 Quality 502005-04-012024-12-20FS/Factsheet_Nifty500Quality50.pdf
lv100Nifty100 Low Volatility 302005-04-012016-07-08FS/Nifty100_LowVolatility30.pdf
lv500Nifty500 Low Volatility 502005-04-012024-12-20FS/Factsheet_Nifty500LowVolatility50.pdf
lv50Nifty Low Volatility 502003-12-312012-11-19FS/Factsheet_nifty_Low_Volatility50.pdf
alpha50Nifty Alpha 502003-12-312012-11-19FS/Factsheet_Nifty_Alpha50.pdf
hb50Nifty High Beta 502003-12-312012-11-19arch/Factsheet_nifty_High_Beta50.pdf
mqvlvNifty500 Multifactor MQVLv 502005-04-012025-02-06arch/Factsheet_Nifty500MultifactorMQVLv50.pdf
aqvlvNifty Alpha Quality Value Low-Volatility 302005-04-012017-07-03FS/Factsheet_NIFTY_Alpha_Quality_Value_Low-Volatility_30.pdf
n50ewNifty50 Equal Weight1995-11-032017-04-13 (press release dated 2017-04-18)FS/Factsheet_Nifty50_Equal_Weight.pdf
n100ewNifty100 Equal Weight2003-01-012013-06-10arch/Factsheet_Nifty_100_EW_Index.pdf
n500ewNifty500 Equal Weight2005-04-012024-05-31arch/Factsheet_Nifty_500_Equal_Weight.pdf
multicapNifty500 Multicap 50:25:252005-04-012020-12-02arch/Factsheet_Nifty500_Multicap_50_25_25_index.pdf
mid150Nifty Midcap 1502005-04-012016-04-01arch/ind_niftymidcap150.pdf
small250Nifty Smallcap 2502005-04-012016-04-01arch/ind_niftysmallcap250.pdf
n500Nifty 5001995-01-01not stated in the factsheet (the old CNX 500, live since the 1990s)FS/ind_Nifty_500.pdf

Methodology breaks: the low-volatility multi-factor family (including aqvlv) changed its quality definition and added an F&O-membership filter effective 2018-06-29 (NSE press release ind_prs15062018.pdf); val50 and aqvlv changed their value scores again from the June-July 2026 rebalance (ind_prs10062026.pdf). History before a break follows the old rules.

The live-only statistics (section 5.4) use these launch dates. Six of the fourteen factor indices went live after 2020, so their live samples are short; that is reported next to every live-only number.

Appendix B. Tax rules by date (as encoded in lab/tax.py)

Sources: Finance Acts as cited; incometaxindia.gov.in for the Cost Inflation Index; AMC tax reckoners (ICICI Prudential 2026-27) as a cross-check. From 1 April 2026 the Income-tax Act, 2025 replaces the 1961 Act and renumbers the sections (111A → 196, 112 → 197, 112A → 198, 50AA → 76(5)(b)) without changing these rates.

Equity-oriented funds (by sale date; long-term if held more than 12 months throughout):

Sale dateShort-termLong-term
to 30 Sep 2004slablower of 20% with indexation and 10% without
1 Oct 2004 to 31 Mar 200810%exempt
1 Apr 2008 to 31 Mar 201815%exempt
1 Apr 2018 to 22 Jul 202415%10% above Rs 1 lakh a year; cost of units bought by 31 Jan 2018 = max(actual cost, min(NAV on 31 Jan 2018, sale value))
from 23 Jul 202420%12.5% above Rs 1.25 lakh a year (the higher exemption applies to all of FY2024-25)

Debt funds, liquid funds, gilt funds, gold ETFs and gold funds of funds (by sale date):

Sale dateLong-term if held more thanLong-term rateShort-term
to 10 Jul 201412 monthslower of 20% with indexation and 10% withoutslab
11 Jul 2014 to 22 Jul 202436 months (no grandfathering of earlier purchases)20% with indexationslab
from 23 Jul 202412 months for listed units (ETFs), 24 months for others12.5%, no indexationslab

Amendments

2026-10-02, before any result was computed (warm-up conflicts found while wiring the rules to the data):

  1. mv_shrink: the mean excess return uses the last 60 months or all available history if shorter, with at least 36 months. Reason: the G-sec index starts in September 2001, so a strict 60-month window would not be warm until October 2006, after the headline window starts.
  2. valuation_cape and valuation_yield_gap: the expanding percentile needs 48 months of signal history, not 60. Reason: the IIMA Sensex CAPE10 starts in April 2000; with the one-month lag, 60 months are first available at the April 2005 month-end, one month after the window starts.
  3. Ensembles: a member model takes part from its first decision (equal weight across the members that have decided); in ensemble_inverse_vol each member’s volatility uses the trailing 252 sessions or its history since its first decision if shorter, with at least 21 sessions, and members without that history wait. Reason: CPPI’s floor is set at the window start, so it has no history before it.
  4. Clarification of mv_shrink: when no asset has a positive expected excess return it holds the long-only minimum-variance portfolio. Clarification of paa: only assets with positive momentum fill the top-3 slots; unfilled slots go to the safe asset.

None of these changes the headline window (2005-04-01 onward).

2026-10-02, cost audit (section 7), applied mechanically: lab/cost_audit.py measured the calendar-year tracking difference (index TRI return minus NAV return, growth option) of every AMFI index fund and ETF matched by name to a library index: direct plans and ETFs from 2013, all plans before (direct plans did not exist); fund-years with a gap above 5 points are dropped as mismatches or NAV errors. Gilt and liquid funds are not audited because they track no single index. Results, with the pre-registered rule (replace when the gap exceeds 0.25 points and at least three funds support it):

Cost classEraObserved medianFunds (fund-years)ScheduleReplaced?
broadto 20121.11%24 (74)0.75%yes, 1.11%
broad2013-20180.48%35 (138)0.30%no (gap 0.18)
broad2019 on0.27%71 (351)0.15%no (gap 0.12)
broad_small2013-20180.42%1 (1)0.50%no (one fund)
broad_small2019 on0.55%36 (104)0.30%yes, 0.55%
factor2013-20180.58%3 (6)0.90%yes, 0.58%
factor2019 on0.52%30 (85)0.75%no (gap 0.23)
goldto 20121.43%12 (31)1.00%yes, 1.43%
gold2013-20181.15%13 (44)0.75%yes, 1.15%
gold2019 on0.67%18 (76)0.50%no (gap 0.17)

Gold ETFs’ gap to the WGC domestic price includes differences in the reference price (LBMA fix, duty and conversion timing) as well as costs; the rule treats it all as cost, which is what an ETF holder experienced. Disclosure: before the audit, an engine smoke test printed pre-tax and after-tax CAGRs for 14 portfolios; the replacement above follows the pre-registered rule with no discretion, and no rule or parameter was changed after that smoke test.

Results log

2026-10-02: first run (2005-04-01 to 2026-09-30; rules as specified and amended above)

136 portfolios, all on the same window. CPI inflation over the window: 6.4% a year. Code: pipeline/tipsheet/lab/run.py; tables .cache/derived/lab_v2_*; bundles lab/v2/*. “Before tax” is after costs. Intervals are 90% stationary block-bootstrap intervals (mean block 12 months) on monthly returns.

PortfolioCAGR before taxAfter taxReal after taxVolatilitySharpeMax drawdownTax dragvs 60/40, 90% interval
Nifty 500 (all equity)13.112.75.821.90.38−64.20.42−2.7 to +4.9
60/4011.911.44.713.10.44−36.50.47n/a
60/20/2013.713.16.212.90.57−36.10.60+0.3 to +3.3
Equal thirds13.012.35.58.90.71−18.90.67−2.2 to +4.2
Permanent11.410.84.16.80.69−14.90.62−3.9 to +2.8
Golden butterfly13.312.75.89.90.67−23.40.63−0.7 to +3.4
All Weather (India)11.210.74.07.50.61−15.50.58−3.5 to +2.1
Endowment-style14.113.46.611.70.64−30.60.63+0.5 to +3.8
Four-factor blend16.415.98.920.70.53−58.20.47+0.8 to +8.5
ERC (risk parity)9.58.82.24.60.62−8.00.66−6.3 to +1.3
Minimum variance8.37.61.14.00.43−5.90.66−8.0 to +0.5
GTAA10.99.83.27.30.59−18.01.10−4.8 to +2.7
Dual momentum13.412.05.218.10.43−45.31.42−5.1 to +8.4
PAA13.712.55.714.30.53−36.61.23−1.4 to +5.2
DAA14.913.06.216.30.54−45.11.84−0.3 to +6.5
VAA12.310.13.414.80.43−31.22.26−3.3 to +4.1
BAA10.68.62.013.10.35−35.32.06−5.1 to +2.3
Valuation glide (CAPE)11.010.23.59.60.47−15.70.86−3.6 to +1.7
Ensemble (equal)11.510.13.58.70.57−25.11.39−2.5 to +1.5

All figures in percent (Sharpe is a ratio; the last column is the bootstrap interval of the difference in annualised return before tax, in points).

What the record says.

Luck and overfitting.

Predictions.

2026-10-03: audit corrections (register docs/research/AUDIT.md, rows L6, L8, L14, L18, V3, V7, D4); rules unchanged

What changed:

Verdicts:

This is docs/research/portfolio_lab_v2_spec.md. The specification was committed before any result was computed; changes after that are logged in it with dates and reasons.