Estrategia de ruptura de la banda de Bollinger

El autor:¿ Qué pasa?, Fecha: 2023-11-13 11:26:50
Las etiquetas:

img

Resumen general

Esta estrategia utiliza las bandas dinámicas superior e inferior de las bandas de Bollinger para ir largo cuando el precio se rompe por encima de la banda superior y cerrar la posición cuando el precio cae por debajo de la banda inferior.

Estrategia lógica

La estrategia se basa principalmente en el indicador de Bollinger Bands para identificar las rupturas.

  1. Línea media: media móvil de n períodos
  2. Banda superior: línea media + desviación estándar de k * n períodos
  3. Banda inferior: línea media - desviación estándar k * n período

Cuando el precio se eleva por encima de la banda superior, el mercado se considera sobrecomprado, y una posición larga se puede iniciar.

La estrategia permite personalizar los parámetros de las bandas de Bollinger: el período de media móvil n y el multiplicador de desviación estándar k. Los valores predeterminados son 20 períodos para la media móvil y 2 para el multiplicador de desviación estándar.

La estrategia comprueba si el precio de cierre se rompe por encima de la banda superior después de cada día de negociación. Si lo hace, se activa una señal larga en la apertura del día siguiente. Una vez larga, la estrategia monitorea si el precio se rompe por debajo de la banda inferior en tiempo real y cierra la posición si lo hace.

La estrategia también incorpora un filtro de promedio móvil que solo genera señales de compra cuando el precio está por encima de la línea de promedio móvil.

Se ofrecen dos opciones de stop loss: stop loss porcentual fijo o seguimiento de la banda inferior.

Ventajas de la estrategia

  • Utilizar bandas de Bollinger para juzgar los niveles de sobrecompra/sobreventa
  • El filtro de la media móvil evita la negociación contra la tendencia
  • Los parámetros de las bandas de Bollinger personalizables se adaptan a diferentes períodos
  • Opción entre dos métodos de stop loss
  • El backtesting permite la optimización de parámetros y la verificación fuera de la muestra

Riesgos de la estrategia

  • Las bandas de Bollinger no pueden determinar completamente el sobrecomprado/sobrevendido
  • El filtro de media móvil puede perder los brotes más rápidos
  • El stop loss fijo puede ser demasiado conservador, el trailing stop puede ser demasiado agresivo
  • Los parámetros necesitan optimización para diferentes productos y plazos
  • Incapaz de limitar el tamaño de las pérdidas, necesita considerar la gestión del dinero

Direcciones de optimización

  • Prueba de diferentes combinaciones de parámetros de media móvil
  • Pruebe con otros parámetros de bandas de Bollinger
  • Comparar el porcentaje fijo de pérdida por parada con la banda inferior de la banda trasera en términos de rendimiento
  • Añadir el módulo de gestión de dinero al límite por pérdida de operaciones
  • Incorporar otros indicadores para confirmar la señal de Bollinger Bands

Conclusión

La estrategia identifica condiciones de sobrecompra/sobreventa utilizando bandas dinámicas de Bollinger Bands, se refiere a filtros de promedio móvil y utiliza paradas para proteger el capital. En comparación con las breakouts tradicionales de nivel fijo, se adapta mejor a las fluctuaciones del mercado. Con una mayor optimización de parámetros y controles de riesgos, la estrategia puede lograr una mayor estabilidad y rendimientos.


/*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



Más.