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 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)
Bands stack outward through elevated β stretched β extreme 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
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:
normalβ BULLISH (structural relationship intact)elevatedβ NEUTRAL (soft drift)stretchedβ BEARISH (notable break)extremeβ BEARISH (regime break)
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:
corr_spy_oil > -0.30β BULLISH (oil and equities decoupled)corr_spy_oil < -0.60β BEARISH (oil shock transmitting directly)- between β NEUTRAL
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
- API β
GET /api/v1/signals/correlationsreturns the four pair values + per-pair classifications./api/v1/signals/score/breakdownreturns the per-pair quality + weight under the Correlations component./api/v1/signals/alignmentreturns the fused verdict + per-leg legs undercategories.correlations. - Alignment card β one dot in the coincident cluster, lit by the fused implication; click for the per-leg drill-down.
- Dashboard correlations table β bespoke composite card showing all four pair values with their dot tints.
- History β
daily_signals.corr_spy_vix,corr_spy_oil,corr_spy_dxy,corr_spy_tnxcolumns persist per cycle; sparklines feed off these.
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
- Five-level signal scale β the dot levels each pair classification ultimately resolves to.
- Leading vs lagging β correlations sits in the coincident cluster.
- Regime overrides β how
energy_shockre-weights this category. - Implementation:
app/signals/correlations.py(computation +PAIR_CONFIG),app/signals/config/correlation_scoring.json(weights + level_quality),app/signals/implications.py(_correlations,_CORR_BASKET_WEIGHTS,_CORR_LEVEL_IMPLICATION).