Pattern double_top_breakout

Double Top Breakout

Bullish: two peaks test the same resistance level, then price breaks out above. Tolerance is normalized by daily volatility (z-scores). Requires minimum 8% retracement between peaks.

Signal family

Pattern — Formal chart-pattern detectors (double tops / bottoms, failed breakouts, HH/HL structure).

Parameters

Name Description Default Range
peak_order Peak detection window 15 5–25
tolerance_zscore Tolerance (z-scores of daily vol) 1.5 0.5–3.0
min_separation Min days between peaks 25 10–60
max_separation Max days between peaks 252 60–504

Historical context

109,934 triggers on 21,077 tickers, 1988-08-31 → 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 α
5-day -0.47%
20-day -0.49%
60-day +0.01%
1-year +3.90%

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

Random-date null check (20-day): Bullish: worse than random (p=1.000)

Double Top Breakout is a single-direction signal — only the bullish side is meaningful.

Where does DOUBLE_TOP_BREAKOUT 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.

Double Top Breakout (double_top_breakout) — 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.

Double Top Breakout (double_top_breakout) — 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.

Double Top Breakout (double_top_breakout) — 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.20% -0.32% +0.06% +1.98% +13.20%
Bench % +0.01% +0.20% +0.89% +2.94% +13.65%
Alpha % -0.22% -0.51% -0.78% -0.96% -0.55%
Median alpha -0.22% -0.79% -1.94% -3.75% -10.52%
Hit rate (α>0) 45.0% 43.0% 41.4% 40.6% 38.2%
p (naive) <0.001 <0.001 <0.001 <0.001 0.0026
p (HAC) <0.001 <0.001 <0.001 <0.001 0.3563
N 106,909 103,270 102,667 100,650 85,944
msci Stock % -0.20% -0.32% +0.06% +1.98% +13.20%
Bench % +0.04% +0.18% +0.74% +2.45% +10.94%
Alpha % -0.24% -0.51% -0.64% -0.51% +2.09%
Median alpha -0.25% -0.78% -1.77% -3.27% -7.88%
Hit rate (α>0) 44.4% 43.1% 42.0% 41.7% 41.0%
p (naive) <0.001 <0.001 <0.001 <0.001 <0.001
p (HAC) <0.001 <0.001 <0.001 <0.001 0.0004
N 106,203 102,600 102,333 99,676 85,353
spxew Stock % -0.20% -0.32% +0.06% +1.98% +13.20%
Bench % +0.02% +0.16% +0.62% +1.94% +9.40%
Alpha % -0.23% -0.47% -0.49% +0.01% +3.90%
Median alpha -0.23% -0.72% -1.59% -2.72% -6.04%
Hit rate (α>0) 45.3% 43.6% 42.9% 43.2% 42.8%
p (naive) <0.001 <0.001 <0.001 0.8516 <0.001
p (HAC) <0.001 <0.001 <0.001 0.8930 <0.001
N 105,982 101,877 101,490 99,245 84,794
Distribution of all 20d alpha outcomes for this direction. Median and winsorized mean shown.
Double Top Breakout (double_top_breakout) — 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.
Double Top Breakout (double_top_breakout) — 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.10% +0.09% [+0.07%, +0.10%] 1.000
1d msci -0.10% +0.09% [+0.08%, +0.11%] 1.000
1d spxew -0.09% +0.08% [+0.06%, +0.09%] 1.000
5d spx -0.08% +0.36% [+0.32%, +0.40%] 1.000
5d msci -0.08% +0.36% [+0.33%, +0.40%] 1.000
5d spxew -0.08% +0.34% [+0.31%, +0.38%] 1.000
20d spx +0.41% +1.15% [+1.08%, +1.22%] 1.000
20d msci +0.44% +1.16% [+1.09%, +1.23%] 1.000
20d spxew +0.47% +1.12% [+1.05%, +1.19%] 1.000
60d spx +1.34% +2.46% [+2.34%, +2.57%] 1.000
60d msci +1.36% +2.47% [+2.37%, +2.59%] 1.000
60d spxew +1.56% +2.39% [+2.29%, +2.50%] 1.000
252d spx +3.62% +4.95% [+4.70%, +5.15%] 1.000
252d msci +3.81% +4.86% [+4.61%, +5.06%] 1.000
252d spxew +3.96% +4.61% [+4.35%, +4.82%] 1.000

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

Six recent bullish DOUBLE_TOP_BREAKOUT 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 DOUBLE_TOP_BREAKOUT looks like when it works)
Weakest outcomes (what DOUBLE_TOP_BREAKOUT 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 8,676 +0.25% +0.78% -0.50% <0.001 +0.25% +0.60% -0.31% <0.001 +0.25% +0.34% -0.06% 0.4774
Trending + High vol Crisis selloff or parabolic rally 52,154 +0.11% +0.90% -0.72% <0.001 +0.11% +0.76% -0.58% <0.001 +0.11% +0.60% -0.39% <0.001
Non-trending + Low vol Quiet chop, summer doldrums 7,529 +0.10% +0.65% -0.56% <0.001 +0.10% +0.44% -0.35% <0.001 +0.10% +0.30% -0.20% 0.0115
Non-trending + High vol Classical "whipsaw zone" for momentum 41,575 +0.05% +0.95% -0.86% <0.001 +0.05% +0.81% -0.74% <0.001 +0.05% +0.76% -0.66% <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 29,932 -1.12% <0.001 -0.89% <0.001 -0.79% <0.001
2020-2022 2020-01-01 → 2023-01-01 30,749 -0.46% <0.001 -0.33% 0.0002 -0.71% <0.001
2023-2026 2023-01-01 → 2099-01-01 49,194 -0.75% <0.001 -0.66% <0.001 -0.15% 0.0473

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

Caution recommended. Bullish 20d alpha is -0.78% and worse than random — triggering on random dates would have produced better long-side returns. Either direction fails the "beats random" test. Don't use Double Top Breakout as a standalone entry trigger. It may still be useful as part of a composite (section 4).

2 · When it works — the setups that drive it

  • Best bullish setup: Trending + Low vol — alpha -0.50% / 20d on 8,676 historical triggers.
  • Best era for bullish: 2020-2022 — alpha -0.46% / 20d.

3 · When it fails — common false positives

  • Weakest bullish cell: Non-trending + High vol — alpha -0.86% / 20d on 41,575 triggers.
  • Worst era for bullish: 2015-2019 — alpha -1.12% / 20d.

Signal-specific failure patterns

Worst bullish-signal alpha in the suite
Double-top breakout bullish at α=−0.80 at 20d (p(HAC)<1e-19, p_perm=1.000) — among the worst point-estimate alphas of any signal. 60d widens to −1.14. Consistent across all sub-periods: 2015-2019 α=−1.15 (worst), 2020-2022 α=−0.44, 2023-2026 α=−0.78. The 'breakout above resistance, chase the continuation' thesis is a structural loser on US large-caps.
evidence: bullish 20d α=−0.80 p_perm=1.000; 60d α=−1.14
The pattern itself is noisy as an entry trigger
Double-top breakout fires when price breaks above the resistance line defined by two earlier highs. Structurally, the thesis is: 'resistance has been tested twice and broken, next leg up is likely'. The data says the opposite — the break above resistance tends to be the top, not the start of a new leg. Classic 'buy the breakout, sell the news' failure pattern.
evidence: consistently negative forward returns, not era-specific
Pattern signal, low N per ticker
19k triggers over 10 years across 3,600 tickers = ~5 triggers per ticker. Double-top pattern is rare by design; individual signals are high-conviction by the signal's logic. The fact that they underperform by 80 bps/month is damning — not explained by trigger-frequency noise.

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.

Reversal-pattern family

Double top and double bottom are canonical two-swing reversal patterns (Edwards & Magee, Technical Analysis of Stock Trends, 11th ed. 2018; Bulkowski, Encyclopedia of Chart Patterns, 3rd ed. 2021). Their statistical properties have been studied in peer-reviewed work (Lo, Mamaysky, and Wang, "Foundations of Technical Analysis", Journal of Finance 55(4), 2000). A completed double-top breakout and a failed-double-top signal on the same stock fire in sequence rather than concurrently — they represent different stages of the same pattern.

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.

  • Volume + sector gateA double-top breakout on 2× volume in a strong sector is plausibly a different population than the default. Filter cost is ~60% of triggers; concentrates the remaining sample. Testable.
  • Use as opportunistic short setupIf the breakout fails (closes back below the double-top level within 5-10 days), THAT is the signal — it's captured by the failed_double_top module. Skip the straight breakout; wait for the failure confirmation.
  • Require structural consolidation pre-breakoutTight-range consolidation (<5% range over 20d) before a breakout is structurally different from a straight-line rally to new highs. Filter derivable from OHLC.

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? Pattern signals carry their own structural context; check that the implied support/resistance levels have historical relevance, not just the most-recent 3-month range.
  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

Pattern fires on only ~5 times per ticker per decade, but the signal's forward returns are consistently negative. Treat it as a documentation-only signal; skip as a primary trigger. If traded, entry open T+1 with tight invalidation (close back below breakout level within 5 days = exit). Raw signal as entry trigger underperforms passive SPX by 80-114 bps per month.