The resource loading... loading...

Dual Moving Average Bollinger Bands Trend Following Strategy

Author: ChaoZhang, Date: 2023-11-01 14:15:11
Tags:

img

Overview

This strategy makes trading decisions based on dual moving average Bollinger Bands to follow the trend. It uses the convergence and divergence of the upper and lower rails of Bollinger Bands to determine trend changes, buying near the lower rail and selling near the upper rail, to achieve buying low and selling high.

Strategy Logic

The strategy applies both simple Bollinger Bands and enhanced Bollinger Bands.

Simple Bollinger Bands use SMA of close prices for the middle band, while enhanced Bollinger Bands use EMA of close prices.

The upper and lower bands are calculated by middle band ± N standard deviations.

The strategy judges the strength of the trend based on the spread between the upper and lower bands. When the spread is below a threshold, it indicates the beginning of a trending period for trend following.

Specifically, when price approaches the lower band, it longs. When price approaches the upper band, it closes the position. The stop loss method is fixed percentage. Trailing stop can also be enabled.

Take profit depends on closing near the middle band or upper band.

The strategy can also choose to only sell at a profit to prevent losses.

Advantage Analysis

The advantages of this strategy:

  1. Dual Bollinger Bands improves efficiency

By comparing simple and enhanced Bollinger Bands, it can choose the better version for higher efficiency.

  1. Spread judges trend strength

When spread narrows, it indicates a strengthening trend. Following the trend has a higher win rate.

  1. Flexible profit taking and stop loss

Fixed percentage stop loss controls single trade loss. Take profit near middle or upper band. Trailing stop locks in more profit.

  1. Protective mechanism against losses

Only selling at a profit prevents loss from expanding.

Risk Analysis

The risks include:

  1. Drawdown risk

Trend following itself carries drawdown risks. Need to endure consecutive losses mentally.

  1. Whipsaw risk

When bands are wide, market may turn sideways. The strategy is less effective. Need to pause trading until trend resumes.

  1. Stop loss triggered risk

Fixed percentage stop loss may be too aggressive. Need more moderate stop like ATR stop.

Optimization Directions

The strategy can optimize on:

  1. Bollinger Bands parameters

Test different MA lengths, standard deviation multiples to find optimal combinations for different markets.

  1. Add filters

Add filters like MACD, KD on top of Bollinger signal to reduce trades during whipsaw markets.

  1. Profit taking and stop loss

Test different trailing stop methods. Or optimize stop loss based on volatility, ATR etc.

  1. Money management

Optimize position sizing per trade. Test different add-on strategies.

Conclusion

This strategy combines the strengths of dual Bollinger Bands, judging trend strength by band width and trading pullbacks during trends. It also sets proper stop loss to control risks. Further improvements can be made through parameter optimization and adding filters.


/*backtest
start: 2023-10-01 00:00:00
end: 2023-10-31 00:00:00
period: 1h
basePeriod: 15m
exchanges: [{"eid":"Futures_Binance","currency":"BTC_USDT"}]
*/

// This source code is subject to the terms of the Mozilla Public License 2.0 at https://mozilla.org/MPL/2.0/
// © JCGMarkets 

//@version=4
strategy("B.Bands | Augmented | Intra-range | Long-Only", shorttitle = "BB|A|IR|L", initial_capital=5000, commission_value=0.075, slippage = 1, overlay = true)

//Technical Indicators Data
show_simp   = input(false, title="Trade on Simple Bollinger Bands ", type= input.bool, group="Select Strategy System")
show_augm   = input(true, title="Trade on Augmented Bollinger Bands", type= input.bool, group="Select Strategy System") 
periods     = input(20, title="Periods for Moving Average", type =input.integer, minval = 2, step = 1, group="Technical Inputs")
std         = input(2, title="Std", type = input.float, minval=0.1 , step = 0.1, group="Technical Inputs")

// Strategy data
max_spread_bb   = input(20000.0, title="Max Spread Tolerance Beetween Bands", type=input.float, step=0.1, group="Strategy Inputs")
entry_source    = input(close, title="Entry data source", type=input.source, group="Strategy Inputs")
exit_source     = input(high, title="Exit data source", type=input.source, group="Strategy Inputs")
take_profit     = input("middle", title = "Profit to band:", options = ["middle", "opposite"], group="Strategy Inputs")
stop_loss       = input(3.00, title="Stop Loss %", type=input.float, step=0.05, group="Strategy Inputs")
trailing        = input(false, title="Activate trailing stop?", type = input.bool, group="Strategy Inputs")
stop_perc       = input(6.00, title="Trailing %", type=input.float, step=0.125, group="Strategy Inputs") * 0.01
sell_profit     = input(false, title="Only sell in profit (Stop Loss still active) ", type= input.bool, group="Strategy Inputs")


var SL = 0.0
var SLT= 0.0


//Simple BB Calculation -> adapt if needed with different std for upper-lower, sma-ema, etc 
middle_sim = sma(close, periods)

//Augmented BB Calculation -> adapt if needed with different std for upper lower, etc
middle_augm  = ema(close, periods)
middle_upp = ema(high, periods)
middle_low = ema(low, periods)

//Multiplier
dev      = stdev(close, periods) * std

//Upper & Lower Bands
upper = (middle_sim + dev)
lower = (middle_sim - dev)

//Augmented Bands
upper_augm = (middle_upp + dev)
lower_augm = (middle_low - dev)

//Bands Spread
spread   = upper - lower
spread_augm   = upper_augm - lower_augm

//From date
filter_from    =   input(  true,    title="===> From", group="Date Control")
from_y         =   input(  2010,    title = "from year", group="Date Control")
from_m         =   input(     1,    title = "from month", minval =1, maxval=12, group="Date Control")
from_d         =   input(     1,    title = "from day",  minval=1, maxval=31, group="Date Control")

//To date
filter_to   =    input( true,   title="===> To", group="Date Control")
to_y        =    input( 2030,   title = "To year", group="Date Control")
to_m        =    input(    1,   title = "To month", minval =1, maxval=12, group="Date Control")
to_d        =    input(    1,  title = "To day",  minval=1, maxval=31, group="Date Control")

// Date Condition
In_date() =>  true

in_position = strategy.position_size > 0 

// Trailing stop 
SLT := if in_position and In_date()
    stop_inicial = entry_source * (1 - stop_perc)
    max(stop_inicial, SLT[1])
else
    0

slts = (low <= SLT) and (trailing == true)


//Essential Trade logics
entry_long = (entry_source <= lower) and (spread < max_spread_bb)
entry_long_augm = (entry_source <= lower_augm) and (spread_augm < max_spread_bb)

// Simple Bollinger Conditions

if (not in_position and show_simp and In_date())
    if entry_long
        // Trigger buy order
        position_size = round( strategy.equity / close ) // All available equity for this strategy example
        strategy.entry("Entry", strategy.long, qty = position_size )
        SL := close * (1 - (stop_loss / 100)) // You could determine wether or not implement stop loss with bool input and if condition here.


if in_position and show_simp and not sell_profit and In_date()
    //Exits if not sell in profit
    if take_profit == "middle" 
        strategy.exit("Target", "Entry", limit = middle_sim, stop = SL, comment="Exit")
    if take_profit == "opposite"
        strategy.exit("Target", "Entry", limit = upper, stop = SL, comment="Exit")    

if in_position and show_simp and sell_profit and In_date()
    //Exits if sell in profit
    if take_profit == "middle" 
        strategy.exit("Target", "Entry", limit = (strategy.openprofit > 0 ? middle_sim: na), stop = SL, comment="Exit")
    if take_profit == "opposite"
        strategy.exit("Target", "Entry", limit = (strategy.openprofit > 0 ? upper: na), stop = SL, comment="Exit")    



if in_position and show_simp and slts and In_date()
    //Trailing activation
    strategy.close("Entry", comment="SLT")

if not In_date()
    //Exit due out of date range
    strategy.close("Entry", comment="Out of date range")



// Augmented Bollinger Conditions

if (not in_position and show_augm and In_date()) 
    if entry_long_augm
        // Trigger buy order
        position_size = round( strategy.equity / close )
        strategy.entry("Entry_A", strategy.long, qty = position_size )
        SL := close * (1 - (stop_loss / 100) )

if in_position and show_augm and not sell_profit and In_date()
    //Exits and not sell in profit
    if take_profit == "middle"
        strategy.exit("Target", "Entry_A", limit = middle_augm, stop = SL, comment="Exit")
    if take_profit == "opposite"
        strategy.exit("Target", "Entry_A", limit = upper_augm, stop = SL, comment="Exit")            
        

if in_position and show_augm and sell_profit and In_date() 
    //Exit only in profit
    if take_profit == "middle"
        strategy.exit("Target", "Entry_A", limit = (strategy.openprofit > 0 ? middle_augm:na), stop = SL, comment="Exit")
    if take_profit == "opposite"
        strategy.exit("Target", "Entry_A", limit = (strategy.openprofit > 0 ? upper_augm: na) , stop = SL, comment="Exit") 


if in_position  and show_augm and slts and In_date()
    //Trigger trailing
    strategy.close("Entry_A", comment="SLT")
    
if not In_date()
    //Out of date trigger
    strategy.close("Entry_A", comment= "Out of date range")




// Plotting

plot(in_position ? SL > 0 ? SL : na : na , style = plot.style_circles, color = color.red, title = "Stop Loss")
plot(in_position ? trailing ? SLT > 0 ? SLT : na : na : na , style = plot.style_circles, color = color.blue, title = "Trailing Stop" )

s = plot(show_simp ? upper : na , color = color.aqua)
plot(show_simp ? middle_sim : na , color=color.red)
i = plot(show_simp ? lower : na , color = color.aqua)
fill(s,i, color=color.new(color.aqua,90))


plot(show_augm ? middle_augm : na , color=color.blue)
s_a = plot( show_augm ? upper_augm : na, color=color.orange)
i_a = plot( show_augm ? lower_augm : na, color= color.orange)
fill(s_a,i_a, color=color.new(color.orange, 90))

More