Indicator · Pine Script v5 · On the chart

Bollinger Bands And EMA

by suyons· code credited to fluxchart

This overlay indicator draws Nadaraya-Watson smoothed Bollinger Bands on the price chart using short, medium, and long periods and deviations.

View on GitHub

GitHub stars7on the repository
Pine versionv5runs as is
Lines of code270
Settings25inputs
LicenceNone statedfile: MPL-2.0
Last update4 months ago21 Jun 2026

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

270 lines of Pine Script v5 from bollinger-bands/06-ema-bb-signal.pine, published under MPL-2.0 (file header). Keep the author's credit and licence lines when you reuse it.

06-ema-bb-signal.pine
// Indicator 1: Bollinger Bands (Nadaraya Smoothed) | Flux Charts
// This source code is subject to the terms of the Mozilla Public License 2.0 at https://mozilla.org/MPL/2.0/
// © fluxchart

//@version=5
indicator("Bollinger Bands And EMA", overlay = true, max_lines_count = 500, max_bars_back = 500)
group_smoothing = "smoothing"
h = input.float(6, "Smoothing Factor", step = .5, tooltip = "The bandwith(h) used in the kernel of the Nadaraya-Watson estimator function." ,group = group_smoothing)
repaint = input.bool(false,"Repaint Smoothing", tooltip = "This setting allows for repainting of the estimation" , group = group_smoothing)
sens = 4

group_boll = "Bollinger Bands Settings (Short, Medium, Long)"
short_period = input.int(20, "Period", inline = "short", group = group_boll)
short_stdev = input.float(3, "Deviation", inline = "short", group = group_boll)
med_period = input.int(75, "Period", inline = "med", group = group_boll)
med_stdev = input.float(4, "Deviation", inline = "med", group = group_boll)
long_period = input.int(100, " Period", inline = "long", group = group_boll)
long_stdev = input.float(4.25, "Deviation", inline = "long", group = group_boll)

group_graphics = "Plots and Labels"
label_src = input.string("High/Low", "Label Source",["Pivots", "Close", "High/Low"] , group = group_graphics, tooltip = "The source used to determine if price is outside of the Bollinger Bands. The Pivots setting has a four bar delay.")
labels = input.bool(true, "Labels", inline = "label" , group = group_graphics , display = display.none)
plots = input.bool(true, "Plot Lines",  group = group_graphics , display = display.none)
plot_thickness = input.int(2, "Line Width", group = group_graphics , display = display.none)

bands_lvl1 = input.bool(true, "Level 1 Bands", inline = "bands", group = group_graphics)
bands_lvl2 = input.bool(true, "Level 2 Bands", inline = "bands", group = group_graphics)

grp = "Color Theme"
pallete_bear = input.color(color.red, "Bearish", inline = "theme", group = grp, display = display.none)
pallete_bull = input.color(color.green, "Bullish", inline = "theme", group = grp, display = display.none)
alpha1 = input.float(.90, "Transparency: Level 1 ", minval = .1, maxval = 1,inline = "alpha", step = .1, display = display.none)
alpha2 = input.float(.85, "Level 2", minval = .1, maxval = 1, inline = "alpha", step = .1, display = display.none)

group_n = "Notifications"
notifs = input.bool(false, "Band Breaks", group = group_n, tooltip = "Recieve notifications when price goes above or below the first level.", display = display.none)

k = input.int(5, 'K - Number of neighbors', group = "INDICATOR: CONFIG")
lookbackPeriod = input.int(400, 'LookbackPeriod', group = "INDICATOR: CONFIG")
trend_low = input.int(40, 'Signal Low', group = "INDICATOR: CONFIG")
trend_high = input.int(60, 'Signal High', group = "INDICATOR: CONFIG")

sig_lookback = input.int(10, 'Lookback', inline='1', group='SIGNAL: CONFIG')
sig_length = input.int(100, 'Length', inline='1', group='SIGNAL: CONFIG')

type pallete 
    color bear
    color bull 
    color shade1_bear 
    color shade1_bull 
    color shade2_bear 
    color shade2_bull

create_pallete(color bear, color bull, float alpha1 = 1, float alpha2 = 2) =>
    float shade1 = 100 * alpha1
    float shade2 = 100 * alpha2
    color shade1_bear = color.new(bear, shade1)
    color shade1_bull = color.new(bull, shade1)
    color shade2_bear = color.new(bear, shade2)
    color shade2_bull = color.new(bull, shade2)
    pallete.new(bear, bull, shade1_bear, shade1_bull, shade2_bear, shade2_bull)
    
theme = create_pallete(pallete_bear, pallete_bull, alpha1, alpha2)

const bool bollinger_bands_lvl1 = true
const bool bollinger_bands_lvl2 = true
float tp = (high + low + close) /3

int n_first = 20 
int n_second = 75 
int n_third = 100

guass_w(x, h) => 
   math.exp (-1 *( (x*x) / (2*(h*h)) ))

bollingers(n, factor = 3) =>
    bolu = ta.sma(tp, n) + (factor * ta.stdev(tp, n))
    bold = ta.sma(tp, n) - (factor * ta.stdev(tp, n))
    [bolu, bold]

[BOLU_FIRST, BOLD_FIRST] = bollingers(n_first, short_stdev) 
float BOL_FIRST_GAP = BOLU_FIRST - BOLD_FIRST

[BOLU_SECOND, BOLD_SECOND] = bollingers(n_second, short_stdev) 
float BOL_SECOND_GAP = BOLU_SECOND - BOLD_SECOND

[BOLU_THIRD, BOLD_THIRD] = bollingers(n_third, med_stdev) 
float BOL_THIRD_GAP = BOLU_THIRD - BOLD_THIRD

[BOLU_FOURTH, BOLD_FOURTH] = bollingers(n_third, long_stdev) 

var pivots = array.new<float>()
var pivots2 = array.new<float>()

pivot_rad = sens

float pivot_high = ta.pivothigh(high, pivot_rad, pivot_rad)
float pivot_low = ta.pivotlow(low, pivot_rad, pivot_rad)


add_cols(mat, h_n = 1) =>
    sum_array = array.new<float>()
    for i=0 to matrix.rows(mat)-1
        array.push(sum_array, array.sum(matrix.row(mat, i))/h_n)
    sum_array

nadaraya(src, color) =>
    smoothed = array.new<float>() 
    if barstate.islast and repaint
        for i=0 to 499
            sum = 0.
            gk = array.new<float>()
            for y =0 to 499
                gk.push(guass_w(y - i, h))
            gk_sum = gk.sum()
            for y =0 to 499
                sum += src[y]*(gk.get(y)/gk_sum)
            smoothed.push(sum)
            if i%2 == 0 and i != 0
                line.new(bar_index[i-1], smoothed.get(i-1), bar_index[i], smoothed.get(i), color = color, width = 2)
    smoothed

running_nadaraya(src, n)=>
    var gk = array.new<float>(0)
    var gk_sum =  0.
    var src_matrix = matrix.new<float>()
    smoothed = 0.
    if barstate.isfirst and not repaint
        for i=0 to n
            gk.push(guass_w(i, h))
        gk_sum := gk.sum()
    if not repaint
        for i=n to 0
            smoothed += src[i] * (gk.get(i)/gk_sum)
    smoothed 

n = 499


smoothed_bolu_1 = running_nadaraya(BOLU_FIRST, n)
smoothed_bold_1 = running_nadaraya(BOLD_FIRST, n)

smoothed_bolu_2 = running_nadaraya(BOLU_SECOND, n)
smoothed_bold_2 = running_nadaraya(BOLD_SECOND, n)

smoothed_bolu_3 = running_nadaraya(BOLU_THIRD, n)
smoothed_bold_3 = running_nadaraya(BOLD_THIRD, n)

smoothed_bolu_4 = running_nadaraya(BOLU_FOURTH, n)
smoothed_bold_4 = running_nadaraya(BOLD_FOURTH, n)

BOLU_FIRST_PLOT = plot(smoothed_bolu_1, display = display.none)
BOLD_FIRST_PLOT = plot(smoothed_bold_1, display = display.none)

BOLU_SECOND_PLOT = plot(smoothed_bolu_2, display = display.none)
BOLD_SECOND_PLOT = plot(smoothed_bold_2, display = display.none)

BOLU_THIRD_PLOT = plot(smoothed_bolu_3, display = display.none)
BOLD_THIRD_PLOT = plot(smoothed_bold_3, display = display.none)

BOLU_FOURTH_PLOT = plot(smoothed_bolu_4, display = display.none)
BOLD_FOURTH_PLOT = plot(smoothed_bold_4, display = display.none)

if repaint
    nadaraya(BOLU_FIRST, theme.bear)
    nadaraya(BOLD_FIRST, theme.bull)
    
spacing = ta.atr(300)

band_test_upper_source = label_src == "Pivots" ? pivot_high : label_src == "Close" ? close : high
band_test_lower_source = label_src == "Pivots" ? pivot_low : label_src == "Close" ? close : low

offset = label_src == "Pivots" ? sens : 0

upper_band_test = band_test_upper_source  >= smoothed_bolu_1[offset] and (band_test_upper_source[1] <= smoothed_bolu_1[offset] or na(band_test_upper_source[1])) and not repaint ? band_test_upper_source + spacing * 1.01  : na
lower_band_test = band_test_lower_source  <= smoothed_bold_1[offset] and (band_test_lower_source[1] >= smoothed_bold_1[offset] or na(band_test_upper_source[1])) and not repaint ? band_test_lower_source - spacing * 1.01  : na

// Parameters for EMAs
shortTermPeriod = 50
longTermPeriod = 200

// Calculate EMAs
shortTermEma = ta.ema(close, shortTermPeriod)
longTermEma = ta.ema(close, longTermPeriod)

// Custom k-NN Algorithm for weighted EMA
var float[] distances = array.new_float(0)
array.clear(distances)
for i = 1 to 100 by 1  // Loop through past 100 data points
    distance = math.abs(shortTermEma - longTermEma[i])
    array.push(distances, distance)
array.sort(distances)

k_distances = array.new_float(0)
for i = 0 to k - 1 by 1
    array.push(k_distances, array.get(distances, i))

// Calculate weighted EMA based on closest k distances
weightShortTermEma = 0.0
totalWeight = 0.0
for i = 0 to k - 1 by 1
    weight = array.get(k_distances, i)
    weightShortTermEma += shortTermEma[i] * weight
    totalWeight += weight
weightShortTermEma /= totalWeight

// Scale weightShortTermEma between 0 - 100
var float minEma = na
var float maxEma = na
// Instead of all the history, only look at the last N bars.

minEma := ta.lowest(weightShortTermEma, lookbackPeriod)
maxEma := ta.highest(weightShortTermEma, lookbackPeriod)
scaledWeightShortTermEma = (weightShortTermEma - minEma) / (maxEma - minEma) * 100
trend_up = scaledWeightShortTermEma > trend_high
trend_down = scaledWeightShortTermEma < trend_low
trend_swap = ta.cross(scaledWeightShortTermEma, trend_low) or ta.cross(scaledWeightShortTermEma, trend_high)

// plot
if notifs and close > smoothed_bolu_1
    alert("Upper Band Crossed on " + syminfo.ticker, alert.freq_once_per_bar_close)
else if notifs and close < smoothed_bold_1
    alert("Lower Band Crossed on " + syminfo.ticker, alert.freq_once_per_bar_close)

// plotshape(labels ? upper_band_test : na, style = shape.triangledown, location = location.absolute, color = theme.bear, offset = -1*offset, size = size.tiny)
// plotshape(labels ? lower_band_test : na, style = shape.triangleup, location = location.absolute, color = theme.bull, offset = -1*offset, size = size.tiny)

buy = trend_up and (labels ? lower_band_test : na)
buy_tp = trend_up and (labels ? upper_band_test : na)
sell = trend_down and (labels ? upper_band_test : na)
sell_tp = trend_down and (labels ? lower_band_test : na)

lookback(side, len) =>
    bool result = false
    for i = 1 to len
        if side[i]
            result := true
    result

buy_condition = buy and not lookback(buy, sig_lookback)
sell_condition = sell and not lookback(sell, sig_lookback)
buy_tp_condition = buy_tp and not lookback(buy_tp, sig_lookback)
sell_tp_condition = sell_tp and not lookback(sell_tp, sig_lookback)

if buy_condition
    line.new(x1 = bar_index, y1 = close, x2 = bar_index + sig_length, y2 = close, color = color.new(color.green, 20), style = line.style_solid, width = 4)
if sell_condition
    line.new(x1 = bar_index, y1 = close, x2 = bar_index + sig_length, y2 = close, color = color.new(color.red, 20), style = line.style_solid, width = 4)
if buy_tp_condition
    line.new(x1 = bar_index, y1 = close, x2 = bar_index + sig_length, y2 = close, color = color.new(color.orange, 20), style = line.style_solid, width = 4)
if sell_tp_condition
    line.new(x1 = bar_index, y1 = close, x2 = bar_index + sig_length, y2 = close, color = color.new(#4dd0e1, 20), style = line.style_solid, width = 4)
if trend_swap
    line.new(x1 = bar_index, y1 = close, x2 = bar_index + sig_length, y2 = close, color = color.new(color.purple, 20), style = line.style_solid, width = 4)

plot(not repaint and plots ? smoothed_bolu_1 : na, linewidth = plot_thickness, color = theme.bear)
plot(not repaint and plots ? smoothed_bold_1 : na, linewidth = plot_thickness, color = theme.bull)

fill(BOLU_FIRST_PLOT, BOLU_SECOND_PLOT, theme.shade1_bear, display = bands_lvl1 ? display.all: display.none)
fill(BOLD_FIRST_PLOT, BOLD_SECOND_PLOT, theme.shade1_bull, display = bands_lvl1 ? display.all: display.none)
fill(BOLU_SECOND_PLOT, BOLU_THIRD_PLOT, theme.shade2_bear, display = bands_lvl2 ? display.all: display.none)
fill(BOLD_SECOND_PLOT, BOLD_THIRD_PLOT, theme.shade2_bull, display = bands_lvl2 ? display.all: display.none)

// emaplot = plot(scaledWeightShortTermEma, title='Scaled Weighted Short-Term EMA', color = color.new(#a6a8a3, 0), linewidth = 1)
// midLinePlot = plot(50, color = na, editable = false, display = display.none)
// fill(emaplot, midLinePlot, 105, 85, top_color = color.new(#057ec4, 0), bottom_color = color.new(#6ca800, 100), title = "Overbought Gradient Fill")
// fill(emaplot, midLinePlot, 15,  -5, top_color = color.new(#a83c91, 100), bottom_color = color.new(#fcf801, 0), title = "Oversold Gradient Fill")
// hline(15, color = color.new(#8b3131, 50))
// hline(50, color = color.new(color.gray, 49))
// hline(85, color = color.new(#2c5c2e, 50))

Overview

A smoothing factor controls the kernel bandwidth, and an optional setting can allow the estimate to repaint. Users can show two band levels, plot lines, and place labels from pivots, close, or high and low. Optional notifications fire when price breaks the first band level. Separate inputs set neighbor count, lookback length, and signal thresholds, though the visible code does not show how those values are applied.

Details

Published by

suyons on GitHub

Code credit

fluxchart, as written in the script's header

Type

Indicator, Pine Script v5

Draws

On the price chart

Category

Volatility & bands, AI / machine learning

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
Smoothing Factorfloat6h
Repaint Smoothingboolfalserepaint
Periodint20short_period
Deviationfloat3short_stdev
Periodint75med_period
Deviationfloat4med_stdev
Periodint100long_period
Deviationfloat4.25long_stdev
Label SourcestringHigh/Lowlabel_src
Labelsbooltruelabels
Plot Linesbooltrueplots
Line Widthint2plot_thickness
Show all 25 settings
SettingTypeDefaultVariable
Level 1 Bandsbooltruebands_lvl1
Level 2 Bandsbooltruebands_lvl2
Bearishcolorcolor.redpallete_bear
Bullishcolorcolor.greenpallete_bull
Transparency: Level 1 float.90alpha1
Level 2float.85alpha2
Band Breaksboolfalsenotifs
K - Number of neighborsint5k
LookbackPeriodint400lookbackPeriod
Signal Lowint40trend_low
Signal Highint60trend_high
Lookbackint10sig_lookback
Lengthint100sig_length
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

46 stars

This indicator uses a Hidden Markov Model (HMM) framework combined with an adaptive dual moving average engine and Directional Movement Index (DMI) features to identify market regimes (bullish, bearish, or ranging/chop). It calculates regime probabilities on e

Indicator v5 AI / machine learningTrend
Updated 7 weeks ago · None stated GitHub repository of HMM Enhanced: Regime Probability

This overlay indicator plots a SuperTrend-style band on the price chart using ATR length, a selectable source, and a base factor. Optional settings enable an adaptive factor driven by rate of change, a volatility filter, K-means clustering, and an LSTM layer w

Indicator v6 TrendVolatility & bands
Updated 4 months ago · MIT GitHub repository of ML SuperTrend (Ultimate) - Auto-Optimized AI with LSTM

This indicator plots an oscillator in a separate pane. It builds several SuperTrend series across a user-defined factor range and step, using ATR as the band width. On each bar it measures the distance between the close and each SuperTrend output, then groups

Indicator v5 TrendVolatility & bands
Updated 2 years ago · GPL-3.0 GitHub repository of AI SuperTrend Clustering Oscillator [LuxAlgo]

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

ICT Liquidity Void Fill

by ArunKBhaskar
575 stars

This overlay indicator marks ICT liquidity void fills on the price chart and can show long and short signals. It places a Fibonacci level inside a displacement candle, defaulting to the midpoint, and draws lines in a chosen style. Optional filters limit the fi

Indicator v6 SMC / ICT / price actionSupport & resistance
Updated 1 year ago · MPL-2.0 GitHub repository of ICT Liquidity Void Fill

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