Skip to content

KB / indicators

Health Score (19-component composite)

Last verified

Composite 0–100 measure of structural market health — not direction. A high score means the plumbing is intact (liquidity flowing, credit calm, institutions buying). Normal markets score 55–60.

Components (19, sum to 100)

ComponentMaxWhat it measuresMetric
Energy10Energy regime (WTI, crack spread, XLE)energy_regime
HY OAS8High-yield credit spreadshy_oas
Stagflation8Weighted breakevens (5Y 40%, 10Y 20%, 5Y5Y 20%, 5Y-10Y inversion 20%)stagflation
DIX7Institutional dark-pool buying activitydix
GEX7Options dealer gamma exposuregex
Breadth7% of S&P 500 above 50-day SMAbreadth_50d
Liquidity7Fed net liquidity directionnet_liquidity
Breakeven65Y breakeven inflation expectations (lower is better)breakeven_5y
Vol Term5VIX term structure (contango/backwardation)vix_regime
SPY/ZGL5Price vs zero gamma levelspy_vs_zgl
0DTE PCR5Same-day options put/call ratiozero_dte_pcr
Sectors5Sector breadth favorabilitysectors
Correlations5Weighted per-pair health (SPY/VIX, SPY/Oil, SPY/DXY, SPY/TNX)correlations
BTC3Bitcoin as liquidity proxybtc
SKEW/VIX3Tail-risk hedging activityskew_vix
Real Yield310Y TIPS real yield levelreal_yield
Copper/Gold2Growth expectation proxy ratiocopper_gold
USD/JPY2Carry trade unwind riskusdjpy
HYG2Credit ETF selling pressure confirmationhyg_confirmation

Pure additive sum. Each band-classifier maps the raw value to a per-component points award between 0 and the listed max. Logic lives in app/signals/score.py — pure, FastAPI-free, directly unit-testable.

Score ranges

Score-range readings

Favorable 70+ Conditions support risk-taking. Most supports intact.
Mixed 50 – 70 Some supports intact, others not. Be selective.
Cautionary 30 – 50 Multiple supports weakening. Reduce risk.
Adverse < 30 Structural supports broken. Defensive only.

The 50–60 band is where most days live.

Regime-conditional rebalancing

score_weights.json → regime_overrides lets any regime declare scaling factors per component. Currently configured:

Adding an override: {trigger: {field, in|eq}, scales: {component: factor}}. Factors must sum-offset so the post-override max stays at 100.

Score vs regime — when they disagree

The dashboard’s bridge text (italicized line under the regime name) labels the divergence:

ScoreRegimeLabelMeaning
70+RISK-ONFavorableScore and regime agree
70+Not RISK-ONFavorable, with cautionScore high but a lagging signal holds the regime back
50-70AnyMixedSome supports intact, others not
50-70RISK-OFF/PANICMixed, deterioratingMid-range score but conditions worsening
30-50AnyCautionaryMultiple supports weakening
< 30PANIC/RISK-OFFAdverseStructural supports broken
< 30OtherAdverse, holdingScore very low but regime hasn’t caught up yet

Scale headroom

A May 2026 regime-quantile audit confirmed the 0-100 scale spans cleanly without saturation:

Regimenp5p50p95max
PANIC1823283940
RISK-OFF28928374957
TRANSITIONAL30820475577
CAUTIOUS46742505874
RISK-ON20548558287

See also