Guide

ta.pivothigh and ta.pivotlow: Why Your Pivot Signals Arrive Late (and Must)

Every pivot-based indicator has a built-in delay that looks like a bug the first time you see it. Here is exactly why the delay exists, how the two parameters control it, and the placement mistake that makes charts lie about where a pivot happened.

10 min read

If you have ever built an indicator around swing highs and lows in Pine Script and noticed that the signal only appears several bars after the actual turning point, you have not found a bug. You have found the correct and unavoidable behavior of ta.pivothigh and ta.pivotlow. These two functions are the foundation of nearly every divergence detector, support and resistance tool, and structure-based indicator on TradingView, and understanding exactly why the delay exists, and how to work with it instead of fighting it, will save you from a lot of confused debugging.

This guide covers what the two parameters actually control, why the confirmation lag is mathematically required and not a design flaw, the specific placement mistake that makes pivot markers appear in the wrong spot on a chart, and how to reason about the tradeoff between an indicator's precision and how quickly it reacts.

What a pivot high or low actually is

A pivot high is a bar whose value is higher than a certain number of bars before it and a certain number of bars after it. A pivot low is the mirror case, lower than the bars on both sides. ta.pivothigh(source, leftbars, rightbars) and ta.pivotlow(source, leftbars, rightbars) check exactly this condition and return the pivot value on the bar where it is confirmed, or na on every other bar.

ParameterMeaningEffect of increasing it
sourceThe series to search for pivots in (usually high or low)No lag effect, just changes what is measured
leftbarsBars before the candidate that must all be lower (pivot high) or higher (pivot low)Requires a more pronounced swing before qualifying
rightbarsBars after the candidate that must all be lower or higherDirectly increases confirmation lag by the same number of bars

The critical thing both parameters share is that they describe a window around a candidate bar, and Pine Script cannot evaluate that window until every bar inside it exists. leftbars is free, in the sense that those bars are already in the past by the time you are looking at a candidate. rightbars is not free, because those bars have not happened yet at the moment the candidate bar forms. This asymmetry is the entire reason the confirmation lag exists.

Why the delay is correct, not a bug

Consider a five-bar swing high with rightbars set to 5. For Pine Script to confirm that the candidate bar is genuinely a pivot, it needs to know that the five bars following it are all lower. On the candidate bar itself, those five bars do not exist yet. The only way to know a bar was a pivot high is to wait and see what happens afterward. There is no computation, no clever indexing trick, and no amount of code review that removes this delay, because the information the function needs literally does not exist until rightbars bars later.

PineScripter explaining why a pivot-based signal is intentionally delayed
pine
//@version=6
indicator("Pivot confirmation lag", overlay=true)

// ta.pivothigh(source, leftbars, rightbars) only returns a value once
// BOTH the left and right lookback windows exist around a candidate bar.
// With leftbars=5, rightbars=5, a pivot at bar N is not confirmed until
// bar N+5. The function is na on every bar until that confirmation happens.
ph = ta.pivothigh(high, 5, 5)
pl = ta.pivotlow(low, 5, 5)

plotshape(ph, "Pivot High", style=shape.triangledown, location=location.abovebar, color=color.red, size=size.small)
plotshape(pl, "Pivot Low",  style=shape.triangleup,   location=location.belowbar, color=color.green, size=size.small)

Run this on a chart and watch the triangles appear. Every single one shows up five bars after the swing point it marks, every time, with no exceptions. If you ever see a pivot-based indicator that claims to mark the exact turning point in real time with no delay, either it is using lookahead=barmerge.lookahead_on in a way that will repaint, or it is not actually using confirmed pivots at all. Our guide on lookahead and why it makes indicators lie covers the mechanism behind that specific shortcut.

The parameter tradeoff: precision against reaction speed

leftbars and rightbars do not have to be equal, and choosing them is a real design decision, not a default to leave alone. A larger rightbars value demands a more pronounced, more reliable swing before confirming a pivot, which filters out noise but adds more lag. A smaller value reacts faster but is more likely to confirm minor wiggles as pivots, producing more signals with less structural significance.

There is no correct universal setting. A scalping indicator on a one-minute chart might use leftbars=2, rightbars=2 to stay responsive at the cost of catching smaller, less meaningful swings. A swing-trading indicator on a daily chart might use leftbars=10, rightbars=10 to only flag genuinely significant turning points, accepting a ten-bar delay as the price of that filtering. The right values depend entirely on what the indicator is meant to catch, and testing a few different combinations on your actual instrument and timeframe is the only reliable way to choose.

The placement mistake that makes charts lie

There is a second, less obvious problem that trips up almost everyone who builds their first pivot-based indicator. It has nothing to do with the confirmation lag itself, and everything to do with where the pivot marker gets drawn.

pine
//@version=6
indicator("Pivot placement — the mistake", overlay=true)

ph = ta.pivothigh(high, 5, 5)

// WRONG: this plots the pivot value on the CURRENT bar (bar_index), which is
// 5 bars to the right of where the actual pivot occurred. Anyone reading the
// chart will assume the triangle marks the pivot bar itself. It does not.
plotshape(ph, "Pivot High (misplaced)", style=shape.triangledown, location=location.abovebar, color=color.orange)

plotshape, like plot, always draws on the current bar unless you tell it otherwise. When ph finally returns a non-na value, that happens on the confirmation bar, which is rightbars bars after the actual pivot. If you plot it directly with plotshape, the triangle lands on the confirmation bar, not the pivot bar. Anyone glancing at that chart will reasonably assume the marker sits exactly on the swing point. It does not. It sits several bars to the right of it.

For a quick visual indicator this discrepancy is often tolerated, since everyone using pivot-based tools on TradingView has implicitly accepted that the markers trail the real turning point. But for anything that needs to reference the pivot's actual location, such as drawing a line from one pivot to the next or measuring the distance between pivots, using the wrong bar index will silently produce wrong results. The fix is to draw with an object that accepts an explicit bar index, and to compute that index yourself.

pine
//@version=6
indicator("Pivot placement — corrected", overlay=true)

pivotRight = input.int(5, "Right bars")
ph = ta.pivothigh(high, 5, pivotRight)

// A confirmed pivot's actual bar is 'pivotRight' bars in the past relative
// to the bar where Pine finally knows about it. label.new and line.new both
// accept an explicit bar_index, so you can draw the mark at the true location.
if not na(ph)
    label.new(bar_index - pivotRight, ph, "H", style=label.style_label_down,
              color=color.new(color.red, 0), textcolor=color.white, size=size.small)

bar_index - pivotRight converts "the bar where the pivot was confirmed" back into "the bar where the pivot actually occurred." label.new, line.new, and box.new all accept an explicit index for exactly this reason. Whenever you draw anything tied to a pivot's location rather than just flashing a shape on the confirmation bar, this offset is the detail to get right.

Where this shows up in real indicators

Divergence detectors are the most common consumer of ta.pivothigh and ta.pivotlow, comparing an oscillator's value at one confirmed pivot against its value at the next to detect divergence between price and momentum. Support and resistance tools use the same functions to mark historical swing points as potential future reaction levels. Structural concepts like order blocks and market structure breaks also lean on confirmed pivots, because any definition of "structure" needs some notion of a validated swing point to anchor to. In every one of these cases, the same lag and the same placement consideration apply. If you have already read our guide on building a non-repainting divergence detector, this is the mechanism underneath the pivot logic used there, explained on its own.

Building pivot-based indicators without relearning this every time

Getting the confirmation lag and the placement offset right by hand is the single most common source of subtly broken pivot indicators, because the code compiles fine either way. Nothing in the Pine Editor warns you that your triangle is five bars away from where it should be. Describing the pivot logic you want in plain English to PineScripter produces Pine Script that applies the correct bar-index offset by default and can walk you through why, using the explanation tab, if you want to verify the logic yourself before trusting it on a chart.


Disclaimer: PineScripter is a coding tool for Pine Script development. It does not provide financial advice and does not guarantee trading profits. Always backtest strategies thoroughly and understand the risks before live trading.