Indicator · Pine Script v5 · On the chart

Key Moving Averages

by etnt· code credited to kruskakli

Plots timeframe-adaptive moving averages and technical overlays directly on the price chart.

View on GitHub

GitHub stars2on the repository
Pine versionv5runs as is
Lines of code305
Settings32inputs
LicenceMPL-2.0
Last update1 year ago3 Aug 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

305 lines of Pine Script v5 from PineScript/kmas.pine, published under MPL-2.0. Keep the author's credit and licence lines when you reuse it.

kmas.pine
// This source code is subject to the terms of the Mozilla Public License 2.0 at https://mozilla.org/MPL/2.0/
// © kruskakli
//
// The key Moving Averages, displayed depending on the timeframe used.
//
// The following (default) values are used:
//
// Daily: MA10, EMA21, MA50, MA200
// Weekly: MA10, MA40
// Intraday: Daily MA5  (idea curtesy: Brian Shannon) and EMA65
//
// The MA200 will be presented in white when On-Balance Volume is positive,
// else it is presented in blue.
//
// It is also possibe to display a Bollinger Band.
//
//@version=5
indicator("Key Moving Averages", shorttitle="KMAs", overlay=true)

src = close

kaki1 = color.new(#fff68f, 0)
blue2 = color.new(#9999ff, 0)
gray2 = color.new(color.gray, 85)
gray3 = color.new(color.gray, 60)
slategray1 = color.new(#c6e2ff, 0)

// On-Balance Volume MA50
obv50 = ta.sma(ta.obv, 50)

//
// What is the Timeframe?
//
dayInMinutes = 24 * 60
isHourly = timeframe.period == "60"
isDaily = timeframe.period == "D"
isWeekly = timeframe.period == "W"
isIntraday = (timeframe.multiplier < dayInMinutes) and (not isDaily) and (not isWeekly)

//
// Plot the Moving Averages
//
plot5 = input(defval=false,  title="Plot MA5",    group="Daily Moving Averages")
plot10 = input(defval=false, title="Plot MA10",  group="Daily Moving Averages")
plot20 = input(defval=true,  title="Plot EMA21",  group="Daily Moving Averages")
plot50 = input(defval=true,  title="Plot MA50",   group="Daily Moving Averages")
plot200 = input(defval=true, title="Plot MA200", group="Daily Moving Averages")

plotMA5 = plot5 and isDaily
plotMA10 = plot10 and isDaily
plotMA20 = plot20 and isDaily
plotMA50 = plot50 and isDaily
plotMA200 = plot200 and isDaily

MA5 = ta.sma(src, 5)
MA10 = ta.sma(src, 10)
MA20 = ta.ema(src, 21)
MA50 = ta.sma(src, 50)
EMA65 = ta.ema(src, 65)
MA200 = ta.sma(src, 200)

color200 = (ta.obv >= obv50) ? color.white : blue2

// Display the close/ema21 ratio in percent
show21ratio = input.bool(defval=true, title="Display close/ema21 ratio")
ema21Ratio = 100 * (close/MA20 -1)
ema21RatioStr = str.tostring(math.round(ema21Ratio)) + "%"

// 
// ADR% calculation
//
// Original: alpine_trader
// Amended 08/12/20 by TheScrutiniser, MikeC on advise from GlinckEastwoot
// ADR% calculation
showADR = input.bool(defval=false, title="Display ADR%")
Length = input(20, title="ADR length")
whigh = request.security(syminfo.tickerid, timeframe.period, high)
wlow  = request.security(syminfo.tickerid, timeframe.period, low)
ADR = 100 * (ta.sma(whigh/wlow, Length) - 1)
ADRstr = str.tostring(math.round(ADR,2)) + "%"

var float adrtrack21 = na
var label adrtrackLabels = na
if barstate.islast and showADR
    adrlbl = label.new(na, na, "", style=label.style_none, size=size.small, textcolor=color.yellow, color=color.black, yloc=yloc.price)
    adrtrackLabels := adrlbl
    adrtrack21 := high
    // Update the label's position, text and tooltip.
    //label.set_xy(lbl, bar_index[highestBarOffset], hi)
    label.set_xy(adrlbl, bar_index, high * 1.15)
    label.set_text(adrlbl, ADRstr)
else
    adrtrackLabels := na
    adrtrack21 := na

if barstate.islast and (adrtrackLabels[1] != na)
    label.delete(adrtrackLabels[1])

var float track21 = na
var label trackLabels = na
// Display the percentage that close is above MA20; only on the last bar.
if barstate.islast and show21ratio and (close > MA20)
    lbl = label.new(na, na, "", style=label.style_none, size=size.small, textcolor=color.yellow, color=color.black, yloc=yloc.price)
    trackLabels := lbl
    track21 := high
    // Update the label's position, text and tooltip.
    //label.set_xy(lbl, bar_index[highestBarOffset], hi)
    label.set_xy(lbl, bar_index, high * 1.03)
    label.set_text(lbl, ema21RatioStr)
else
    trackLabels := na
    track21 := na

if barstate.islast and (trackLabels[1] != na)
    label.delete(trackLabels[1])


//
// Display a Bollinger Band
//
plotBB = input(defval=false, title="Plot Bollinger Band")
[_middle, upper, lower] = ta.bb(close, 20, 2)

upperPlot = plot(plotBB and isDaily ? upper : na, color=gray3)
lowerPlot = plot(plotBB and isDaily ? lower : na, color=gray3)
fill(upperPlot, lowerPlot, color=gray2)


//
// Calculate DMA5 for intraday timeframes.
//
openHours = input(defval=6.5, title="Open: Hours a Day")
plotD5 = input(defval=true, title="Plot DMA5", group="Intraday Moving Averages")
dayMinutes = math.round(openHours * 60)
intraDayLength = 5
dminutes = intraDayLength * dayMinutes
dlen = dminutes / timeframe.multiplier
DMA5 = ta.sma(close, dlen)
plotDMA5 = isIntraday and plotD5 ? true : false

// Maybe plot EMA65 on Hourly?
plot65 = input(defval=true, title="Plot EMA65", group="Intraday Moving Averages")
plotEMA65 = plot65 and isIntraday


//
// Calculate MA10 and MA40 for the Weekly timeframe.
//
plotW10 = input(defval=true, title="Plot short Weekly MA", group="Weekly Moving Averages")
plotW40 = input(defval=true, title="Plot long Weekly MA", group="Weekly Moving Averages")
shortWeeklyMA = input.int(defval=10, title="Short Weekly MA", group="Weekly Moving Averages")
longWeeklyMA = input.int(defval=30, title="Long Weekly MA", group="Weekly Moving Averages")
weekly = request.security(syminfo.tickerid, 'W', close)
WMA10 = ta.sma(weekly, shortWeeklyMA)
WMA40 = ta.sma(weekly, longWeeklyMA)
plotWMA10 = isWeekly and plotW10 ? true : false
plotWMA40 = isWeekly and plotW40 ? true : false

//
// Plot the Moving Averages
//
plot(series=plotDMA5 ? DMA5 : na,   linewidth=1, color=color.green,  title="DMA5",   style=plot.style_line)
plot(series=plotVWMA ? VWMA : na,   linewidth=1, color=color.blue,   title="VWMA",    style=plot.style_line)
plot(series=plotMA5 ? MA5 : na,     linewidth=1, color=slategray1,   title="MA5",    style=plot.style_line)
plot(series=plotMA10 ? MA10 : na,   linewidth=1, color=color.blue,   title="MA10",   style=plot.style_line)
plot(series=plotMA20 ? MA20 : na,   linewidth=1, color=color.yellow, title="MA20",   style=plot.style_line)
plot(series=plotMA50 ? MA50 : na,   linewidth=1, color=color.green,  title="MA50",   style=plot.style_cross)
plot(series=plotMA200 ? MA200 : na, linewidth=1, color=color200,     title="MA200",  style=plot.style_circles)
plot(series=plotEMA65 ? EMA65 : na, linewidth=1, color=color.purple, title="EMA65",  style=plot.style_circles)
plot(series=plotWMA10 ? WMA10 : na, linewidth=1, color=color.teal,   title="WMA10",  style=plot.style_line)
plot(series=plotWMA40 ? WMA40 : na, linewidth=1, color=color.fuchsia,title="WMA40",  style=plot.style_circles)



show_inside_bar = input(defval=true,  title="Show Inside Bar")
float inside = 0.0
if show_inside_bar
    if (high <= high[1]) and (low >= low[1])
        inside := 1
    else if inside[1] == 1
        int i = 2
        while true
            if inside[i] == 1
                i := i + 1
                continue
            else if (high <= high[i]) and (low >= low[i])
                inside := 1
                break
            else
                inside := 0
                break
    else
        inside := 0

vh = ta.valuewhen(inside[1] == 0 and inside == 1, high[1], 0)
vl = ta.valuewhen(inside[1] == 0 and inside == 1, low[1],  0)

color_line = input.color(defval=color.yellow, title="Inside Bars Color")
transp = input.int(defval=90, title="Color transparency", minval=0, maxval=100)
line_width = input.int(defval=1, title="Line width", minval=1, maxval=4)
color_fill = color.new(color_line, transp)

ph = plot(inside == 1 ? vh : na, title="Inside Bars High", style=plot.style_linebr, linewidth=line_width, color=color_line)
pl = plot(inside == 1 ? vl : na, title="Inside Bars Low", style=plot.style_linebr, linewidth=line_width, color=color_line)
fill(ph, pl, color=color_fill)

//
// Rate of Change Divergence
//
show_roc = input(defval=true,  title="Show Rate of Change Divergence")
roc_len1 = input.int(title="ROC Length 1",  minval=1, defval=10)
roc_len2 = input.int(title="ROC Length 2", minval=1, defval=30)

proc1 = ((close - close[roc_len1]) / close[roc_len1]) * 100
proc2 = ((close - close[roc_len2]) / close[roc_len2]) * 100

// Check for a possible Bullish trend shift.
proc1_rising = proc1[0] > proc1[1]
proc2_falling = proc2[0] < proc2[1]

is_possible_trend_shift = ta.rsi(close,14) < 50 and proc1_rising and proc2_falling

plot(is_possible_trend_shift and show_roc ? low * 0.95 : na, color=color.blue, style=plot.style_circles, linewidth=3)


// Keltner Channels use ATR to calculate the upper and lower bands, while Bollinger Bands use standard deviation.
//
// The BB/KC Squeeze Indicator combines the concepts of Bollinger Bands and Keltner Channels to identify periods
// of low volatility or “squeeze” conditions. A squeeze is identified when the Bollinger Bands are within
// the Keltner Channels, indicating that the price may soon break out of its current range.
//
// Periods of high volatility on a daily chart is indicated based on the width of Bollinger Bands.
// It plots a yellow marker at the top of the chart when volatility is high and increasing/stable,
// and a red marker when volatility is high but decreasing. The script helps visualize when volatility
// spikes and whether those spikes are potentially ending.

//
// Calculate the Squeeze
//
show_squeeze = input(defval=true,  title="Show Squeeze")

bb_src = input(defval=close, title="BB source")
length = input(defval=20,    title="BB Length")
mult = input(defval=2.0,     title="BB MultFactor")
kc_src = input(defval=hlc3,  title="KC source")
lengthKC = input(defval=20,  title="KC Length")
multKC = input(defval=1.0,   title="KC MultFactor")

// Calculate BB
[basis, upperBB, lowerBB] = ta.bb(bb_src, length, mult)

// Calculate KC
ma = ta.sma(kc_src, lengthKC)
atr_range = ta.atr(lengthKC) * multKC
upperKC = ma + atr_range
lowerKC = ma - atr_range

sqzOn  = (lowerBB > lowerKC) and (upperBB < upperKC)

lwst = ta.lowest(close, 100) * 0.75

// Calculate the dotted line representing the 'squeeze'
float sq = sqzOn? lwst : na
sq := sqzOn ? (na(sq[1]) ? lwst : sq[1]) : na

plotshape(show_squeeze ? sq : na, title="Squeeze markings", location=location.top, color=color.white)

gee = color.new(#eeeeee, 95)
bgcolor(title="Squeeze area", color=show_squeeze and sqzOn ? gee : na)

//
// Trends die after High Volatility
//
show_high_volatility = input(defval=true,  title="Show High Volatility")
[smiddle, supper, slower] = ta.bb(close, 20, 2)

bbw = ((supper - slower) / smiddle) * 100

vol_clr = (bbw > 25) ? (bbw[0] >= bbw[1] ? color.yellow : color.red) : na

plotshape((bbw > 25) and show_high_volatility ? close : na, title="Volatility markings", location=location.top, color=vol_clr)

// -------------------
// H O L Y   G R A I L
// -------------------
show_holy_grail = input(defval=true,  title="Show Holy Grail")
ma20 = ta.sma(close, 20)

// 1. 'low' is below MA-20
low_below20 = low < ma20

// 2. 'close' is above MA-20 and in the upper quarter of todays range
var xrange = high - low
upper_quarter = low + (xrange * 0.75)
close_above20 = (close > ma20) and (close > upper_quarter)

// 3. MA20 is increasing 
ma20_increasing = ma20 > ma20[1]

// 4. 'low' of previous day is greater than 'low' of the MA20 of the previous day
prev_low_gt = low[1] > ma20[1]

is_holy_grail = (low_below20 and close_above20 and ma20_increasing and prev_low_gt)

plotshape(is_holy_grail and show_holy_grail ? close : na, title="Holy Grail +label", location=location.bottom, style=shape.labelup, size=size.tiny, text="H", color=color.black, textcolor=color.lime)

Overview

Key Moving Averages displays standard daily moving averages, weekly trends, or intraday averages depending on the active timeframe. The 200-period simple moving average dynamically shifts color based on On-Balance Volume momentum relative to its 50-period average. Additional features include an Average Daily Range percentage label, close-to-EMA21 ratio tracking, inside bar highlights, Bollinger Bands, and a volatility squeeze detector.

Details

Published by

etnt on GitHub

Code credit

kruskakli, as written in the script's header

Type

Indicator, Pine Script v5

Draws

On the price chart

Category

Moving averages, Volatility & bands, Candlestick patterns

Markets

Stocks

Timeframes

M15-H1, H4 and above

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
Plot MA5boolfalseplot5
Plot MA10boolfalseplot10
Plot EMA21booltrueplot20
Plot MA50booltrueplot50
Plot MA200booltrueplot200
Display close/ema21 ratiobooltrueshow21ratio
Display ADR%boolfalseshowADR
ADR lengthint20Length
Plot Bollinger BandboolfalseplotBB
Open: Hours a Dayfloat6.5openHours
Plot DMA5booltrueplotD5
Plot EMA65booltrueplot65
Show all 32 settings
SettingTypeDefaultVariable
Plot short Weekly MAbooltrueplotW10
Plot long Weekly MAbooltrueplotW40
Short Weekly MAint10shortWeeklyMA
Long Weekly MAint30longWeeklyMA
Show Inside Barbooltrueshow_inside_bar
Inside Bars Colorcolorcolor.yellowcolor_line
Color transparencyint90transp
Line widthint1line_width
Show Rate of Change Divergencebooltrueshow_roc
ROC Length 1int10roc_len1
ROC Length 2int30roc_len2
Show Squeezebooltrueshow_squeeze
BB sourcesourceclosebb_src
BB Lengthint20length
BB MultFactorfloat2.0mult
KC sourcesourcehlc3kc_src
KC Lengthint20lengthKC
KC MultFactorfloat1.0multKC
Show High Volatilitybooltrueshow_high_volatility
Show Holy Grailbooltrueshow_holy_grail
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

15 stars

This indicator plots Bollinger Bands and smoothed HAMA candles on the price chart. It builds each candle from separate moving averages of open, high, low and close, using selectable EMA, SMA or WMA lengths. A simple moving average forms the band basis, and an

Indicator v5 Candlestick patternsMoving averages
Updated 4 months ago · Apache-2.0 GitHub repository of NSDT HAMA Candles with Bollinger Bands

Calculates adaptive arithmetic candles by blending price data with Kaufman Efficiency Ratio smoothing and a linear regression anchor. It measures trend health using a zero-to-hundred Quality Score derived from market efficiency, regression slope, and normalize

Indicator v6 TrendCandlestick patterns
Updated yesterday · MIT GitHub repository of Adaptive Arithmetic Candles + Quality Score [WavesUnchained]

Candle Filters

by Web3Degenerate
2 stars

Candle Filters is an overlay indicator that marks bullish and bearish engulfing candles on the price chart. A bullish signal requires a prior down candle whose body is engulfed by the current candle near a swing low, while a bearish signal does the reverse nea

Indicator v5 Candlestick patternsMoving averages
Updated 2 years ago · None stated GitHub repository of Candle Filters

Fractal Enculfing Caldles

by fiji-solutions
1 stars

This indicator identifies bullish and bearish engulfing candlestick patterns directly on the price chart. To refine signal quality, it provides three optional filters: an ATR filter to cap maximum candle size relative to volatility, a swing high or low filter

Indicator v5 Candlestick patternsMoving averages
Updated 2 years ago · None stated GitHub repository of Fractal Enculfing Caldles

Hampel Filter

by everget
948 stars

The Hampel Filter indicator identifies and filters out price outliers to smooth price data directly on the main chart. It calculates the rolling median and Median Absolute Deviation (MAD) of the chosen source series over a specified length. When a data point e

Indicator v4 Moving averagesVolatility & bands
Updated 10 days ago · GPL-3.0 GitHub repository of Hampel Filter

This overlay indicator plots a Supertrend from an ATR length and factor, four exponential moving averages, and a selectable VWAP or TWAP taken from a chosen timeframe on the price chart. It also shows MACD and stochastic with adjustable lengths, smoothing, ver

Indicator v5 Oscillators & momentumMoving averages
Updated 1 year ago · MPL-2.0 GitHub repository of Momentum Setup: Vijay Thakare Option Buying Scalping Setup

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