Price Action: Swings, Pivots, Highs and Lows
The chart without indicators is the cleanest chart you will ever read.
4.0Why this chapter exists
Pure price action, the chart with no overlays, is the most data-efficient way to read a market. Every indicator on the screen is a function of price; reading the price directly removes a layer of lossy compression. A trader who is fluent in structural reading can extract more from a clean candlestick chart than an indicator-saturated trader can extract from twelve overlaid signals.
Chapter 2 established the formal swing structure (the ATR-conditioned ZigZag) and the sequence taxonomy (HH, HL, LH, LL, BoS, CHoCH). This chapter takes that vocabulary and turns it into something you can read off a chart in real time.
We start with pivot strength, which is the parameter most retail charts get wrong, and the ATR-conditioned ZigZag in pseudocode you can implement on any platform. Trend channels follow, with the two-line model and a candid look at its limitations (most channels redraw before they predict). The failed-swing pattern comes after that, and it is one of the highest-quality reversal signals in pure price action when its four conditions all hold. The chapter closes by connecting structural reading to the institutional concepts in Part III, where order flow and volume profile compound on top of clean structural reads.
The difficulty is intermediate. Chapter 2 is a prerequisite. The chapter is also one of the shorter ones in the book, because price action does not need eight thousand words of explanation. It needs precise definitions and disciplined practice.
4.1Pivot strength: how many bars should a pivot dominate?
A swing high or low requires a pivot bar to be a local extremum within some window. The window size, called pivot strength or fractal strength, controls how often pivots get labeled.
The tradeoff
A small pivot strength (N = 1 or 2) labels almost every local extremum. The chart fills with pivot dots, most of which represent noise. A large pivot strength (N = 10 or more) labels only the most prominent extremes. The chart is clean but slow; you find out about a pivot ten bars after it formed, which on a 5-min chart is fifty minutes too late for an entry.
The right pivot strength depends on the timeframe and the trader's purpose:
- 5-minute intraday on liquid index futures: N = 3 to 5 is the practitioner sweet spot. N = 3 catches faster swings useful for intra-IB tactical entries; N = 5 produces more durable swing labels suitable for trend assessment.
- 15-minute and 30-minute intraday: N = 3 is sufficient because each bar already aggregates more information.
- Hourly: N = 2 to 3.
- Daily: N = 2 (a bar that exceeds both neighbors is a meaningful pivot).
The asymmetry of the most recent pivot
A pivot at bar t is only confirmed after bar t + N prints. The most recent pivot on the chart is therefore always provisional until N additional bars elapse. Retail traders frequently treat the latest local extreme as a confirmed swing, then re-label when later bars contradict it. The desk practice is to mark the most recent pivot with a lower-confidence visual indicator (a dot rather than a label, or a dashed extension) and only commit to the structural interpretation once the pivot is confirmed.
This single discipline, accepting the asymmetry of confirmation, eliminates a category of repeated mistakes for retail-trained traders: chasing the most recent extreme as if it were a finished swing.
4.2The ATR-conditioned ZigZag in pseudocode
We introduced the concept in Chapter 2. Here is the working pseudocode that produces a usable swing series:
input: bars (OHLC), atr_period = 14, k = 1.5, pivot_strength_n = 3
state:
last_swing_high = null (price)
last_swing_low = null
last_swing_high_bar = null (bar index)
last_swing_low_bar = null
trend = "unknown" (one of: "up", "down", "unknown")
pending_high = null
pending_low = null
for each bar t:
atr_t = wilder_smoothed_atr(bars, atr_period, t)
is_local_max = is_local_max(bars[t].high, n_left=N, n_right=N)
is_local_min = is_local_min(bars[t].low, n_left=N, n_right=N)
if is_local_max:
if last_swing_low is null or
(bars[t].high - last_swing_low) >= k * atr_t:
pending_high = (bar=t, price=bars[t].high)
if is_local_min:
if last_swing_high is null or
(last_swing_high - bars[t].low) >= k * atr_t:
pending_low = (bar=t, price=bars[t].low)
... (confirmation logic: a pending_high becomes confirmed once
a subsequent decline of >= k * atr exceeds the threshold;
symmetrically for pending_low)
emit confirmed_swing events for downstream code
The key design choices:
- Wilder-smoothed ATR (not simple moving range), because Wilder smoothing weights recent volatility more heavily without overreacting to single bars.
- k = 1.5 as the swing-magnitude threshold, on liquid index futures intraday. Validated on ES 5-min over 2022 to 2025 by the author's own informal sampling: this value catches 95%+ of moves that intuition would call "swings" while suppressing the chop that the eye also dismisses. For lower-liquidity products (CL on news days, or any product overnight) increase k to 2.0.
- N = 3 for the local extremum test on 5-min, scaled per timeframe as discussed.
- Confirmation only on the next opposing extension, not on bar close, to suppress the "this looks like a swing now but then the next bar negates it" failure mode.
Implementation notes
A correctly implemented ATR-conditioned ZigZag has two properties that distinguish it from naive implementations:
- Swings cannot toggle direction without an intervening retrace of magnitude k × ATR. If the last confirmed swing was a high, the next confirmed swing must be a low at least k × ATR below it; otherwise the prior high is updated rather than replaced.
- The most recent swing is provisional, marked but not committed, until a confirming retrace prints.
A subtle bug in many retail implementations: the ATR threshold is computed at the time the pivot is detected, not at the time the prior pivot was detected. The correct semantics is to use the ATR at the time of the current candidate pivot. Otherwise, in a regime where ATR is changing rapidly, you get spurious swings that are inconsistent with current volatility.
4.3The structural sequence: HH, HL, LH, LL, BoS, CHoCH
Once the swing series is mechanical, the structural sequence becomes mechanical too.
Definitions (recap from Chapter 2)
- HH (Higher High): new confirmed swing high above the prior swing high.
- HL (Higher Low): new confirmed swing low above the prior swing low.
- LH (Lower High): new confirmed swing high below the prior swing high.
- LL (Lower Low): new confirmed swing low below the prior swing low.
Trend states
Three states, plus transitions:
- Up-trend: sequence of HH and HL, alternating. A clean up-trend looks like HL, HH, HL, HH, HL, HH.
- Down-trend: sequence of LH and LL alternating. A clean down-trend looks like LH, LL, LH, LL, LH, LL.
- Range: mixed sequence where neither HH/HL nor LH/LL persist. A range looks like H, L, H', L, H'', L, where H, H', H'' all cluster within a narrow band.
The transition events
- BoS (Break of Structure): in an up-trend, the most recent HL is broken to the downside (price prints a low below the prior HL). In a down-trend, the most recent LH is broken to the upside. BoS is the first signal of a possible regime change but is not a confirmed reversal; trend pullbacks frequently overshoot the most recent HL by a small amount before resuming, which is a BoS without a CHoCH.
- CHoCH (Change of Character): in an up-trend after a BoS, the next attempted high prints a lower high (LH) rather than a new HH. The structural sequence has flipped. CHoCH confirms the trend change.
The BoS-then-CHoCH sequence is the institutional reading of a trend reversal. It is more demanding than "a moving average crossed," but it is also a sequence event with a meaningful base rate of follow-through, whereas MA crossovers in liquid futures are documented to be marginal-to-negative net of slippage on intraday horizons (Chapter 6 covers this).
4.4Trend channels: the two-line model and its limits
A trend channel is two parallel (or near-parallel) lines drawn through the swing extremes of a confirmed trend. In an up-trend, the lower line connects the HL pivots; the upper line is offset to touch the HH pivots. In a down-trend, the upper line connects the LH pivots; the lower line touches the LL pivots.
Channels are useful for two things and only two things:
- Visualizing the trajectory, so a trader can quickly see whether the trend is steepening, flattening, or near a structural extension.
- Marking probable, not certain, reaction zones at the channel boundaries.
The honest limitations
- Channels are anchored to past data. A channel drawn through three HL pivots projects forward as if the trend will continue at the same slope. It frequently does not. The channel boundary is a hypothesis, not a level.
- Channel touches are not entries. A channel-touch with no other confluence (no AVWAP confluence, no volume profile node, no order-flow confirmation) is one of the lowest-quality entry triggers in this book's stack.
- Channels redraw. The fourth pivot frequently does not respect the line drawn through the first three. This is normal, not a flaw of the trader; it is an honest reading of the data. Update the channel when needed; do not get attached to the prior version.
When channels add value
A channel is high-information when: - It coincides with another structural level (an AVWAP, a value-area edge, a round number). - It has been tested at least twice on each side without breaking. - The current touch occurs in a regime classification that supports continuation (Trend-Vol or Trend-Calm from Chapter 2).
A channel without any of these conditions is decoration. Recognize it as such and do not size around it.
4.5The failed-swing pattern
The failed-swing pattern is the highest-quality pure-price-action reversal signal in this book.
The pattern
In an up-trend, the structural sequence is HL, HH, HL, HH, HL, HH. A failed swing occurs when, after the most recent HH, the price retraces, makes a HL, attempts to print another HH, fails (prints a lower-high LH), and then breaks the most recent HL to the downside (BoS). The full sequence is then HL, HH, HL, LH, BoS-down.
The failed swing is significant because it is a combined CHoCH (the LH printed before the new HH could form) and BoS in immediate succession. The ambiguity of "is this a continuation pullback or a reversal" is resolved within a single sequence: the trend is over.
The conditions that make it tradeable
A failed-swing pattern is much more reliable when:
- The HH that preceded the LH was at structure (a prior session's high, the upper edge of a longer-timeframe range, an AVWAP confluence).
- The LH printed with order-flow divergence (CVD lower at the LH than at the prior HH, per the Chapter 3 vocabulary).
- The BoS occurred with expanding volume (real participation, not a low-volume drift through the level).
- The regime was already showing signs of transition (composite from Chapter 2 had drifted from Trend-Vol toward Range in the bars before the LH).
A failed swing satisfying all four is a high-conviction reversal trigger. A failed swing without any of them is a flag, not a trigger.
A worked example
Imagine NQ on a 5-min RTH session. The morning trend is up. The structural sequence by 11:00 ET is HL₁, HH₁, HL₂, HH₂, HL₃, HH₃. At 11:30 ET, price prints HL₄ at 22,420, attempts a new high but fails at 22,442 (the prior HH₃ was 22,448), and then drops sharply through 22,420 by 12:15 ET. The combined LH plus BoS is a failed-swing reversal.
If at 22,442 the chart was also testing the high of yesterday's session (22,440), if CVD showed a divergence (the prior HH₃ on stronger CVD, the new LH on weaker CVD), and if the morning's Trend-Vol composite had drifted toward Trend-Calm by 11:00 ET, the failed swing is a textbook short trigger. Stop above 22,448 (above the prior HH₃, ~30 ticks); target the morning's IB midpoint at ~22,360 (~80 ticks); R:R approximately 2.7.
The trade plan template (Framework 7) for this entry would include: - Setup: Failed-swing reversal at PDH confluence - Regime: Trend-Calm drifting toward Range, composite confirmed - Level: 22,442 (LH) versus 22,448 (prior HH and PDH) - Order-flow: CVD bearish divergence at LH; absorption visible on rejection bars (Chapter 11 covers absorption identification) - Entry, stop, targets, size, all per template
4.6Common pitfalls in price-action reading
Compiled from journals of traders moving from indicator-saturated charts to clean price-action reading.
-
Treating an unconfirmed pivot as confirmed. The most recent local extreme is provisional until N additional bars elapse. Marking it with a less-prominent visual indicator (dot rather than label) is the institutional discipline.
-
Mistaking a deep pullback for a CHoCH. A pullback that exceeds 0.6 × IB range often looks like a CHoCH but is structurally a continuation pullback that overshot. The diagnostic: did a new swing low (LL relative to the prior HL) form? If not, the trend is intact.
-
Drawing channels through three pivots and treating the projection as gospel. The channel is a hypothesis. The fourth pivot is the test. When the test fails, redraw, do not rationalize.
-
Confusing range-bound consolidation with a proper trend. A series of small-magnitude HHs and HLs that cluster within a 0.5 × ATR band is a range that happens to be drifting, not a trend. The composite from Chapter 2 will flag this as Range-Calm, not Trend-Calm. Trade the structural levels, not the trend.
-
Reading too-short timeframes for structural decisions. The 1-min chart prints too many noise pivots to support reliable structural reading. Use 5-min as the minimum for structural classification on ES/NQ; 15-min on slower products.
-
Reacting to the most recent swing without checking the prior swing. A LH is informative because it follows an HH; without the HH, "lower high" has no referent. Always read structure as a sequence, not a single bar.
4.7Price action and the rest of the book
Price action provides the structural skeleton on which every later concept hangs.
- Chapter 5 (S/R level quality) scores levels for tradeability; the structural pivots from this chapter are the inputs.
- Chapter 9 (Volume Profile) classifies sessions by day type; the BoS and CHoCH events are the formal signature of day-type transitions.
- Chapter 11 (Order Flow detailed) layers footprint and CVD onto the structural reading; CVD divergence at a structural pivot is one of the highest-quality multi-layer signals.
- Chapter 12 (Liquidity) treats stop pools at structural pivots; equal highs and equal lows in this chapter's vocabulary become the explicit pools.
- Chapter 13 (Open Type) classifies the day's open in part by its structural signature in the first 15 minutes.
A trader who is fluent in pure price-action reading reads the institutional layer as additive. A trader who jumps into the institutional layer without the structural fluency frequently reads it as a substitute, which is one of the more expensive misreadings in the book.
4.8Diagram concepts referenced in this chapter
- D4.1: Pivot strength comparison. A 100-bar 5-min ES chart shown three times at N=1, N=3, and N=10. The reader should see the noise-to-signal trade-off visually.
- D4.2: ATR-conditioned ZigZag walkthrough. A worked chart with each pivot annotated as "candidate, pending, confirmed" at the bar it acquired each status, illustrating the asymmetry of confirmation.
- D4.3: Failed-swing pattern annotated. The full HL-HH-HL-LH-BoS sequence on a real or representative chart, with the four conditions checked off in a side panel.
- D4.4: Channel construction and channel failure. Two panels: panel A, a channel that held through five touches; panel B, a channel that broke on the fourth touch and what the structural reading was at that moment.
4.10Exercises
Exercise 4.1: Implement the ATR-conditioned ZigZag. In Pine, Python, or pseudocode, implement the algorithm in §4.2. Run it on ES 5-min data for the most recent 60 RTH sessions. Count confirmed swings per session. Compare to a naive percent-based ZigZag (e.g., 0.3% retrace threshold). Note the difference in count and the regime conditioning of the ATR-conditioned version.
Exercise 4.2: Pivot-strength sensitivity. Take a single 5-min ES session and tag pivots at N = 1, 3, 5, 7, 10. For each N, count the pivots and tag which were genuinely meaningful (your subjective call). The N at which "meaningful pivots" stop adding and noise pivots are minimal is your local sweet spot. Most readers find this between 3 and 5.
Exercise 4.3: Failed-swing scan. Over the most recent two trading weeks on NQ, find every failed-swing pattern you can identify on the 5-min chart. For each: - Was the prior HH at structure (PDH, equal high, naked POC)? - Was the regime classified Trend at the time of the prior HH? - Did the failed-swing reversal succeed (price reached at least 1.5 × ATR away from the LH within 20 bars)?
Tabulate. The conditional success rate when all confluence is present should be visibly higher than when it is absent. This is the diagnostic test for the framework.
Exercise 4.4: Channel construction and falsification. Draw a trend channel on a recent ES Trend-Calm session. Project it forward. On the next session, observe whether the channel held, broke, or was redrawn. Note the conditions under which it held versus broke. Most readers find that channels in Trend-Calm regimes have a higher "hold" rate than in Trend-Vol regimes; confirm or reject from your own observation.
Exercise 4.5: Pure price action reading exercise. Open any recent ES RTH session with all indicators removed. Annotate every confirmed swing, every BoS, every CHoCH, and every failed-swing pattern. Note the regime classification from Chapter 2 at each transition. Spend at least 30 minutes on a single session. The fluency this builds is what separates structural reading from pattern matching.
Next chapter: support, resistance, and the level quality score, the function that ranks horizontal levels by their tradeability.