Methodology
Methodology
The methodology is the product. Every signal documented, every weight visible, every normalization rule explained, so you can audit (or disagree with) the regime read instead of taking it on faith.
The Stance score in one paragraph
Stance is the headline daily regime read, expressed 0 to 100. A high Stance means trend, breadth, leadership, and macro are mostly aligned and supportive. A low Stance means most of those pillars are warning. The number is composed from four equally-named-but-differently-weighted subscores, then optionally pulled toward zero by a macro-stress multiplier.
The composition formula: 30% Trend, 30% Breadth, 20% Leadership, 20% Macro. Trend and Breadth carry equal headline weight because they are the two most-direct reads on the tape. Leadership and Macro are weighted lower because Leadership overlaps with Breadth (leadership signals are partially priced into breadth signals) and Macro contains the most-predictive but least-immediate inputs (yield curve and credit spreads lead recessions by 6-to-18 months, so their daily noise should not whipsaw the headline read).
How each signal is normalized
Every raw signal value (a percentage spread, a count, a ratio) gets normalized to a 0-to-100 scale before it contributes to its subscore. Three normalization kinds are in use, each chosen to match the signal's natural distribution.
Linear with fixed bounds. Used when a signal has a stable natural range. For example, the SPY-versus-50-day-moving-average signal is normalized linearly between -5% (below the 50-day, weak) and +10% (well above, strong). Above the upper bound saturates at 100; below the lower bound saturates at 0.
Percentile rank versus a 250-day window. Used when the natural range shifts with the rates regime or volatility regime. The yield curve and credit spreads use this because a 0.5% spread might be calm in some cycles and panicky in others. The percentile rank auto-calibrates: "is today's spread unusual versus the last year, and in which direction?"
Piecewise (anchor-and-interpolate). Used when the relationship is non-monotonic. The volatility regime signal uses a U-shape on the VIX axis: extreme low VIX (8-to-11, complacency) and extreme high VIX (35+, panic) both score lower than the calm middle (14-to-18). The dashboard interpolates linearly between published anchor points.
Trend subscore (30% of Stance)
Four signals, 25% each. The Trend subscore answers: is the index above important moving averages, is the trend accelerating or rolling over, and how unusual is today's pullback?
spy_vs_50d. SPY price relative to its 50-day moving average. Linear, -5% to +10%. The 50-day is roughly 10 trading weeks of price. Above and rising is the short-term trend filter every swing-trading school agrees on.
spy_vs_200d. SPY price relative to its 200-day moving average. Linear, -10% to +20%. The 200-day is roughly 40 trading weeks. The 200-day cross is the canonical bull-vs-bear dividing line.
spy_dist_from_high. Distance from the 52-week high. Percentile rank versus the trailing 250 days, with a linear fallback (-20% to 0%) for the first year of operation. Distance from the high has a skewed distribution (it is rare to be at the high, common to be 5% below), so the percentile rank auto-calibrates to "is today's pullback unusual?"
spy_200d_slope. The slope of the 200-day moving average over the last 20 sessions. Percentile-ranked over 250 days. Catches the difference between "above the 200-day, slope rising fast" (confirmed uptrend) and "above the 200-day, slope flat or rolling over" (late-cycle, topping). Replaced an earlier IWM-vs-50d signal in 2026-04 because small-cap trend was already partially read by the leadership.small_vs_large signal.
Breadth subscore (30% of Stance)
Seven signals. The Breadth subscore answers: how many stocks are actually participating?
pct_above_50d (20%). Percentage of stocks above their 50-day moving average. Linear, 0 to 1. Above 80% means broad participation; below 20% means a washout is in progress.
pct_above_200d (20%). Percentage of stocks above their 200-day moving average. Linear, 0 to 1. The structural participation read.
distribution_days (15%). Count of recent distribution days (a session declining 0.2% or more on volume higher than the prior day) on SPY and Nasdaq. Inverse-capped at 10. A count of 4 or 5 is normal; 6+ is the IBD warning threshold.
new_highs_lows (10%). The net new-52-week-high count divided by the eligible universe size. Linear, -5% to +5%. Expansion of new highs confirms uptrend; expansion of new lows confirms downtrend.
ad_ratio (5%). Daily advance-decline ratio (advancers minus decliners over total). The breadth oscillator's daily snapshot. Linear, -0.5 to +0.5.
mcclellan (15%). The McClellan Oscillator (a 19-day EMA minus a 39-day EMA of net advances). The momentum-of-breadth gauge. Linear, -100 to +100. Sustained positive readings confirm a healthy uptrend; extreme negative readings (-100 or below) often coincide with climax lows.
follow_through_day (15%). A binary 0/1 signal that flips to 1 when an IBD-style Follow-Through Day fires (a major index gains 1.7%+ on heavier volume during a rally attempt off a low). Coarse-grained but high-information at regime transitions, which is why distribution_days and follow_through_day appear together as the entry-and-exit pair for the breadth subscore.
Leadership subscore (20% of Stance)
Five signals. The Leadership subscore answers: who is driving the move?
sector_rs (40%). The fraction of S&P sectors beating SPY over the rolling lookback. Linear, 0.3 (narrow) to 0.7 (broad). Anchor signal at 40% weight because it is the broadest summary of who is leading.
small_vs_large (20%). The IWM-minus-SPY 60-day spread. Linear, -5% to +5%. Small-caps leading is a risk-on tell because small-caps are more sensitive to credit, growth expectations, and risk appetite.
qqq_vs_spy (15%). The QQQ-minus-SPY 60-day spread. Linear, -5% to +5%. Tech and growth leadership, the regime that favors the Qullamaggie-style breakout setup.
cyclical_vs_defensive (15%). Cyclicals (XLY, XLI, XLB, XLF, XLE) minus defensives (XLP, XLV, XLU) over 60 days. Linear, -3% to +3%. Positive means risk-on, negative means a defensive rotation is underway.
equal_vs_cap_weight (10%). The RSP (equal-weight S&P) minus SPY 60-day spread. Linear, -3% to +3%. Catches mega-cap masking, the regime where SPY looks healthy but the median stock is rolling over (the 2023 narrow-leadership tape).
Macro subscore (20% of Stance)
Five signals. The Macro subscore answers: are background conditions (volatility, credit, rates) helping or pressuring risk assets?
yield_curve (25%). The 10-year minus 3-month Treasury yield spread. Percentile-ranked over 250 days, with a linear fallback (-1 to +1). Forward direction: higher (steeper) is good. Auto-calibrates to the rates regime so a spread that was concerning in one cycle and normal in another scores correctly.
credit_spreads (25%). High-yield option-adjusted spread (negated, so higher equals tighter equals better). Percentile-ranked over 250 days, with a linear fallback. Captures stress relative to the regime: a 3% spread is calm in some periods and panicky in others. Credit spreads have led every major equity top since 1997 by a median of seven months.
fed_funds (15%). Year-over-year change in the effective Fed funds rate. Linear, -2 to +2. A rapidly hiking Fed pressures equities; a cutting Fed supports them.
vol_regime (20%). The VIX level (negated), normalized via a piecewise U-shape. Anchors: VIX 14-to-18 (calm sweet spot, score 90), VIX 11 (very calm, score 70 with a slight complacency penalty), VIX 8 (extreme complacency, score 40), VIX 25 (stressed, score 50), VIX 35 (deep fear, score 25), VIX 50 (extreme panic, score 50, contrarian-bullish).
vix_change (15%). The 5-day VIX change (negated, so positive equals VIX falling equals good). Linear, -5 to +5. A VIX dropping 5 points on a calm-down is excellent; a VIX rising 5 points is panic onset.
The macro-stress multiplier and the Conflict measure
Once the four subscores are composed into a raw Stance value, two adjustments happen.
The macro-stress multiplier. When the Macro subscore drops below 30, Stance gets multiplied by macro/30, scaling linearly to zero at macro=0. Above 30, no penalty. The intent: when Macro is screaming (yield curve inverted, credit spreads blowing out, VIX panicking), the headline Stance should reflect real regime risk rather than averaging "bullish trend" over "credit blowup". Modeled as a smooth gradient instead of a hard cap so the score remains explainable and does not cliff-edge at a threshold.
The Conflict measure. Standard deviation of the four subscore values. A high Conflict (above 20) means the pillars disagree. For example, Trend at 80 but Breadth at 30 (a narrow rally) or Leadership at 70 but Macro at 20 (defensive rotation despite cyclical leadership). High Conflict marks unsettled regimes where confidence in the headline number should be lower; the dashboard surfaces it explicitly so users can downweight a high Stance that is being driven by only one or two pillars.
How and when the snapshot publishes
The pipeline runs once per US trading day, after market close (approximately 21:30 UTC). It fetches end-of-day price and volume data from Polygon for the broad universe, plus FRED for the macro series (yield curve, credit spreads, Fed funds, VIX). All registered signals compute, all four subscores compose, the Stance score is calculated, and the result is written to the database in a single transaction.
Publishing discipline: only complete pipeline runs replace the prior snapshot. If a data provider is late or a compute step fails, the previous complete snapshot stays visible until the next successful run. A half-computed regime read is worse than a one-day-stale complete one.
Every snapshot is versioned via a computeVersion field. When weights change or new signals are added, the version increments so historical replay can recreate the regime read as it stood on any past trading day under the version that was in force then.
What the score is and is not
Stance is a structured way to ask whether today's conditions support pressing, pausing, or stepping back. It is not a forecast of tomorrow. It is not a buy or sell signal on any individual stock. It cannot know your holdings, your risk limits, your tax situation, or your time horizon.
The Stance bands (broadly: 70+ supportive, 40-70 mixed, below 40 defensive) are intentionally wide. They describe the environment, they do not forecast it with false precision. A stance of 65 versus 68 is the same regime; a stance of 65 versus 35 is a different conversation.
Frequently asked questions
How is the Stance score calculated?
Stance is a weighted blend of four subscores (Trend 30%, Breadth 30%, Leadership 20%, Macro 20%), each composed from named signals normalized to a 0-to-100 scale. A macro-stress multiplier pulls the headline toward zero when Macro is below 30, so a credit blowup actually drags the regime read down.
Why does Trend have the same weight as Breadth?
Trend and Breadth are the two most direct reads on the tape, and either one being weak is itself a regime warning. An uptrend with collapsing breadth (the 2023 Magnificent Seven tape) is fragile, and the 30/30 weighting ensures broken breadth pulls Stance down even when Trend looks fine.
Why is Macro only 20%?
Because Macro contains the most predictive but least immediate signals (yield curve and credit spreads lead recessions by 6 to 18 months). Overweighting them would make Stance too slow on regime change. The macro-stress multiplier handles the asymmetric downside instead, scaling Stance toward zero when Macro screams.
How are signals normalized?
Three ways. Linear with fixed bounds (when the natural range is stable, like SPY vs the 50-day average). Percentile rank versus 250 days (when the range shifts with regime, like credit spreads). Piecewise anchored interpolation (when the relationship is non-monotonic, like the U-shape on VIX where both extreme low and extreme high score lower than the calm middle).
What is the Conflict measure?
The standard deviation of the four subscore values. A high Conflict (above 20) means the pillars disagree (for example Trend at 80 but Breadth at 30). It flags unsettled regimes where confidence in the headline Stance should be lower, and the dashboard surfaces it explicitly.
How often does Stance update?
Once per US trading day, after the market close at approximately 21:30 UTC. The pipeline pulls end-of-day data, computes every signal and subscore, and writes the result in one transaction. No intraday tick chasing.
What happens if a data provider is late?
The prior complete snapshot stays visible. The dashboard never shows a half-computed regime read. If the next day's pipeline succeeds, the snapshot updates as normal; if not, the stale-but-complete read is left in place rather than replaced by partial data.
Can I see the value of every signal?
Yes. The dashboard exposes every signal value alongside the subscore it feeds and the Stance value it composes into. You can disagree with the weighting and form your own read from the components. The methodology is the product.
Is the methodology open source?
The implementation lives in a single repository under packages/core/src/signals (one file per signal) and packages/core/src/subscores/configs.ts (the weight configuration). Tickerstance.com is the live deployment; the methodology page documents the formulas and the source code documents the implementation.
How are weights chosen?
Empirically. Subscore and signal weights are tuned against historical regime turns (2008-2009, 2020 COVID, 2022 bear, 2023 narrow leadership) so the score moves with regime change rather than against it. Weights are versioned via the computeVersion field so historical snapshots can be reconstructed under the version that was active at that point in time.
Related reading