Indicator · Pine Script v6 · On the chart

Peak Reversal v3

by Zettt

Peak Reversal v3 identifies potential mean-reversion points by plotting configurable Keltner Channels alongside a squeeze detector directly on the price chart.

View on GitHub

GitHub stars43on the repository
Pine versionv6current
Lines of code142
Settings21inputs
LicenceNone statedfile: MPL-2.0
Last update1 year ago23 Sep 2025

Add It to Your TradingView Chart

  1. 1Copy the scriptPress Copy script above, or the copy button on the code below.
  2. 2Open the Pine EditorOpen a TradingView chart and choose Pine Editor at the bottom of the screen.
  3. 3Paste over the templateCreate a new indicator, select all of the template text and paste. Press Save and give it a name.
  4. 4Add it to the chartPress Add to chart, then adjust the settings with the gear icon next to its name.

Source Code

142 lines of Pine Script v6 from Peak Reversal.pine, published under MPL-2.0 (file header). Keep the author's credit and licence lines when you reuse it.

Peak Reversal.pine
// This source code is subject to the terms of the Mozilla Public License 2.0 at https://mozilla.org/MPL/2.0/
// © Zettt
// August-2025

//@version=6
indicator('Peak Reversal v3', shorttitle = 'Peak Reversal', overlay = true)

//
// INPUTS
//
// Keltner Channel Settings
group_keltner = "Keltner Channel Settings"
maType = input.string(title = 'MA Type', defval = 'EMA', options = ['EMA', 'SMA', 'RMA', 'VMA', 'HMA'], tooltip = 'MA Type and Length for the Keltner Channel basis.', group = group_keltner, inline = "1")
keltnerEMAlength = input.int(title = 'MA Length', defval = 20, minval = 1, group = group_keltner, inline = "1")
atrLength = input.int(title = 'ATR Length', defval = 14, minval = 1, tooltip = 'The length of the ATR used for the Keltner Channels. Defaults MA=20, ATR=14.', group = group_keltner, inline = "2")
innerBandMultiplier = input.float(title = 'Inner', defval = 2, minval = 0.1, step = 0.25, group = group_keltner, inline = "3", tooltip = "Multipliers for the inner and outer bands.")
outerBandMultiplier = input.float(title = 'Outer', defval = 3, minval = 0.1, step = 0.25, group = group_keltner, inline = "3")

// Display Settings
group_display = "Display Settings"
showMeanEMA = input.bool(title = 'Show Mean EMA?', defval = true, group = group_display, inline = "1")
showInnerBand = input.bool(title = 'Inner Band?', defval = true, group = group_display, inline = "1")
showOuterBand = input.bool(title = 'Outer Band?', defval = false, group = group_display, tooltip = 'Allows to display the mean or the bands individually.', inline = "1")

// Reversal & Momentum Settings
group_reversal_momentum = "Reversal & Momentum Settings"
showOnlyFirstSignal = input.bool(title = 'Show Only First Signal', defval = true, tooltip = 'If checked, only the first signal after a reset will be shown.', group = group_reversal_momentum)
showBandCross = input.bool(title = 'Band Crosses?', defval = true, group = group_reversal_momentum, inline = "1")
showFreeBars = input.bool(title = 'Free Bars?', defval = false, tooltip = 'Band Cross: Show when candle, either wick or close, start crossing the bands.\nFree Bar: When the entire candle is outside of the selected band, which can indicate extreme momentum.', group = group_reversal_momentum, inline = "1")
showDeviations = input.bool(title = 'Show Mean Deviations?', defval = true, tooltip = 'Indicates when candles start going outside the bands (momentum bars). The more candles outside the bands, the likelier a return to the mean becomes.', group = group_reversal_momentum)
signalBand = input.string(title = 'Signal Band', defval = 'Inner', options = ['Inner', 'Outer'], tooltip = 'Signal Band: Which band to use for signals.\nSignal Source: "Wick" (high/low) or "Close".', group = group_reversal_momentum, inline = "2")
signalSource = input.string(title = 'Signal Source', defval = 'Wick', options = ['Wick', 'Close'], group = group_reversal_momentum, inline = "2")
maxMomentumBars = input.int(title = 'Max Momentum Bars', defval = 5, minval = 1, tooltip = 'The number of bars at which momentum coloring reaches its peak intensity. After this many bars, the color will stay constant.', group = group_reversal_momentum)
upMomentumColor = input.color(title = 'Up', defval = color.new(#ee5e48, 0), tooltip = 'Base colors for upward and downward momentum signals.', group = group_reversal_momentum, inline = "3")
downMomentumColor = input.color(title = 'Down', defval = color.new(#25bc3e, 0), group = group_reversal_momentum, inline = "3")

// Squeeze Detector
group_squeeze = "Squeeze Detector"
showSqueeze = input.bool(title = 'Show Squeeze?', defval = true, tooltip = 'Highlights periods of low volatility (a "squeeze"), which can often precede significant price movements.', group = group_squeeze)
squeezeLength = input.int(title = 'Lookback', defval = 200, minval = 1, tooltip = 'Lookback: Historical period for bandwidth percentile.\nPercentile: The threshold to trigger a squeeze.', group = group_squeeze, inline = "1")
squeezeThresh = input.float(title = 'Percentile', defval = 5.0, minval = 0.1, maxval = 100.0, step = 0.1, group = group_squeeze, inline = "1")
squeezeColorInput = input.color(title = 'Squeeze Color', defval = color.new(color.gray, 60), tooltip = 'Color for the squeeze intensity. A gradient will be applied.', group = group_squeeze)

// Calculate Keltner Bands
float basis = switch maType
    'EMA' => ta.ema(close, keltnerEMAlength)
    'SMA' => ta.sma(close, keltnerEMAlength)
    'RMA' => ta.rma(close, keltnerEMAlength)
    'VMA' => ta.vwma(close, keltnerEMAlength)
    'HMA' => ta.hma(close, keltnerEMAlength)
    => na

atrRange = ta.atr(atrLength)
upInnerKeltnerBand = basis + atrRange * innerBandMultiplier
downInnerKeltnerBand = basis - atrRange * innerBandMultiplier
upOuterKeltnerBand = basis + atrRange * outerBandMultiplier
downOuterKeltnerBand = basis - atrRange * outerBandMultiplier


// Plot Bands
plot(showMeanEMA ? basis : na, title = 'Keltner Channel EMA (Mean)', color = color.new(#BB6083, 40), linewidth = 1)
p1 = plot(showInnerBand ? upInnerKeltnerBand : na, title = 'Inner Upper Band', color = color.new(#DD8EAD, 30), linewidth = 1)
p2 = plot(showInnerBand ? downInnerKeltnerBand : na, title = 'Inner Lower Band', color = color.new(#DD8EAD, 30), linewidth = 1)
p3 = plot(showOuterBand ? upOuterKeltnerBand : na, title = 'Outer Upper Band', color = color.new(#DD8EAD, 40), linewidth = 1)
p4 = plot(showOuterBand ? downOuterKeltnerBand : na, title = 'Outer Lower Band', color = color.new(#DD8EAD, 40), linewidth = 1)

// Squeeze Calculation
keltnerBandwidth = signalBand == 'Inner' ? (upInnerKeltnerBand - downInnerKeltnerBand) / basis : (upOuterKeltnerBand - downOuterKeltnerBand) / basis

bandwidthRank = ta.percentrank(keltnerBandwidth, squeezeLength)
inSqueeze = bandwidthRank <= squeezeThresh

startColor = color.new(color.black, 100)
squeezeColor = color.from_gradient(bandwidthRank, 0, squeezeThresh, squeezeColorInput, startColor)


fill(p1, p2, color = showSqueeze and inSqueeze and not showOuterBand ? squeezeColor : na, title = 'Inner Band Squeeze Fill')
fill(p3, p4, color = showSqueeze and inSqueeze and showOuterBand ? squeezeColor : na, title = 'Outer Band Squeeze Fill')

// Plot Mean Deviations
// Outside the Upper Band
firstFreeBarUp = if signalBand == 'Inner'
    high <= upInnerKeltnerBand and close <= upInnerKeltnerBand
else if signalBand == 'Outer'
    high <= upOuterKeltnerBand and close <= upOuterKeltnerBand
numFreeBarsUp = ta.barssince(firstFreeBarUp)

color colorBarUp = color.from_gradient(numFreeBarsUp, 0, maxMomentumBars, color.new(upMomentumColor, 80), upMomentumColor)
barcolor(showDeviations and numFreeBarsUp > 0 ? colorBarUp : na, title = 'Mean Deviations Up')

// Outside Lower Band
firstFreeBarDown = if signalBand == 'Inner'
    low >= downInnerKeltnerBand and close >= downInnerKeltnerBand
else if signalBand == 'Outer'
    low >= downOuterKeltnerBand and close >= downOuterKeltnerBand
numFreeBarsDown = ta.barssince(firstFreeBarDown)

color colorBarDown = color.from_gradient(numFreeBarsDown, 0, maxMomentumBars, color.new(downMomentumColor, 80), downMomentumColor)
barcolor(showDeviations and numFreeBarsDown > 0 ? colorBarDown : na, title = 'Mean Deviations Down')


// Plot band crosses
longCrossCandle = signalSource == 'Wick' ? high : close
shortCrossCandle = signalSource == 'Wick' ? low : close

longCross = if barstate.isconfirmed
    if signalBand == 'Inner'
        longCrossCandle >= upInnerKeltnerBand
    else if signalBand == 'Outer'
        longCrossCandle >= upOuterKeltnerBand
else
    false

shortCross = if barstate.isconfirmed
    if signalBand == 'Inner'
        shortCrossCandle <= downInnerKeltnerBand
    else if signalBand == 'Outer'
        shortCrossCandle <= downOuterKeltnerBand
else
    false

showlongCross = showOnlyFirstSignal ? longCross and not longCross[1] : longCross
showshortCross = showOnlyFirstSignal ? shortCross and not shortCross[1] : shortCross

upTriangleShape = shape.triangledown
downTriangleShape = shape.triangleup

plotshape(showBandCross ? showlongCross : false, title = 'Normal Band Cross Up', style = upTriangleShape, size = size.tiny, color = color.new(upMomentumColor, 50), location = location.abovebar)
plotshape(showBandCross ? showshortCross : false, title = 'Normal Band Cross Down', style = downTriangleShape, size = size.tiny, color = color.new(downMomentumColor, 50), location = location.belowbar)

// Plot Free Bars Breakouts
longFreeBar = if signalBand == 'Inner'
    low >= upInnerKeltnerBand
else if signalBand == 'Outer'
    low >= upOuterKeltnerBand
shortFreeBar = if signalBand == 'Inner'
    high <= downInnerKeltnerBand
else if signalBand == 'Outer'
    high <= downInnerKeltnerBand

plotchar(showFreeBars ? longFreeBar : false, title = 'Free Bar Up', char = '★', size = size.tiny, color = color.new(upMomentumColor, 50), location = location.abovebar) //, display=display.none)
plotchar(showFreeBars ? shortFreeBar : false, title = 'Free Bar Down', char = '★', size = size.tiny, color = color.new(downMomentumColor, 50), location = location.belowbar) //, display=display.none)

Overview

Peak Reversal v3 detects momentum extremes when candles cross or trade completely outside the inner or outer channel bands. Users can customize the moving average type (EMA, SMA, RMA, VMA, HMA), band multipliers, signal sources (wick or close), and squeeze percentile parameters. Visual alerts highlight band crosses, free bars, and mean deviations, helping traders spot overextended price moves returning toward the baseline average.

Details

Published by

Zettt on GitHub

Type

Indicator, Pine Script v6

Draws

On the price chart

Category

Mean reversion, Volatility & bands, Trend

Markets

Any symbol

Timeframes

Any timeframe

Risk Notes

No repainting, lookahead or stacked entries were found in its code or README. That says nothing about how well it works: test it first.

Settings

The inputs you can change from the gear icon on the chart. Defaults are the author's.

SettingTypeDefaultVariable
MA TypestringEMAmaType
MA Lengthint20keltnerEMAlength
ATR Lengthint14atrLength
Innerfloat2innerBandMultiplier
Outerfloat3outerBandMultiplier
Show Mean EMA?booltrueshowMeanEMA
Inner Band?booltrueshowInnerBand
Outer Band?boolfalseshowOuterBand
Show Only First SignalbooltrueshowOnlyFirstSignal
Band Crosses?booltrueshowBandCross
Free Bars?boolfalseshowFreeBars
Show Mean Deviations?booltrueshowDeviations
Show all 21 settings
SettingTypeDefaultVariable
Signal BandstringInnersignalBand
Signal SourcestringWicksignalSource
Max Momentum Barsint5maxMomentumBars
Upcolorcolor.new(#ee5e48, 0)upMomentumColor
Downcolorcolor.new(#25bc3e, 0)downMomentumColor
Show Squeeze?booltrueshowSqueeze
Lookbackint200squeezeLength
Percentilefloat5.0squeezeThresh
Squeeze Colorcolorcolor.new(color.gray, 60)squeezeColorInput
Third-party code. ForexR did not write, test or endorse this script, and the summary above was generated automatically from its code and README. Indicators describe the past, and a strategy's backtest can look better than live trading, especially when a script repaints. Check it in the Strategy Tester and on a demo account before you rely on it.

Similar Pine Scripts

6 stars

This indicator classifies price action into four distinct market regimes: Trend, Expansion, Compression, or Mean Reversion. It determines states using a simplified Hurst exponent estimate, ADX slope thresholds, and realized volatility measurements. Based on th

Indicator v6 TrendVolatility & bands
Updated 8 weeks ago · MIT GitHub repository of Capital41 Regime Matrix v3

This indicator classifies market behavior into momentum, mean-reversion, noise, or mixed regimes. It fuses four statistical predictability sensors—Variance Ratio across multiple horizons, return autocorrelation, Hurst exponent approximation, and fractal effici

Indicator v6 TrendMean reversion
Updated yesterday · MIT GitHub repository of Predictability Regime Index [WavesUnchained]

Chandelier Exit

by everget
948 stars

This indicator plots an ATR-based Chandelier Exit trailing stop on the price chart. It tracks a long stop under recent highs and a short stop above recent lows, then switches direction when price crosses the opposite stop. Buy and sell labels mark those flips.

Indicator v6 Volatility & bandsTrend Repaints
Updated 10 days ago · GPL-3.0 GitHub repository of Chandelier Exit

The Momentum Setup is a multi-indicator visual tool designed for trend and momentum analysis directly on the price chart. It combines Relative Strength Index (RSI), Money Flow Index (MFI), Commodity Channel Index (CCI), Supertrend, and a 250-bar percentage cha

Indicator v5 Oscillators & momentumTrend
Updated 1 year ago · MPL-2.0 GitHub repository of Momentum Setup - Ankush Bajaj Momentum Investing Setup

This indicator plots trade entry and exit signals on the price chart by combining Bollinger Bands with optional filter conditions. It calculates standard Bollinger Bands along with RSI, Aroon, and moving average filters. Reversal signals trigger when price bre

Indicator v4 Volatility & bandsOscillators & momentum
Updated 2 months ago · None stated GitHub repository of Bollinger Bands Winner IND [Alorse]

More indicators

Trade Your Charts With a Regulated Broker

Compare brokers that connect to TradingView, with spreads, minimum deposits and leverage side by side.

Compare brokers