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
- 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?
- 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?
- Did factor indices beat their parent index after realistic fund costs, and how much of their record is back-tested history from before launch?
- Did risk-based and tactical rules beat a static mix of the same assets after costs and tax?
- 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)
- P1. No tactical rule beats the static equal-weight mix of the same assets after tax, with a 90% block-bootstrap interval that excludes zero.
- P2. Adding 10% to 30% gold to an equity / G-sec mix raised the Sharpe ratio over the window, but the bootstrap interval for the CAGR difference includes zero.
- P3. Risk-based portfolios have smaller drawdowns and lower after-tax CAGR than 60/40.
- P4. Each factor index did worse after its launch date than in its back-tested history before launch, measured as annualised return over the parent index.
- P5. The probability of backtest overfitting (CSCV) for “pick the best portfolio in the library” is above 0.3.
- P6. Tax costs the tactical rules more than 1.5 percentage points a year of CAGR, against under 0.8 for annually rebalanced static mixes.
3. Assets
All total return, in rupees, on the NSE trading calendar. Levels never leave the pipeline; only rebased curves and statistics are published.
| Key | Asset | Source | Tax class | Cost class | First date |
|---|---|---|---|---|---|
n50, n100, n500, next50 | Nifty 50, 100, 500, Next 50 TRIs | index_tri / asset_tri_daily (repaired against the price index) | equity | broad | 1999 to 2003 |
mid100, small100 | Nifty Midcap 100, Smallcap 100 TRIs | same | equity | broad_small | 2003-01, 2004-01 |
mid150, small250 | Nifty Midcap 150, Smallcap 250 TRIs | same | equity | broad_small | 2005-04 |
multicap | Nifty500 Multicap 50:25:25 TRI (stands in for the total market: the Nifty Total Market TRI exists in our data only from 2026-06) | same | equity | broad_small | 2005-04 |
n50ew, n100ew, n500ew | Equal-weight Nifty 50, 100, 500 TRIs | same | equity | broad_small | 1995, 2003, 2005 |
| factor indices | listed in section 5.4 | same | equity | factor | 2005-04 (mostly back-tested) |
gsec5 | NSE 5-year benchmark G-sec index | asset_tri_daily | debt | gilt | 2001-09 |
gsec10 | Synthetic 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. | derived | debt | gilt | 2001-05 |
gold | Domestic gold in rupees including import duty (WGC INR; USD gold × USD/INR before 2005) | asset_tri_daily | gold | gold | 1995 |
cash | Accrued 91-day T-bill yield, standing in for a liquid fund | asset_tri_daily | debt | liquid | 1993 |
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
- Headline window: the first session of April 2005 (2005-04-01, which is also the start of FY2005-06) to the latest date common to all assets. Every portfolio in the library is evaluated on this window, so all comparisons are like for like. Rules may use data from before the window to warm up their signals.
- Long window: 2002-10-01 onward, for the portfolios that use only
n500,gsec5,gold,cashandgsec10(static mixes, both grids, risk-based, GTAA, dual momentum, volatility-managed, CPPI, 60/40 with trend). It is reported as a robustness check, not as the headline. - Live-only window: for each factor index, from its launch date (section 5.4), compared with its parent index over the same dates.
- Sub-periods (regime splits): FY2006 to FY2008 (the boom), April 2008 to March 2009 (the crisis), FY2010 to FY2014, FY2015 to FY2020 (to March 2020), and April 2020 onward. Also halves of the headline window.
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
| Code | Name and source | Indian construction | Substitutions disclosed |
|---|---|---|---|
sixty_forty | 60/40 | n500 60, gsec5 40 | none |
sixty_twenty_twenty | 60/20/20 | n500 60, gsec5 20, gold 20 | none |
equal_thirds | Equal thirds | n500, gsec5, gold at 33.3 each | none |
permanent | Permanent portfolio (Browne 1987, Why the Best-Laid Investment Plans Usually Go Wrong) | n500 25, gsec10 25, gold 25, cash 25 | long bond is the synthetic 10-year G-sec (Browne used long Treasuries) |
golden_butterfly | Golden butterfly (Tyler, Portfolio Charts) | n500 20, small250 20, gsec10 20, cash 20, gold 20 | small-cap value becomes Nifty Smallcap 250 (no small-cap value index); short-term Treasuries become the T-bill |
all_weather | All 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 15 | commodities go into gold (no Indian commodity total-return series). This is an approximation of a published retail version, not Bridgewater’s fund |
three_fund | Three-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 |
endowment | Endowment-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 15 | the 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.
| Code | Rule | Source |
|---|---|---|
one_over_n | 1/3 each, rebalanced monthly | DeMiguel, Garlappi and Uppal (2009) |
inverse_vol | weights proportional to 1/σ | common practice |
erc | equal risk contribution | Maillard, Roncalli and Teïletche (2010) |
min_variance | long-only minimum variance on the Ledoit-Wolf shrunk covariance | Clarke, de Silva and Thorley (2006); Ledoit and Wolf (2004) |
max_diversification | maximise (w·σ) / √(w’Σw), long-only | Choueifaty and Coignard (2008) |
hrp | hierarchical risk parity, single linkage on the correlation distance, recursive bisection | López de Prado (2016) |
mv_shrink | long-only maximum Sharpe using the Ledoit-Wolf covariance and the trailing 60-month mean excess return of each asset | Markowitz (1952); Ledoit and Wolf (2004) |
black_litterman | Black-Litterman with no views, reference portfolio 60/40 (n500/gsec5), δ = 2.5, τ = 0.05 | Black 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)
| Code | Rule as published | Indian adaptation |
|---|---|---|
gtaa | Faber (2007): each of N sleeves is held when its month-end price is above its 10-month average, else cash | sleeves n500, gsec5, gold, 1/3 each |
sixty_forty_trend | v1 rule: 60/40 with the equity sleeve in cash below its 10-month average | unchanged from v1 |
dual_momentum | Antonacci (2014) GEM: the better of home and foreign equity over 12 months if it beats T-bills, else bonds | home n500, “foreign” leg replaced by gold, bonds gsec5 (as v1) |
paa | Keller 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 momentum | risky N = 5: n50, next50, mid100, small100, gold; T = 3; safe asset gsec5 |
vaa | Keller 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 defensive | offensive n50, mid100, small100, gold; defensive gsec5, cash |
daa | Keller 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 13612W | canaries 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 |
baa | Keller (2022) BAA-G4: if any canary is negative by 13612W, go to the top defensive by price/SMA12; else the top offensive by price/SMA12 | canaries 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_managed | Moreira 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 |
cppi | Perold 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_cape | Valuation 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 gsec5 | uses the previous month-end CAPE (one-month publication lag); percentile needs 60 months of history |
valuation_yield_gap | Same 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
ensemble_equal: equal weight of the target weights of every rule in 5.5 and 5.6 (19 rules), recombined each month.ensemble_inverse_vol: weights proportional to 1/σ of each rule’s pre-tax paper return over the trailing 252 sessions.
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 class | to 2012 | 2013 to 2018 | 2019 on | Trade 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% |
| factor | 1.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% |
| liquid | 0.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
- Number of trials: every portfolio in sections 5.1 to 5.7, counted once (about 140). Rebalancing and tax sensitivities are not counted as separate trials, because none is used to pick a winner.
- Deflated Sharpe ratio (Bailey and López de Prado 2014) for each portfolio, using the number of trials and the cross-sectional variance of their Sharpe ratios, with skew and kurtosis of each portfolio’s monthly excess returns.
- PBO by CSCV (Bailey, Borwein, López de Prado and Zhu 2017): the monthly after-cost excess-return matrix split into 16 blocks; for each of the 12,870 combinations, pick the best in-sample portfolio by Sharpe and record its out-of-sample rank. Reported for the whole library and separately for the tactical family and the multi-asset grid.
- Block bootstrap: stationary bootstrap (Politis and Romano 1994), mean block 12 months, 5,000 resamples, for each portfolio’s CAGR and Sharpe and for its difference from 60/40 (paired, same resampled months). 90% intervals.
- Walk-forward selection: at each January from 2010, pick the multi-asset grid mix with the best trailing 5-year Sharpe and hold it for the year; compare the chained out-of-sample record with the in-sample best and with equal thirds. This shows what “pick the past winner” would have earned.
- Start-date sensitivity: CAGR from every start month to the end, and the share of 5-year windows in which each portfolio beat 60/40 (overlapping windows; the number of independent 5-year periods is about 4, and is stated next to the share).
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
- About 21 years in the headline window: three or four equity cycles at most. Differences between sensible portfolios are rarely significant.
- Domestic-only: a reader allocating globally would see different numbers.
- One bond maturity is real (5-year); the 10-year is synthetic.
- Factor indices are mostly back-tested before about 2015 to 2020.
- Gold’s record includes a long rupee depreciation and the 2012-13 import-duty increases.
- Costs before 2013 are estimates of regular-plan index funds and early ETFs.
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.
| Key | Index | Base date | Launch (live) date | Source |
|---|---|---|---|---|
mom200 | Nifty200 Momentum 30 | 2005-04-01 | 2020-08-25 | FS/Factsheet_Nifty200_Momentum30.pdf |
mom500 | Nifty500 Momentum 50 | 2005-04-01 | 2024-06-04 | FS/FactsheetNifty500Momentum50.pdf |
val200 | Nifty200 Value 30 | 2005-04-01 | 2024-06-12 | FS/Factsheet_Nifty200_Value30.pdf |
val500 | Nifty500 Value 50 | 2005-04-01 | 2018-10-24 | FS/FactsheetNIFTY500Value50.pdf |
val50 | Nifty50 Value 20 | 2009-01-01 | 2014-03-28 | FS/Nifty50_Value20.pdf |
qual200 | Nifty200 Quality 30 | 2005-04-01 | 2018-04-17 | FS/Factsheet_NIFTY200_Quality30.pdf |
qual500 | Nifty500 Quality 50 | 2005-04-01 | 2024-12-20 | FS/Factsheet_Nifty500Quality50.pdf |
lv100 | Nifty100 Low Volatility 30 | 2005-04-01 | 2016-07-08 | FS/Nifty100_LowVolatility30.pdf |
lv500 | Nifty500 Low Volatility 50 | 2005-04-01 | 2024-12-20 | FS/Factsheet_Nifty500LowVolatility50.pdf |
lv50 | Nifty Low Volatility 50 | 2003-12-31 | 2012-11-19 | FS/Factsheet_nifty_Low_Volatility50.pdf |
alpha50 | Nifty Alpha 50 | 2003-12-31 | 2012-11-19 | FS/Factsheet_Nifty_Alpha50.pdf |
hb50 | Nifty High Beta 50 | 2003-12-31 | 2012-11-19 | arch/Factsheet_nifty_High_Beta50.pdf |
mqvlv | Nifty500 Multifactor MQVLv 50 | 2005-04-01 | 2025-02-06 | arch/Factsheet_Nifty500MultifactorMQVLv50.pdf |
aqvlv | Nifty Alpha Quality Value Low-Volatility 30 | 2005-04-01 | 2017-07-03 | FS/Factsheet_NIFTY_Alpha_Quality_Value_Low-Volatility_30.pdf |
n50ew | Nifty50 Equal Weight | 1995-11-03 | 2017-04-13 (press release dated 2017-04-18) | FS/Factsheet_Nifty50_Equal_Weight.pdf |
n100ew | Nifty100 Equal Weight | 2003-01-01 | 2013-06-10 | arch/Factsheet_Nifty_100_EW_Index.pdf |
n500ew | Nifty500 Equal Weight | 2005-04-01 | 2024-05-31 | arch/Factsheet_Nifty_500_Equal_Weight.pdf |
multicap | Nifty500 Multicap 50:25:25 | 2005-04-01 | 2020-12-02 | arch/Factsheet_Nifty500_Multicap_50_25_25_index.pdf |
mid150 | Nifty Midcap 150 | 2005-04-01 | 2016-04-01 | arch/ind_niftymidcap150.pdf |
small250 | Nifty Smallcap 250 | 2005-04-01 | 2016-04-01 | arch/ind_niftysmallcap250.pdf |
n500 | Nifty 500 | 1995-01-01 | not 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 date | Short-term | Long-term |
|---|---|---|
| to 30 Sep 2004 | slab | lower of 20% with indexation and 10% without |
| 1 Oct 2004 to 31 Mar 2008 | 10% | exempt |
| 1 Apr 2008 to 31 Mar 2018 | 15% | exempt |
| 1 Apr 2018 to 22 Jul 2024 | 15% | 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 2024 | 20% | 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 date | Long-term if held more than | Long-term rate | Short-term |
|---|---|---|---|
| to 10 Jul 2014 | 12 months | lower of 20% with indexation and 10% without | slab |
| 11 Jul 2014 to 22 Jul 2024 | 36 months (no grandfathering of earlier purchases) | 20% with indexation | slab |
| from 23 Jul 2024 | 12 months for listed units (ETFs), 24 months for others | 12.5%, no indexation | slab |
- Section 50AA (from 1 April 2023): units of a “specified mutual fund” bought on or after 1 April 2023 are taxed at slab whatever the holding period. Until 31 March 2025 this covered every fund with 35% or less in Indian equity (debt, liquid, gold, international). For sales from 1 April 2025 the definition is a fund with more than 65% in debt and money-market instruments, so gold ETFs and gold funds of funds bought after April 2023 return to the normal rules above, while debt, gilt and liquid funds stay at slab.
- Vehicle assumption: the gold sleeve is a gold ETF (listed); the gilt and liquid sleeves are open-ended funds (unlisted units).
- Cess: 2% from FY2004-05, 3% from FY2007-08, 4% from FY2018-19. No cess before FY2004-05. Surcharge is ignored.
- Cost Inflation Index (2001-02 = 100): 100, 105, 109, 113, 117, 122, 129, 137, 148, 167, 184, 200, 220, 240, 254, 264, 272, 280, 289, 301, 317, 331, 348, 363, 376 (2025-26), 384 (2026-27, S.O. 3889(E)).
- Set-off: short-term losses against any gain; long-term losses only against long-term gains; unabsorbed losses carried forward eight years. Exempt long-term equity gains (2004 to 2018) cannot absorb losses, and long-term equity losses in that era could not be set off either.
Amendments
2026-10-02, before any result was computed (warm-up conflicts found while wiring the rules to the data):
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.valuation_capeandvaluation_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.- Ensembles: a member model takes part from its first decision (equal weight across the members that have decided); in
ensemble_inverse_voleach 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. - Clarification of
mv_shrink: when no asset has a positive expected excess return it holds the long-only minimum-variance portfolio. Clarification ofpaa: 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 class | Era | Observed median | Funds (fund-years) | Schedule | Replaced? |
|---|---|---|---|---|---|
| broad | to 2012 | 1.11% | 24 (74) | 0.75% | yes, 1.11% |
| broad | 2013-2018 | 0.48% | 35 (138) | 0.30% | no (gap 0.18) |
| broad | 2019 on | 0.27% | 71 (351) | 0.15% | no (gap 0.12) |
| broad_small | 2013-2018 | 0.42% | 1 (1) | 0.50% | no (one fund) |
| broad_small | 2019 on | 0.55% | 36 (104) | 0.30% | yes, 0.55% |
| factor | 2013-2018 | 0.58% | 3 (6) | 0.90% | yes, 0.58% |
| factor | 2019 on | 0.52% | 30 (85) | 0.75% | no (gap 0.23) |
| gold | to 2012 | 1.43% | 12 (31) | 1.00% | yes, 1.43% |
| gold | 2013-2018 | 1.15% | 13 (44) | 0.75% | yes, 1.15% |
| gold | 2019 on | 0.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.
| Portfolio | CAGR before tax | After tax | Real after tax | Volatility | Sharpe | Max drawdown | Tax drag | vs 60/40, 90% interval |
|---|---|---|---|---|---|---|---|---|
| Nifty 500 (all equity) | 13.1 | 12.7 | 5.8 | 21.9 | 0.38 | −64.2 | 0.42 | −2.7 to +4.9 |
| 60/40 | 11.9 | 11.4 | 4.7 | 13.1 | 0.44 | −36.5 | 0.47 | n/a |
| 60/20/20 | 13.7 | 13.1 | 6.2 | 12.9 | 0.57 | −36.1 | 0.60 | +0.3 to +3.3 |
| Equal thirds | 13.0 | 12.3 | 5.5 | 8.9 | 0.71 | −18.9 | 0.67 | −2.2 to +4.2 |
| Permanent | 11.4 | 10.8 | 4.1 | 6.8 | 0.69 | −14.9 | 0.62 | −3.9 to +2.8 |
| Golden butterfly | 13.3 | 12.7 | 5.8 | 9.9 | 0.67 | −23.4 | 0.63 | −0.7 to +3.4 |
| All Weather (India) | 11.2 | 10.7 | 4.0 | 7.5 | 0.61 | −15.5 | 0.58 | −3.5 to +2.1 |
| Endowment-style | 14.1 | 13.4 | 6.6 | 11.7 | 0.64 | −30.6 | 0.63 | +0.5 to +3.8 |
| Four-factor blend | 16.4 | 15.9 | 8.9 | 20.7 | 0.53 | −58.2 | 0.47 | +0.8 to +8.5 |
| ERC (risk parity) | 9.5 | 8.8 | 2.2 | 4.6 | 0.62 | −8.0 | 0.66 | −6.3 to +1.3 |
| Minimum variance | 8.3 | 7.6 | 1.1 | 4.0 | 0.43 | −5.9 | 0.66 | −8.0 to +0.5 |
| GTAA | 10.9 | 9.8 | 3.2 | 7.3 | 0.59 | −18.0 | 1.10 | −4.8 to +2.7 |
| Dual momentum | 13.4 | 12.0 | 5.2 | 18.1 | 0.43 | −45.3 | 1.42 | −5.1 to +8.4 |
| PAA | 13.7 | 12.5 | 5.7 | 14.3 | 0.53 | −36.6 | 1.23 | −1.4 to +5.2 |
| DAA | 14.9 | 13.0 | 6.2 | 16.3 | 0.54 | −45.1 | 1.84 | −0.3 to +6.5 |
| VAA | 12.3 | 10.1 | 3.4 | 14.8 | 0.43 | −31.2 | 2.26 | −3.3 to +4.1 |
| BAA | 10.6 | 8.6 | 2.0 | 13.1 | 0.35 | −35.3 | 2.06 | −5.1 to +2.3 |
| Valuation glide (CAPE) | 11.0 | 10.2 | 3.5 | 9.6 | 0.47 | −15.7 | 0.86 | −3.6 to +1.7 |
| Ensemble (equal) | 11.5 | 10.1 | 3.5 | 8.7 | 0.57 | −25.1 | 1.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.
- The equity/debt frontier is smooth and monotone. From 100% G-sec (6.5% after tax, 0.1% real) to 100% Nifty 500 (12.7%, 5.8% real), each extra 10 points of equity added between 1.0 point of after-tax CAGR (at low equity shares) and 0.1 point (near 100%), and 5 to 7 points of maximum drawdown. Sharpe ratios were flat (0.38 to 0.46) from 20% to 100% equity: no mix was a free lunch.
- Gold earned its place in this sample. Every three-asset mix with 10% to 30% gold beat the equity/G-sec mix with the same equity share, by about 0.8 points of CAGR per 10 points of gold, with bootstrap intervals above zero (see P2). The best Sharpe ratios in the library came from mixes of all three assets (equal thirds and the grid mixes with 20% to 40% equity and 30% to 50% gold, all 0.71 to 0.72). This rests on one 21-year sample in which rupee gold compounded at 15.9% a year before costs (14.7% through an ETF).
- The named portfolios are mostly 60/40 with gold. Permanent, All Weather, golden butterfly and equal thirds cut drawdowns to between 15% and 23%, against 36% for 60/40, at a similar or slightly lower after-tax return. The three-fund portfolio is identical to 60/40 here (no international sleeve).
- Factor indices beat the Nifty 500 on paper, mostly before they existed. Momentum 500 (19.7% after tax), multi-factor MQVLv (17.6%) and Alpha 50 (17.2%) top the equity list, but these records are back-tested before launch. In live data, momentum 200 beat its parent by 1.2 points a year (against 6.1 in its back-test), quality 200 lagged by 1.3 (against +5.8), and momentum 500 has lagged by 5.6 points since its June 2024 launch. Low volatility kept most of its edge (Low Volatility 50: +2.0 points a year live since 2012). High beta lost 3.8 points a year live and 12 in its back-test.
- Risk-based portfolios bought safety at a steep price. Inverse volatility, ERC, maximum diversification, minimum variance and HRP had drawdowns of only 6% to 9%, but after-tax returns of 7.2% to 8.9%, 2.5 to 4.2 points below 60/40. With G-secs the least volatile asset by far, every risk-based rule ended up 70% to 80% in bonds. 1/N rebalanced monthly (11.9%) beat every optimiser, as DeMiguel et al. found in the US. Black-Litterman without views reproduced 60/40’s weights exactly; its lower return (10.8%) is the cost of rebalancing monthly instead of yearly.
- Tactical rules lost to tax and to their own static mixes. Tax cost them 1.1 to 2.3 points a year (VAA, BAA and DAA traded 5 to 8 times a year). None beat the static equal-weight mix of its own assets after tax. The volatility-managed and CPPI rules trailed buy-and-hold equity by 2.6 and 1.6 points. CPPI with a floor that never resets became plain equity after the 2005-07 rally and fell 64% in 2008.
- Rebalancing once a year beat monthly, quarterly, band-based and no rebalancing for all four portfolios tested, by 0.3 to 0.6 points before tax (60/40: 11.9% annual, 11.3% monthly). This is consistent with momentum in asset-class returns at horizons under a year, but it is one sample.
- Tax drag was 0.4 to 0.7 points a year for static mixes and buy-and-hold, with buy-and-hold equity the lowest thanks to the 2018 grandfathering. Switching on the annual equity exemption for a Rs 10 lakh portfolio, or moving to a 20% slab, changed after-tax CAGRs by under 0.25 points for every static portfolio.
Luck and overfitting.
- PBO (CSCV, 16 blocks, 12,870 splits): 0.65 for the whole library, 0.71 for the tactical family, 0.52 for risk-based and tactical together, 0.44 for the three-asset grid and 0.45 for the factor family. Picking the best-looking portfolio in-sample gives a below-median out-of-sample rank more often than not.
- Deflated Sharpe against 60/40: no portfolio reaches 0.5. The highest are MQVLv (0.39), momentum 500 (0.26), the four-factor blend (0.22) and low volatility 500 (0.18), all back-tested factor indices. None of the differences survive 136 trials.
- Walk-forward: picking each January the three-asset mix with the best trailing 5-year Sharpe earned 8.2% a year from 2011 to 2026, against 10.9% for equal thirds held throughout and 9.6% for the mix that turned out best in hindsight (edg 20/60/20).
- The long window (from October 2002, 98 portfolios) gives the same ordering: 60/20/20 at 15.0% after tax against 13.5% for 60/40; equal thirds 13.2%; ERC 9.1%; GTAA 10.0%.
Predictions.
- P1 holds. No tactical or risk-based rule beat the static equal-weight mix of its own assets after tax with an interval above zero. Every after-tax difference is negative (−0.1 to −5.1 points). Six risk-based rules (all but 1/N and Black-Litterman) and three tactical rules (GTAA, volatility-managed, CPPI) are significantly worse.
- P2 half holds. Gold raised the Sharpe ratio in every one of the 15 pairs. But the CAGR difference was not inside a zero-straddling interval: adding 10, 20 or 30 points of gold raised CAGR by 0.9, 1.8 and 2.6 points, with lower bounds of 0.15, 0.28 and 0.39. We predicted the return gain would be indistinguishable from zero; in this sample it was not. That is a statement about 2005-2026 rupee gold, not a forecast.
- P3 mostly holds: for inverse volatility, ERC, minimum variance, maximum diversification and HRP. It fails for 1/N (higher after-tax CAGR than 60/40, 11.9 against 11.4), and for mean-variance and Black-Litterman (deeper drawdowns than 60/40, because mean-variance chased gold and equity and Black-Litterman is 60/40 rebalanced monthly).
- P4 fails as a universal claim. Nine of thirteen factor indices did worse against their parent after launch than in their back-test (momentum 200 and 500, value 20, quality 200 and 500, the three low-volatility indices except Low Vol 500, MQVLv, AQVLV). Value 200, Value 500, Alpha 50 and Low Vol 500 did better live. Live samples are short for six indices launched after 2020.
- P5 holds: PBO for the whole library is 0.65.
- P6 partly holds. Static annually rebalanced mixes all paid under 0.8 points (0.42 to 0.67). But only VAA, BAA and DAA paid more than 1.5 points; dual momentum (1.42), yield-gap glide (1.29), PAA (1.23), GTAA (1.10), CAPE glide (0.86), volatility-managed (0.61), 60/40 with trend (0.53) and CPPI (0.45) paid less.
2026-10-03: audit corrections (register docs/research/AUDIT.md, rows L6, L8, L14, L18, V3, V7, D4); rules unchanged
What changed:
- The yield-gap signal (L6, V3, V7).
valuation_yield_gapread the Nifty 500 earnings yield from NSE’s raw P/E, which steps up about 20% on 2021-03-31. It read it from a copy of the valuation table that had stopped updating on 2026-09-28. Both are fixed: the signal is on the consolidated-basis P/E and reads the maintained table.- Results:
valuation_yield_gapafter-tax CAGR 10.74% → 10.32%; pre-tax 12.03% → 11.42%; tax drag 1.29 → 1.09; share of 5-year windows ahead of 60/40 56% → 42%. - P1 row: the after-tax gap to its static mix is −0.11 → −0.50 points; pre-tax interval −1.53 to +1.72.
- Results:
- Cash (D4). The 91-day T-bill index compounded a simple 91-day yield daily (about 6 bp a year too much at 7%). It now compounds per bill. Every portfolio moved by under 0.1 point (median 0.02). 60/40 after tax 11.44% → 11.40%.
- Labels (L8, L14). Every bootstrap interval in the lab is on returns after costs and before tax. The bundles now carry
meta.interval_basissaying so. After-tax differences are point estimates with no interval.- Dual momentum shows why it matters: +0.42 before tax, −0.34 after.
- Overlap disclosure (L18).
independent_5y_periods(4 for the 2005–2026 window) is now published besideshare_5y_beating_60_40_pct, as this spec promised.
Verdicts:
- P1 holds: every tactical and risk-based rule trails its static mix after tax, and no pre-tax interval lies above zero.
- P2 to P6: unchanged. P5’s PBO is 0.65 → 0.65.
- P6: tactical tax drag is 0.52–2.25 points.