Strategy · Pine Script v5 · On the chart

Bollinger Bands Strategy

by blenessy

This strategy plots custom upper and lower bands on the price chart using configurable moving average types and deviation multipliers.

View on GitHub

GitHub stars4on the repository
Pine versionv5runs as is
Lines of code328
Settings40inputs
LicenceMIT
Last update4 years ago22 Sep 2022

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 strategy, select all of the template text and paste. Press Save and give it a name.
  4. 4Add it to the chartPress Add to chart. The trades and results appear in the Strategy Tester tab; adjust the settings with the gear icon.

Source Code

328 lines of Pine Script v5 from strategies/bb.sh.pine, published under MIT. Keep the author's credit and licence lines when you reuse it.

bb.sh.pine
//@version=5
strategy(shorttitle=${SHORT_TITLE:-'BBS'},
      title=${TITLE:-${SHORT_TITLE:-'BBS'}},
      overlay=true,
      pyramiding=${PYRAMIDING:-1},
      initial_capital=${INITIAL_CAPITAL:-1000},
      commission_value=${COMMISSION_PERCENT:-.07},
      default_qty_type=${QTY_TYPE:-strategy.cash},
      default_qty_value=${QTY_SIZE:-100})

// hidden input.8 functions, which just return the default value
hidden_input_float(defval, title='', minval=0, maxval=0, step=0, tooltip='', inline='', group='', confirm='') => defval
hidden_input_int(defval, title='', minval=0, maxval=0, step=0, tooltip='', inline='', group='', confirm='') => defval

// Group names
lbgrp = 'Low Band - Buy'
ubgrp = 'Upper Band - Sell'
advgrp = 'Pyramiding'
scgrp = 'Short confirmation'
lcgrp = 'Long confirmation'
uigrp = 'UI configuration'
tslgrp = 'Trailing stop loss'

// General Inputs
longEnabled = input.bool(${LONG_TRADES:-true}, 'Long trades', inline='mode')
shortEnabled = input.bool(${SHORT_TRADES:-false}, 'Short trades', inline='mode')
devMode = input.bool(${DEV_MODE:-true}, 'Development mode', inline='mode')
if not devMode and timeframe.period != ${CHART_TIMEFRAME:-'60'}
    runtime.error('expecting chart timeframe: ' + ${CHART_TIMEFRAME:-'60'})
if not devMode and syminfo.tickerid != ${CHART_TICKERID:-'FTX:BTCPERP'}
    runtime.error('expecting tickerid: ' + ${CHART_TICKERID:-'FTX:BTCPERP'})
backtestEnd = input.time(timestamp(${BACKTEST_END:-'2022-04-01T00:00:00'}), 'Backtest end')

// BB Low configuration
lbMAType = input.string(${LB_MA_TYPE:-${MA_TYPE:-'EMA'}}, 'Moving average', inline='lbma1', group=lbgrp,
     options=['ALMA', 'EMA', 'SMA', 'WMA', 'VWMA', 'RMA', 'HMA', 'VAMA', 'MGD'])
lbMALen = input.int(${LB_MA_LENGTH:-${MA_LENGTH:-9}}, 'MA length', step=1, minval=3, inline='lbma1', group=lbgrp)
lbMASource = input.source(${LB_MA_SOURCE:-${MA_SOURCE:-low}}, 'MA source', group=lbgrp)

lbAlmaOffset = ${LB_ALMA_INPUT_PREFIX:-hidden_input_}float(${LB_ALMA_OFFSET:-${ALMA_OFFSET:-.85}}, 'ALMA offset', step=.01, minval=.01, inline='lbalma', group=lbgrp)
lbAlmaSigma = ${LB_ALMA_INPUT_PREFIX:-hidden_input_}float(${LB_ALMA_SIGMA:-${ALMA_SIGMA:-6}}, 'ALMA sigma', step=.1, minval=.1, inline='lbalma', group=lbgrp)

lbVamaFastLen = ${LB_VAMA_INPUT_PREFIX:-hidden_input_}int(${LB_VAMA_FAST_LENGTH:-${VAMA_FAST_LENGTH:-3}}, 'VAMA fast length', step=1, minval=2, inline='lbvama', group=lbgrp)
lbVamaWeight = ${LB_VAMA_INPUT_PREFIX:-hidden_input_}float(${LB_VAMA_WEIGHT:-${VAMA_WEIGHT:-.2}}, 'VAMA weight', step=.01, minval=.01, inline='lbvama', group=lbgrp)

lbDevType = ${LB_DEV_TYPE:-${DEV_TYPE:-'STD'}}
lbDevLen = input.int(${LB_DEV_LENGTH:-${LB_MA_LENGTH:-${MA_LENGTH:-9}}}, 'Dev. length', step=1, minval=3, inline='lbdev1', group=lbgrp)
lbDevSource = input.source(${LB_DEV_SOURCE:-${LB_MA_SOURCE:-${MA_SOURCE:-low}}}, 'Dev. source', inline='lbdev2', group=lbgrp)
lbDevScale =  input.float(${LB_DEV_SCALE:-${DEV_SCALE:-2}}, 'Dev. scale factor', minval=.01, maxval=50, step=.05, inline='lbdev2', group=lbgrp)

lbLongSource = input.source(${LB_LONG_SOURCE:-${SOURCE:-close}}, 'Buy source', inline='long1', group=lbgrp)
lbLongStrategy = input.string(${LB_LONG_STRATEGY:-'Price x-under MA'}, 'Strategy', inline='long1', group=lbgrp,
     options=['Price x-over MA', 'Price x-under MA', 'Price < MA'])

// Long position confirmation configuration
longCfrm = input.string(${LONG_CONFIRMATION:-'Disabled'}, title='Oscillator', inline='lc1', group=lcgrp, options=['Disabled', 'RSI'])
longCfrmSource = input.source(${LONG_CONFIRMATION_SOURCE:-ohlc4}, 'Source', inline='lc1', group=lcgrp)
longCfrmLength = input.int(${LONG_CONFIRMATION_LENGTH:-10}, minval=1, title='Length', inline='lc2', group=lcgrp)
longCfrmLimit = input.int(${LONG_CONFIRMATION_LIMIT:-30}, minval=1, title='Limit', inline='lc2', group=lcgrp)

lcMAType = input.string(${LC_MA_TYPE:-'Disabled'}, 'Moving average', inline='lcma', group=lcgrp, options=['Disabled', 'ALMA', 'EMA', 'SMA', 'WMA', 'VWMA', 'RMA', 'HMA', 'VAMA', 'MGD'])
lcMALength = input.int(${LC_MA_LENGTH:-5}, 'MA length', step=1, minval=3, inline='lcma', group=lcgrp)
lcAlmaOffset = ${LC_ALMA_INPUT_PREFIX:-input.}float(${LC_ALMA_OFFSET:-0.85}, 'ALMA offset', step=.01, minval=.01, inline='lcalma', group=lcgrp)
lcAlmaSigma = ${LC_ALMA_INPUT_PREFIX:-input.}float(${LC_ALMA_SIGMA:-6.0}, 'ALMA sigma', step=.1, minval=.1, inline='lcalma', group=lcgrp)
lcVamaFastLength = ${LC_VAMA_INPUT_PREFIX:-hidden_input_}int(${LC_VAMA_FAST_LENGTH:-3}, 'VAMA fast length', step=1, minval=2, inline='lcvama', group=lcgrp)
lcVamaWeight = ${LC_VAMA_INPUT_PREFIX:-hidden_input_}float(${LC_VAMA_WEIGHT:-.2}, 'VAMA weight', step=.01, minval=.01, inline='lcvama', group=lcgrp)

// BB High configuration
ubMAType = input.string(${UB_MA_TYPE:-${MA_TYPE:-'EMA'}}, 'Moving average', inline='ubma1', group=ubgrp,
     options=['ALMA', 'EMA', 'SMA', 'WMA', 'VWMA', 'RMA', 'HMA', 'VAMA', 'MGD'])
ubMALen = input.int(${UB_MA_LENGTH:-${MA_LENGTH:-9}}, 'MA length', step=1, minval=3, inline='ubma1', group=ubgrp)
ubMASource = input.source(${UB_MA_SOURCE:-${MA_SOURCE:-high}}, 'MA source', group=ubgrp)

ubAlmaOffset = ${UB_ALMA_INPUT_PREFIX:-hidden_input_}float(${UB_ALMA_OFFSET:-${ALMA_OFFSET:-.85}}, 'ALMA offset', step=.01, minval=.01, inline='ubalma', group=ubgrp)
ubAlmaSigma = ${UB_ALMA_INPUT_PREFIX:-hidden_input_}float(${UB_ALMA_SIGMA:-${ALMA_SIGMA:-6}}, 'ALMA sigma', step=.1, minval=.1, inline='ubalma', group=ubgrp)

ubVamaFastLen = ${UB_VAMA_INPUT_PREFIX:-hidden_input_}int(${UB_VAMA_FAST_LENGTH:-${VAMA_FAST_LENGTH:-3}}, 'VAMA fast length', step=1, minval=2, inline='ubvama', group=ubgrp)
ubVamaWeight = ${UB_VAMA_INPUT_PREFIX:-hidden_input_}float(${UB_VAMA_WEIGHT:-${VAMA_WEIGHT:-.2}}, 'VAMA weight', step=.01, minval=.01, inline='ubvama', group=ubgrp)

ubDevType = ${UB_DEV_TYPE:-${DEV_TYPE:-'STD'}}
ubDevLen = input.int(${UB_DEV_LENGTH:-${UB_MA_LENGTH:-${MA_LENGTH:-9}}}, 'Dev. length', step=1, minval=3, inline='ubdev1', group=ubgrp)
ubDevSource = input.source(${UB_DEV_SOURCE:-${UB_MA_SOURCE:-${MA_SOURCE:-high}}}, 'Dev. source', inline='ubdev2', group=ubgrp)
ubDevScale =  input.float(${UB_DEV_SCALE:-${DEV_SCALE:-2}}, 'Dev. scale factor', minval=.01, maxval=50, step=.05, inline='ubdev2', group=ubgrp)

ubShortSource = input.source(${UB_SHORT_SOURCE:-${SOURCE:-close}}, 'Sell source', inline='short1', group=ubgrp)
ubShortStrategy = input.string(${UB_SHORT_STRATEGY:-'Price > MA'}, 'Strategy', inline='short1', group=ubgrp,
     options=['Price x-over MA', 'Price x-under MA', 'Price > MA'])

// Short position confirmation configuration
shortCfrm = input.string(${SHORT_CONFIRMATION:-'Disabled'}, title='Oscillator', inline='sc1', group=scgrp, options=['Disabled', 'RSI'])
shortCfrmSource = input.source(${SHORT_CONFIRMATION_SOURCE:-ohlc4}, 'Source', inline='sc1', group=scgrp)
shortCfrmLength = input.int(${SHORT_CONFIRMATION_LENGTH:-10}, minval=1, title='Length', inline='sc2', group=scgrp)
shortCfrmLimit = input.int(${SHORT_CONFIRMATION_LIMIT:-70}, minval=1, title='Limit', inline='sc2', group=scgrp)

scMAType = input.string(${SC_MA_TYPE:-'Disabled'}, 'Moving average', inline='scma', group=scgrp, options=['Disabled', 'ALMA', 'EMA', 'SMA', 'WMA', 'VWMA', 'RMA', 'HMA', 'VAMA', 'MGD'])
scMALength = input.int(${SC_MA_LENGTH:-5}, 'MA length', step=1, minval=3, inline='scma', group=scgrp)
scAlmaOffset = ${SC_ALMA_INPUT_PREFIX:-input.}float(${SC_ALMA_OFFSET:-0.85}, 'ALMA offset', step=.01, minval=.01, inline='scalma', group=scgrp)
scAlmaSigma = ${SC_ALMA_INPUT_PREFIX:-input.}float(${SC_ALMA_SIGMA:-6.0}, 'ALMA sigma', step=.1, minval=.1, inline='scalma', group=scgrp)
scVamaFastLength = ${SC_VAMA_INPUT_PREFIX:-hidden_input_}int(${SC_VAMA_FAST_LENGTH:-3}, 'VAMA fast length', step=1, minval=2, inline='scvama', group=scgrp)
scVamaWeight = ${SC_VAMA_INPUT_PREFIX:-hidden_input_}float(${SC_VAMA_WEIGHT:-.2}, 'VAMA weight', step=.01, minval=.01, inline='scvama', group=scgrp)

// Advanced configuration options
rebuyResistance = input.float(${PYRAMIDING_REBUY_RESISTANCE:-.01}, 'Rebuy resistance', minval=.01, step=.01, group=advgrp, inline='rebuy')
rebuyUsePosAvgPrice = input.bool(${PYRAMIDING_REBUY_USE_POS_AVG_PRICE:-true}, 'Use pos. average price', group=advgrp, inline='rebuy')
irreduciblePositions = input.int(${PYRAMIDING_IRREDUCIBLE_POSITIONS:-100}, 'Irreducible positions', step=1, minval=1, group=advgrp)
takeProfitPercent = input.float(${TAKE_PROFIT_PERCENT:-100}, 'Take profit %', minval=.1, step=.2, group=advgrp, inline='tp')
takeProfitRevertsPosition = input.bool(${TAKE_PROFIT_REVERTS_POSITION:-false}, 'Take profit reverts position', group=advgrp, inline='tp')
barsLimit = input.int(${BARS_LIMIT:-10000}, 'Limit max bars in entry', step=1, minval=1, group=advgrp, inline='bars')
barsLimitBreakEven = input.bool(${BARS_LIMIT_BREAK_EVEN:-false}, 'break even', group=advgrp, inline='bars')

tslType = input.string(${TSL_TYPE:-'ATR'}, 'Stop loss type', group=tslgrp, inline='tsl1', options=['Disabled', 'Percent', 'ATR'])
tslPercent = input.float(${TSL_PERCENT:-18}, 'Percent', minval=.1, step=.2, group=tslgrp, inline='tsl1')
tslATRLength = input.int(${TSL_ATR_LENGTH:-100}, 'ATR length', minval=1, step=1, group=tslgrp, inline='tslatr')
tslATRMult = input.int(${TSL_ATR_MULTIPLIER:-16}, 'ATR multiplier', minval=1, step=1, group=tslgrp, inline='tslatr')
tslRevertsPosition = input.bool(${TSL_REVERTS_POSITION:-false}, 'Trailing stop loss reverts position', group=tslgrp)

// UI specific configuration options
showInfoBox = input.bool(${SHOW_INFO_BOX:-false}, 'Show info box', group=uigrp)
showSignalMarkers = input.bool(${SHOW_SIGNAL_MARKERS:-false}, 'Show signal markers', group=uigrp)

// https://medium.com/the-investors-handbook/a-new-variation-on-the-bollinger-bands-in-python-7f6d91f02c5e
vama(src, dev1Len, dev2Len, scale) =>
    longStdDev = ta.stdev(src, dev2Len >= dev1Len ? dev2Len : dev1Len)
    shortStdDev = ta.stdev(src, dev2Len >= dev1Len ? dev1Len : dev2Len)
    alpha = shortStdDev / longStdDev * scale
    ma = float(na)
    ma := alpha * src + (1 - alpha) * nz(ma[1])

// https://blog.earn2trade.com/mcginley-dynamic-indicator/
mcginley_dynamic(src, length) =>
    ma = 0.0
    ma := barstate.isfirst ? src : ma[1] + (src - ma[1]) / (0.6 * length * math.pow(src / ma[1], 4))

movingAverage(type, src, length) =>
    switch type
        'EMA' => ta.ema(src, length)
        'HMA' => ta.ema(src, length)
        'RMA' => ta.rma(src, length)
        'SMA' => ta.sma(src, length)
        'VWMA' => ta.vwma(src, length)
        'WMA' => ta.wma(src, length)
        'MGD' => mcginley_dynamic(src, length)
        => float(na)

deviation(type, src, length) =>
    switch type
        'STD' => ta.stdev(src, length)
        'MEAN' => ta.dev(src, length)
        => float(na)

compareSources(type, sigsrc, masrc) =>
    switch type
        'Price > MA' => sigsrc > masrc
        'Price < MA' => sigsrc < masrc
        'Price x-over MA' => ta.crossover(sigsrc, masrc)
        'Price x-under MA' => ta.crossunder(sigsrc, masrc)
        => bool(na)

// Lower Band Calculation
lbMovingAverage = switch lbMAType
    'ALMA' => ta.alma(lbMASource, lbMALen, lbAlmaOffset, lbAlmaSigma)
    'VAMA' => vama(lbMASource, lbMALen, lbVamaFastLen, lbVamaWeight)
    => movingAverage(lbMAType, lbMASource, lbMALen)
lowerBand = lbMovingAverage - deviation(lbDevType, lbDevSource, lbDevLen) * lbDevScale

// Upper Band Calculation
ubMovingAverage = switch ubMAType
    'ALMA' => ta.alma(ubMASource, ubMALen, ubAlmaOffset, ubAlmaSigma)
    'VAMA' => vama(ubMASource, ubMALen, ubVamaFastLen, ubVamaWeight)
    => movingAverage(ubMAType, ubMASource, ubMALen)
upperBand = ubMovingAverage + deviation(ubDevType, ubDevSource, ubDevLen) * ubDevScale

// Short Confirmation Calculation - Oscillator step
shortCfrmEnabled = shortCfrm == 'Disabled' ? false : true
shortCfrmSignal = switch shortCfrm
    'RSI' => ta.rsi(shortCfrmSource, shortCfrmLength)
    => na
//overBought
shortConfirmed = shortCfrmEnabled and (shortCfrmSignal > shortCfrmLimit)

// Short confirmation calculation - MA step
scMAEnabled = scMAType == 'Disabled' ? false : shortCfrmEnabled ? true : false
scMASource = shortCfrmEnabled ? shortCfrmSignal : na
scMASignal = switch scMAType
    'ALMA' => ta.alma(scMASource, scMALength, scAlmaOffset, scAlmaSigma)
    'VAMA' => vama(scMASource, scMALength, scVamaFastLength, scVamaWeight)
    => movingAverage(scMAType, scMASource, scMALength)

shortConfirmed := scMAEnabled ? (scMASignal > shortCfrmLimit) : shortConfirmed

// Long Confirmation Calculation - Oscillator step
longCfrmEnabled = longCfrm == 'Disabled' ? false : true
longCfrmSignal = switch longCfrm
    'RSI' => ta.rsi(longCfrmSource, longCfrmLength)
    => na
//overSold
longConfirmed = longCfrmEnabled and (longCfrmSignal < longCfrmLimit)

// Long confirmation calculation - MA step
lcMAEnabled = lcMAType == 'Disabled' ? false : longCfrmEnabled ? true : false
lcMASource = longCfrmEnabled ? longCfrmSignal : na
lcMASignal = switch lcMAType
    'ALMA' => ta.alma(lcMASource, lcMALength, lcAlmaOffset, lcAlmaSigma)
    'VAMA' => vama(lcMASource, lcMALength, lcVamaFastLength, lcVamaWeight)
    => movingAverage(lcMAType, lcMASource, lcMALength)

longConfirmed := lcMAEnabled ? (lcMASignal < longCfrmLimit) : longConfirmed

// Color Palette
colorBandLine = color.new(color.gray, 0)
colorTradeZone = color.new(color.teal, 50)
colorConfirmation = color.new(color.green, 0)
p1 = plot(upperBand, 'BB Upper', color = shortConfirmed ? colorConfirmation : colorBandLine)
p2 = plot(lowerBand, 'BB Lower', color = longConfirmed ? colorConfirmation : colorBandLine)
fill(p1, p2, color=color.new(color.gray, 80))
p3 = plot(lbLongSource, 'Buy', color=colorTradeZone)
p4 = plot(ubShortSource, 'Sell', color=colorTradeZone)
fill(p3, p4, color=colorTradeZone)

lbSignal = showSignalMarkers and compareSources(lbLongStrategy, lbLongSource, lowerBand) ? lbLongSource*0.99 : na
ubSignal = showSignalMarkers and compareSources(ubShortStrategy, ubShortSource, upperBand) ? ubShortSource*1.01 : na
plot(lbSignal, color=color.yellow, linewidth=2, style=plot.style_circles)
plot(ubSignal, color=color.yellow, linewidth=2, style=plot.style_circles)

// Information box about the strategy performance
AverageBarsInTrades() =>
    result = 0
    for i = 0 to strategy.closedtrades-1
        result += strategy.closedtrades.exit_bar_index(i) - strategy.closedtrades.entry_bar_index(i) + 1
    math.round(strategy.closedtrades > 0 ? result / strategy.closedtrades : na)

avgBarsInTrades = AverageBarsInTrades()
barsSinceLastEntry = bar_index - strategy.opentrades.entry_bar_index(strategy.opentrades - 1)

if barstate.islastconfirmedhistory
    t0 = strategy.closedtrades.entry_time(0)
    t1 = strategy.closedtrades.exit_time(strategy.closedtrades - 1)
    prettyMonth = (month(t0) > 9 ? '' : '0') + str.tostring(month(t0))
    prettyDay = (dayofmonth(t0) > 9 ? '' : '0') + str.tostring(dayofmonth(t0))
    profitPerDay = nz(strategy.netprofit) / ((t1 - t0) / 86400000)
    risk = math.min(tslPercent / 100, 1.0) * strategy.initial_capital
    summary = str.format('First trade date: {0}-{1}-{2}\nDaily profit: \${3}\nPosition average price: \${4}\nUnrealized profit/loss: \${5}\nRisk/Reward ratio: {6}\nAvarage bars in trades: {7}\nBars since last entry: {8}',
         str.tostring(year(t0)), prettyMonth, prettyDay,
         math.round(profitPerDay, 2),
         math.round(nz(strategy.position_avg_price), 2),
         math.round(nz(strategy.openprofit), 2),
         math.round(risk / strategy.netprofit, 2),
         avgBarsInTrades,
         barsSinceLastEntry)

    if showInfoBox
        tbl = table.new(position.top_right, 1, 1, bgcolor=color.black, frame_color=color.silver, frame_width=1, border_color=color.gray, border_width=1)
        table.cell(tbl, 0, 0, summary, text_color = color.silver, text_halign=text.align_left, text_size=size.small)
    else
        tbl = table.new(position.top_right, 1, 1)
        table.cell(tbl, 0, 0, 'ⓘ', text_color = color.blue, tooltip=summary)

// calculate the profit as it stands right now
profitPercent = strategy.opentrades > 0 ? 100 * (close / strategy.position_avg_price - 1) : .0

// Trailing Stop Loss calculation
tslSource = close
getTrailingStopLoss() =>
    // Use Percent when the number of available bars is too low, giving ATR = 0
    switch tslType
        'Percent' => (tslSource * tslPercent) / 100
        'ATR' => (bar_index - barsSinceLastEntry) < tslATRLength ? (tslSource * tslPercent) / 100 : ta.atr(tslATRLength) * tslATRMult
        => na

// Initialize trailing stop loss
var float trailingSL = na
tslTriggered = false
tslValue = getTrailingStopLoss()

// Determine position direction
positionIsLong = strategy.position_size > 0 ? true : false
positionIsShort = strategy.position_size < 0 ? true : false

if (tslType != 'Disabled')
    trailingSL := positionIsLong ? ( barsSinceLastEntry == 0 ? tslSource - tslValue : math.max(tslSource - tslValue, nz(trailingSL[1])) )
               :  positionIsShort ? ( barsSinceLastEntry == 0 ? tslSource + tslValue : math.min(tslSource + tslValue, nz(trailingSL[1])) )
               :  na
    
    tslTriggered := (positionIsLong and ta.crossunder(tslSource, nz(trailingSL)))
                 or (positionIsShort and ta.crossover(tslSource, nz(trailingSL)))
    
plot(trailingSL, color=color.gray, linewidth=1, style=plot.style_circles)

// Determine if short/long should be reverted when the Trailing Stop Loss or Take Profit is triggered
revertLong = positionIsLong and ( (tslTriggered and tslRevertsPosition) or (takeProfitRevertsPosition and profitPercent >= takeProfitPercent) ) ? true : false
revertShort = positionIsShort and ( (tslTriggered and tslRevertsPosition) or (takeProfitRevertsPosition and profitPercent <= -takeProfitPercent) ) ? true : false

// determine whether it is a good idea to enter long/short positions
shouldBuy = compareSources(lbLongStrategy, lbLongSource, lowerBand) and (longCfrmEnabled ? longConfirmed : true)
shouldSell = compareSources(ubShortStrategy, ubShortSource, upperBand) and (shortCfrmEnabled ? shortConfirmed : true)

// determine if bars limit is reached in current trade
barsLimitReached = barsSinceLastEntry > barsLimit and (not barsLimitBreakEven or strategy.openprofit >= 0)

// -- must close positions here --

// reduce position - only effective if there are any positions with the corresponding id
strategy.close('average down', comment='reduce long', when=(close >= strategy.position_avg_price))
strategy.close('average up', comment='reduce short', when=(close <= strategy.position_avg_price))

// take profit
strategy.close('buy', when=(shouldSell or profitPercent >= takeProfitPercent or barsLimitReached or tslTriggered),
     comment=str.format('{0}: {1}%', profitPercent >= 0 ? 'profit' : 'loss',  math.round(math.abs(profitPercent), 2)))
strategy.close('sell', when=(shouldBuy or profitPercent <= -takeProfitPercent or barsLimitReached or tslTriggered),
     comment=str.format('{0}: {1}%', profitPercent <= 0 ? 'profit' : 'loss',  math.round(math.abs(profitPercent), 2)))

// -- open positions here --

// GOTCHA: strategy.opentrades* is NOT updated by strategy.close

// Make sure consecutive buys/sells improve the average position price
posPrice = rebuyUsePosAvgPrice ? strategy.position_avg_price : strategy.opentrades.entry_price(strategy.opentrades - 1)
okOpenLong = longEnabled and (na(posPrice) or (posPrice / close) >= rebuyResistance) and
     (devMode ? time <= backtestEnd : time > backtestEnd)
okOpenShort = shortEnabled and (na(posPrice) or (close / posPrice) >= rebuyResistance) and
     (devMode ? time <= backtestEnd : time > backtestEnd)

// increase position
longId = strategy.opentrades.entry_id(0) == 'buy' and strategy.opentrades >= irreduciblePositions ? 'average down' : 'buy'
strategy.entry(longId, strategy.long, when=(okOpenLong and (shouldBuy or revertShort)))
shortId = strategy.opentrades.entry_id(0) == 'sell' and strategy.opentrades >= irreduciblePositions ? 'average up' : 'sell'
strategy.entry(shortId, strategy.short, when=(okOpenShort and (shouldSell or revertLong)))
EOF

Overview

Bollinger Bands Strategy opens long or short positions when price crosses above or below these envelopes, with optional RSI oscillator filters for trade confirmation. Users can adjust the moving average type, calculation source, deviation factor, and enable or disable individual trade directions. It overlays directly on the price chart. Position exits depend on band conditions or user settings, and no default stop loss is enforced.

Details

Published by

blenessy on GitHub

Draws

On the price chart

Markets

Crypto

Timeframes

Any timeframe

Risk Notes

  • The strategy enters trades without a stop loss

Settings

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

SettingTypeDefaultVariable
Long tradesbool${LONG_TRADES:-true}longEnabled
Short tradesbool${SHORT_TRADES:-false}shortEnabled
Development modebool${DEV_MODE:-true}devMode
Backtest endtimetimestamp(${BACKTEST_END:-'2022-04-01T00:00:00'})backtestEnd
Moving averagestring${LB_MA_TYPE:-${MA_TYPE:-'EMA'}}lbMAType
MA lengthint${LB_MA_LENGTH:-${MA_LENGTH:-9}}lbMALen
MA sourcesource${LB_MA_SOURCE:-${MA_SOURCE:-low}}lbMASource
Dev. lengthint${LB_DEV_LENGTH:-${LB_MA_LENGTH:-${MA_LENGTH:-9}}}lbDevLen
Dev. sourcesource${LB_DEV_SOURCE:-${LB_MA_SOURCE:-${MA_SOURCE:-low}}}lbDevSource
Dev. scale factorfloat${LB_DEV_SCALE:-${DEV_SCALE:-2}}lbDevScale
Buy sourcesource${LB_LONG_SOURCE:-${SOURCE:-close}}lbLongSource
Strategystring${LB_LONG_STRATEGY:-'Price x-under MA'}lbLongStrategy
Show all 40 settings
SettingTypeDefaultVariable
Oscillatorstring${LONG_CONFIRMATION:-'Disabled'}longCfrm
Sourcesource${LONG_CONFIRMATION_SOURCE:-ohlc4}longCfrmSource
Lengthint${LONG_CONFIRMATION_LENGTH:-10}longCfrmLength
Limitint${LONG_CONFIRMATION_LIMIT:-30}longCfrmLimit
Moving averagestring${LC_MA_TYPE:-'Disabled'}lcMAType
MA lengthint${LC_MA_LENGTH:-5}lcMALength
Moving averagestring${UB_MA_TYPE:-${MA_TYPE:-'EMA'}}ubMAType
MA lengthint${UB_MA_LENGTH:-${MA_LENGTH:-9}}ubMALen
MA sourcesource${UB_MA_SOURCE:-${MA_SOURCE:-high}}ubMASource
Dev. lengthint${UB_DEV_LENGTH:-${UB_MA_LENGTH:-${MA_LENGTH:-9}}}ubDevLen
Dev. sourcesource${UB_DEV_SOURCE:-${UB_MA_SOURCE:-${MA_SOURCE:-high}}}ubDevSource
Dev. scale factorfloat${UB_DEV_SCALE:-${DEV_SCALE:-2}}ubDevScale
Sell sourcesource${UB_SHORT_SOURCE:-${SOURCE:-close}}ubShortSource
Strategystring${UB_SHORT_STRATEGY:-'Price > MA'}ubShortStrategy
Oscillatorstring${SHORT_CONFIRMATION:-'Disabled'}shortCfrm
Sourcesource${SHORT_CONFIRMATION_SOURCE:-ohlc4}shortCfrmSource
Lengthint${SHORT_CONFIRMATION_LENGTH:-10}shortCfrmLength
Limitint${SHORT_CONFIRMATION_LIMIT:-70}shortCfrmLimit
Moving averagestring${SC_MA_TYPE:-'Disabled'}scMAType
MA lengthint${SC_MA_LENGTH:-5}scMALength
Rebuy resistancefloat${PYRAMIDING_REBUY_RESISTANCE:-.01}rebuyResistance
Use pos. average pricebool${PYRAMIDING_REBUY_USE_POS_AVG_PRICE:-true}rebuyUsePosAvgPrice
Irreducible positionsint${PYRAMIDING_IRREDUCIBLE_POSITIONS:-100}irreduciblePositions
Take profit %float${TAKE_PROFIT_PERCENT:-100}takeProfitPercent
Take profit reverts positionbool${TAKE_PROFIT_REVERTS_POSITION:-false}takeProfitRevertsPosition
Limit max bars in entryint${BARS_LIMIT:-10000}barsLimit
break evenbool${BARS_LIMIT_BREAK_EVEN:-false}barsLimitBreakEven
Stop loss typestring${TSL_TYPE:-'ATR'}tslType
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

This mean-reversion strategy plots Bollinger Bands and a long-term moving average directly on the price chart. It enters trades when price candles penetrate the outer Bollinger Bands beyond a user-defined body percentage, anticipating a price reversal. Entries

Strategy v5 Mean reversionVolatility & bands
Updated 2 months ago · None stated GitHub repository of Bollinger Bands Winner PRO [Alorse]
257 stars

This overlay strategy targets one-minute ES futures and plots volume-weighted moving-average bands from the open, scaled by a standard-deviation multiple, plus a slower band from ten-minute opens. A higher-timeframe exponential moving average can filter trades

Strategy v6 Moving averagesVolatility & bands
Updated 7 weeks ago · None stated GitHub repository of ES - Futures 1M No Repaint

STRG-BBForce

by harryguiacorn
220 stars

STRG-BBForce is a Bollinger Bands trend-following strategy. It calculates standard Bollinger Bands and tracks directional alignment across the upper, lower, and basis lines. When all three lines slope upward, it opens a long position and closes short positions

Strategy v5 TrendVolatility & bands
Updated 1 year ago · None stated GitHub repository of STRG-BBForce
106 stars

This trading strategy combines three Exponential Moving Averages (8, 14, and 50 periods by default) with Stochastic RSI and Average True Range (ATR). It enters a long position when EMA 1 is above EMA 2, EMA 2 is above EMA 3, and the Stochastic RSI %K line cros

Strategy v4 TrendOscillators & momentum
Updated 2 years ago · GPL-3.0 GitHub repository of 3 x EMA + Stochastic RSI + ATR

Bollinger Bands Strategy

by cklose2000
105 stars

This strategy generates buy and sell signals based on Bollinger Band crossovers combined with a simple moving average trend filter. It enters a long position when price crosses above the lower band while trading above the SMA filter line, and enters a short po

Strategy v5 Volatility & bandsMoving averages
Updated 1 year ago · None stated GitHub repository of Bollinger Bands Strategy
104 stars

This overlay strategy combines a Chandelier Exit with a zero-lag least squares moving average. Although the header mentions Heikin Ashi candles, the calculations use ordinary closes. An ATR trailing stop sets long and short levels, and direction flips when pri

Strategy v5 TrendMoving averages
Updated 3 years ago · MIT GitHub repository of Chandelier Exit & ZLSMA Strategy

More Volatility & bands scripts

Related on ForexR

Trade Your Charts With a Regulated Broker

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

Compare brokers