Edgoro
Indicator · Signals and price structure

Detect higher highs and lower lows in Pine Script

Higher highs, higher lows, lower highs and lower lows in Pine Script v6: bar-by-bar staircases and swing comparisons with ta.valuewhen(), as alert conditions.

Short answer

Bar by bar, a higher high is simply high > high[1]; for swings, find pivots with ta.pivothigh() and compare the latest one with the one before using ta.valuewhen(not na(ph), ph, 0) and ta.valuewhen(not na(ph), ph, 1). An uptrend in structure terms is a higher swing high together with a higher swing low.

The script

Two definitions in one script: runs of bars with higher highs and higher lows, and confirmed swings compared with the previous swing, with alerts. Paste it into a new indicator in the Pine Editor, then save and add it to the chart.

Pine Script v6
//@version=6
indicator("Higher highs and lower lows", overlay = true)

swingLen = input.int(5, "Swing strength (bars each side)", minval = 1)
stairLen = input.int(3, "Staircase: bars in a row", minval = 2, maxval = 10)

// Bar by bar: consecutive bars with a higher high and a higher low (or lower and lower)
var int upRun   = 0
var int downRun = 0
upRun   := high > high[1] and low > low[1] ? upRun + 1 : 0
downRun := high < high[1] and low < low[1] ? downRun + 1 : 0

// Swings, confirmed swingLen bars after they happen
ph = ta.pivothigh(high, swingLen, swingLen)
pl = ta.pivotlow(low, swingLen, swingLen)
bool newHigh = not na(ph)
bool newLow  = not na(pl)

// The last two swing highs and the last two swing lows
float hi0 = ta.valuewhen(newHigh, ph, 0)
float hi1 = ta.valuewhen(newHigh, ph, 1)
float lo0 = ta.valuewhen(newLow, pl, 0)
float lo1 = ta.valuewhen(newLow, pl, 1)

bool higherHigh = newHigh and hi0 > hi1
bool lowerHigh  = newHigh and hi0 < hi1
bool higherLow  = newLow and lo0 > lo1
bool lowerLow   = newLow and lo0 < lo1

// Structure on every bar: the last swing high and the last swing low point the same way
bool upStructure   = hi0 > hi1 and lo0 > lo1
bool downStructure = hi0 < hi1 and lo0 < lo1

bgcolor(upStructure ? color.new(color.teal, 92) : downStructure ? color.new(color.red, 92) : na, title = "Structure")
barcolor(upRun >= stairLen ? color.teal : downRun >= stairLen ? color.red : na, title = "Staircase")
plotshape(higherHigh, "Higher high", shape.triangleup, location.abovebar, color.teal, offset = -swingLen, size = size.tiny)
plotshape(lowerHigh, "Lower high", shape.triangledown, location.abovebar, color.orange, offset = -swingLen, size = size.tiny)
plotshape(higherLow, "Higher low", shape.triangleup, location.belowbar, color.teal, offset = -swingLen, size = size.tiny)
plotshape(lowerLow, "Lower low", shape.triangledown, location.belowbar, color.red, offset = -swingLen, size = size.tiny)

// Fire on the confirmation bar, swingLen bars after the swing itself
alertcondition(higherLow and upStructure, "Higher low in an uptrend", "Higher low confirmed on {{ticker}}")
alertcondition(lowerHigh and downStructure, "Lower high in a downtrend", "Lower high confirmed on {{ticker}}")

How it works

  • The staircase. upRun counts bars in a row whose high and low are both above the previous bar's. It lives in a var variable and drops to 0 on the first bar that breaks the pattern, so no loop is needed. Bars that reach stairLen are recolored.
  • Swing values. hi0 and hi1 are the last two pivot highs, lo0 and lo1 the last two pivot lows, available on every bar thanks to ta.valuewhen().
  • Events and state. higherHigh is true on the bar that confirms a new swing high above the previous one. upStructure is true on every bar while the latest swing high and latest swing low are both higher than the ones before: that's the background.
  • Equal swings. A swing exactly equal to the previous one is neither higher nor lower here, so it fires nothing and leaves the structure neutral.
  • Alerts. A higher low inside an uptrend and a lower high inside a downtrend are typical pullback signals; both fire on the confirmation bar.

Pitfalls

  • Markers drawn back in time. offset = -swingLen places each triangle on the swing bar, but the swing was only known swingLen bars later. Read the chart with that in mind, and never backtest from the drawn position. See repainting explained.
  • Comparing with a pivot that's still na. Before two swings of a kind exist, hi1 is na and hi0 > hi1 is false. That's why the structure stays neutral on the first bars; don't read it as "no trend".
  • Pivots inside if blocks. if close > open followed by ta.pivothigh(...) gives the function a partial history (should be called on each calculation). Call it on its own line at the top level.
  • Highs and closes. high > high[1] and close > close[1] describe different things: wicks or closes. Pick one definition and keep it for highs and lows alike.

Variations

Labels instead of triangles. The market structure guide labels each swing HH, HL, LH or LL and adds breaks of structure. Several swings in a row: keep the last pivot highs in an array (see the arrays example) and test whether each one is above the previous one.

React before the next swing is confirmed. A close above the last swing high is known at once, without waiting for a new pivot:

// Close breaks the last swing high (known on this bar, no pivot delay)
breaksHigh = ta.crossover(close, hi0)
breaksLow  = ta.crossunder(close, lo0)
plotshape(breaksHigh, "Close above last swing high", shape.circle, location.abovebar, color.teal, size = size.tiny)
plotshape(breaksLow, "Close below last swing low", shape.circle, location.belowbar, color.red, size = size.tiny)

Questions

How do I detect a higher high in Pine Script?

For single bars, high > high[1]. For swings, take ph = ta.pivothigh(high, 5, 5), then compare ta.valuewhen(not na(ph), ph, 0) with ta.valuewhen(not na(ph), ph, 1) on the bars where a new pivot appears.

Why do my higher-high markers appear late?

A swing high needs bars on its right to be confirmed: with 5 bars each side, the pivot is known 5 bars later. Lower the right-side bars for earlier but less reliable swings, or react to a close above the last swing instead.

How do I know if the trend is up in terms of swings?

When the latest swing high is above the previous swing high and the latest swing low is above the previous swing low. The script's upStructure variable is exactly that test, true on every bar while it holds.

Every script on this page was checked with Edgoro Pine Check (Pine Script v6 rules). Pine Check is a static checker, not TradingView's compiler: TradingView compiles the script when you save it in the Pine Editor. The scripts are examples to learn from, not trading systems. Trading involves substantial risk of loss and is not suitable for every investor. Edgoro is a software tool, not a broker or financial adviser: nothing it produces is investment advice, and past or backtested performance does not guarantee future results.

Need a different version of this script?

Describe the change in plain English and Edgoro writes the Pine Script v6 for you, checked with Pine Check. For example:

“Color the background green when the last swing high and swing low are both higher than the previous ones, red when both are lower, and alert me on each higher low in an uptrend.”