Data to 8 October 2026
About

Pre-registered research

Tipsheet models, v1: pre-registered specification

Written 2026-10-08, before any result for the new rules in section 4 was computed. It follows the proposal in model_library_proposal.md (2026-10-03) and the owner’s decisions of 2026-10-08:

If a rule changes after its results are seen, the change is logged under Amendments with the date and the reason, and the original result is kept.

What is already known, stated up front. I have already seen the results of every rule in section 3: lab v2’s results log (2026-10-02, corrected 2026-10-03) and the trend and sector logs in trend_momentum_spec.md. Those rules are promoted to the model board unchanged. Their backtests are not new evidence, and their entry dates stay the dates of their original specs.

1. What this adds

  1. A model board. Each model’s target today, the date of its last change, and how close it is to switching. The same board appears in compact form on the homepage.
  2. A live record. From each model’s entry date, an append-only log of its daily return and holdings that is never recomputed (section 6).
  3. New rules (section 4):
    • global dual momentum and global GTAA, with the S&P 500 and the Nasdaq-100 in rupees;
    • size rotation;
    • sector rotation v2 on a de-duplicated sector list;
    • the house trend model, local and global (five or six markets, each sized by its trend score), and the trend score as a graded Nifty 50 exposure.
  4. A “didn’t survive” shelf for retired and failed models, with the reason and the evidence.

Out of scope: stock-level models, leverage, shorting, and anything driven by Fear & Greed or flows (twelve pre-registered versions of the mood index failed, market_mood_spec.md).

2. Three dates per model

Every model carries:

Charts shade three periods differently:

A table gives each model’s return in each period beside the full history. The return after the entry date is the only one that is not a backtest, and it is published that way.

3. Promoted unchanged (already registered and already run)

Board codeRuleSpec and codeSource dateEntry date
n50_sma10mNifty 50, 10-month ruletrend_momentum_spec.md, compute/trend.pyFaber 20072026-10-01
n50_sma200dNifty 50, 200-day rulesameBrock et al. 19922026-10-01
n50_blendNifty 50, 1/3/6/12-month blendsameHurst et al. 20172026-10-01
mid150_sma200d, small250_sma200d200-day rule on mid and small capssameBrock et al. 19922026-10-01
gtaaFaber GTAA, domestic: Nifty 500, 5-year G-sec, goldportfolio_lab_v2_spec.md 5.6, lab/rules.pyFaber 20072026-10-02
dual_momentumDual momentum, domestic: Nifty 500 vs gold, else G-secsameAntonacci 20122026-10-02
paa, daa, vaa, baaKeller and Keuning familysame2016, 2018, 2017, 20222026-10-02
vol_managed, cppiVolatility-managed equity; CPPIsame2017; 19882026-10-02
valuation_cape, valuation_yield_gapValuation glidessameCampbell and Shiller 19982026-10-02
sixty_forty_trend60/40 with a trend filter on equitysame2026-10-01 (v1)2026-10-02
ensemble_equalEqual blend of the lab’s rulessamen/a2026-10-02
Benchmarks60/20/20, 60/40, permanent, equal thirdssameBogle 1994; Browne 19872026-10-02
RetiredSector momentum v1 (mom12_1, mom6_1)trend_momentum_spec.mdJegadeesh and Titman 19932026-10-01

Local twins. The domestic gtaa and dual_momentum are first-class board models, not footnotes. Each global rule in section 4 has a local twin built only from Indian assets: gtaa for gtaa_global, dual_momentum for gem_spx and gem_ndx, and trend_local for trend_global. The board shows each pair side by side (section 5), so a reader who doesn’t use LRS has a model to follow, and every reader can see what the foreign leg added or cost.

4. New rules

All rules here:

4.1 Global legs

Assets added to the lab universe:

KeySeriesSourceFirst date
spx_inrS&P 500 (price) in rupeeskite_global_index_history US500 to 2016-05-30, then FRED SP500. This is the same splice as compute/world.py; Kite equals the official close on all 2,125 overlapping days.2004-01-02
ndx_inrNasdaq-100 (price) in rupeesnasdaq_giw_index_history NDX (official)1990
ivv_inriShares S&P 500 ETF, total return, in rupeesetf_issuer_nav_history IVV, distributions reinvested (world.total_return_index)2000-05-15

How the foreign sleeve is held (LRS, direct, a US-listed index ETF):

Rules:

CodeRuleSource
gem_spxIf Nifty 500’s 12-month total return beats cash’s, hold whichever of n500 and spx_inr has the higher 12-month return. Otherwise hold gsec5.Antonacci 2012 (GEM); home equity is the absolute-momentum test, as in the original
gem_ndxAs gem_spx, with ndx_inr as the foreign legSame; Nasdaq-100 is the foreign index most Indian investors actually buy
gtaa_globalFour sleeves of 25%: n500, spx_inr, gsec5, gold. Each sleeve is held when its month-end level is above its 10-month average, otherwise it goes to cash.Faber 2007. No Indian REIT or broad commodity series is long enough, so gold stands in for commodities and there is no real-estate sleeve

Static counterparts (for prediction P1):

All are rebalanced each January, as in the lab.

Window:

4.2 Size rotation

CodeRuleSource
size_dmIf Nifty 500’s 12-month total return beats cash’s, hold the one of n50, mid150 and small250 with the highest 12-month return. Otherwise hold gsec5.Antonacci’s dual momentum, applied to size segments

4.3 Sector rotation, v2

v1 failed. mom12_1 trailed equal weight, 14.5% against 15.5% with a deflated Sharpe of 0.33; mom6_1 led but was not significant (DSR 0.66). v1’s log noted that NSE’s sectoral group contains near-duplicates (Bank, Private Bank and Financial Services), so the top three could be one bet held three times. v2 fixes the universe in advance.

Universe: one index per economic sector, the broadest Nifty version of each. Twelve sectors:

Dropped as sub-sets or variants of a kept sector:

The list is fixed here. A sector NSE adds later joins only by amendment.

Rules:

CodeRule
sec_mom_v2At each month-end, rank eligible sectors by total return over months t−12 to t−1. Hold the top 3 equally for the next month. This is v1’s rule on the new universe.
sec_dm_v2As sec_mom_v2, but a top-3 slot goes to cash when that sector’s t−12 to t−1 return is below cash’s over the same months (absolute momentum per slot)

Two universes per rule, both counted as trials:

Benchmarks: equal weight over the same eligible universe with the same costs, and Nifty 500.

Costs: sector index funds and ETFs at the lab’s factor cost class. Taxed as equity.

Rotation view (not a test): /models/ also gets a descriptive sector rotation chart: each sector’s 6-month return relative to Nifty 500 against its 1-month change, with a 12-week tail. It carries no claim and is labelled as a description.

4.4 Trend models: the flagship and the single-market version

The trend family’s headline is a multi-asset trend model, the house trend model. It is the classic diversified trend-following design (Hurst, Ooi and Pedersen 2017, long-only): many markets, each sized by its own trend, with what isn’t invested held in cash. Single-market rules such as n50_sma10m stay on the board as the simple reference.

All three rules use the pre-registered trend score of trend_barometer_spec.md: 24 votes on the market’s level over cash, as a share from 0 to 100.

CodeRuleSource
trend_localFive equal sleeves of 20%: n50, mid150, small250, gold, gsec5. Each sleeve’s exposure is its trend score ÷ 100, and the rest of the sleeve is in cash. Decided weekly at each week’s last session.Hurst, Ooi and Pedersen 2017 (long-only); score from trend_barometer_spec.md
trend_globalAs trend_local with a sixth sleeve, spx_inr; six equal sleeves of 1/6Same
n50_trend_scoreNifty 50 alone: exposure = its trend score ÷ 100, the rest in cash, decided weeklySame

5. What is reported

For every model in sections 3 and 4:

Trial count for the deflated Sharpe:

The board:

Homepage: the aggregate equity share and a compact board with one row per model family. A family that has a local and a global version shows both in two columns, “India only” and “With global”:

RowIndia onlyWith global
Trendtrend_localtrend_global
Nifty 50 trendn50_sma10mn/a
GTAAgtaagtaa_global
Dual momentumdual_momentumgem_spx
Size rotationsize_dmn/a
Sector rotationsec_dm_v2 (live-only universe)n/a
Valuation glidevaluation_capen/a
Benchmark60/20/20n/a

The homepage shows no backtest curve.

6. The live record

7. Predictions (graded in the results log)

8. Known limits

9. Build list

  1. lab/universe.py: spx_inr, ndx_inr and ivv_inr with the one-session lag; cost class lrs; tax class foreign. Test that a month-end decision never reads a US close dated on or after the NSE decision date.
  2. lab/tax.py: the foreign class with worked examples.
  3. lab/rules.py: generalise _dual_momentum (home, contenders, absolute test, fallback as parameters) for gem_* and size_dm; a cross-sectional sector kind for sec_*_v2; weekly decision dates and the 5-point band for trend_local, trend_global and n50_trend_score, with the look-ahead scramble test extended to them.
  4. models/registry.yaml: one entry per board model, with its code, its lab or trend rule, its three dates, its static counterpart and its shelf status.
  5. The live log (section 6), with a test that a rewritten past row fails the chain check.
  6. Bundles models/board, models/aggregate and models/<code>; the licence-gate exception, with tests.
  7. Site: /models/ (board, aggregate, rotation view, shelf), /models/<code>/, and the homepage board. Today /models/ redirects to /trend/evidence/; it becomes the board, with links to the evidence pages.
  8. Docs: docs/methods/models.md, docs/STATUS.md, and a results log entry here.

Amendments

2026-10-08, before any result was computed (found while wiring the rules to the engine and the data):

  1. Foreign tax holding periods (4.1). US-listed ETF units held through LRS are shares not listed in India, not fund units. So the long-term threshold follows the date of sale:

    • 12 months for sales to 2014-07-10;
    • 36 months to 2016-03-31 (Finance (No. 2) Act 2014);
    • 24 months from 2016-04-01 (Finance Act 2016).

    The rates are as written: 20% after indexation, then 12.5% without it from 2024-07-23. The draft’s “36 months to 2024-07-22” was the debt-fund rule. Worked examples are in tests/test_lab_tax.py.

  2. Windows. Each model starts at the first session after its first decision, and never before 2005-04-01. Its counterpart runs over the same dates. The spec’s single headline window can’t hold every model: size rotation and the trend models need Midcap 150 and Smallcap 250 history, which start in 2005-04.

  3. The band (4.4). “A sleeve trades only when it moves 5 points” is implemented as: at each weekly decision, the whole model rebalances only if some asset’s weight is more than 5 points off its new target (lab engine policy decision_bands).

  4. The live log (6) lives in the pipeline’s derived cache, which the cloud refresh keeps in R2, and is published in full as the models/live bundle. The published bundle is the public copy, so there is no monthly repo mirror; the cloud run can’t commit to the repo.

  5. Promoted trend rules (3) run on the lab engine with the lab’s cost schedule, like every other model. The rules themselves are unchanged. Their numbers can differ slightly from /trend/evidence/, which uses the trend spec’s flat costs.

  6. Sector live-only start (4.3). On the frozen launch dates, the sixth eligible sector arrives at the 2012-07 month-end, not “about 2011-09”.

  7. Deflated Sharpe variance. The cross-sectional variance of Sharpe ratios is taken across the board’s own models (the 184-trial count is unchanged). The earlier trials’ Sharpe ratios live in other tables, with other windows.

  8. Timing luck (5) covers the monthly rules other than the valuation glides. A glide reads a monthly external signal that cannot be shifted with the prices. Offsets relabel sessions so that each “month” ends k sessions before the true month-end, k = 0 to 20.

2026-10-08, after the first results (presentation only; no rule, window or test changed):

  1. Tipsheet indices. Eleven board models are also published as index levels: total return, base 1,000, before any cost or tax. The method is in docs/methods/tipsheet_indices.md. Each launch date is the model’s entry date. After launch, levels chain the gross returns written to the live log, which now records each index model’s gross return beside its after-cost return. Because results had been seen, the eleven are chosen by a fixed rule: the models on the homepage board plus its benchmark, never by performance. No model was added to or removed from the board after the results.
  2. Wording. “Static counterpart” and “fixed mix” read as “benchmark” on the site: the same assets held in fixed shares, named on each model’s page.

2026-10-08, after the first results, before any result for the rules below was computed (owner’s review):

  1. Nifty 100 and Nifty 500 versions, and factor rotation.

    • Why the change: the board treats the Nifty 500 as the market everywhere else (GTAA, dual momentum, the market rows). Its large-cap sleeves used the Nifty 50, which overlaps nothing and covers less. The Nifty 100, Midcap 150 and Smallcap 250 together make up exactly the Nifty 500, with no overlap.
    • New rules (entry 2026-10-08), each counted as a trial:
      • trend_india and trend_world: trend_local and trend_global with the Nifty 100 in place of the Nifty 50;
      • size_rotation: size_dm with the Nifty 100 in place of the Nifty 50;
      • n500_sma10m: the 10-month rule on the Nifty 500;
      • factor_dm and factor_dm_live: at each month-end, hold equally the top two of four factor indices (Nifty200 Momentum 30, Nifty200 Quality 30, Nifty500 Value 50, Nifty100 Low Volatility 30), ranked by return over months t−12 to t−1. A slot goes to cash when its factor’s return is below cash’s. factor_dm uses NSE’s history. factor_dm_live counts each factor only from 13 months after its launch and runs from the first month with three. Benchmark: equal weight across the same eligible factors, monthly. Sources: Gupta and Kelly (2019) on factor momentum; the absolute filter as in 4.3.
    • The board and the indices move to the new versions, and Tipsheet Factor Rotation (factor_dm_live) joins the index family.
    • The first versions (trend_local, trend_global, size_dm, n50_sma10m) stay listed with their records and entry dates. They are not deleted or rewritten.
    • Trial count: 190.
  2. Prediction P8 (for the rules in 11).

    • Neither factor rotation rule beats its equal-weight benchmark with a pre-tax 90% range above zero.
    • The Nifty 100 versions’ after-tax gaps to their benchmarks are within 1 point a year of the Nifty 50 versions’ gaps. The change is structural, and I expect it to change little.
  3. Which version is on the board and in the index family (owner decision, 2026-10-08, after results).

    • Sector and factor rotation: the board and the indices use the versions on NSE’s full history (sec_dm_v2, factor_dm). The launch-counted versions (sec_dm_v2_live, factor_dm_live) stay listed as models.
    • Why: every index then follows one policy. NSE’s histories are used in full, and back-calculated years are marked, as the trend and size indices already did with Midcap 150 and Smallcap 250.
    • This changes which version is shown, not any rule or result. The launch-counted version of sector rotation did better on its shorter window (−1.98 against −3.06 points after tax); of factor rotation, worse (−6.05 against −4.55).
  4. Allocation styles and Tipsheet Dynamic Allocation (owner’s request, 2026-10-08, before any result for the new rule was computed).

    • Allocation styles join the board as models, with no new trials: the portfolio lab’s risk parity (equal risk contribution), minimum variance, inverse volatility, hierarchical risk parity, maximum diversification, All Weather (India), golden butterfly and endowment-style portfolios. They are the lab’s rules and results, unchanged (entry 2026-10-02). The fixed mixes among them have no benchmark of their own; the risk-based ones keep the lab’s (equal weight of their assets).
    • New rule dyn_alloc (entry 2026-10-08, one trial; total 191). At each month-end:
      • valuation half: the Nifty 500 P/E (consolidated basis, valuation_nifty_500, same-day) is ranked against every month-end since 1999 up to that date, with at least 60 needed. It maps to an equity share of 80% at or below the 20th percentile, falling linearly to 20% at or above the 80th;
      • trend half: the Nifty 500’s trend score (24 votes on its level over cash) maps to 20% equity at 0, rising linearly to 80% at 100;
      • equity: Nifty 500, at the average of the two halves; the rest in the 5-year G-sec. It trades at the next close, only when a weight is more than 5 points off target.
      • Benchmark: 50% Nifty 500 + 50% 5-year G-sec, rebalanced each January.
      • Sources: valuation-based allocation as in balanced advantage funds and NSE’s Nifty50 and Short Duration Debt – Dynamic P/E index; trend as in 4.4.
    • Index family: Tipsheet Dynamic Allocation replaces Tipsheet Valuation Glide, whose CAPE input is a reference file that is not refreshed daily. The family stays at twelve. The valuation glides stay as models.
    • P9: dyn_alloc does not beat its benchmark after tax with a pre-tax 90% range above zero, and its worst fall is shallower than the benchmark’s.

Results log

2026-10-08: first run (data to 2026-10-06; rules as specified and amended above)

These ran exactly as registered and amended, with no parameter changed after results were seen. Code: pipeline/tipsheet/models/run.py. Tables: .cache/derived/models_*. Bundles: models/*.

The models from the lab reproduce lab v2’s published figures:

The new rules against their fixed mixes. Returns are a year after costs; the 90% range is on the gap before tax.

ModelFromBefore taxAfter taxFixed mix, after taxGap after tax (pts)90% range (pts)Worst fallFixed mix’s worst fall
Trend, India only2006-0411.459.4013.01−3.61−4.66 to +0.26−17.6−38.1
Trend, with global2006-0410.748.5313.16−4.62−5.39 to −0.66−16.1−36.4
Nifty 50 graded by trend score2005-0410.679.1812.18−3.00−5.75 to +2.24−25.0−59.9
GTAA with global2005-0411.029.5712.51−2.94−3.47 to −0.87−14.8−19.2
Dual momentum, S&P 5002005-0411.949.9113.44−3.53−6.81 to +2.65−45.1−51.2
Dual momentum, Nasdaq-1002005-0410.988.5016.24−7.74−11.45 to −0.45−45.1−49.4
Size rotation2006-059.698.6611.73−3.06−8.61 to +3.59−49.1−69.6
Sectors, top 3 (NSE history)2005-0412.4711.1313.35−2.21−5.74 to +2.58−66.0−66.5
Sectors, top 3 with cash filter (NSE history)2005-0411.5610.2913.35−3.06−7.57 to +2.47−54.4−66.5
Sectors, top 3 (from launch)2012-0813.8812.1711.75+0.42−2.39 to +5.37−38.6−45.3
Sectors, top 3 with cash filter (from launch)2012-0811.169.7611.75−1.98−6.48 to +3.72−37.4−45.3

Deflated Sharpe ratios against the fixed mix, 184 trials: all between 0.00 and 0.13.

Sensitivities:

Timing luck. Deciding 0 to 20 sessions before the month-end moved before-tax returns widely:

Predictions:

What the record says. Every rule in this batch cut drawdowns, the trend models by half. Every rule but one paid for it with a lower after-tax return than simply holding its assets in fixed proportions. The foreign leg added nothing to any rule. The fixed 50/50 mixes with the S&P 500 or the Nasdaq-100 were among the best performers in the batch: 13.4% and 16.2% after tax over a window in which the rupee fell from about 44 to about 88 to the dollar. That is a statement about a fixed allocation abroad in this one sample, not about switching into it.

2026-10-08: the rules of amendment 11 (data to 2026-10-06)

Returns are after costs. “Gap” is the gap to the benchmark after tax. The range is the 90% range of the gap before tax.

ModelFromAfter taxBenchmark after taxGap (pts)90% range (pts)Worst fallBenchmark’s worst fall
Trend, India only (Nifty 100)2006-049.5313.10−3.57−4.63 to +0.38−17.6−38.5
Trend, with global (Nifty 100)2006-048.5613.24−4.68−5.41 to −0.72−16.1−36.8
Nifty 500, 10-month rule2005-0411.2812.67−1.39−5.81 to +4.14−33.9−64.2
Size rotation (Nifty 100)2006-058.6011.80−3.20−9.07 to +3.54−49.1−70.2
Factor rotation (NSE history)2006-059.0013.56−4.55−9.57 to +0.30−51.5−59.8
Factor rotation (from launch)2019-117.5513.59−6.05−14.33 to +0.72−29.1−34.2

Deflated Sharpe ratios against the benchmarks, 190 trials: all 0.00.

P8 holds on both counts:

2026-10-08: the rule of amendment 14 (data to 2026-10-06)

dyn_alloc, from 2005-04:

For comparison, the CAPE glide it replaces in the index family returned 10.15% after tax, with a worst fall of −15.7%.

P9 is half held:

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