Skip to content

KB / framework

Cross-asset correlations basket

Last verified

The correlations category tracks 20-day rolling Pearson correlations between SPY and four anchor assets: VIX, the US Dollar Index, the 10-year Treasury yield, and WTI crude. Each pair carries a healthy historical range; drift outside that range is the structural break the basket flags. The category fuses into one alignment leg and one health-score component — the same four pair classifications drive both.

The four-pair basket

Pair weights — from app/signals/config/correlation_scoring.json:pair_weights

SPY / VIX 1.0 (full weight) The structural-inverse anchor. Healthy regime = deep negative; correlation rising toward zero means dealer reflexivity is breaking down.
SPY / Oil 1.0 (full weight) Energy-shock transmission channel. Healthy regime = near-zero; rising positive means oil is repricing equity directly.
SPY / DXY 0.5 (half weight) Dollar-strength regime indicator. Secondary informational weight.
SPY / TNX 0.5 (half weight) Rates-equity coupling indicator. Secondary informational weight.

SPY/VIX and SPY/Oil carry full weight because their relationships are the structural backbone — VIX is the canonical inverse anchor (when SPY rises VIX falls, by construction of how vol prices fear), and SPY/Oil is the primary transmission channel for energy shocks into equity pricing. SPY/DXY and SPY/TNX get half weight because their relationships are real but secondary in information content for equity direction.

Per-pair classification

Each pair’s current 20-day rolling correlation is classified into one of four levels via thresholds in correlation_scoring.json. The thresholds are pair-specific because healthy ranges differ — SPY/VIX is structurally negative, SPY/Oil structurally near-zero, SPY/DXY mildly negative, SPY/TNX mildly positive.

Per-pair healthy bands (from correlation_scoring.json + correlations.py:PAIR_CONFIG)

SPY / VIX normal -0.95 to -0.70 Deep negative — structural inverse anchor intact.
SPY / DXY normal -0.40 to +0.10 Mild negative to flat — dollar-strength regime stable.
SPY / TNX normal -0.20 to +0.40 Mild positive — rates-equity coupling in the healthy band.
SPY / Oil normal -0.30 to +0.20 Near-zero — equities + energy on independent narratives.

Bands stack outward through elevatedstretchedextreme per PAIR_CONFIG in app/signals/correlations.py. Cross-pair, the structural break direction varies — SPY/VIX extreme is correlation rising past -0.30 (the inverse anchor cracking), SPY/Oil extreme is correlation past +0.75 or below -0.80 (energy directly repricing equity), and so on.

Level-quality scoring

level_quality maps each classification to a quality factor used by the score component math:

level_quality — from correlation_scoring.json

normal 1.0 Pair in healthy band — full credit for the structural relationship being intact.
elevated 0.7 Soft drift outside healthy band — partial credit.
stretched 0.3 Notable break from healthy band — minimal credit.
extreme 0.0 Regime break — pair contributes zero to the Correlations component.

The score-layer formula at runtime: weighted average of pair qualities × max_points. The Correlations component max is 5 points by default (doubles to 10 under the energy_shock regime override). A pair at normal contributes its full share of points; a pair at extreme contributes nothing.

Worked example. SPY/VIX normal (1.0), SPY/Oil elevated (0.7), SPY/DXY normal (1.0), SPY/TNX stretched (0.3). Weighted average = (1.0×1.0 + 0.7×1.0 + 1.0×0.5 + 0.3×0.5) / (1.0 + 1.0 + 0.5 + 0.5) = 2.35 / 3.0 = 0.783. Component points = 0.783 × 5 ≈ 3.9.

Fusion into the alignment category

The alignment layer maps each pair’s classification level to a directional implication via _CORR_LEVEL_IMPLICATION in implications.py:

Each pair becomes a leg; legs combine through the shared _fuse_legs helper using the alignment-layer basket weights (0.50/0.20/0.15/0.15). The fused verdict + per-leg breakdown surfaces on /api/v1/signals/alignment under categories.correlations.

Legacy single-pair fallback

When only corr_spy_oil is available — the pre-B2 contract that some backfilled rows still match — the fuse degrades to the legacy single-pair thresholds in implications.py:

This preserves byte-identical behaviour for pre-B2 callers and fixture rows. The 4-pair fuse takes over the moment any second pair is also populated. No silent degradation.

Where it surfaces

Regime-override interaction

The energy_shock regime override doubles the Correlations score-component max from 5 to 10. Under energy stress, correlation breakdowns become the dominant transmission channel — equities, oil, dollar, and rates all start moving together as the macro tape is repriced wholesale. Doubling the weight reflects that empirical observation. See regime overrides for the override schema.

See also