0bps

Strategy vault / Martingale

Diversified Indicators Aggregation Strategy

This strategy aims to create the most flexible Bollinger Bands strategy, providing a wide range of customizable options to suit different traders' needs.

Author
ChaoZhang
Complexity
Advanced · 754 lines
Parameters
68
Backtest bar
1d · swing
Tested on
BTC/USDT · futures

Overview

This strategy aims to create the most flexible Bollinger Bands strategy, providing a wide range of customizable options to suit different traders' needs.

Strategy Logic

The strategy uses a single custom moving average as the middle band. Allows customizing moving average period, type, and price source.

The upper and lower bands are calculated based on standard deviation of the middle band, with customizable multiples. ATR can also be chosen instead of standard deviation to calculate upper and lower band locations.

The strategy provides combinations of multiple open and close conditions, including:

  • Price crossing above or below the upper, middle, lower bands
  • Price being above or below the upper, middle, lower bands
  • Band width greater or less than custom thresholds
  • Percentage B greater or less than custom thresholds

Open and close conditions can be used alone or in combination, with a customizable strategy window.

Take profit and stop loss are customizable percentages.

Advantages

  • Customizable moving average period, type, and price source to suit different needs
  • Individual conditions can be used alone or combined freely for flexibility
  • Supports upper, middle, lower 3 bands for broad monitoring
  • Supports band width and percentage B as conditions for diverse indicators combined
  • Customizable take profit and stop loss percentages for controllable risk
  • Supports all varieties on current exchanges for wide applicability
  • Customizable backtest and live trading time range for strategy analysis

The strategy provides great flexibility through abundant customizable options, allowing personalized optimization for different varieties and market conditions to achieve better performance.

Risks

  • Too much flexibility increases difficulty of parameter and condition combination, requiring careful testing and optimization
  • Moving average lag may miss short-term opportunities
  • Stop loss set too small may increase exposure to risk
  • Percentage B is prone to false breakout effects

The following countermeasures can be taken:

  1. Utilize backtesting to test different parameter combinations step-by-step to find the optimum
  2. Use shorter cycle indicators to identify short-term opportunities
  3. Set reasonable stop loss based on ATR etc.
  4. Combine other indicators to confirm percentage B signals

Optimization Directions

  • Add position management functions such as fixed size, martingale, money management etc.
  • Add auto varierty switching capabilities
  • Optimize moving average parameters to increase win rate
  • Optimize stop loss and take profit settings for better risk reward ratio
  • Test different indicator parameter combinations
  • Add machine learning etc. algorithms to automatically find optimum parameters

Summary

This strategy provides a very flexible and comprehensive trading solution through in-depth extensions of Bollinger Bands. Although there are many parameter combinations requiring testing, it can be customized to suit individual needs. Overall, the strategy has great application value as a high-quality representative of Bollinger Bands strategies. With ongoing optimizations, especially the introduction of quantitative and machine learning methods, it has the potential to achieve even better trading performance. It provides traders a powerful and creative tool.

Parameters

NameDefaultWhat it controls
v_input_120Middle Band Period
v_input_20Middle Band Type: SMA | RMA | EMA | WMA | HMA | DEMA | TEMA | VWMA
v_input_3_close0Middle Band Source: close | high | low | open | hl2 | hlc3 | hlcc4 | ohlc4
v_input_40Middle Band Resolution: 00 Current | 01 1m | 02 3m | 03 5m | 04 15m | 05 30m | 06 45m | 07 1h | 08 2h | 09 3h | 10 4h | 11 1D | 12 1W | 13 1M
v_input_50Middle Band Candle Type: 00 Current | 01 Heikin Ashi | 02 Renko | 03 Line Break | 04 Kagi | 05 Point & Figure
v_input_6trueMiddle Band Visible
v_input_72Upper Band Deviation Multiplier
v_input_82Lower Band Deviation Multiplier
v_input_9falseUse ATR Deviation Instead of Standard Deviation?
v_input_1014ATR Deviation Period
v_input_11trueHighlight Inclusions
v_input_12trueShow Inclusion Ghost Trail
v_input_130Forecast Bias: Neutral | Bullish | Bearish
v_input_1414Forecast Bias Period
v_input_15trueForecast Bias Magnitude
v_input_16trueShow Forecasts
v_input_17falseHide Fill
v_input_18trueUse Basic Fill - No Gradient
v_input_19falseShow Details
v_input_20trueUpper Band Smoothing Period
v_input_210Upper Band Smoothing MA Type: SMA | RMA | EMA | WMA | HMA | DEMA | TEMA | VWMA
v_input_22trueLower Band Smoothing Period
v_input_230Lower Band Smoothing MA Type: SMA | RMA | EMA | WMA | HMA | DEMA | TEMA | VWMA
v_input_24trueFrom Month
v_input_25trueFrom Day
v_input_262020From Year
v_input_27trueThru Month
v_input_28trueThru Day
v_input_292112Thru Year
v_input_30trueShow Date Range
v_input_31100Take Profit %
v_input_32100Stop Loss %
v_input_330Open Conditions Requirement: All | Any | Minimum count
v_input_34trueOpen Conditions Minimum Count
v_input_350Close Conditions Requirement: All | Any | Minimum count
v_input_36trueClose Conditions Minimum Count
v_input_370Crossover Upper Band: Not Used | Long Open | Long Close | Long Open and Long Close
v_input_380Crossover Middle Band: Not Used | Long Open | Long Close | Long Open and Long Close
v_input_390Crossover Lower Band: Not Used | Long Open | Long Close | Long Open and Long Close
v_input_400Crossunder Upper Band: Not Used | Long Open | Long Close | Long Open and Long Close
v_input_410Crossunder Middle Band: Not Used | Long Open | Long Close | Long Open and Long Close
v_input_420Crossunder Lower Band: Not Used | Long Open | Long Close | Long Open and Long Close
v_input_430Price Above Upper Band: Not Used | Long Open | Long Close | Long Open and Long Close
v_input_440Price Above Middle Band: Not Used | Long Open | Long Close | Long Open and Long Close
v_input_450Price Above Lower Band: Not Used | Long Open | Long Close | Long Open and Long Close
v_input_460Price Below Upper Band: Not Used | Long Open | Long Close | Long Open and Long Close
v_input_470Price Below Middle Band: Not Used | Long Open | Long Close | Long Open and Long Close
v_input_480Price Below Lower Band: Not Used | Long Open | Long Close | Long Open and Long Close
v_input_490.02Band Width Condition Value 1
v_input_500.04Band Width Condition Value 2
v_input_510Band Width Crossover Above Band Value 1: Not Used | Long Open | Long Close | Long Open and Long Close
v_input_520Band Width Crossover Above Band Value 2: Not Used | Long Open | Long Close | Long Open and Long Close
v_input_530Band Width Crossunder Below Band Value 1: Not Used | Long Open | Long Close | Long Open and Long Close
v_input_540Band Width Crossunder Below Band Value 2: Not Used | Long Open | Long Close | Long Open and Long Close
v_input_550Band Width Above Band Value 1: Not Used | Long Open | Long Close | Long Open and Long Close
v_input_560Band Width Above Band Value 2: Not Used | Long Open | Long Close | Long Open and Long Close
v_input_570Band Width Below Band Value 1: Not Used | Long Open | Long Close | Long Open and Long Close
v_input_580Band Width Below Band Value 2: Not Used | Long Open | Long Close | Long Open and Long Close
v_input_590.35Percent B Condition Value 1
v_input_600.7Percent B Condition Value 2
v_input_610Percent B Crossover Above Percent B 1: Not Used | Long Open | Long Close | Long Open and Long Close
v_input_620Percent B Crossover Above Percent B 2: Not Used | Long Open | Long Close | Long Open and Long Close
v_input_630Percent B Crossunder Below Percent B 1: Not Used | Long Open | Long Close | Long Open and Long Close
v_input_640Percent B Crossunder Below Percent B 2: Not Used | Long Open | Long Close | Long Open and Long Close
v_input_650Percent B Above Percent B 1: Not Used | Long Open | Long Close | Long Open and Long Close
v_input_660Percent B Above Percent B 2: Not Used | Long Open | Long Close | Long Open and Long Close
v_input_670Percent B Below Percent B 1: Not Used | Long Open | Long Close | Long Open and Long Close
v_input_680Percent B Below Percent B 2: Not Used | Long Open | Long Close | Long Open and Long Close

Source Pine Script

/*backtest
start: 2022-09-26 00:00:00
end: 2023-09-26 00:00:00
period: 1d
basePeriod: 1h
exchanges: [{"eid":"Futures_Binance","currency":"BTC_USDT"}]
args: [["v_input_37",1],["v_input_38",2]]
*/

//@version=4

//
// Pine Script v4
// @author BigBitsIO
// Script Library: https://www.tradingview.com/u/BigBitsIO/#published-scripts
//

strategy(title="Fancy Bollinger Bands Strategy [BigBitsIO]", shorttitle="Fancy Bollinger Bands Strategy [BigBitsIO]", overlay=true, pyramiding=1, default_qty_type=strategy.percent_of_equity, default_qty_value=100, commission_type=strategy.commission.percent, commission_value=.1, slippage=0, initial_capital=100)

MAPeriod = input(20, title="Middle Band Period", minval=1, step=1)
MAType = input(title="Middle Band Type", defval="SMA", options=["RMA", "SMA", "EMA", "WMA", "HMA", "DEMA", "TEMA", "VWMA"])
MASource = input(title="Middle Band Source", type=input.source, defval=close)
MAResolution = input(title="Middle Band Resolution", defval="00 Current", options=["00 Current", "01 1m", "02 3m", "03 5m", "04 15m", "05 30m", "06 45m", "07 1h", "08 2h", "09 3h", "10 4h", "11 1D", "12 1W", "13 1M"])
MACandleType = input(title="Middle Band Candle Type", defval="00 Current", options=["00 Current", "01 Heikin Ashi", "02 Renko", "03 Line Break", "04 Kagi", "05 Point & Figure"])
MAVisible = input(title="Middle Band Visible", type=input.bool, defval=true) 

UpperBandMultiplier = input(title="Upper Band Deviation Multiplier", defval=2, minval=0.001, maxval=50, step=.25, type=input.float)
LowerBandMultiplier = input(title="Lower Band Deviation Multiplier", defval=2, minval=0.001, maxval=50, step=.25, type=input.float)
UseATRDeviation = input(false, title="Use ATR Deviation Instead of Standard Deviation?")
ATRPeriod = input(14, title="ATR Deviation Period", minval=1, step=1)

HighlightInclusion = input(title="Highlight Inclusions", type=input.bool, defval=true)
ShowGhostTrail = input(title="Show Inclusion Ghost Trail", type=input.bool, defval=true)

ForecastBias = input(title="Forecast Bias", defval="Neutral", options=["Neutral", "Bullish", "Bearish"])
ForecastBiasPeriod = input(14, title="Forecast Bias Period")
ForecastBiasMagnitude = input(1, title="Forecast Bias Magnitude", minval=0.25, maxval=20, step=0.25)
ShowForecast = input(title="Show Forecasts", type=input.bool, defval=true)

HideFill = input(false, title="Hide Fill")
UseBasicFill = input(true, title="Use Basic Fill - No Gradient")
ShowBBDetails = input(false, title="Show Details")

UpperBandSmoothingMAPeriod = input(1, title="Upper Band Smoothing Period", minval=1, step=1)
UpperBandSmoothingMAType = input(title="Upper Band Smoothing MA Type", defval="SMA", options=["RMA", "SMA", "EMA", "WMA", "HMA", "DEMA", "TEMA", "VWMA"])

LowerBandSmoothingMAPeriod = input(1, title="Lower Band Smoothing Period", minval=1, step=1)
LowerBandSmoothingMAType = input(title="Lower Band Smoothing MA Type", defval="SMA", options=["RMA", "SMA", "EMA", "WMA", "HMA", "DEMA", "TEMA", "VWMA"])


// Begin Citation - Allanster backtest period
// === INPUT BACKTEST RANGE ===
fromMonth = input(defval = 1,    title = "From Month",      type = input.integer, minval = 1, maxval = 12)
fromDay   = input(defval = 1,    title = "From Day",        type = input.integer, minval = 1, maxval = 31)
fromYear  = input(defval = 2020, title = "From Year",       type = input.integer, minval = 1970)
thruMonth = input(defval = 1,    title = "Thru Month",      type = input.integer, minval = 1, maxval = 12)
thruDay   = input(defval = 1,    title = "Thru Day",        type = input.integer, minval = 1, maxval = 31)
thruYear  = input(defval = 2112, title = "Thru Year",       type = input.integer, minval = 1970)

// === INPUT SHOW PLOT ===
showDate  = input(defval = true, title = "Show Date Range", type = input.bool)

// === FUNCTION EXAMPLE ===
start     = timestamp(fromYear, fromMonth, fromDay, 00, 00)        // backtest start window
finish    = timestamp(thruYear, thruMonth, thruDay, 23, 59)        // backtest finish window
window()  => true       // create function "within window of time"

// === PLOTTING ===
bgcolor(color = showDate and window() ? color.gray : na, transp = 90)    
// End Citation - uses the window() funciton later on

takeProfitPercent = input(100, title="Take Profit %", type=input.float, step=.25)
stopLossPercent = input(100, title="Stop Loss %", type=input.float, step=.25)

OpenConditionsRequirement = input(title="Open Conditions Requirement", defval="All", options=["Any", "All", "Minimum count"])
OpenConditionsMinimumCount = input(1, title="Open Conditions Minimum Count", minval=1, type=input.integer)
CloseConditionsRequirement = input(title="Close Conditions Requirement", defval="All", options=["Any", "All", "Minimum count"])
CloseConditionsMinimumCount = input(1, title="Close Conditions Minimum Count", minval=1, type=input.integer)

CrossoverUpperBand = input(title="Crossover Upper Band", defval="Not Used", options=["Not Used", "Long Open", "Long Close", "Long Open and Long Close"])
CrossoverMiddleBand = input(title="Crossover Middle Band", defval="Not Used", options=["Not Used", "Long Open", "Long Close", "Long Open and Long Close"])
CrossoverLowerBand = input(title="Crossover Lower Band", defval="Not Used", options=["Not Used", "Long Open", "Long Close", "Long Open and Long Close"])

CrossunderUpperBand = input(title="Crossunder Upper Band", defval="Not Used", options=["Not Used", "Long Open", "Long Close", "Long Open and Long Close"])
CrossunderMiddleBand = input(title="Crossunder Middle Band", defval="Not Used", options=["Not Used", "Long Open", "Long Close", "Long Open and Long Close"])
CrossunderLowerBand = input(title="Crossunder Lower Band", defval="Not Used", options=["Not Used", "Long Open", "Long Close", "Long Open and Long Close"])

PriceAboveUpperBand = input(title="Price Above Upper Band", defval="Not Used", options=["Not Used", "Long Open", "Long Close", "Long Open and Long Close"])
PriceAboveMiddleBand = input(title="Price Above Middle Band", defval="Not Used", options=["Not Used", "Long Open", "Long Close", "Long Open and Long Close"])
PriceAboveLowerBand = input(title="Price Above Lower Band", defval="Not Used", options=["Not Used", "Long Open", "Long Close", "Long Open and Long Close"])

PriceBelowUpperBand = input(title="Price Below Upper Band", defval="Not Used", options=["Not Used", "Long Open", "Long Close", "Long Open and Long Close"])
PriceBelowMiddleBand = input(title="Price Below Middle Band", defval="Not Used", options=["Not Used", "Long Open", "Long Close", "Long Open and Long Close"])
PriceBelowLowerBand = input(title="Price Below Lower Band", defval="Not Used", options=["Not Used", "Long Open", "Long Close", "Long Open and Long Close"])

BandWidth1 = input(.020, title="Band Width Condition Value 1", minval=0.005, maxval=20, step=0.005)
BandWidth2 = input(.040, title="Band Width Condition Value 2", minval=0.005, maxval=20, step=0.005)

BandWidthCrossoverBandValue1 = input(title="Band Width Crossover Above Band Value 1", defval="Not Used", options=["Not Used", "Long Open", "Long Close", "Long Open and Long Close"])
BandWidthCrossoverBandValue2 = input(title="Band Width Crossover Above Band Value 2", defval="Not Used", options=["Not Used", "Long Open", "Long Close", "Long Open and Long Close"])

BandWidthCrossunderBandValue1 = input(title="Band Width Crossunder Below Band Value 1", defval="Not Used", options=["Not Used", "Long Open", "Long Close", "Long Open and Long Close"])
BandWidthCrossunderBandValue2 = input(title="Band Width Crossunder Below Band Value 2", defval="Not Used", options=["Not Used", "Long Open", "Long Close", "Long Open and Long Close"])

BandWidthAboveBandValue1 = input(title="Band Width Above Band Value 1", defval="Not Used", options=["Not Used", "Long Open", "Long Close", "Long Open and Long Close"])
BandWidthAboveBandValue2 = input(title="Band Width Above Band Value 2", defval="Not Used", options=["Not Used", "Long Open", "Long Close", "Long Open and Long Close"])

BandWidthBelowBandValue1 = input(title="Band Width Below Band Value 1", defval="Not Used", options=["Not Used", "Long Open", "Long Close", "Long Open and Long Close"])
BandWidthBelowBandValue2 = input(title="Band Width Below Band Value 2", defval="Not Used", options=["Not Used", "Long Open", "Long Close", "Long Open and Long Close"])

PercentB1 = input(.35, title="Percent B Condition Value 1", step=0.05)
PercentB2 = input(.70, title="Percent B Condition Value 2", step=0.05)

PercentBCrossoverPercentBValue1 = input(title="Percent B Crossover Above Percent B 1", defval="Not Used", options=["Not Used", "Long Open", "Long Close", "Long Open and Long Close"])
PercentBCrossoverPercentBValue2 = input(title="Percent B Crossover Above Percent B 2", defval="Not Used", options=["Not Used", "Long Open", "Long Close", "Long Open and Long Close"])

PercentBCrossunderPercentBValue1 = input(title="Percent B Crossunder Below Percent B 1", defval="Not Used", options=["Not Used", "Long Open", "Long Close", "Long Open and Long Close"])
PercentBCrossunderPercentBValue2 = input(title="Percent B Crossunder Below Percent B 2", defval="Not Used", options=["Not Used", "Long Open", "Long Close", "Long Open and Long Close"])

PercentBAbovePercentBValue1 = input(title="Percent B Above Percent B 1", defval="Not Used", options=["Not Used", "Long Open", "Long Close", "Long Open and Long Close"])
PercentBAbovePercentBValue2 = input(title="Percent B Above Percent B 2", defval="Not Used", options=["Not Used", "Long Open", "Long Close", "Long Open and Long Close"])

PercentBBelowPercentBValue1 = input(title="Percent B Below Percent B 1", defval="Not Used", options=["Not Used", "Long Open", "Long Close", "Long Open and Long Close"])
PercentBBelowPercentBValue2 = input(title="Percent B Below Percent B 2", defval="Not Used", options=["Not Used", "Long Open", "Long Close", "Long Open and Long Close"])





// A bit of borrowed code, modified here using @PineCoders gradient Framework
f_cRedLime(_g, _hide, _basic)      => _hide ? #00000000 : _basic ? #0080FF35 : _g <= 0 ? #FF000035 : _g <= .25 ? #FF000020 : _g <= .5 ? #FF000010 : _g <= .75 ? #00FF0010 : _g <= 1 ? #00FF0020 : #00FF0035
f_cRedLimeShadow(_g, _hide, _basic)      => _hide ? #00000000 : _basic ? #0080FF09 : _g <= 0 ? #FF000009 : _g <= .25 ? #FF000006 : _g <= .5 ? #FF000003 : _g <= .75 ? #00FF0009 : _g <= 1 ? #00FF0006 : #00FF0003

ma(MAType, MASource, MAPeriod) =>
    if MAPeriod > 0
        if MAType == "SMA"
            sma(MASource, MAPeriod)
        else
            if MAType == "EMA"
                ema(MASource, MAPeriod)
            else
                if MAType == "WMA"
                    wma(MASource, MAPeriod)
                else
                    if MAType == "RMA"
                        rma(MASource, MAPeriod)
                    else
                        if MAType == "HMA"
                            hma(MASource, MAPeriod)
                        else
                            if MAType == "DEMA"
                                e = ema(MASource, MAPeriod)
                                2 * e - ema(e, MAPeriod)
                            else
                                if MAType == "TEMA"
                                    e = ema(MASource, MAPeriod)
                                    3 * (e - ema(e, MAPeriod)) + ema(ema(e, MAPeriod), MAPeriod)
                                else
                                    if MAType == "VWMA"
                                        vwma(MASource, MAPeriod)
                                
res(MAResolution) =>
    if MAResolution == "00 Current"
        timeframe.period
    else
        if MAResolution == "01 1m"
            "1"
        else
            if MAResolution == "02 3m"
                "3"
            else
                if MAResolution == "03 5m"
                    "5"
                else
                    if MAResolution == "04 15m"
                        "15"
                    else
                        if MAResolution == "05 30m"
                            "30"
                        else
                            if MAResolution == "06 45m"
                                "45"
                            else
                                if MAResolution == "07 1h"
                                    "60"
                                else
                                    if MAResolution == "08 2h"
                                        "120"
                                    else
                                        if MAResolution == "09 3h"
                                            "180"
                                        else
                                            if MAResolution == "10 4h"
                                                "240"
                                            else
                                                if MAResolution == "11 1D"
                                                    "1D"
                                                else
                                                    if MAResolution == "12 1W"
                                                        "1W"
                                                    else
                                                        if MAResolution == "13 1M"
                                                            "1M"
                                                             
gettickerid(MACandleType) =>
    if MACandleType == "00 Current"
        syminfo.tickerid
    else
        if MACandleType == "01 Heikin Ashi"
            heikinashi(syminfo.tickerid) 
        else
            if MACandleType == "02 Renko"
                renko(syminfo.tickerid, "ATR", 10)  
            else
                if MACandleType == "03 Line Break"
                    linebreak(syminfo.tickerid, 3)
                else
                    if MACandleType == "04 Kagi"
                        kagi(syminfo.tickerid, 3) 
                    else
                        if MACandleType == "05 Point & Figure"
                            pointfigure(syminfo.tickerid, "hl", "Traditional", 1, 3)

MA = security(gettickerid(MACandleType), res(MAResolution), ma(MAType, MASource, MAPeriod))

plot(MAVisible ? MA : na, color=color.white, linewidth=2, title="Middle Band", show_last= HighlightInclusion ? MAPeriod : 0)
plot(MAVisible and HighlightInclusion and ShowGhostTrail ? MA[MAPeriod-1] : na, color=color.black, linewidth=2, title="MA Trail", offset=((MAPeriod-1)*-1), transp=10)


Deviation = UseATRDeviation ? security(gettickerid(MACandleType), res(MAResolution), atr(ATRPeriod)) :  security(gettickerid(MACandleType), res(MAResolution), stdev(MASource, MAPeriod))

UpperBand = MA + (Deviation * UpperBandMultiplier)
LowerBand = MA - (Deviation * LowerBandMultiplier)

SmoothedUpperBand = ma(UpperBandSmoothingMAType, UpperBand, UpperBandSmoothingMAPeriod)
SmoothedLowerBand = ma(LowerBandSmoothingMAType, LowerBand, LowerBandSmoothingMAPeriod)

UpperPlot = plot(SmoothedUpperBand, color=color.white, linewidth=1, title="Upper Band", show_last= HighlightInclusion ? MAPeriod : 0)
UpperShadowPlot = plot(HighlightInclusion and ShowGhostTrail ? SmoothedUpperBand[MAPeriod-1] : na, color=color.black, linewidth=1, title="Upper Band Trail", offset=((MAPeriod-1)*-1), transp=10)
LowerPlot = plot(SmoothedLowerBand, color=color.white, linewidth=1, title="Lower Band", show_last= HighlightInclusion ? MAPeriod : 0)
LowerShadowPlot = plot(HighlightInclusion and ShowGhostTrail ? SmoothedLowerBand[MAPeriod-1] : na, color=color.black, linewidth=1, title="Lower Band Trail", offset=((MAPeriod-1)*-1), transp=10)

PercentB = (security(gettickerid(MACandleType), res(MAResolution), close) - LowerBand) / (UpperBand - LowerBand)

fill(UpperPlot, LowerPlot, color = f_cRedLime(PercentB, HideFill, UseBasicFill), show_last= HighlightInclusion ? MAPeriod : 100000000)
fill(UpperShadowPlot, LowerShadowPlot, color = f_cRedLimeShadow(PercentB[MAPeriod-1], HideFill, UseBasicFill))

BBWidth = (UpperBand - LowerBand) / MA


if(ShowBBDetails)
    label Label = label.new(bar_index, na, "\nFancy Bollinger Band Details:\n\nUpper Band: " + tostring(UpperBand) + "\nMid Band: " + tostring(MA) + "\nLower Band: " + tostring(LowerBand) + "\n%B: " + tostring(PercentB) + "\nBollinger Band Width: " + tostring(BBWidth) + "\n\nHide this message in settings.\nUncheck Show Details", 
      color=color.black, 
      textcolor=color.white,
      style=label.style_label_down, size=size.normal, textalign=text.align_left)
    label.set_y(Label, high > UpperBand ? high : UpperBand)
    label.delete(Label[1])


// Forecasting - forcasted prices are calculated using our MAType and MASource for the MAPeriod - the last X candles.
//              it essentially replaces the oldest X candles, with the selected source * X candles
// Bias - We'll add an "adjustment" for each additional candle being forecasted based on ATR of the previous X candles
bias(Bias, BiasPeriod) =>
    if Bias == "Neutral"
        0
    else
        if Bias == "Bullish"
            (atr(BiasPeriod) * ForecastBiasMagnitude)
        else
            if Bias == "Bearish"
                ((atr(BiasPeriod)  * ForecastBiasMagnitude) * -1) // multiplying by -1 to make it a negative, bearish bias

// Note - Can not show forecasts on different resolutions at the moment, x-axis is an issue
Bias = bias(ForecastBias, ForecastBiasPeriod) // 14 is default atr period
MAForecast1 = MAPeriod > 1 ? (security(syminfo.tickerid, res(MAResolution), ma(MAType, MASource, MAPeriod - 1)) * (MAPeriod - 1) + ((MASource * 1) + (Bias * 1))) / MAPeriod : na
MAForecast2 = MAPeriod > 2 ? (security(syminfo.tickerid, res(MAResolution), ma(MAType, MASource, MAPeriod - 2)) * (MAPeriod - 2) + ((MASource * 2) + (Bias * 2))) / MAPeriod : na
MAForecast3 = MAPeriod > 3 ? (security(syminfo.tickerid, res(MAResolution), ma(MAType, MASource, MAPeriod - 3)) * (MAPeriod - 3) + ((MASource * 3) + (Bias * 3))) / MAPeriod : na
MAForecast4 = MAPeriod > 4 ? (security(syminfo.tickerid, res(MAResolution), ma(MAType, MASource, MAPeriod - 4)) * (MAPeriod - 4) + ((MASource * 4) + (Bias * 4))) / MAPeriod : na
MAForecast5 = MAPeriod > 5 ? (security(syminfo.tickerid, res(MAResolution), ma(MAType, MASource, MAPeriod - 5)) * (MAPeriod - 5) + ((MASource * 5) + (Bias * 5))) / MAPeriod : na

plot(MAResolution == "00 Current" and ShowForecast and MAVisible ? MAForecast1 : na, color=color.white, linewidth=1, style=plot.style_circles, title="Middle Band Forecast 1", offset=1, show_last=1)
plot(MAResolution == "00 Current" and ShowForecast and MAVisible ? MAForecast2 : na, color=color.white, linewidth=1, style=plot.style_circles, title="Middle Band Forecast 2", offset=2, show_last=1)
plot(MAResolution == "00 Current" and ShowForecast and MAVisible ? MAForecast3 : na, color=color.white, linewidth=1, style=plot.style_circles, title="Middle Band Forecast 3", offset=3, show_last=1)
plot(MAResolution == "00 Current" and ShowForecast and MAVisible ? MAForecast4 : na, color=color.white, linewidth=1, style=plot.style_circles, title="Middle Band Forecast 4", offset=4, show_last=1)
plot(MAResolution == "00 Current" and ShowForecast and MAVisible ? MAForecast5 : na, color=color.white, linewidth=1, style=plot.style_circles, title="Middle Band Forecast 5", offset=5, show_last=1)


// Take Profit and Stop Loss
profitTarget = (close * (takeProfitPercent / 100)) / syminfo.mintick
lossTarget = (close * (stopLossPercent / 100)) / syminfo.mintick

float longOpen = 0
float longOpenCount = 0
float longClose = 0
float longCloseCount =0

bool validLongOpen = true 
bool validLongClose = true


testLongOpen(Conditionlo)=>
    if Conditionlo
        if OpenConditionsRequirement == "All" and validLongOpen
            [1, longOpenCount, true]
        else if OpenConditionsRequirement == "Any"
            [1, longOpenCount, validLongOpen]
        else if OpenConditionsRequirement == "Minimum count"
            [0, longOpenCount + 1, validLongOpen]
        else
            [longOpen, longOpenCount, validLongOpen]
    else
        [0, longOpenCount, false]
        
testLongClose(Conditionlc)=>
    if Conditionlc
        if CloseConditionsRequirement == "All" and validLongClose
            [1, longCloseCount, true]
        else if CloseConditionsRequirement == "Any"
            [1, longCloseCount, validLongClose]
        else if CloseConditionsRequirement == "Minimum count"
            [0, int(longCloseCount + 1), validLongClose]
        else
            [longClose, longCloseCount, validLongClose]
    else
        [0, longCloseCount, false]
        
        




//------------------------------CONDITIONS-----------------------------
bool isCrossoverUpperBand = crossover(close, UpperBand)

if CrossoverUpperBand == "Long Open" or CrossoverUpperBand == "Long Open and Long Close"
    [a,b,c] = testLongOpen(isCrossoverUpperBand)
    longOpen := a
    longOpenCount := b
    validLongOpen := c
if CrossoverUpperBand == "Long Close" or CrossoverUpperBand == "Long Open and Long Close"
    [a,b,c] = testLongClose(isCrossoverUpperBand)
    longClose := a
    longCloseCount := b
    validLongClose := c
            
bool isCrossunderUpperBand = crossunder(close, UpperBand)            
            
if CrossunderUpperBand == "Long Open" or CrossunderUpperBand == "Long Open and Long Close"
    [a,b,c] = testLongOpen(isCrossunderUpperBand) 
    longOpen := a
    longOpenCount := b
    validLongOpen := c
if CrossunderUpperBand == "Long Close" or CrossunderUpperBand == "Long Open and Long Close"
    [a,b,c] = testLongClose(isCrossunderUpperBand) 
    longClose := a
    longCloseCount := b
    validLongClose := c        
            
bool isCrossoverMiddleBand = crossover(close, MA)

if CrossoverMiddleBand == "Long Open" or CrossoverMiddleBand == "Long Open and Long Close"
    [a,b,c] = testLongOpen(isCrossoverMiddleBand)
    longOpen := a
    longOpenCount := b
    validLongOpen := c
if CrossoverMiddleBand == "Long Close" or CrossoverMiddleBand == "Long Open and Long Close"
    [a,b,c] = testLongClose(isCrossoverMiddleBand) 
    longClose := a
    longCloseCount := b
    validLongClose := c   
            
bool isCrossunderMiddleBand = crossunder(close, MA)            
            
if CrossunderMiddleBand == "Long Open" or CrossunderMiddleBand == "Long Open and Long Close"
    [a,b,c] = testLongOpen(isCrossunderMiddleBand)
    longOpen := a
    longOpenCount := b
    validLongOpen := c
if CrossunderMiddleBand == "Long Close" or CrossunderMiddleBand == "Long Open and Long Close"
    [a,b,c] = testLongClose(isCrossunderMiddleBand) 
    longClose := a
    longCloseCount := b
    validLongClose := c                 
            




bool isCrossoverLowerBand = crossover(close, LowerBand)

if CrossoverLowerBand == "Long Open" or CrossoverLowerBand == "Long Open and Long Close"
    [a,b,c] = testLongOpen(isCrossoverLowerBand)
    longOpen := a
    longOpenCount := b
    validLongOpen := c
if CrossoverLowerBand == "Long Close" or CrossoverLowerBand == "Long Open and Long Close"
    [a,b,c] = testLongClose(isCrossoverLowerBand) 
    longClose := a
    longCloseCount := b
    validLongClose := c   
            
bool isCrossunderLowerBand = crossunder(close, LowerBand)            
            
if CrossunderLowerBand == "Long Open" or CrossunderLowerBand == "Long Open and Long Close"
    [a,b,c] = testLongOpen(isCrossunderLowerBand)
    longOpen := a
    longOpenCount := b
    validLongOpen := c
if CrossunderLowerBand == "Long Close" or CrossunderLowerBand == "Long Open and Long Close"
    [a,b,c] = testLongClose(isCrossunderLowerBand) 
    longClose := a
    longCloseCount := b
    validLongClose := c   
            
            
            
            
bool isPriceAboveUpperBand = close > UpperBand

if PriceAboveUpperBand == "Long Open" or PriceAboveUpperBand == "Long Open and Long Close"
    [a,b,c] = testLongOpen(isPriceAboveUpperBand)
    longOpen := a
    longOpenCount := b
    validLongOpen := c
if PriceAboveUpperBand == "Long Close" or PriceAboveUpperBand == "Long Open and Long Close"
    [a,b,c] = testLongClose(isPriceAboveUpperBand) 
    longClose := a
    longCloseCount := b
    validLongClose := c   
            
bool isPriceBelowUpperBand = close < UpperBand            
            
if PriceBelowUpperBand == "Long Open" or PriceBelowUpperBand == "Long Open and Long Close"
    [a,b,c] = testLongOpen(isPriceBelowUpperBand)
    longOpen := a
    longOpenCount := b
    validLongOpen := c
if PriceBelowUpperBand == "Long Close" or PriceBelowUpperBand == "Long Open and Long Close"
    [a,b,c] = testLongClose(isPriceBelowUpperBand) 
    longClose := a
    longCloseCount := b
    validLongClose := c              
            
bool isPriceAboveMiddleBand = close > MA

if PriceAboveMiddleBand == "Long Open" or PriceAboveMiddleBand == "Long Open and Long Close"
    [a,b,c] = testLongOpen(isPriceAboveMiddleBand)
    longOpen := a
    longOpenCount := b
    validLongOpen := c
if PriceAboveMiddleBand == "Long Close" or PriceAboveMiddleBand == "Long Open and Long Close"
    [a,b,c] = testLongClose(isPriceAboveMiddleBand) 
    longClose := a
    longCloseCount := b
    validLongClose := c   
            
bool isPriceBelowMiddleBand = close < MA          
            
if PriceBelowMiddleBand == "Long Open" or PriceBelowMiddleBand == "Long Open and Long Close"
    [a,b,c] = testLongOpen(isPriceBelowMiddleBand)
    longOpen := a
    longOpenCount := b
    validLongOpen := c
if PriceBelowMiddleBand == "Long Close" or PriceBelowMiddleBand == "Long Open and Long Close"
    [a,b,c] = testLongClose(isPriceBelowMiddleBand) 
    longClose := a
    longCloseCount := b
    validLongClose := c                 
            




bool isPriceAboveLowerBand = close > LowerBand

if PriceAboveLowerBand == "Long Open" or PriceAboveLowerBand == "Long Open and Long Close"
    [a,b,c] = testLongOpen(isPriceAboveLowerBand)
    longOpen := a
    longOpenCount := b
    validLongOpen := c
if PriceAboveLowerBand == "Long Close" or PriceAboveLowerBand == "Long Open and Long Close"
    [a,b,c] = testLongClose(isPriceAboveLowerBand) 
    longClose := a
    longCloseCount := b
    validLongClose := c   
            
bool isPriceBelowLowerBand = close < LowerBand           
            
if PriceBelowLowerBand == "Long Open" or PriceBelowLowerBand == "Long Open and Long Close"
    [a,b,c] = testLongOpen(isPriceBelowLowerBand)
    longOpen := a
    longOpenCount := b
    validLongOpen := c
if PriceBelowLowerBand == "Long Close" or PriceBelowLowerBand == "Long Open and Long Close"
    [a,b,c] = testLongClose(isPriceBelowLowerBand) 
    longClose := a
    longCloseCount := b
    validLongClose := c       
            
            
bool isBandWidthCrossoverBandValue1 = crossover(BBWidth, BandWidth1)           
            
if BandWidthCrossoverBandValue1 == "Long Open" or BandWidthCrossoverBandValue1 == "Long Open and Long Close"
    [a,b,c] = testLongOpen(isBandWidthCrossoverBandValue1)
    longOpen := a
    longOpenCount := b
    validLongOpen := c
if BandWidthCrossoverBandValue1 == "Long Close" or BandWidthCrossoverBandValue1 == "Long Open and Long Close"
    [a,b,c] = testLongClose(isBandWidthCrossoverBandValue1) 
    longClose := a
    longCloseCount := b
    validLongClose := c   
            
bool isBandWidthCrossoverBandValue2 = crossover(BBWidth, BandWidth2)           
            
if BandWidthCrossoverBandValue2 == "Long Open" or BandWidthCrossoverBandValue2 == "Long Open and Long Close"
    [a,b,c] = testLongOpen(isBandWidthCrossoverBandValue2)
    longOpen := a
    longOpenCount := b
    validLongOpen := c
if BandWidthCrossoverBandValue2 == "Long Close" or BandWidthCrossoverBandValue2 == "Long Open and Long Close"
    [a,b,c] = testLongClose(isBandWidthCrossoverBandValue2) 
    longClose := a
    longCloseCount := b
    validLongClose := c     
            
            
            
            
bool isBandWidthCrossunderBandValue1 = crossunder(BBWidth, BandWidth1)           
            
if BandWidthCrossunderBandValue1 == "Long Open" or BandWidthCrossunderBandValue1 == "Long Open and Long Close"
    [a,b,c] = testLongOpen(isBandWidthCrossunderBandValue1)
    longOpen := a
    longOpenCount := b
    validLongOpen := c
if BandWidthCrossunderBandValue1 == "Long Close" or BandWidthCrossunderBandValue1 == "Long Open and Long Close"
    [a,b,c] = testLongClose(isBandWidthCrossunderBandValue1) 
    longClose := a
    longCloseCount := b
    validLongClose := c    
            
bool isBandWidthCrossunderBandValue2 = crossunder(BBWidth, BandWidth2)           
            
if BandWidthCrossunderBandValue2 == "Long Open" or BandWidthCrossunderBandValue2 == "Long Open and Long Close"
    [a,b,c] = testLongOpen(isBandWidthCrossunderBandValue2)
    longOpen := a
    longOpenCount := b
    validLongOpen := c
if BandWidthCrossunderBandValue2 == "Long Close" or BandWidthCrossunderBandValue2 == "Long Open and Long Close"
    [a,b,c] = testLongClose(isBandWidthCrossunderBandValue2) 
    longClose := a
    longCloseCount := b
    validLongClose := c     
            
            


bool isBandWidthAboveBandValue1 = BBWidth > BandWidth1          
            
if BandWidthAboveBandValue1 == "Long Open" or BandWidthAboveBandValue1 == "Long Open and Long Close"
    [a,b,c] = testLongOpen(isBandWidthAboveBandValue1)
    longOpen := a
    longOpenCount := b
    validLongOpen := c
if BandWidthAboveBandValue1 == "Long Close" or BandWidthAboveBandValue1 == "Long Open and Long Close"
    [a,b,c] = testLongClose(isBandWidthAboveBandValue1) 
    longClose := a
    longCloseCount := b
    validLongClose := c     
            
bool isBandWidthAboveBandValue2 = BBWidth > BandWidth2           
            
if BandWidthAboveBandValue2 == "Long Open" or BandWidthAboveBandValue2 == "Long Open and Long Close"
    [a,b,c] = testLongOpen(isBandWidthAboveBandValue2)
    longOpen := a
    longOpenCount := b
    validLongOpen := c
if BandWidthAboveBandValue2 == "Long Close" or BandWidthAboveBandValue2 == "Long Open and Long Close"
    [a,b,c] = testLongClose(isBandWidthAboveBandValue2) 
    longClose := a
    longCloseCount := b
    validLongClose := c     
            
            
            
            
bool isBandWidthBelowBandValue1 = BBWidth < BandWidth1           
            
if BandWidthBelowBandValue1 == "Long Open" or BandWidthBelowBandValue1 == "Long Open and Long Close"
    [a,b,c] = testLongOpen(isBandWidthBelowBandValue1)
    longOpen := a
    longOpenCount := b
    validLongOpen := c
if BandWidthBelowBandValue1 == "Long Close" or BandWidthBelowBandValue1 == "Long Open and Long Close"
    [a,b,c] = testLongClose(isBandWidthBelowBandValue1) 
    longClose := a
    longCloseCount := b
    validLongClose := c    
            
bool isBandWidthBelowBandValue2 = BBWidth < BandWidth2         
            
if BandWidthBelowBandValue2 == "Long Open" or BandWidthBelowBandValue2 == "Long Open and Long Close"
    [a,b,c] = testLongOpen(isBandWidthBelowBandValue2)
    longOpen := a
    longOpenCount := b
    validLongOpen := c
if BandWidthBelowBandValue2 == "Long Close" or BandWidthBelowBandValue2 == "Long Open and Long Close"
    [a,b,c] = testLongClose(isBandWidthBelowBandValue2) 
    longClose := a
    longCloseCount := b
    validLongClose := c     





bool isPercentBCrossoverPercentBValue1 = crossover(PercentB, PercentB1)           
            
if PercentBCrossoverPercentBValue1 == "Long Open" or PercentBCrossoverPercentBValue1 == "Long Open and Long Close"
    [a,b,c] = testLongOpen(isPercentBCrossoverPercentBValue1)
    longOpen := a
    longOpenCount := b
    validLongOpen := c
if PercentBCrossoverPercentBValue1 == "Long Close" or PercentBCrossoverPercentBValue1 == "Long Open and Long Close"
    [a,b,c] = testLongClose(isPercentBCrossoverPercentBValue1) 
    longClose := a
    longCloseCount := b
    validLongClose := c    
            
bool isPercentBCrossoverPercentBValue2 = crossover(PercentB, PercentB2)           
            
if PercentBCrossoverPercentBValue2 == "Long Open" or PercentBCrossoverPercentBValue2 == "Long Open and Long Close"
    [a,b,c] = testLongOpen(isPercentBCrossoverPercentBValue2)
    longOpen := a
    longOpenCount := b
    validLongOpen := c
if PercentBCrossoverPercentBValue2 == "Long Close" or PercentBCrossoverPercentBValue2 == "Long Open and Long Close"
    [a,b,c] = testLongClose(isPercentBCrossoverPercentBValue2) 
    longClose := a
    longCloseCount := b
    validLongClose := c    
            
            
            
            
bool isPercentBCrossunderPercentBValue1 = crossunder(PercentB, PercentB1)           
            
if PercentBCrossunderPercentBValue1 == "Long Open" or PercentBCrossunderPercentBValue1 == "Long Open and Long Close"
    [a,b,c] = testLongOpen(isPercentBCrossunderPercentBValue1)
    longOpen := a
    longOpenCount := b
    validLongOpen := c
if PercentBCrossunderPercentBValue1 == "Long Close" or PercentBCrossunderPercentBValue1 == "Long Open and Long Close"
    [a,b,c] = testLongClose(isPercentBCrossunderPercentBValue1) 
    longClose := a
    longCloseCount := b
    validLongClose := c      
            
bool isPercentBCrossunderPercentBValue2 = crossunder(PercentB, PercentB2)           
            
if PercentBCrossunderPercentBValue2 == "Long Open" or PercentBCrossunderPercentBValue2 == "Long Open and Long Close"
    [a,b,c] = testLongOpen(isPercentBCrossunderPercentBValue2)
    longOpen := a
    longOpenCount := b
    validLongOpen := c
if PercentBCrossunderPercentBValue2 == "Long Close" or PercentBCrossunderPercentBValue2 == "Long Open and Long Close"
    [a,b,c] = testLongClose(isPercentBCrossunderPercentBValue2) 
    longClose := a
    longCloseCount := b
    validLongClose := c     
            
            


// bool isPercentBAbovePercentBValue1 = PercentB > PercentB1          
            
// if PercentBAbovePercentBValue1 == "Long Open" or PercentBAbovePercentBValue1 == "Long Open and Long Close"
//     [a,b,c] = testLongOpen(isPercentBAbovePercentBValue1)
//     longOpen := a
//     longOpenCount := b
//     validLongOpen := c
// if PercentBAbovePercentBValue1 == "Long Close" or PercentBAbovePercentBValue1 == "Long Open and Long Close"
//     [a,b,c] = testLongClose(isPercentBAbovePercentBValue1) 
//     longClose := a
//     longCloseCount := b
//     validLongClose := c     
            
// bool isPercentBAbovePercentBValue2 = PercentB > PercentB2           
            
// if PercentBAbovePercentBValue2 == "Long Open" or PercentBAbovePercentBValue2 == "Long Open and Long Close"
//     [a,b,c] = testLongOpen(isPercentBAbovePercentBValue2)
//     longOpen := a
//     longOpenCount := b
//     validLongOpen := c
// if PercentBAbovePercentBValue2 == "Long Close" or PercentBAbovePercentBValue2 == "Long Open and Long Close"
//     [a,b,c] = testLongClose(isPercentBAbovePercentBValue2) 
//     longClose := a
//     longCloseCount := b
//     validLongClose := c      
             
            
            
            
// bool isPercentBBelowPercentBValue1 = PercentB < PercentB1           
            
// if PercentBBelowPercentBValue1 == "Long Open" or PercentBBelowPercentBValue1 == "Long Open and Long Close"
//     [a,b,c] = testLongOpen(isPercentBBelowPercentBValue1)
//     longOpen := a
//     longOpenCount := b
//     validLongOpen := c
// if PercentBBelowPercentBValue1 == "Long Close" or PercentBBelowPercentBValue1 == "Long Open and Long Close"
//     [a,b,c] = testLongClose(isPercentBBelowPercentBValue1) 
//     longClose := a
//     longCloseCount := b
//     validLongClose := c    
            
// bool isPercentBBelowPercentBValue2 = PercentB < PercentB2         
            
// if PercentBBelowPercentBValue2 == "Long Open" or PercentBBelowPercentBValue2 == "Long Open and Long Close"
//     [a,b,c] = testLongOpen(isPercentBBelowPercentBValue2)
//     longOpen := a
//     longOpenCount := b
//     validLongOpen := c
// if PercentBBelowPercentBValue2 == "Long Close" or PercentBBelowPercentBValue2 == "Long Open and Long Close"
//     [a,b,c] = testLongClose(isPercentBBelowPercentBValue2) 
//     longClose := a
//     longCloseCount := b
//     validLongClose := c     



//-------------------------------------END CONDITIONS-------------------------------------------        


    
if OpenConditionsRequirement == "Minimum count"
    if longOpenCount >= OpenConditionsMinimumCount
        longOpen := 1
if CloseConditionsRequirement == "Minimum count"
    if longCloseCount >= CloseConditionsMinimumCount
        longClose := 1

// Tie breaker
if longClose == 1 and longOpen == 1
    longOpen := 0

if longOpen == 1 and window()
    strategy.entry("Long", true) // buy by market
    strategy.exit("Take Profit or Stop Loss", "Long", profit = profitTarget, loss = lossTarget)
else if longClose == 1 and window()
    strategy.close("Long")
else if not window()
    strategy.close("Long")

Published by ChaoZhang on the FMZ strategy square · last modified 2023-09-27. Collected in the Quant Trading Vault. Unvalidated research: re-test with your own data and costs before trusting it.

Build this with AgenKit

A spec is not a trading system. Paste this into your coding agent and AgenKit runs a specialist team — architecture, test-first build, risk gates, review — with paper trading on by default.

in Claude Code, Cursor, Codex & 4 more
/agenkit build a production-grade trading bot with a backtest engine, risk controls and paper trading by default from strategies/Diversified-Indicators-Aggregation-Strategy.md
  1. npx agenkit install engineering-kit in your project
  2. Paste the command into your agent and approve each phase
  3. Get tests, backtest, risk controls and a paper-trading switch

Understand it first

Before you run this, read the lesson behind the idea and how to backtest it honestly. Specs like this one are the survivors of an unknown number of trials; deflate their results accordingly.

Related specs

esc

Try , , or .

↑↓ move↵ open