Estratégia de ruptura da banda de Bollinger

Autora:ChaoZhang, Data: 2023-11-13 11:26:50
Tags:

img

Resumo

Esta estratégia utiliza as faixas dinâmicas superior e inferior das Bandas de Bollinger para ir longo quando o preço quebra acima da faixa superior e fechar a posição quando o preço cai abaixo da faixa inferior.

Estratégia lógica

A estratégia baseia-se principalmente no indicador Bollinger Bands para identificar breakouts.

  1. Linha média: média móvel de n períodos
  2. Faixa superior: linha média + desvio padrão de k * n períodos
  3. Faixa inferior: linha média - desvio padrão k * n períodos

Quando o preço sobe acima da faixa superior, o mercado é considerado sobrecomprado e uma posição longa pode ser iniciada.

A estratégia permite a personalização dos parâmetros das Bandas de Bollinger: o período da média móvel n e o multiplicador do desvio padrão k. Os valores por defeito são 20 períodos para a média móvel e 2 para o multiplicador do desvio padrão.

A estratégia verifica se o preço de fechamento quebra acima da faixa superior após cada dia de negociação. Se isso acontecer, um sinal longo é acionado na abertura do dia seguinte. Uma vez longo, a estratégia monitora se o preço quebra abaixo da faixa inferior em tempo real e fecha a posição se isso acontecer.

A estratégia também incorpora um filtro de média móvel que só gera sinais de compra quando o preço está acima da linha média móvel.

São fornecidas duas opções de stop loss: stop loss porcentual fixo ou seguir a faixa inferior.

Vantagens da estratégia

  • Utilizar Bandas de Bollinger para avaliar os níveis de sobrecompra/supervenda
  • Filtro de média móvel evita negociação contra a tendência
  • Parâmetros de Bollinger Bands personalizáveis adequados a diferentes períodos
  • Escolha entre dois métodos de stop loss
  • O backtesting permite a otimização de parâmetros e a verificação fora da amostra

Riscos da Estratégia

  • As bandas de Bollinger não podem determinar totalmente a sobrecompra/supervenda
  • O filtro de média móvel pode perder breakouts mais rápidos
  • O stop loss fixo pode ser demasiado conservador, o trailing stop pode ser demasiado agressivo
  • Os parâmetros precisam de otimização para diferentes produtos e prazos
  • Incapaz de limitar o tamanho das perdas, precisa de considerar a gestão de dinheiro

Orientações de otimização

  • Teste diferentes combinações de parâmetros da média móvel
  • Tente diferentes parâmetros Bollinger Bands
  • Comparar percentual fixo de stop loss versus faixa inferior de atraso em termos de retorno
  • Adicionar módulo de gestão de dinheiro ao limite por perda de negociação
  • Incorporar outros indicadores para confirmar o sinal Bollinger Bands

Conclusão

A estratégia identifica condições de sobrecompra/supervenda usando bandas dinâmicas de Bollinger Bands, refere-se a filtros de média móvel e usa paradas para proteger o capital. Em comparação com as breakouts tradicionais de nível fixo, ela se adapta melhor às flutuações do mercado. Com mais otimização de parâmetros e controles de risco, a estratégia pode alcançar maior estabilidade e retornos.


/*backtest
start: 2022-11-06 00:00:00
end: 2023-11-12 00:00:00
period: 1d
basePeriod: 1h
exchanges: [{"eid":"Futures_Binance","currency":"BTC_USDT"}]
*/

//@version=5

// Revision:        1
// Author:          @millerrh
// Strategy:  
//      Entry: Buy when price breaks out of upper Bollinger Band
//      Exit: Trail a stop with the lower Bollinger Band 
// Conditions/Variables:
//    1. Can add a filter to only take setups that are above a user-defined moving average on current timeframe and/or longer timeframe (helps avoid trading counter trend) 
//    2. Manually configure which dates to back test
//    3. User-Configurable Bollinger Band Settings
//    4. Optionally use a tighter initial stop level.  Once Bollinger Band catches up, trail with lower Bollinger Band to give more breathing room.

// strategy('Donchian Breakout', overlay=true, initial_capital=100000, currency='USD', default_qty_type=strategy.percent_of_equity, calc_on_every_tick = true,
//   default_qty_value=100, commission_type=strategy.commission.percent, commission_value=0.1)

strategy('Bollinger Breakout', overlay=true, initial_capital=100000, currency='USD', default_qty_type=strategy.percent_of_equity,
  default_qty_value=100, commission_type=strategy.commission.percent, commission_value=0.0, calc_on_order_fills=true)

// === BACKTEST RANGE ===
Start = input(defval = timestamp("01 Jan 2019 06:00 +0000"), title = "Backtest Start Date", group = "backtest window")
Finish = input(defval = timestamp("01 Jan 2100 00:00 +0000"), title = "Backtest End Date", group = "backtest window")

// == INPUTS ==
// Bollinger Band Inputs
bbLength = input.int(20, minval=1, group = "Bollinger Band Settings", title="Bollinger Band Length",
  tooltip = "Bollinger Band moving average length.")
bbMultTop = input.float(2.0, minval=0.001, maxval=50, title="Standard Deviation (Top)")
bbMultBot = input.float(2.0, minval=0.001, maxval=50, title="Standard Deviation (Bottom)")

useTightStop = input.bool(title='Use Fixed Percentage for Initial Stop?', defval=false, group = "order entry",
  tooltip = "'Keep your losers small and let winners run' is the saying.  This will allow you to use a tight initial stop
  until the lower Bollinger Band catches up.")
percStop = input.int(title="Stop", defval=8, group = "order entry", inline = "perc")
trigInput = input.string(title='Execute Trades On...', defval='Wick', options=['Wick', 'Close'], group = "order entry",
  tooltip = "Useful for comparing standing stop orders at the Bollinger Band boundary (executing on the wick) vs. waiting for candle closes prior to taking action")

// Moving Average Filtering Inputs
useMaFilter = input.bool(title='Use Moving Average for Filtering (Current Timeframe)?', defval=false, group = "moving average filtering",
  tooltip = "Signals will be ignored when price is under this moving average.  The intent is to keep you out of bear periods and only buying when 
             price is showing strength.")
maType = input.string(defval='SMA', options=['EMA', 'SMA'], title='MA Type For Filtering', group = "moving average filtering")
maLength = input.int(defval=50, title="Moving Average:    Length", minval=1, group = "moving average filtering", inline = "1ma")
ma1Color = input.color(color.new(color.green, 50), title = " Color", group = "moving average filtering", inline = "1ma")
useMaFilter2 = input.bool(title='Use Moving Average for Filtering (High Timeframe)?', defval=false, group = "moving average filtering")
tfSet = input.timeframe(defval="D", title="Timeframe of Moving Average", group = "moving average filtering",
  tooltip = "Allows you to set a different time frame for a moving average filter.  Trades will be ignored when price is under this moving average.
  The idea is to keep your eye on the larger moves in the market and stay on the right side of the longer term trends and help you be pickier about 
  the stocks you trade.")
ma2Type = input.string(defval='SMA', options=['EMA', 'SMA'], title='MA Type For Filtering', group = "moving average filtering")
ma2Length = input.int(defval=50, title="Moving Average:    Length", minval=1, group = "moving average filtering", inline = "2ma")
ma2Color = input.color(color.new(color.white, 50), title = " Color", group = "moving average filtering", inline = "2ma")


// === THE BOLLINGER BAND ===
// Logic
bbBasis = ta.sma(close, bbLength)
bbUpper = bbBasis + bbMultTop * ta.stdev(close, bbLength)
bbLower = bbBasis - bbMultBot * ta.stdev(close, bbLength)

// Plotting
plot(bbBasis, "Basis", color=color.new(color.white, 50))
p1 = plot(bbUpper, color=color.new(color.blue, 50), linewidth=1, title='Upper Bollinger Band')
p2 = plot(bbLower, color=color.new(color.blue, 50), linewidth=1, title='Lower Bollinger Band')
fill(p1, p2, title = "Background", color=color.rgb(33, 150, 243, 95))

// == FILTERING LOGIC ==
// Declare function to be able to swap out EMA/SMA
ma(maType, src, length) =>
    maType == 'EMA' ? ta.ema(src, length) : ta.sma(src, length)  //Ternary Operator (if maType equals EMA, then do ema calc, else do sma calc)
maFilter = ma(maType, close, maLength)
maFilter2 = request.security(syminfo.tickerid, tfSet, ma(ma2Type, close, ma2Length))

// Plotting
plot(useMaFilter ? maFilter : na, title='Trend Filter MA - CTF', color=ma1Color, linewidth=2, style=plot.style_line)
plot(useMaFilter2 ? maFilter2 : na, title='Trend Filter MA - HTF', color=ma2Color, linewidth=2, style=plot.style_line)


// == ENTRY AND EXIT CRITERIA ==
// Trigger stop based on candle close or High/Low (i.e. Wick)
trigResistance = trigInput == 'Close' ? close : trigInput == 'Wick' ? high : na
trigSupport = trigInput == 'Close' ? close : trigInput == 'Wick' ? low : na
buySignal = trigResistance >= bbUpper 

buyConditions = (useMaFilter ? bbUpper > maFilter : true) and
  (useMaFilter2 ? bbUpper > maFilter2 : true) 
  
// == STOP AND PRICE LEVELS ==
// Configure initial stop level
inPosition = strategy.position_size > 0
stopLevel = strategy.position_avg_price - (strategy.position_avg_price * percStop/100)
posStop = stopLevel > bbLower ? stopLevel : bbLower


// Check if using stop vs. not
stop = useTightStop ? posStop : bbLower
plot(inPosition ? stop : na, style=plot.style_linebr, color=color.new(color.red, 40), linewidth = 1, title = "Stop Levels", trackprice=false)

sellSignal = trigSupport <= stop

// == STRATEGY ENTRIES & EXITS ==
// This string of code enters and exits at the candle close
if trigInput == 'Close'
    strategy.entry('Long', strategy.long, when=buyConditions and buySignal)
    strategy.close('Long', when=sellSignal)

// This string of code enters and exits at the wick (i.e. with pre-set stops)
if trigInput == 'Wick'
    strategy.entry('Long', strategy.long, stop=bbUpper, when=buyConditions)
    strategy.exit('Exit Long', from_entry='Long', stop=stop)
strategy.cancel('Long',when= not(buyConditions)) // Resets stop level once buyConditions aren't true anymore



Mais.