Trend hh_hl_streak

Highs/Lows streak

Detects sustained trend structure. Bullish: N consecutive days of higher highs AND higher lows. Bearish: N consecutive days of lower lows AND lower highs. Triggers on the day the streak is first confirmed.

Signal family

Trend — Signals that fire when price is continuing or reversing an established directional move. Momentum-following by nature.

Parameters

Name Description Default Range
streak Minimum consecutive days 3 2–10

Historical context

2,703,506 triggers on 23,607 tickers, 1995-10-13 → 2026-05-01. Universe: US large-cap (mcap ≥ $100,000,000, price ≥ $1). Long-only convention: BUY at open T+1, hold the horizon, compare to S&P 500 Equal Weight over the same window.

Methodology footnotes

Benchmarks shown in the detail tables: spxew (S&P 500 Equal Weight — primary, median-stock view, avoids the 2020+ megacap-concentration distortion), spx (S&P 500 cap-weighted, distorted post-2020), msci (MSCI World USD). Per-stock regime tags: trending = ADX(14) ≥ 25, high vol = 20d realized annualized vol ≥ 20%. 1d return = intraday T+1 open→close; 20d = open T+1 to close T+20.

At a glance — alpha vs S&P 500 Equal Weight, US-only

Holding-period sensitivity. Bullish columns: positive = signal worked (long the trigger beat the index). Bearish columns: negative = signal worked (the flagged stock underperformed).

Horizon Bullish α Bearish α
5-day -0.04% +0.00%
20-day +0.25% +0.04%
60-day +0.54% +0.27%
1-year +3.12% +1.89%

Sign flip across horizons. Bullish triggers go from -0.04% (5d) to +3.12% (1y) — short-term fade but longer holding recovers and wins.

Random-date null check (20-day): Bullish: beats random (p=0.005).
Bearish: beats random (p=0.010).

Where does HH_HL_STREAK actually fire?

The bucket distribution often reveals what the signal really is, regardless of its textbook label. Heavy concentration in "non-trending + high vol" = it's mostly a chop-market event. Heavy in "trending + low vol" = it picks up the smooth grinds. Read the chart before the alpha numbers — context shapes everything that follows.

Highs/Lows streak (hh_hl_streak) — trigger count distribution by per-stock regime quadrant (trending/non-trending × high/low realized volatility) for , US-only universe

Does it work in every regime?

Trigger alpha split by the host stock's own regime on the trigger date — trending or ranging, high-vol or low-vol. The 20d alpha you'd actually capture if you took the trade. Bars matching your direction's "right" sign (green) = the signal worked in that regime; opposite sign = avoid it there. A signal with one strong-positive bar and three flat ones isn't a "20d alpha" signal — it's a "20d alpha when the stock is X" signal.

Highs/Lows streak (hh_hl_streak) — mean 20-day alpha versus S&P 500 Equal Weight by per-stock regime quadrant,  side by side
Trending + Low vol
Stock in a clean directional move with low realized volatility. Textbook "trend-following paradise" — smooth grind with little whipsaw risk.
Trending + High vol
Violent directional moves — parabolic rallies, crisis selloffs. Trend exists but the path is noisy. Signal timing may be imprecise.
Non-trending + Low vol
Quiet chop, summer doldrums, consolidations. No directional bias but also no big swings — small edges become reliable if they exist at all.
Non-trending + High vol
Choppy and violent — the classical "whipsaw zone" for momentum signals. Crossovers and breakouts fire repeatedly without follow-through.

Does it work in every era?

A multi-year average can hide major instability. The sample splits into three windows: 2015–2019 (pre-COVID), 2020–2022 (pandemic + 2022 bear), and 2023+ (post-ZIRP + AI megacap rally). All three matching your direction's "right" sign = the signal is durable. One era doing all the work = a regime-specific edge that may not repeat. The bigger the variance across eras, the smaller the position you should run.

Highs/Lows streak (hh_hl_streak) — 20-day alpha split by historical sub-period (2015-2019, 2020-2022, 2023+) to check consistency across market regimes

↑ Bullish triggers

Bench Metric 1d 5d 20d 60d 252d
spx Stock % -0.03% +0.16% +1.03% +2.77% +13.26%
Bench % +0.02% +0.27% +1.03% +3.03% +13.97%
Alpha % -0.05% -0.10% +0.03% -0.27% -0.70%
Median alpha -0.10% -0.36% -0.90% -2.50% -9.35%
Hit rate (α>0) 47.3% 46.2% 45.6% 43.3% 38.9%
p (naive) <0.001 <0.001 0.0024 <0.001 <0.001
p (HAC) <0.001 <0.001 0.0120 <0.001 <0.001
N 1,291,960 1,257,419 1,243,373 1,220,910 1,075,767
msci Stock % -0.03% +0.16% +1.03% +2.77% +13.26%
Bench % +0.05% +0.25% +0.91% +2.60% +11.53%
Alpha % -0.08% -0.08% +0.17% +0.17% +1.62%
Median alpha -0.14% -0.35% -0.79% -2.07% -6.99%
Hit rate (α>0) 46.6% 46.4% 46.1% 44.4% 41.5%
p (naive) <0.001 <0.001 <0.001 <0.001 <0.001
p (HAC) <0.001 <0.001 <0.001 <0.001 <0.001
N 1,284,988 1,248,979 1,239,570 1,211,624 1,064,550
spxew Stock % -0.03% +0.16% +1.03% +2.77% +13.26%
Bench % +0.04% +0.21% +0.82% +2.22% +10.20%
Alpha % -0.08% -0.04% +0.25% +0.54% +3.12%
Median alpha -0.11% -0.29% -0.66% -1.66% -5.37%
Hit rate (α>0) 47.3% 47.0% 46.8% 45.5% 43.3%
p (naive) <0.001 <0.001 <0.001 <0.001 <0.001
p (HAC) <0.001 <0.001 <0.001 <0.001 <0.001
N 1,281,082 1,238,875 1,227,860 1,204,454 1,058,784
Distribution of all 20d alpha outcomes for this direction. Median and winsorized mean shown.
Highs/Lows streak (hh_hl_streak) — bullish 20-day alpha histogram showing distribution of per-trigger returns
Observed 20d alpha (vertical line) against the null distribution of random-date firing. If the line is deep inside the null cloud, the signal adds no information. If it sits in a tail, the signal is doing real work in that direction.
Highs/Lows streak (hh_hl_streak) — bullish 20-day observed alpha versus random-date permutation null (200 iterations)
Permutation null detail — all horizons × both benchmarks
200-iteration null: for each ticker, sample N random dates from its history (matching observed trigger count) and compute the same alpha. Both observed and null are baseline-centered per ticker (each ticker's own baseline alpha is subtracted), so the null distribution is centered on ~0 and the comparison tests signal effect alone — not the universe-selection lift that all surviving large-caps share. pperm = one-sided fraction of null iters with mean in the "signal was right" tail (right for bullish, left for bearish).
Horizon Bench Observed lift Null mean Null 95% CI pperm
1d spx +0.06% +0.08% [+0.07%, +0.08%] 1.000
1d msci +0.06% +0.08% [+0.08%, +0.09%] 1.000
1d spxew +0.05% +0.07% [+0.06%, +0.07%] 1.000
5d spx +0.28% +0.33% [+0.32%, +0.34%] 1.000
5d msci +0.30% +0.33% [+0.32%, +0.35%] 1.000
5d spxew +0.30% +0.31% [+0.30%, +0.32%] 1.000
20d spx +1.17% +1.07% [+1.05%, +1.09%] 0.005
20d msci +1.19% +1.08% [+1.07%, +1.10%] 0.005
20d spxew +1.15% +1.04% [+1.02%, +1.06%] 0.005
60d spx +2.14% +2.37% [+2.33%, +2.41%] 1.000
60d msci +2.16% +2.39% [+2.35%, +2.43%] 1.000
60d spxew +2.20% +2.30% [+2.27%, +2.34%] 1.000
252d spx +4.51% +4.83% [+4.75%, +4.89%] 1.000
252d msci +4.50% +4.77% [+4.70%, +4.84%] 1.000
252d spxew +4.28% +4.49% [+4.41%, +4.56%] 1.000

Example triggers on US large-caps (2023+, mcap ≥ $30B)

Six recent bullish HH_HL_STREAK triggers on US mega-caps. Top three: the signal's best outcomes. Bottom three: the worst. Catalyst-driven outliers (|α| > 25%) excluded so what's left is the signal's own typical good and bad days, not earnings shocks.

Strongest outcomes (what HH_HL_STREAK looks like when it works)
Weakest outcomes (what HH_HL_STREAK looks like when it fails)
Stock-regime quadrants (2×2 per-stock, 20d alpha detail table)
Each quadrant groups triggers by the stock's own ADX(14) and RV(20) at the trigger date — the textbook conditioning variable (not market-level). Stock %, bench %, alpha %, and HAC p-value shown for each benchmark.
Quadrant N Stock % (spx) Bench % (spx) Alpha % (spx) p (HAC) Stock % (msci) Bench % (msci) Alpha % (msci) p (HAC) Stock % (spxew) Bench % (spxew) Alpha % (spxew) p (HAC)
Trending + Low vol Clean directional grind, low whipsaw 79,621 +0.46% +0.65% -0.19% <0.001 +0.46% +0.52% -0.05% 0.0584 +0.46% +0.32% +0.16% <0.001
Trending + High vol Crisis selloff or parabolic rally 481,979 +1.21% +1.12% +0.13% <0.001 +1.21% +0.96% +0.30% <0.001 +1.21% +0.90% +0.37% <0.001
Non-trending + Low vol Quiet chop, summer doldrums 146,162 +0.58% +0.72% -0.17% <0.001 +0.58% +0.59% -0.03% 0.1020 +0.58% +0.44% +0.13% <0.001
Non-trending + High vol Classical "whipsaw zone" for momentum 622,493 +1.11% +1.08% +0.07% 0.0002 +1.11% +0.98% +0.17% <0.001 +1.11% +0.92% +0.23% <0.001
Sub-period breakdown table (20d alpha)
Historical clustering check. If alpha concentrates in one era, the signal's robustness is questionable.
Period N Alpha % (spx) p (HAC) Alpha % (msci) p (HAC) Alpha % (spxew) p (HAC)
2015-2019 2015-01-01 → 2020-01-01 368,182 -0.35% <0.001 -0.17% <0.001 -0.09% <0.001
2020-2022 2020-01-01 → 2023-01-01 412,942 +0.40% <0.001 +0.55% <0.001 +0.14% <0.001
2023-2026 2023-01-01 → 2099-01-01 548,959 +0.02% 0.3192 +0.12% <0.001 +0.58% <0.001

↓ Bearish triggers negative alpha = signal was right (stock underperformed market)

Bench Metric 1d 5d 20d 60d 252d
spx Stock % -0.02% +0.19% +1.00% +3.02% +12.46%
Bench % +0.00% +0.21% +1.21% +3.35% +14.26%
Alpha % -0.03% -0.02% -0.15% -0.33% -1.80%
Median alpha -0.05% -0.22% -1.04% -2.62% -10.51%
Hit rate (α>0) 48.7% 47.9% 45.1% 43.3% 38.2%
p (naive) <0.001 0.0001 <0.001 <0.001 <0.001
p (HAC) <0.001 0.0001 <0.001 <0.001 <0.001
N 1,332,659 1,287,936 1,281,654 1,245,019 1,117,686
msci Stock % -0.02% +0.19% +1.00% +3.02% +12.46%
Bench % +0.04% +0.22% +1.10% +3.02% +11.97%
Alpha % -0.06% -0.02% -0.06% +0.07% +0.24%
Median alpha -0.09% -0.23% -0.95% -2.24% -8.34%
Hit rate (α>0) 48.0% 47.7% 45.5% 44.1% 40.3%
p (naive) <0.001 0.0002 <0.001 0.0003 <0.001
p (HAC) <0.001 0.0002 <0.001 0.0273 0.1232
N 1,325,341 1,281,407 1,269,455 1,239,091 1,111,263
spxew Stock % -0.02% +0.19% +1.00% +3.02% +12.46%
Bench % +0.04% +0.19% +1.00% +2.76% +10.57%
Alpha % -0.06% +0.00% +0.04% +0.27% +1.89%
Median alpha -0.08% -0.20% -0.80% -1.93% -6.74%
Hit rate (α>0) 48.3% 48.1% 46.2% 44.9% 42.0%
p (naive) <0.001 0.6486 <0.001 <0.001 <0.001
p (HAC) <0.001 0.6512 0.0002 <0.001 <0.001
N 1,321,148 1,275,289 1,266,679 1,229,786 1,103,964
Distribution of all 20d alpha outcomes for this direction. Median and winsorized mean shown.
Highs/Lows streak (hh_hl_streak) — bearish 20-day alpha histogram showing distribution of per-trigger returns
Observed 20d alpha (vertical line) against the null distribution of random-date firing. If the line is deep inside the null cloud, the signal adds no information. If it sits in a tail, the signal is doing real work in that direction.
Highs/Lows streak (hh_hl_streak) — bearish 20-day observed alpha versus random-date permutation null (200 iterations)
Permutation null detail — all horizons × both benchmarks
200-iteration null: for each ticker, sample N random dates from its history (matching observed trigger count) and compute the same alpha. Both observed and null are baseline-centered per ticker (each ticker's own baseline alpha is subtracted), so the null distribution is centered on ~0 and the comparison tests signal effect alone — not the universe-selection lift that all surviving large-caps share. pperm = one-sided fraction of null iters with mean in the "signal was right" tail (right for bullish, left for bearish).
Horizon Bench Observed lift Null mean Null 95% CI pperm
1d spx +0.10% +0.09% [+0.08%, +0.09%] 1.000
1d msci +0.09% +0.09% [+0.09%, +0.10%] 0.692
1d spxew +0.08% +0.08% [+0.08%, +0.08%] 0.562
5d spx +0.40% +0.38% [+0.37%, +0.39%] 1.000
5d msci +0.39% +0.38% [+0.37%, +0.40%] 0.950
5d spxew +0.38% +0.36% [+0.35%, +0.37%] 1.000
20d spx +1.18% +1.21% [+1.19%, +1.23%] 0.010
20d msci +1.16% +1.23% [+1.21%, +1.25%] 0.005
20d spxew +1.15% +1.19% [+1.17%, +1.20%] 0.010
60d spx +2.65% +2.63% [+2.60%, +2.66%] 0.821
60d msci +2.64% +2.66% [+2.63%, +2.70%] 0.129
60d spxew +2.51% +2.57% [+2.54%, +2.61%] 0.005
252d spx +5.26% +5.31% [+5.24%, +5.38%] 0.065
252d msci +5.05% +5.26% [+5.20%, +5.33%] 0.005
252d spxew +4.91% +4.99% [+4.92%, +5.07%] 0.020

Example triggers on US large-caps (2023+, mcap ≥ $30B)

Six recent bearish HH_HL_STREAK triggers on US mega-caps. Top three: the signal's best outcomes. Bottom three: the worst. Catalyst-driven outliers (|α| > 25%) excluded so what's left is the signal's own typical good and bad days, not earnings shocks.

Strongest outcomes (what HH_HL_STREAK looks like when it works)
Weakest outcomes (what HH_HL_STREAK looks like when it fails)
Stock-regime quadrants (2×2 per-stock, 20d alpha detail table)
Each quadrant groups triggers by the stock's own ADX(14) and RV(20) at the trigger date — the textbook conditioning variable (not market-level). Stock %, bench %, alpha %, and HAC p-value shown for each benchmark.
Quadrant N Stock % (spx) Bench % (spx) Alpha % (spx) p (HAC) Stock % (msci) Bench % (msci) Alpha % (msci) p (HAC) Stock % (spxew) Bench % (spxew) Alpha % (spxew) p (HAC)
Trending + Low vol Clean directional grind, low whipsaw 65,023 +0.20% +1.05% -0.82% <0.001 +0.20% +0.91% -0.68% <0.001 +0.20% +0.73% -0.48% <0.001
Trending + High vol Crisis selloff or parabolic rally 491,358 +1.55% +1.28% +0.34% <0.001 +1.55% +1.11% +0.45% <0.001 +1.55% +0.99% +0.59% <0.001
Non-trending + Low vol Quiet chop, summer doldrums 131,551 +0.16% +1.05% -0.86% <0.001 +0.16% +0.92% -0.72% <0.001 +0.16% +0.77% -0.57% <0.001
Non-trending + High vol Classical "whipsaw zone" for momentum 685,298 +0.87% +1.22% -0.28% <0.001 +0.87% +1.13% -0.22% <0.001 +0.87% +1.07% -0.16% <0.001
Sub-period breakdown table (20d alpha)
Historical clustering check. If alpha concentrates in one era, the signal's robustness is questionable.
Period N Alpha % (spx) p (HAC) Alpha % (msci) p (HAC) Alpha % (spxew) p (HAC)
2015-2019 2015-01-01 → 2020-01-01 359,865 -0.50% <0.001 -0.32% <0.001 -0.34% <0.001
2020-2022 2020-01-01 → 2023-01-01 437,420 +0.19% <0.001 +0.30% <0.001 -0.06% 0.0079
2023-2026 2023-01-01 → 2099-01-01 575,778 -0.19% <0.001 -0.17% <0.001 +0.37% <0.001

Methodology and caveats

How to read. Entry at open of T+1 (one trading day after the signal fires on close of T). 20d = open T+1 to close T+20. Alpha = stock return − benchmark return over the same window (Convention A, single-sided, textbook). For bullish triggers, POSITIVE alpha = signal was right. For bearish triggers, NEGATIVE alpha = signal was right (stock underperformed market). No sign-flipping; the direction of the bet determines what "good" looks like. Per-stock regime is each stock's own ADX(14) and RV(20) at the trigger date — not market-wide state.

Three p-values, three robustness tests. (a) p_naive: scipy one-sample t-test on winsorized alphas. Optimistic because overlapping 20d windows on the same ticker inflate effective N. (b) p_hac: Newey-West HAC with lag = horizon — corrects for the overlap and is the academic-finance standard. (c) p_perm: fraction of 200 random-date null iterations with mean ≥ observed. Tests whether the signal beats random date selection at all. A signal that clears all three (pnaive, phac, pperm all < 0.05) has real information; a signal that fails pperm has zero edge even if the t-test says "significant."

Caveats. (i) Universe reflects today's active tickers; delisted losers pruned → survivorship bias. (ii) Mcap ≥ $100M filter uses today's snapshot, not point-in-time — mild lookahead on which stocks enter the sample, not on returns. (iii) Means and p-values use winsorized alphas (1/99 percentile) to prevent data errors from dominating. Medians and hit rates use raw data. (iv) Zero transaction costs assumed. Realistic bid-ask + commissions remove 20–40bps from 20d alpha on US large-caps, more on small-cap. Sub-20bps alpha is noise in practice. (v) Past performance does not predict future results.

How to use this

1 · When to reach for this signal

Highs/Lows streak has real edge in both directions. 20d alpha: bullish +0.03% (beats random ), bearish -0.15% (beats random ). Both are legitimate tiles in a long/short screen stack.

2 · When it works — the setups that drive it

  • Best bullish setup: Trending + High vol — alpha +0.13% / 20d on 481,979 historical triggers.
  • Best bearish setup: Trending + High vol — alpha +0.34% / 20d on 491,358 historical triggers.
  • Best era for bullish: 2020-2022 — alpha +0.40% / 20d.
  • Best era for bearish: 2020-2022 — alpha +0.19% / 20d.

3 · When it fails — common false positives

  • Weakest bullish cell: Trending + Low vol — alpha -0.19% / 20d on 79,621 triggers.
  • Weakest bearish cell: Non-trending + Low vol — alpha -0.86% / 20d on 131,551 triggers.
  • Worst era for bullish: 2015-2019 — alpha -0.35% / 20d.
  • Worst era for bearish: 2015-2019 — alpha -0.50% / 20d.

Signal-specific failure patterns

Both directions are real but small on equal-weight
Higher-high / higher-low streak (N consecutive HH+HL bars) and the symmetric LH/LL streak both show modest positive alpha on equal-weight that the random-date null check confirms is real — but the magnitudes are small. See the at-a-glance table for current values. The signal captures persistent directional flow at the per-stock level, not a regime-changing edge.
2020-2022 was a noise period
The COVID-era window saw both directions of the streak signal lose their sign. Outside that window the bearish (LH+LL) side is more consistent than the bullish side. If trading the signal directly, weight conviction toward eras that resemble the post-2023 dispersion regime rather than the COVID rebound.

4 · Pairing inside a screen

The statements below describe how this signal relates to others by construction — which indicator family it belongs to, and where same-family redundancy might reduce the independence of evidence inside a Daily Report. These are taxonomic classifications drawn from standard technical-analysis texts; they are not pairing backtests. A multi-signal convergence backtest is planned but not yet run.

Trend-structure family

Higher-high / higher-low streak encodes the classical Dow-theory definition of an uptrend — a price structure of successively higher swing highs and swing lows (Dow theory as presented in Murphy, Technical Analysis of the Financial Markets, 1999; Edwards & Magee, Technical Analysis of Stock Trends, 11th ed. 2018). This overlaps with HH/HL structure, moving-average crossover, and long-term trend-break signals, which infer the same trend state from different measurements.

What would likely rescue this signal

This block calls out the data or conditions that could turn a technically weak signal into a usable one in a composite screen. Based on signal mechanics and the observed failure patterns above; individual combinations are not yet backtested.

  • Reverse-engineer the bullish thesisIf the bullish side fires late, a streak-RESET signal (first HH+HL after a downtrend break) might capture the bullish turn earlier. Separate signal design task.
  • Hold trades the full 60d windowAlpha compounds from 20d to 60d on both sides. Time stops above profit stops.

See also Why technical-only signals don't survive on their own for the broader argument.

5 · Before you act — a 5-point checklist

  1. Normal trading day? Rule out earnings (within ±3 days), ex-dividend, or known corporate-action dates — the signal is almost certainly reading noise, not momentum, in those windows.
  2. Where is price vs its own 50 / 200 DMA? A trend signal is only as credible as the underlying trend it claims to confirm. Check the 200DMA orientation before acting.
  3. What's the sector breadth doing? An isolated signal in a broadly down-trending sector is a lower-confidence setup than one firing with the rest of its peer group.
  4. Is ADV20 enough for your size? If the trigger is on a $500M name and you want to move $1M notional, you're the tape. Consider adv20d ≥ 5% of your intended position.
  5. What invalidates you? Define a price level (for longs: a close below the trigger-day low; for shorts: close above the trigger-day high) and honor it. The backtest alpha is an average; any one trade can be at either tail.

Execution notes

Modest edge in both directions, with longer horizons compounding more than the 20-day point estimate suggests. Entry open T+1. Most useful as a regime confirmation tile layered with other triggers; the standalone alpha is too small to justify high turnover.