Estrategia Bollinger Percent B Reversion
Este enfoque opera contra los extremos de precio más allá de las Bollinger Bands usando el indicador Percent B. Los movimientos por encima de la banda superior o por debajo de la banda inferior sugieren sobreextensión.
Las pruebas indican un rendimiento anual promedio de aproximadamente 142%. Funciona mejor en el mercado de acciones.
Cuando Percent B es menor que cero o mayor que uno, el sistema apuesta por un retorno al centro de la banda. Un umbral de salida cierra las operaciones una vez que el momentum se normaliza.
Los stops se colocan a un porcentaje fijo desde la entrada.
Detalles
- Criterios de entrada: Percent B fuera del rango 0–1.
- Largo/Corto: Ambas direcciones.
- Criterios de salida: Percent B cruza
ExitValueo stop. - Stops: Sí.
- Valores predeterminados:
BollingerPeriod= 20BollingerDeviation= 2.0mExitValue= 0.5mStopLossPercent= 2.0mCandleType= TimeSpan.FromMinutes(5)
- Filtros:
- Categoría: Reversión a la media
- Dirección: Ambos
- Indicadores: Bollinger Bands
- Stops: Sí
- Complejidad: Básico
- Marco temporal: Intradía
- Estacionalidad: No
- Redes neuronales: No
- Divergencia: No
- Nivel de riesgo: Medio
using System;
using System.Collections.Generic;
using Ecng.Common;
using StockSharp.Algo.Indicators;
using StockSharp.Algo.Strategies;
using StockSharp.BusinessEntities;
using StockSharp.Messages;
namespace StockSharp.Samples.Strategies;
/// <summary>
/// Strategy that trades on Bollinger %B indicator.
/// Bollinger %B shows where price is relative to the Bollinger Bands.
/// Values below 0 or above 1 indicate price outside the bands.
/// </summary>
public class BollingerPercentBStrategy : Strategy
{
private readonly StrategyParam<int> _bollingerPeriod;
private readonly StrategyParam<decimal> _bollingerDeviation;
private readonly StrategyParam<decimal> _exitValue;
private readonly StrategyParam<DataType> _candleType;
private readonly StrategyParam<int> _cooldownBars;
private int _cooldown;
/// <summary>
/// Period for Bollinger Bands calculation.
/// </summary>
public int BollingerPeriod
{
get => _bollingerPeriod.Value;
set => _bollingerPeriod.Value = value;
}
/// <summary>
/// Deviation for Bollinger Bands calculation.
/// </summary>
public decimal BollingerDeviation
{
get => _bollingerDeviation.Value;
set => _bollingerDeviation.Value = value;
}
/// <summary>
/// Exit threshold for %B.
/// </summary>
public decimal ExitValue
{
get => _exitValue.Value;
set => _exitValue.Value = value;
}
/// <summary>
/// Type of candles used for strategy calculation.
/// </summary>
public DataType CandleType
{
get => _candleType.Value;
set => _candleType.Value = value;
}
/// <summary>
/// Cooldown bars between trades.
/// </summary>
public int CooldownBars
{
get => _cooldownBars.Value;
set => _cooldownBars.Value = value;
}
/// <summary>
/// Initialize the Bollinger %B Reversion strategy.
/// </summary>
public BollingerPercentBStrategy()
{
_bollingerPeriod = Param(nameof(BollingerPeriod), 20)
.SetDisplay("Bollinger Period", "Period for Bollinger Bands calculation", "Indicators")
.SetOptimize(10, 30, 5);
_bollingerDeviation = Param(nameof(BollingerDeviation), 2.0m)
.SetDisplay("Bollinger Deviation", "Deviation for Bollinger Bands calculation", "Indicators")
.SetOptimize(1.5m, 2.5m, 0.25m);
_exitValue = Param(nameof(ExitValue), 0.5m)
.SetDisplay("Exit %B Value", "Exit threshold for %B", "Exit")
.SetOptimize(0.3m, 0.7m, 0.1m);
_candleType = Param(nameof(CandleType), TimeSpan.FromMinutes(1).TimeFrame())
.SetDisplay("Candle Type", "Type of candles to use", "General");
_cooldownBars = Param(nameof(CooldownBars), 500)
.SetRange(1, 1000)
.SetDisplay("Cooldown Bars", "Bars to wait between trades", "General");
}
/// <inheritdoc />
public override IEnumerable<(Security sec, DataType dt)> GetWorkingSecurities()
{
return [(Security, CandleType)];
}
/// <inheritdoc />
protected override void OnReseted()
{
base.OnReseted();
_cooldown = default;
}
/// <inheritdoc />
protected override void OnStarted2(DateTime time)
{
base.OnStarted2(time);
_cooldown = 0;
var bollinger = new BollingerBands
{
Length = BollingerPeriod,
Width = BollingerDeviation
};
var subscription = SubscribeCandles(CandleType);
subscription
.BindEx(bollinger, ProcessCandle)
.Start();
var area = CreateChartArea();
if (area != null)
{
DrawCandles(area, subscription);
DrawIndicator(area, bollinger);
DrawOwnTrades(area);
}
}
private void ProcessCandle(ICandleMessage candle, IIndicatorValue bollingerValue)
{
if (candle.State != CandleStates.Finished)
return;
if (!bollingerValue.IsFormed)
return;
var bb = (BollingerBandsValue)bollingerValue;
if (bb.UpBand is not decimal upperBand ||
bb.LowBand is not decimal lowerBand)
return;
if (_cooldown > 0)
{
_cooldown--;
return;
}
// Calculate Bollinger %B: (Price - Lower Band) / (Upper Band - Lower Band)
decimal percentB = 0;
if (upperBand != lowerBand)
percentB = (candle.ClosePrice - lowerBand) / (upperBand - lowerBand);
if (Position == 0)
{
if (percentB < 0)
{
BuyMarket();
_cooldown = CooldownBars;
}
else if (percentB > 1)
{
SellMarket();
_cooldown = CooldownBars;
}
}
else if (Position > 0)
{
if (percentB > ExitValue)
{
SellMarket();
_cooldown = CooldownBars;
}
}
else if (Position < 0)
{
if (percentB < ExitValue)
{
BuyMarket();
_cooldown = CooldownBars;
}
}
}
}
import clr
clr.AddReference("StockSharp.Messages")
clr.AddReference("StockSharp.Algo")
clr.AddReference("StockSharp.Algo.Indicators")
clr.AddReference("StockSharp.Algo.Strategies")
from System import TimeSpan
from StockSharp.Messages import DataType, CandleStates
from StockSharp.Algo.Indicators import BollingerBands
from StockSharp.Algo.Strategies import Strategy
class bollinger_percent_b_strategy(Strategy):
"""
Bollinger %B strategy.
Buys when %B < 0 (below lower band), sells when %B > 1 (above upper band).
"""
def __init__(self):
super(bollinger_percent_b_strategy, self).__init__()
self._bb_period = self.Param("BollingerPeriod", 20).SetDisplay("Bollinger Period", "Period for Bollinger Bands calculation", "Indicators")
self._bb_deviation = self.Param("BollingerDeviation", 2.0).SetDisplay("Bollinger Deviation", "Deviation for Bollinger Bands calculation", "Indicators")
self._exit_value = self.Param("ExitValue", 0.5).SetDisplay("Exit %B Value", "Exit threshold for %B", "Exit")
self._candle_type = self.Param("CandleType", DataType.TimeFrame(TimeSpan.FromMinutes(1))).SetDisplay("Candle Type", "Type of candles to use", "General")
self._cooldown_bars = self.Param("CooldownBars", 500).SetDisplay("Cooldown Bars", "Bars to wait between trades", "General")
self._cooldown = 0
@property
def candle_type(self):
return self._candle_type.Value
def OnReseted(self):
super(bollinger_percent_b_strategy, self).OnReseted()
self._cooldown = 0
def OnStarted2(self, time):
super(bollinger_percent_b_strategy, self).OnStarted2(time)
self._cooldown = 0
bb = BollingerBands()
bb.Length = self._bb_period.Value
bb.Width = self._bb_deviation.Value
subscription = self.SubscribeCandles(self.candle_type)
subscription.BindEx(bb, self._process_candle).Start()
area = self.CreateChartArea()
if area is not None:
self.DrawCandles(area, subscription)
self.DrawIndicator(area, bb)
self.DrawOwnTrades(area)
def _process_candle(self, candle, bb_val):
if candle.State != CandleStates.Finished:
return
if not bb_val.IsFormed:
return
if bb_val.UpBand is None or bb_val.LowBand is None:
return
if self._cooldown > 0:
self._cooldown -= 1
return
upper = float(bb_val.UpBand)
lower = float(bb_val.LowBand)
close = float(candle.ClosePrice)
cd = self._cooldown_bars.Value
exit_v = float(self._exit_value.Value)
pct_b = 0.0
if upper != lower:
pct_b = (close - lower) / (upper - lower)
if self.Position == 0:
if pct_b < 0:
self.BuyMarket()
self._cooldown = cd
elif pct_b > 1:
self.SellMarket()
self._cooldown = cd
elif self.Position > 0:
if pct_b > exit_v:
self.SellMarket()
self._cooldown = cd
elif self.Position < 0:
if pct_b < exit_v:
self.BuyMarket()
self._cooldown = cd
def CreateClone(self):
return bollinger_percent_b_strategy()