Date: 2026-03-30
Status: Draft
Pipeline ID: shakeout
Source: Reminiscences of a Stock Operator (Livermore), Market Wizards (Schwager), Wall Street Phantom (Simpson)
Related: Pure price action — no fundamentals required
When price breaks through a key level (support or resistance) but immediately reverses back through the level — trapping weak hands who entered on the breakout. The "smart money" has used the breakout to distribute to weak buyers (shakeout) or cover shorts (false breakout), then pushes price back through the level.
This is a mean-reversion / contrarian setup that trades the reversal, not the breakout. It is the mirror image of the breakout pipelines (Darvas, Turtle, Minervini) — where those catch continuation after a clean break, the shakeout catches the failed break.
| Variant | Direction | Mechanism | Entry Trigger |
|---|---|---|---|
| Long Shakeout Trap | Bullish | Price breaks below support → stops hit → immediate reversal above | Strong bar closes above broken support |
| Short False Breakout | Bearish | Price breaks above resistance → weak buyers chase → immediate reversal below | Strong bar closes below broken resistance |
Both share the same scoring logic; direction is determined by:
| Existing Pipeline | Entry Trigger | Opposite / Complementary |
|---|---|---|
| Darvas Box | Price breaks above box high | False breakout above box = shakeout short |
| Turtle | Price breaks above 20-day high | Failed 20-day high breakout = shakeout |
| Brooks Pullback | Price pulls back to EMA | If pullback accelerates through EMA = potential shakeout |
| Minervini Breakout | Price breaks above pivot | Failed pivot = shakeout |
The shakeout pipeline is a quality filter on all breakout pipelines: if a breakout immediately fails, the shakeout pipeline scores it as a potential reversal.
| Level Type | Description | Confidence |
|---|---|---|
| ATH / ATL | All-time high / low | Highest |
| 52-Week High / Low | Within current year | High |
| Reaction High / Low | Prior swing highs / lows (≥10 bars apart) | Medium-High |
| Structure Break | Prior consolidation boundary | Medium |
| Trendline | Diagonal support / resistance | Medium |
| Gap Level | Prior unfilled gap | Medium |
For each candidate level within lookback window (e.g., 100 bars):
1. Count how many times price approached the level (within 1 ATR)
2. If approached ≥2 times → valid level
3. Rate by: recency of last touch, number of tests, level type
Shakeout (false break):
True breakdown (invalid for shakeout — price continues in the direction of the break):
Shakeout Score = TrendFilter(30) + Setup(40) + SignalBar(15) + Volume(15) - RiskGates
TrendFilter (shared, 30 pts): Same as all pipelines. Biases toward:
Setup (pipeline-specific, 40 pts): Level quality + break quality + pre-break behavior
SignalBar (shared, 15 pts): Reversal bar quality — same function as all pipelines
Volume (shared, 15 pts): Volume pattern — same function as all pipelines
| Factor | Points | Description |
|---|---|---|
| Level type | 0-6 | ATH/ATL=6, 52w=5, Reaction=4, Structure=3, Trendline=2, Gap=2 |
| Level tests | 0-5 | 1 test=1, 2 tests=3, 3+ tests=5 |
| Recency | 0-4 | Tested in last 20 bars=4, 20-50 bars=2, 50+ bars=1, never=0 |
| Factor | Points | Description |
|---|---|---|
| Break conviction | 0-5 | Gap break=5, Strong bar=4, Moderate bar=2 |
| Reversal speed | 0-5 | Next bar=5, 2 bars=4, 3 bars=2, 4+ bars=0 |
| Break depth | 0-3 | <0.5%=3, 0.5-1%=2, 1-2%=1, >2%=0 |
| Gap fill (for reversal) | 0-2 | If gap was filled on reversal=2 |
| Factor | Points | Description |
|---|---|---|
| Pre-break extension | 0-4 | Extended from EMA20=0, Near=2, Touching=4 |
| Climax move before break | 0-3 | Strong climax bar just before break=3 (distribution pattern) |
| Volume dry-up before break | 0-3 | Volume contracting into the level=3 (exhaustion) |
Long Shakeout: Buy when:
Short False Breakout: Sell when:
| Variant | Stop Level |
|---|---|
| Long Shakeout | Below the shakeout bar's low (tight stop — 1-2 ATR) |
| Short False Breakout | Above the false breakout bar's high (1-2 ATR) |
Stop distance is typically tighter than other pipelines because the entry is "late" (after reversal confirmation). Penalty based on ATR distance:
4 ATR: -5 penalty
| Sub-Factor | Category | Range | Description |
|---|---|---|---|
levelType |
ShakeoutLevel | [0, 6] | ATH, 52w, reaction, etc. |
levelTests |
ShakeoutLevel | [0, 5] | Number of prior approaches |
levelRecency |
ShakeoutLevel | [0, 4] | When last tested |
breakConviction |
ShakeoutBreak | [0, 5] | Gap vs bar break |
reversalSpeed |
ShakeoutBreak | [0, 5] | Bars until reversal |
breakDepth |
ShakeoutBreak | [0, 3] | How far through level |
gapFill |
ShakeoutBreak | [0, 2] | Gap was filled |
preExtension |
ShakeoutContext | [0, 4] | Pre-break EMA distance |
preClimax |
ShakeoutContext | [0, 3] | Climax before break |
preVolumeDryup |
ShakeoutContext | [0, 3] | Volume contraction |
stopPenalty |
ShakeoutPenalties | [-5, 0] | ATR-based stop distance |
| File | Purpose |
|---|---|
convex/services/scoringShakeout.ts |
Shakeout setup scoring (level quality, break quality, context) |
convex/services/scoringShakeoutLevels.ts |
Key level detection (ATH/52w/reaction/trendline) |
convex/services/scoringShakeoutBreak.ts |
Break validation and reversal detection |
| File | Changes |
|---|---|
convex/types/stockBreakout.ts |
Add 'shakeout' to PipelineId union + entryMode union |
convex/services/scoringThresholds.ts |
Add SHAKEOUT namespace with level/break/context thresholds |
convex/services/scoringBreakout.ts |
Add buildShakeoutResult() builder; call shakeout scoring in orchestrator |
shared/src/core/scoring/pipelineRegistry.ts |
Add shakeout pipeline definition |
convex/services/scoringRiskGates.ts |
Ensure SRG rule applies to shakeout |
amber-500 (#f59e0b) — orange/amber for "trap" conceptred-500 for short variant connotationThe shakeout pipeline should be computed in addition to the existing pipelines, not replacing them. A stock can simultaneously have:
The max(pipelines) selection means: if a stock has both a clean breakout AND a shakeout signal, whichever scores higher wins.
Alternatively, shakeout can be used as a negative signal: if any breakout pipeline is active AND the shakeout pipeline is also scoring high (failed break detected), reduce the breakout pipeline's score or flag it as suspicious.
This is a design choice: "trade the breakout OR the failed breakout" vs "avoid breakouts that are failing."
→ Not a shakeout. If the break continues, the relevant pipeline is whatever the trend suggests (Turtle for long continuation, or no trade).
→ Likely a true breakdown, not a shakeout. Score 0.
→ Score the most recent valid level break. Older levels are lower priority.
→ If the break has very low volume (no one was watching this level), it's less likely to be an intentional stop hunt. Score lower on breakConviction.
→ Apply earnings risk gate. Shakeouts near earnings are risky (catalyst may resolve against you).
interface ShakeoutResult {
score: number;
direction: 'long' | 'short' | 'none';
levelPrice: number;
levelType: 'ath' | '52w' | 'reaction' | 'structure' | 'trendline';
breakConviction: number;
reversalSpeed: number;
reversalBarQuality: number;
stopLevel: number;
}
// 1. Find candidate levels (ATH, 52w, reaction highs/lows, trendlines)
// 2. For each level, check if price recently broke through it
// 3. If break detected, validate it's a shakeout (not true breakdown):
// - Reversal within N bars
// - Reversal bar conviction
// - Volume pattern (contracting on reversal)
// 4. Score the shakeout quality
// 5. Return best result if score > threshold
function detectShakeout(ohlc: OHLC[], atr: number): ShakeoutResult[] {
const levels = detectKeyLevels(ohlc); // ATH, 52w, reaction highs/lows
const results: ShakeoutResult[] = [];
for (const level of levels) {
const breakEvent = detectLevelBreak(ohlc, level);
if (!breakEvent) continue;
const reversalBar = findReversalBar(ohlc, breakEvent);
if (!reversalBar) continue;
const isShakeout = validateShakeout(breakEvent, reversalBar, atr);
if (!isShakeout) continue;
const score = scoreShakeoutSetup(breakEvent, reversalBar, atr);
results.push({ score, ... });
}
return results.sort((a, b) => b.score - a.score);
}
Bidirectional vs. separate pipelines: Should shakeout be one pipeline (bidirectional) or two (shakeout_long, shakeout_short)? The current architecture uses separate long/short pipelines (e.g., pullback/short_pullback).
Interaction with other pipelines: Should a high shakeout score on a stock with a breakout active reduce the breakout score? Or should they compete independently via max()?
Level types to support: ATH/52w are easiest to detect reliably. Reaction highs/lows require swing detection. Trendlines require drawing. Should we support all three or start with just ATH/52w?
Lookback window for level detection: 100 bars? 200 bars? Should be long enough to catch multi-month levels but not so long that stale levels are scored.
Reversal bar threshold: What constitutes "immediate" reversal — 3 bars? 5 bars? This affects how many shakeouts are detected vs. missed.