MMT

Education

Concepts and mechanics behind every page on the dashboard. Skim the table of contents and jump to whatever you're curious about.

Sector Rotation (Stovall)

A top-down framework that rotates capital between economic sectors as the business cycle turns. It is the strongest-performing strategy our backtests have surfaced (see /backtest/stovall), and the live read-out is on /market-stage.

The core idea: different sectors lead at different points in the cycle. Early-cycle cyclicals (financials, technology, consumer discretionary) lead coming out of a recession; late-cycle inflation plays (energy, materials, staples) lead into the top; defensives (health care, utilities, staples) lead through the downturn. Rotate with the cycle and you ride each sector's best window.

The crucial subtlety: the stock market leads the economy by ~6-9 months because investors price in expected conditions, not current ones. So the market cycle (when sectors actually move) runs ahead of the economic cycle (when GDP and production confirm it). Our classifier reads the market cycle from weekly sector money-flow, which is why it can flag a stage turn before the macro data does.

Market cycle vs economic cycle

The market curve (solid) leads the economy (dotted). Labels follow the Way2Wealth / Stovall cycle-position framework — education only, not a live signal.

TroughEarly RecoveryMiddle RecoveryPeakMiddle RecessionBottomXLF · XLK · XLYEarly BullXLK · XLI · XLBLate BullXLB · XLE · XLPTopXLB · XLE · XLPEarly BearXLE · XLP · XLVMiddle BearXLV · XLU · XLFLate BearXLV · XLU · XLFFull RecessionEarly RecoveryFull RecoveryEarly RecessionMarket BottomBull MarketMarket TopBear MarketStock Market (leads)Economy (lags)

Concept inspired by Sam Stovall / Way2Wealth TIME TO KNOWLEDGE cycle-position education (market leads economy); rebuilt for MRT (not a copy of any publisher chart).

The four economic stages

Stovall characterizes each economic stage by the state of four macro series. These are the conditions the market positions around one to two quarters before they show up in the data.

Economic stageConsumer exp.Ind. productionInterest ratesYield curve
Full RecessionRevivingBottoming outFallingNormal
Early RecoveryRisingRisingBottoming outNormal (steep)
Full RecoveryDecliningFlatRising rapidlyFlattening
Early RecessionFalling sharplyFallingPeakingFlat / inverted

Sector leadership by stage

Our classifier maps the market cycle into five stages. For each, the leaders are the sectors the model goes long; the laggards are the textbook underperformers to avoid or underweight.

Market Bottomecon phase: Full Recession
↑ XLF XLK XLY↓ XLE XLP

Rates have fallen, credit is loosening, and the market anticipates recovery before the data confirms it. Interest-sensitive cyclicals begin accumulating first.

Early Recoveryecon phase: Early Recovery
↑ XLK XLI XLB↓ XLU XLV

Industrial production turns up and capex resumes; capital goods plus raw-materials demand lead. Defensives lag as risk appetite broadens.

Market Topecon phase: Full Recovery
↑ XLB XLE XLP↓ XLK XLY

The economy runs hot, inflation and rates climb, and the late-cycle inflation trade leads while early-cycle growth starts to roll over.

Bear Marketecon phase: Early Recession
↑ XLE XLP XLV↓ XLF XLY XLI

Demand falls and earnings estimates get cut. Defensives with inelastic demand and still-elevated Energy hold up while cyclicals sell off.

Late Bearecon phase: Recession → Recovery transition
↑ XLV XLU XLF↓ XLK XLB

The deepest part of the downturn. Rate-sensitive defensives lead, and Financials begin to base in anticipation of the next easing cycle.

Macro signals at each turn

The cycle transitions don't announce themselves — these are the macro tells that historically precede each hand-off, most of which the dashboard already tracks (yield curve, HY spreads, PMI, breadth). The live Market Stage page reads them against the current stage in real time.

Bottom forming (Late Bear → Market Bottom)
  • Yield curve steepening off the lows as the Fed cuts
  • Credit spreads (HY OAS) peaking and beginning to narrow
  • ISM Manufacturing PMI troughing below 50 but ticking up
  • Initial jobless claims rolling over from the peak
Recovery confirmed (Market Bottom → Early Recovery)
  • PMI back above 50, new-orders sub-index leading
  • Copper / gold ratio rising (cyclical demand)
  • Breadth thrust — % of stocks above 200dma expanding fast
  • Credit spreads firmly tightening
Late cycle (Early Recovery → Market Top)
  • Yield curve flattening as the Fed hikes into strength
  • Core inflation / breakevens rising
  • Energy and materials taking relative-strength leadership
  • Tech / discretionary momentum fading vs the tape
Rolling over (Market Top → Bear Market)
  • Yield curve flat or inverted
  • Credit spreads widening from the tights
  • Defensives (staples, health care, utilities) outperforming
  • Breadth deteriorating — fewer stocks above 50/200dma

Strengths and pitfalls

  • Works: aligns positioning with the business cycle instead of fighting it; top-down and rules-based with a handful of liquid sector ETFs; empirically strong (+53pp cumulative alpha vs SPY over 4.5y, Sharpe 1.12 vs 0.75 in our backtest); leadership rotation is a leading signal of the turn.
  • Breaks: entry/exit timing matters a lot at the turns; a contrarian misread of the stage leaves you in the wrong sectors; over-trading the transitions racks up costs and whipsaw; stage boundaries are fuzzy in real time (the classifier emits unknown and falls back to neutral weights when no signature fires).

References

  • Sam Stovall, Standard & Poor's Guide to Sector Investing (1995) and Sector Investing (1996) — the original framework.
  • Way2wealth, “Sector Rotation” research note — applies the Stovall model to the economic cycle (source of the four-stage indicator table).

What this dashboard does

A single composite score (−100 to +100) that classifies the current equity-market regime as Strong Bull / Bull / Neutral / Bear / Strong Bear, plus a Pozsar-style plumbing kill switch that overrides everything when liquidity infrastructure breaks.

The score is built from six weighted lenses (Macro, Credit, Breadth, Volatility, Cross-asset, Sentiment) each containing 5–8 indicators, plus a seventh lens (Plumbing) that doesn't add to the composite weight but can force the regime label to Defensive — Plumbing Stress.

Indicator data is persisted in Postgres and re-fetched daily. A 15-year backtest runs in ~30 s warm. Every page on the site is a different read-out of that same underlying data.

Financial plumbing

“Plumbing” is the financial-system equivalent of what plumbing means in a building: the boring infrastructure that nobody pays attention to until something breaks. The term was popularised by Zoltan Pozsar (ex-Credit Suisse, ex-NY Fed) — he literally calls it that because it's pipes through which dollars flow between banks, the Fed, and the Treasury.

When the plumbing works, no one notices. When it breaks, forced selling dominates everything else — sentiment, technicals, fundamentals all get overridden because dealers can't hedge, banks can't lend, and someone with leverage has to liquidate. That's why this dashboard treats it as a kill switch, not a regular lens.

What we actually track in the plumbing lens

IndicatorWhy it mattersStress condition
SOFR − IORB spreadBanks would rather borrow in repo than from the Fed → reserves are scarcePersistent > +5 bps
RRP balanceThe Fed's "shock-absorber" cash pile — when drained, QT bites real reservesRapid drain toward zero
Bank reserves / GDPPozsar's "LCLOR" thesis — system breaks below ~10% of GDP< 10% of GDP
MOVE indexTreasury rates volatility — when bonds get jumpy, dealers can't make marketsSustained > 140
SRF usageStanding Repo Facility taps mean open-market repo isn't workingAny non-trivial usage
10Y swap spread proxyPersistently negative = collateral / duration stressPersistently negative

Each emits a 0→1 stress reading. The lens combines them into a plumbing stress score. When the score crosses 0.5, the headline regime label gets force-overridden to “Defensive — Plumbing Stress” regardless of what the bullish lenses say.

Three famous plumbing-breakdown events

  • Sept 2019 repo spike — SOFR jumped to ~10% intraday. Corporate tax payments + Treasury settlements drained reserves below LCLOR. Fed had to restart balance-sheet expansion (“not-QE”).
  • March 2020 Treasury market dislocation — 10Y bid-ask widened to multiples of normal as foreign sellers dumped USTs. Fed had to buy $1T of Treasuries in three weeks.
  • March 2023 SVB / regional banks — duration losses on bank HTM portfolios → deposit run → KRE/KBE ratio collapsed → Fed launched BTFP. The kre_kbe_ratio indicator was specifically built for this pattern.

Why it's the only strategy that pays

Per /strategy on 10y prod data: a strategy that is long SPY by default, cash when the kill switch trips has Sharpe 0.91 vs buy-and-hold's 0.80, with a max drawdown of −9.6% vs −33.9%. The reason: plumbing breaks are step-change events. By the time the equity market has fully priced it in, you've already lost 15-30%. By the time SOFR breaks IORB, everyone is selling because their hedges aren't working — but the kill switch fires before the equity market has repriced. So you exit a few days early, sit out the worst, and get back in once the Fed responds.

Composite & lens system

The composite score is a weighted sum of six lens scores, scaled to [−100, +100]:

composite = Σ (lens_score × lens_weight) × 100

with default lens weights:
  macro      0.20
  credit     0.20
  breadth    0.15
  volatility 0.20
  crossasset 0.10
  sentiment  0.15
                       (sums to 1.0)

Each lens score is itself a weighted average of its indicators' regime contributions in [−1, +1]. An indicator's regime contribution is its percentile rank within a 3-year lookback, mapped to [−1, +1] via its Direction:

  • HIGHER_IS_BULLISH — high percentile → +1 (bullish); e.g. high HY OAS, high VIX (after our May 2026 polarity flip).
  • LOWER_IS_BULLISH — low percentile → +1; e.g. falling DXY.
  • HIGHER_IS_BEARISH_CONTRARIAN — high percentile → −1; e.g. AAII bull-bear at extreme highs (crowded long is bearish input).
  • LOWER_IS_BEARISH_CONTRARIAN — low → −1; capitulation = bullish input (the framework already inverts contrarian indicators internally so "positive contribution = bullish" is always the convention).

Regime classification thresholds

composite >= +60   → Strong Bull
composite >= +20   → Bull
composite >= -20   → Neutral
composite >= -60   → Bear
composite <  -60   → Strong Bear

plumbing trip      → Defensive — Plumbing Stress  (overrides all of above)

The headline label is then run through hysteresis: a flip only sticks if the new label has held for at least 3 consecutive raw classifications. This stops the regime label from oscillating on noise. Plumbing trips bypass hysteresis (they're step-changes that demand action immediately).

Information Coefficient (IC)

IC is Spearman rank correlation between an indicator's reading and forward S&P returns. It answers: “does this signal predict anything?”

We compute IC at three horizons: 5d (week-ahead), 20d (month-ahead), 60d (quarter-ahead). For equity-index forward returns the industry-standard thresholds are:

|IC|VerdictAction
≥ 0.10Real signalKeep, weight up
≥ 0.05MarginalBorderline; depends on stability
< 0.05NoiseDrop or down-weight

Sign matters too. Positive IC = indicator predicts forward returns directionally. Negative IC = indicator is a contrarian signal (high reading → low forward return). The May 2026 indicator polarity audit found that 19 of our 36 actionable indicators had their Direction declared opposite to what the data said — those got flipped at the source.

Where to see this: /validation shows composite IC, per-lens IC, and per-indicator IC with polarity verdicts.

Validation framework

Six interlocking analytics, all run against the cached backtest. No look-ahead — each backtest point uses only data available on that day; forward returns are pulled after the fact for the analytics layer only.

  • Composite IC. Does the headline score predict forward returns? At 5/20/60d.
  • Per-lens IC. Which lens does the work? Which is noise? Sorted by 20d magnitude.
  • Per-indicator IC + polarity. For each indicator, does its Direction match what the data says? Outputs a keep / flip / drop recommendation per indicator.
  • Quintile bucketing. Sort backtest points into 5 buckets by composite score. Compute mean forward return per bucket. If the model works, top quintile beats bottom quintile monotonically.
  • Plumbing kill-switch validation. Forward returns when plumbing tripped vs clear, with the edge in bps and hit rate for each.
  • Walk-forward + out-of-sample. IC computed year-by-year, with a regression-slope trend (stable / decaying / improving). Plus a single 60/40 in-sample / out-of-sample split with a sign-flip-aware verdict.

The whole thing lives at /validation and runs in ~30-90 s on a 10y window once the cache is warm.

Strategy concepts

The /strategy page converts the composite score into actual P&L. Four strategies, all rebalanced on the backtest cadence (default weekly), with configurable transaction cost, no look-ahead:

  • Directional — long SPY when composite ≥ +20, cash otherwise.
  • Contrarian — long SPY when composite ≤ −20, cash otherwise.
  • Plumbing-defensive — long SPY always except when the kill switch trips.
  • Buy-and-hold — the benchmark.

Stats explained

  • CAGR — compound annual growth rate. The single number for “how fast did it grow.”
  • Sharpe ratio — risk-adjusted return. mean(daily_return) / std(daily_return) × √252. Higher is better; 0.5 is mediocre, 1.0 is good, 2.0 is exceptional.
  • Max drawdown — biggest peak-to-trough loss along the equity curve. A higher CAGR with a worse drawdown isn't obviously better; allocators care about both.
  • Time in market — fraction of days with non-zero position. A 100% in-market strategy has no opportunity cost from cash; a 30% in-market strategy is mostly sitting out.
  • Hit rate — fraction of closed entry-to-exit trade pairs that finished profitable. Watch out for small samples (a 100% hit rate over 2 trades is not the same signal as 60% over 50).

Yield curve concepts

The yield curve is the term structure of US Treasury rates from 1 month to 30 years. It carries more macro information per data point than almost anything else.

  • Slope — long minus short. 2s10s is the classic. Negative = inverted = recession warning. Re-steepening from inversion is historically the actual recession trigger, not the inversion itself.
  • Real yield = Nominal − Breakeven. The 10y nominal yield (DGS10) decomposes into 10y real yield (DFII10, a TIPS yield) and 10y breakeven inflation (T10YIE, the difference). When 10y nominal moves, look at the decomposition: rising real yields (restrictive Fed) hurt risk multiples; rising breakevens (inflation expectations breaking out) hurt bonds without helping equities.
  • Curve regimes — /yield classifies the last month's move as:
    • Bull steepener: rates falling, curve steeper (cuts priced)
    • Bull flattener: rates falling, curve flatter (growth scare)
    • Bear steepener: rates rising, curve steeper (reflation)
    • Bear flattener: rates rising, curve flatter (Fed hiking)
    • Inverted: 2s10s < 0 (recession armed)
    • Steepening from inversion: re-normalisation (recession trigger)
  • Why TIPS don't exist at 2Y — Treasury doesn't issue 2-year Treasury Inflation-Protected Securities, so there's no 2Y real yield or breakeven on FRED. The 2Y row on the breakevens panel shows nominal-only with — for real / breakeven.

Persistence & caching

Every indicator's daily reading is persisted to a Postgres table (indicator_observations). Every backtest reads from there instead of re-fetching from Yahoo / FRED on every request. That's why a 15-year backtest runs in ~30 s warm and a 1-year backtest in ~9 s.

  • Cache fill: a one-time admin endpoint POST /api/v1/admin/backfill?years=15 seeds 15 years of every indicator.
  • Live edge: when a request reaches up to today, the cache layer fetches only the last ~7 days from upstream and merges. Backtest historical part comes straight from DB.
  • Resilience: if a source 403s (CBOE, AAII), the cache returns the most recent persisted reading instead of dropping the indicator. CBOE total P/C falls back to a SPY chain-derived ratio when the CDN blocks us.
  • SP500 closes: 500 individual constituent close series are persisted in daily_closes. Breadth indicators read from there instead of yfinance-bulk on every call.

Multi-ticker overlay on /compare

The /compare page overlays up to 10 stocks or ETFs on a single chart so you can see how they've moved against each other over time. Different from the correlation view — that one tells you statistical relationship; this one tells you actual relative performance.

The three views (Rebased / Return % / Price)

  • Rebased (default) — every line anchored to 100 at the start of the window. Reads as a normalised index: a line at 200 means +100% from start, 50 means −50%. Best for seeing which tickers led / lagged regardless of price level.
  • Return % — same data, but the y-axis is in percent return (start = 0%). Clearer when you want to read magnitudes off the axis directly.
  • Price — raw dollars. Only useful when the tickers are similarly priced (e.g. comparing two ETFs at $50 vs $60). With mixed-price names, one expensive ticker dominates the y-axis.

Time horizons

1M / 3M / 6M / YTD / 1Y / 3Y / 5Y / 10Y. Use the same structural-vs-tactical framework as the correlations page — short windows show recent leadership, long windows show whether the recent picture is normal or a regime change. A 10Y overlay of the Mag 7 vs SPY is a quick way to see how concentrated the index leadership has been.

Load top 10 of any ETF

Click 📊 Load top 10 of an ETF, pick from the searchable catalog of 41+ cached ETFs (sectors, themes, ARK funds, semis baskets, defense, energy, etc.), and the chart replaces its tickers with that ETF's top 10 holdings. The benchmark also switches from SPY to the parent ETF — which is the right comparison: you want to see how each constituent has moved versus the basket itself, not vs the broad index. Click “reset to SPY” to restore the default if you want both views.

Same holdings file the /rotation Holdings drilldown uses; refreshes quarterly. Useful for spotting concentration risk (e.g. SMH's top 10 are ~75% of the ETF — if NVDA drops 15%, the whole basket follows).

Built-in presets

Eight one-click loaders that drop the right basket onto the chart:

  • Mag 7 — AAPL, MSFT, GOOGL, AMZN, META, NVDA, TSLA
  • AI capex chain — NVDA, AVGO, MU, ANET, VRT, CEG, AMAT (the bottleneck stack from /themes)
  • Sector ETFs — all 9 cyclical / defensive sector SPDRs
  • Risk-on / risk-off — SPY, TLT, GLD, DXY, HYG, VIXY (the macro asset palette)
  • Themes — SMH, ARKK, CIBR, ICLN, BOTZ, FINX, URA
  • Defensives — XLP, XLU, XLV, GLD, TLT (late-cycle hideouts)
  • Reshoring — CAT, DE, URI, MTZ, ROK, ETN, EMR
  • Energy security — CEG, VST, GEV, LNG, KMI, CCJ, URA

Legend = leaderboard

Below the chart, every ticker is listed with total return, annualised return, max drawdown, peak / trough range, and last price — sorted by total return descending. Click any row to toggle the line on or off, useful when one outlier is compressing the y-axis (e.g. NVDA over 5Y dominates everything else; toggle it off to see the rest).

Optional SPY benchmark

Toggle Show SPY benchmark to add SPY as a dashed reference line. Anything visibly above SPY beat the index over the window; anything below underperformed. The dashed style makes it easy to read "alpha" off the chart at a glance.

Compare vs Correlations — when to use which

QuestionUse
Did A and B move together?/rotation → Correlations rolling chart
Which of these 7 stocks won the year?/compare with rebased view
How concentrated was the rally?/compare with Mag 7 preset + SPY benchmark on
When did relationship X break?/rotation → Correlations rolling at progressively shorter windows

Math

rebased(t) = price(t) / price(start) × 100. Anchor is the first trading day in the requested window. Tickers with no data in the window (e.g. an ETF launched after the rebase date in a 5Y window) are dropped silently with a notice. Max drawdown is computed peak-to-trough within the window. All fetches reuse the existing rotation panel cache, so a typical comparison is one bulk yfinance call cached for the rest of the day.

Historical correlations on /rotation

The Correlations tab on /rotation shows two complementary views of how sectors / themes move together:

  • Matrix heatmap — pairwise correlation of daily log returns over a rolling window. Cells coloured green-to-red on the level view, or indigo-to-amber on the shift view (latest window correlation − same window 30 days ago).
  • Rolling correlation chart — pick any two tickers, see how their correlation has evolved. Reference lines at ±1, ±0.5, 0.

Time horizons: 20d → 10Y

Both views support multiple windows. They're not redundant — each one tells you a different story:

WindowWhat it showsBest for
20d / 60d / 120dTactical — recent regimeShort-term pair trading, risk-on / risk-off reads
1YCyclical — current cycleWhere the relationship sits in the present economic regime
3YMulti-cycle — last full economic phaseSmooths cycle noise, exposes regime shifts
5YStrategic — long-run trendConfirms structural relationships
10YStructural — pre-COVID baselineAnchor: what the “normal” correlation has been

The matrix supports all windows up to 10Y. The rolling chart caps at 5Y because a 10Y rolling correlation is so smoothed it's effectively a single number — no trajectory to plot.

The structural-vs-tactical framework

Long windows tell you what's structural — the baseline relationship that has held across cycles. Short windows tell you what's tactical — where the relationship sits today. The interesting trade isn't the level of either one; it's the gap between them.

Worked example — XLK / TLT over three windows on the same day:

WindowMeanLatestRead
5Y rolling−0.19+0.05Decade-long inverse relationship is decaying
1Y rolling+0.07+0.08Cyclical regime: now slightly positive
60d rolling+0.02+0.42Tactical break is sharply accelerating

Three windows, three different stories of the same pair. The 5Y mean (−0.19) tells you what the relationship used to be — textbook “rates-up = tech-down.” The 1Y mean (+0.07) tells you that's already broken at the cycle level. The 60d latest (+0.42) tells you the break is now sharply accelerating. Without the long windows you can't tell whether the +0.42 today is normal or a regime change — they give you the anchor against which the tactical reading is evaluated.

When the matrix views diverge

On the matrix view, each pair has a 10Y structural number and a 60d-by-default tactical number. When they agree (XLK / XLY at +0.83 over 10Y, ~+0.7 today) the relationship is stable. When they disagree by 0.4+ points, treat that pair as a regime change candidate — drill into it on the rolling chart at progressively shorter windows to see when the break started.

Why shifts matter more than levels

Two sectors being correlated +0.8 isn't inherently informative — if they've been +0.8 for years (e.g. XLK vs XLY), that's just structure. What's informative is when a long-stable relationship breaks. The shift column on the matrix surfaces the recent break for every pair at once; the rolling chart shows the full trajectory of any single pair.

Pre-set pairs worth watching

  • XLK / TLT — tech vs long bonds. 5Y mean historically inverse; recent regime has flipped to positive. Worked example above.
  • XLE / TLT — commodity inflation vs duration; typically inverse. Decoupling = inflation regime resolving.
  • XLY / XLP — cyclicals vs defensives. Deep negative = clear risk-on/off split; decoupling = transition.
  • IWM / SPY — small caps vs large caps. Persistent decoupling marks narrow-leadership / breadth-deterioration, the standard late-cycle pattern.
  • XLF / XLRE — banks vs REITs. Both rate-sensitive but in opposite directions on the curve. Decoupling = curve regime change.
  • GLD / DXY — textbook inverse. When gold rallies with a strong dollar, something macro-structural has shifted.

Math

Both views use Pearson correlation on daily log returns (more numerically stable than simple returns around extreme moves). The window length is in trading days (1Y = 252, 5Y = 1260, 10Y = 2520) but the underlying fetch is in calendar days, so the engine pulls ~1.6× the trading-day window to make sure the correlation has full samples.

On the themes universe, ETFs launched within the last few years (e.g. some clean-energy or AI funds from 2020+) will have shorter histories than the requested 10Y window. Pandas handles this with pairwise complete observations — the cell value reflects whatever overlap each pair actually has, so you'll see lower-confidence numbers for newer themes rather than NaN.

How the macro-theme engine works

The /themes page implements a 5-step inference chain: (1) macro / geopolitical situation → (2) sectors disrupted or advanced → (3) bottlenecks within those sectors → (4) companies fulfilling the bottlenecks → (5) best stock pick within the company set. The chain has two distinct halves with different feasibility profiles, and the engine handles them differently:

Steps 1–3: human-curated catalog

Steps 1–3 are qualitative and require domain knowledge that a purely quantitative engine can't generate. They live in a human-authored YAML catalog (backend/app/data/themes.yaml) covering 6 active themes:

  • AI Infrastructure & Compute
  • Energy Security & Dispatchable Power
  • US Reshoring & Industrial Renaissance
  • Defense & Geopolitical Tension
  • Healthcare Innovation: GLP-1 & Beyond
  • Deglobalization & Supply Chain Re-routing

Each theme bundles: a thesis paragraph, a list of macro indicators with the direction the theme prefers, a list of sector ETFs it touches, and a list of bottlenecks each with 2–4 candidate company tickers.

Steps 4–5: pure quant scoring

Steps 4–5 are quantitative. The engine scores activation and ranks companies entirely from market data:

ComponentWeightWhat it measures
Theme activation (0–100)
Macro match40%How the live indicator readings align with the theme's preferred direction
Sector strength35%Weighted RS-Ratio + RS-Mom + RS-Accel of the theme's sector ETFs
Money flow25%Weighted flow_intensity of the theme's sector ETFs
Best-pick score per company (0–100)
RS vs SPY 60d35%Outperformance over 60 trading days, in pp
20d return25%Recent month price action
20d acceleration25%2nd-order RoC: is the move speeding up or fading?
5d return15%Recency / confirmation

What the system does not do

  • No live news feed. The macro/geopolitical situation is inferred from market indicators (yields, oil, dollar, credit spreads), not from headlines.
  • No fundamental analysis. Best-pick scoring is pure technical / momentum. PE, growth, margins, balance-sheet quality are not in the score.
  • No theme auto-discovery. Themes are human-curated; novel macro themes need to be added to the catalog by hand.
  • Not a recommendation. A high-scoring stock on this screen is a candidate worth deeper diligence, nothing more.

Worked example

When AI Infrastructure scores 60.9/100 and the best pick is MU at score 98, the chain looked like:

  1. Macro match 49 — neutral; macro is mixed but not hostile to capex.
  2. Sector strength 77 — XLK is in Leading with rising RS-Accel.
  3. Flow 57 — money positive into the theme's ETFs.
  4. Bottleneck = HBM memory & advanced packaging — MU sits here as the dedicated US pure-play.
  5. MU score 98 — 20d return +51%, 60d RS vs SPY +72.5pp, and momentum is ▲ confirming (still accelerating).

Each link in the chain is independently verifiable on the dashboard: the macro composite on /, sector strength on /rotation, the company's setup on its rotation row.

Acceleration in sector / theme rotation

The 2nd-order RoC also lights up on equity prices — sector ETFs, themes, individual stocks. Rotation in particular is where it shines: a sector that's leading and still accelerating is structurally different from one that's leading but decelerating, even though both sit in the same RRG quadrant.

Where it's surfaced on /rotation

  • Performance heatmap — every cell now carries a momentum glyph next to the return:
    • ▲ confirming — positive return that's also accelerating (this period outpaced the prior one).
    • △ fading — positive return that's decelerating; the up-move is losing steam even before it rolls over.
    • ▽ improving — negative return whose decline is decelerating; an early bottoming signal.
    • ▼ worsening — negative return whose decline is accelerating.
  • RRG table — new RS-Accel column (2nd-order RoC of RS-Ratio, centred 100). Above 100 = the sector's relative-strength momentum is still rising (gaining ground vs the benchmark). Below 100 = momentum is fading even if the sector is still in Leading.
  • Money flow + holdings drilldowns —return_5d_accel_pp andreturn_20d_accel_pp on each reading, available via the API. Future UI surfacing as needed.

The four states matter

Two sectors can both be Leading and have +5% returns over a month, but if one is ▲ confirming (this month +5%, last month +3%) and the other is △ fading (this month +5%, last month +9%), they're not the same trade. The fading one is closer to a top; the confirming one is still adding capital in the right direction.

Same goes the other way — a Lagging sector that's ▽ improving (down 8% this month, was down 12% last month) is a much better setup for a contrarian add than a ▼ worsening one (down 8% this month, was down 4% last month).

2nd-order RoC: catching inflections before the level moves

The 2nd-order rate of change — “RoC of RoC”, or acceleration — is one of the most useful tools for monitoring flows. It catches inflections before the underlying level peaks/troughs, because the rate of change peaks first.

The math

Given a daily series X (price, yield, spread, liquidity level), with a window of n days:

  • 1st-order RoC: ROC₁(t,n) = (X_t / X_{t-n} − 1) × 100 (in %)
  • 2nd-order RoC: ROC₂(t,n) = ROC₁(t,n) − ROC₁(t-n,n) (in pp)

We use the additive form (differences of differences) rather than the ratio of ratios, because the ratio blows up when the 1st-order RoC crosses zero — which is exactly when the interesting inflections happen.

Why it leads

Levels move because flows happen. Flows accelerate before they peak. So:

  • Acceleration positive → flow speeding up. Trend gaining momentum.
  • Acceleration crosses zero (down) → flow decelerating. Trend losing steam — early topping signal even while the 1st-order RoC is still positive.
  • Acceleration negative and falling → flow in outright reversal. Levels follow with a lag.

May 2026 experiment results

Built 7 acceleration variants across 5 underlying series, all launched at weight=0 for IC measurement. After one month of data: 4 of 7 carry signal at the 60d horizon — a much higher hit rate than the 1st-order flow experiment (0/3) that immediately preceded it.

IndicatorLensIC60dDirectionWeight
net_liquidity_accel_12wmacro−0.143LOWER=BULL (flipped)10%
net_liquidity_accel_8wmacro+0.089HIGHER=BULL5%
hy_oas_accel_20dcredit−0.077LOWER=BULL (flipped)5%
real_yield_accel_20dmacro−0.074LOWER=BULL5%
net_liquidity_accel_4wmacro+0.045— hold —0%
dxy_accel_20dmacro−0.024— noise —0%
vix_accel_20dvolatility+0.012— noise —0%

Headline finding: net_liquidity_accel_12w

The 12-week 2nd-order RoC of net liquidity scored IC60d = −0.143 — same magnitude as the level itself. The hypothesis was positive acceleration → flow speeding up → bullish; the data inverted it. Decelerating quarterly liquidity flow (Fed drain intensifying / liquidity-cycle troughs) precedes higher forward returns. That's the classic "max liquidity pessimism marks the bottom" pattern showing up empirically at the macro tier — when the flow is falling fastest, that's when forward returns are best.

Why the credit-cycle hypothesis flipped

We expected accelerating widening of HY OAS to be a capitulation signal (forward bullish at medium horizons). Data says otherwise: at all measured horizons (5d, 20d, 60d), accelerating widening is the leading edge of a credit cycle and continues bearish. The capitulation rebound presumably plays out at >90d horizons we don't measure. For a 5–60d strategy, accelerating HY widening is bearish — which is what the live indicator now reports.

Result: macro lens grew from 5 active indicators to 8 (+3 accel), credit from 8 to 9 (+1 accel). The total promoted weight is 25% (3×5% + 1×10%). The lens-level renormaliser handles the redistribution; the level signals all keep their original weights.

Tactical vs strategic composite (20d vs 60d)

The dashboard reports two composite scores, both built from the same lens scores but with different weights:

  • Tactical (20d-tilted) — original weights tuned for monthly entry/exit timing. Sentiment, volatility, breadth dominate (these are the lenses with the strongest 20d-horizon IC).
  • Strategic (60d-tilted) — weights proportional to per-lens IC at 60d, the quarterly horizon. Macro, credit, cross-asset dominate (these are the lenses that operate at economic-cycle timeframes).

Why two scores

The validation work showed that lenses don't all predict at the same horizon. Sentiment and volatility predict the next month well; macro and credit predict the next quarter much better than the next month. A single composite has to pick a horizon, and either one under-uses the other lenses.

Per-lens IC at each horizon (10y prod data, post-polarity audit):

LensIC 5dIC 20dIC 60d20d wt60d wt
credit+0.096+0.216+0.32920%27%
macro+0.067+0.083+0.30320%25%
crossasset+0.116+0.113+0.22210%18%
volatility+0.121+0.179+0.16020%13%
breadth+0.089+0.155+0.11615%9%
sentiment+0.076+0.129+0.09315%8%

When they disagree

The disagreement is the signal. If tactical is +30 (Bull) and strategic is −10 (Neutral), the short-term picture is bullish but the quarterly drivers (macro, credit) are softer — late-cycle momentum chase. Reverse case (tactical Neutral, strategic Bull): the macro setup is supportive but sentiment / vol haven't confirmed yet — the constructive case for adding risk is building, not yet active.

The overview gauge (/) draws both needles on the same dial — 20d as the solid needle, 60d as the dashed one. If they're ≥20 points apart, a small footnote calls out the direction of disagreement.

Why macro is the hardest lens

After the May 2026 polarity audit, every lens except macro showed clean positive 20d IC. Macro stayed near zero. The reason: macro signals operate at quarterly horizons (60d), not monthly (20d).

Per-indicator IC for the macro lens on 10y prod data:

IndicatorIC 5dIC 20dIC 60d
yield_curve_2s10s+0.054+0.105+0.229
real_yield_10y+0.076+0.083+0.141
dxy+0.094+0.077+0.161
net_liquidity−0.005−0.090−0.143
breakeven_5y5y−0.028−0.086−0.206

Every indicator's 60d IC is 1.5–2.5× larger than its 20d IC. The 2s10s spread alone has an IC of +0.229 at 60d — a strong signal that's diluted at 20d.

Why? Short-term equity moves are dominated by sentiment, vol regimes, and price-momentum effects. Macro liquidity flows take a quarter or more to fully feed through into asset prices — bank-reserve changes flow into deposit growth → loan growth → corporate spending; rate moves flow through duration, into multiples, into earnings. The 20d horizon catches mostly noise; 60d catches the economic-cycle signal.

Levels vs flows: the Druckenmiller distinction

The original framework treated net_liquidity (WALCL − TGA − RRP) as “rising = bullish.” That's Druckenmiller paraphrased — but his actual thesis is about changes (the Fed adding or draining), not the level. The 10y data agrees: high levels of liquidity coincide with cycle peaks (mean reversion); the flow direction is what predicts.

Two changes in May 2026:

  • net_liquidity level direction inverted (LOWER = bullish because high levels are late-cycle).
  • New net_liquidity_change_4w indicator added to the lens at 5% weight — this captures the actual flow signal Druckenmiller meant. It will be evaluated against the level on the next /validation cycle.

Practical takeaway

When using the dashboard's composite for tactical (1–2 week) decisions, the macro lens is mostly noise. When using it for strategic (1–3 month) regime calls, the macro lens dominates. The /validation page's composite IC at 60d (currently +0.256, GREEN) is the right number for macro-tilted positioning; the 20d IC (+0.23) is better for everything else.

May 2026 polarity audit

The biggest research finding of the project so far. Per-indicator IC analysis on 10 years of cached data showed 19 of 36 actionable indicators had their Direction declared opposite to what the data said. Specifically:

  • Credit lens (8 of 8 inverted, all flipped). Tight spreads / strong credit ETFs / strong KRE-KBE all preceded LOWER forward returns, not higher. This is the late-cycle / crowded-credit pattern: tight spreads mark complacency, not opportunity. The framework's “tight = risk-on” framing was empirically wrong over 10y.
  • Volatility lens (4 of 7 inverted, all flipped). Low VIX / contango term structure / low VVIX are crowded-short-vol complacency that mean-reverts. High VIX (panic) marks the actionable bottom. Skew and MOVE were noise (dropped to weight 0).
  • Breadth lens (3 of 6 inverted, all flipped). Stretched breadth (high % above MA, small-cap leadership) is crowded longs that mean-revert. Depressed breadth marks the entry.
  • Cross-asset lens (4 of 5 inverted, all flipped). Stretched copper/gold, AUD/JPY, oil, and gold-vs-real-yield richness are late-cycle euphoria signals; safety-bid moves coincide with bottoms.
  • Sentiment lens (6 of 6 correctly oriented). The only lens that worked as built. AAII, NAAIM, SPY-200dma deviation, VIX complacency, global breadth, HYG appetite all score OK / keep.
  • Macro lens (mostly noise). Net liquidity, NFCI, real yields, breakevens — most scored below the noise threshold. Yield curve 2s10s and DXY were the survivors.

The full per-indicator table with current IC / polarity / weight is at /validation (scroll to “Per-indicator IC”). Weight changes are documented in docs/INDICATORS.md with 0.0 ⚠ markers for zero-weighted indicators.

connecting…