Estrategia Bollinger ADX
Estrategia que combina las Bandas de Bollinger y el indicador ADX. Busca rupturas con fuerte confirmación de tendencia.
Las pruebas indican un retorno anual promedio de aproximadamente el 46%. Funciona mejor en el mercado de acciones.
Los movimientos de precio fuera de las Bandas de Bollinger se filtran mediante ADX para confirmar la fortaleza. Las operaciones se activan cuando una ruptura de banda coincide con un ADX elevado.
Útil para aumentos de volatilidad acompañados de tendencias fuertes. El tamaño del stop se determina mediante el ATR.
Detalles
- Criterios de entrada:
- Largo:
Close < LowerBand && ADX > AdxThreshold - Corto:
Close > UpperBand && ADX > AdxThreshold
- Largo:
- Largo/Corto: Ambos
- Criterios de salida:
- Reversión a la media de Bollinger
- Stops: Basados en ATR usando
AtrMultiplier - Valores predeterminados:
BollingerPeriod= 20BollingerDeviation= 2.0mAdxPeriod= 14AdxThreshold= 25mAtrMultiplier= 2.0mCandleType= TimeSpan.FromMinutes(5).TimeFrame()
- Filtros:
- Categoría: Reversión a la media
- Dirección: Ambos
- Indicadores: Bollinger Bands, ADX
- Stops: Sí
- Complejidad: Intermedio
- Marco temporal: Medio plazo
- 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 combining Bollinger Bands with manual ADX trend strength.
/// Buys on upper band breakout with strong trend, sells on lower band breakout.
/// </summary>
public class BollingerAdxStrategy : Strategy
{
private readonly StrategyParam<DataType> _candleType;
private readonly StrategyParam<int> _bollingerPeriod;
private readonly StrategyParam<decimal> _bollingerDeviation;
private readonly StrategyParam<int> _adxPeriod;
private readonly StrategyParam<decimal> _adxThreshold;
private readonly StrategyParam<int> _cooldownBars;
private readonly List<decimal> _highs = new();
private readonly List<decimal> _lows = new();
private readonly List<decimal> _closes = new();
private int _cooldown;
/// <summary>
/// Candle type for strategy calculation.
/// </summary>
public DataType CandleType
{
get => _candleType.Value;
set => _candleType.Value = value;
}
/// <summary>
/// Bollinger Bands period.
/// </summary>
public int BollingerPeriod
{
get => _bollingerPeriod.Value;
set => _bollingerPeriod.Value = value;
}
/// <summary>
/// Bollinger Bands deviation multiplier.
/// </summary>
public decimal BollingerDeviation
{
get => _bollingerDeviation.Value;
set => _bollingerDeviation.Value = value;
}
/// <summary>
/// ADX period.
/// </summary>
public int AdxPeriod
{
get => _adxPeriod.Value;
set => _adxPeriod.Value = value;
}
/// <summary>
/// ADX threshold for strong trend.
/// </summary>
public decimal AdxThreshold
{
get => _adxThreshold.Value;
set => _adxThreshold.Value = value;
}
/// <summary>
/// Cooldown bars between trades.
/// </summary>
public int CooldownBars
{
get => _cooldownBars.Value;
set => _cooldownBars.Value = value;
}
/// <summary>
/// Initialize strategy.
/// </summary>
public BollingerAdxStrategy()
{
_candleType = Param(nameof(CandleType), TimeSpan.FromMinutes(5).TimeFrame())
.SetDisplay("Candle Type", "Type of candles to use", "General");
_bollingerPeriod = Param(nameof(BollingerPeriod), 20)
.SetRange(10, 30)
.SetDisplay("Bollinger Period", "Period for Bollinger Bands", "Indicators");
_bollingerDeviation = Param(nameof(BollingerDeviation), 2.0m)
.SetDisplay("Bollinger Deviation", "Standard deviation multiplier", "Indicators");
_adxPeriod = Param(nameof(AdxPeriod), 14)
.SetRange(7, 21)
.SetDisplay("ADX Period", "Period for ADX calculation", "Indicators");
_adxThreshold = Param(nameof(AdxThreshold), 25m)
.SetDisplay("ADX Threshold", "ADX level for strong trend", "Indicators");
_cooldownBars = Param(nameof(CooldownBars), 100)
.SetDisplay("Cooldown Bars", "Bars between trades", "General")
.SetRange(5, 500);
}
/// <inheritdoc />
public override IEnumerable<(Security sec, DataType dt)> GetWorkingSecurities()
{
return [(Security, CandleType)];
}
/// <inheritdoc />
protected override void OnReseted()
{
base.OnReseted();
_highs.Clear();
_lows.Clear();
_closes.Clear();
_cooldown = 0;
}
/// <inheritdoc />
protected override void OnStarted2(DateTime time)
{
base.OnStarted2(time);
var bb = new BollingerBands
{
Length = BollingerPeriod,
Width = BollingerDeviation
};
var subscription = SubscribeCandles(CandleType);
subscription
.BindEx(bb, ProcessCandle)
.Start();
var area = CreateChartArea();
if (area != null)
{
DrawCandles(area, subscription);
DrawIndicator(area, bb);
DrawOwnTrades(area);
}
}
private void ProcessCandle(ICandleMessage candle, IIndicatorValue bbValue)
{
if (candle.State != CandleStates.Finished)
return;
if (!IsFormedAndOnlineAndAllowTrading())
return;
var high = candle.HighPrice;
var low = candle.LowPrice;
var close = candle.ClosePrice;
_highs.Add(high);
_lows.Add(low);
_closes.Add(close);
var bbTyped = (BollingerBandsValue)bbValue;
if (bbTyped.UpBand is not decimal upperBand || bbTyped.LowBand is not decimal lowerBand || bbTyped.MovingAverage is not decimal middleBand)
return;
var adxPeriod = AdxPeriod;
// Calculate manual ADX trend strength
decimal trendStrength = 0;
if (_closes.Count >= adxPeriod + 2)
{
decimal sumTr = 0, sumDmPlus = 0, sumDmMinus = 0;
var count = _highs.Count;
for (int i = count - adxPeriod; i < count; i++)
{
var h = _highs[i];
var l = _lows[i];
var prevC = _closes[i - 1];
var prevH = _highs[i - 1];
var prevL = _lows[i - 1];
var tr = Math.Max(h - l, Math.Max(Math.Abs(h - prevC), Math.Abs(l - prevC)));
sumTr += tr;
var upMove = h - prevH;
var downMove = prevL - l;
if (upMove > downMove && upMove > 0)
sumDmPlus += upMove;
if (downMove > upMove && downMove > 0)
sumDmMinus += downMove;
}
if (sumTr > 0)
{
var diPlus = 100m * sumDmPlus / sumTr;
var diMinus = 100m * sumDmMinus / sumTr;
var diSum = diPlus + diMinus;
trendStrength = diSum > 0 ? 100m * Math.Abs(diPlus - diMinus) / diSum : 0;
}
}
// Trim lists
var maxKeep = adxPeriod * 3;
if (_highs.Count > maxKeep)
{
var trim = _highs.Count - adxPeriod * 2;
_highs.RemoveRange(0, trim);
_lows.RemoveRange(0, trim);
_closes.RemoveRange(0, trim);
}
var strongTrend = trendStrength > AdxThreshold;
if (_cooldown > 0)
{
_cooldown--;
return;
}
// Buy: price above upper band + strong trend
if (close > upperBand && strongTrend && Position == 0)
{
BuyMarket();
_cooldown = CooldownBars;
}
// Sell: price below lower band + strong trend
else if (close < lowerBand && strongTrend && Position == 0)
{
SellMarket();
_cooldown = CooldownBars;
}
// Exit long: price returns to middle band
if (Position > 0 && close < middleBand)
{
SellMarket();
_cooldown = CooldownBars;
}
// Exit short: price returns to middle band
else if (Position < 0 && close > middleBand)
{
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
from datatype_extensions import *
class bollinger_adx_strategy(Strategy):
"""
Strategy combining Bollinger Bands with manual ADX trend strength.
"""
def __init__(self):
super(bollinger_adx_strategy, self).__init__()
self._candle_type = self.Param("CandleType", tf(5)) \
.SetDisplay("Candle Type", "Type of candles to use", "General")
self._bollinger_period = self.Param("BollingerPeriod", 20) \
.SetRange(10, 30) \
.SetDisplay("Bollinger Period", "Period for Bollinger Bands", "Indicators")
self._bollinger_deviation = self.Param("BollingerDeviation", 2.0) \
.SetDisplay("Bollinger Deviation", "Standard deviation multiplier", "Indicators")
self._adx_period = self.Param("AdxPeriod", 14) \
.SetRange(7, 21) \
.SetDisplay("ADX Period", "Period for ADX calculation", "Indicators")
self._adx_threshold = self.Param("AdxThreshold", 25.0) \
.SetDisplay("ADX Threshold", "ADX level for strong trend", "Indicators")
self._cooldown_bars = self.Param("CooldownBars", 100) \
.SetDisplay("Cooldown Bars", "Bars between trades", "General") \
.SetRange(5, 500)
self._highs = []
self._lows = []
self._closes = []
self._cooldown = 0
@property
def candle_type(self):
return self._candle_type.Value
def OnStarted2(self, time):
super(bollinger_adx_strategy, self).OnStarted2(time)
self._highs = []
self._lows = []
self._closes = []
self._cooldown = 0
bb = BollingerBands()
bb.Length = self._bollinger_period.Value
bb.Width = self._bollinger_deviation.Value
subscription = self.SubscribeCandles(self.candle_type)
subscription.BindEx(bb, self.ProcessCandle).Start()
area = self.CreateChartArea()
if area is not None:
self.DrawCandles(area, subscription)
self.DrawIndicator(area, bb)
self.DrawOwnTrades(area)
def ProcessCandle(self, candle, bb_value):
if candle.State != CandleStates.Finished:
return
high = float(candle.HighPrice)
low = float(candle.LowPrice)
close = float(candle.ClosePrice)
self._highs.append(high)
self._lows.append(low)
self._closes.append(close)
if bb_value.UpBand is None or bb_value.LowBand is None or bb_value.MovingAverage is None:
return
upper_band = float(bb_value.UpBand)
lower_band = float(bb_value.LowBand)
middle_band = float(bb_value.MovingAverage)
adx_p = self._adx_period.Value
# Manual ADX trend strength
trend_strength = 0.0
if len(self._closes) >= adx_p + 2:
sum_tr = 0.0
sum_dm_plus = 0.0
sum_dm_minus = 0.0
count = len(self._highs)
for i in range(count - adx_p, count):
h = self._highs[i]
l = self._lows[i]
prev_c = self._closes[i - 1]
prev_h = self._highs[i - 1]
prev_l = self._lows[i - 1]
tr = max(h - l, max(abs(h - prev_c), abs(l - prev_c)))
sum_tr += tr
up_move = h - prev_h
down_move = prev_l - l
if up_move > down_move and up_move > 0:
sum_dm_plus += up_move
if down_move > up_move and down_move > 0:
sum_dm_minus += down_move
if sum_tr > 0:
di_plus = 100.0 * sum_dm_plus / sum_tr
di_minus = 100.0 * sum_dm_minus / sum_tr
di_sum = di_plus + di_minus
trend_strength = 100.0 * abs(di_plus - di_minus) / di_sum if di_sum > 0 else 0.0
# Trim lists
max_keep = adx_p * 3
if len(self._highs) > max_keep:
trim = len(self._highs) - adx_p * 2
self._highs = self._highs[trim:]
self._lows = self._lows[trim:]
self._closes = self._closes[trim:]
strong_trend = trend_strength > self._adx_threshold.Value
if self._cooldown > 0:
self._cooldown -= 1
return
cd = self._cooldown_bars.Value
# Buy: price above upper band + strong trend
if close > upper_band and strong_trend and self.Position == 0:
self.BuyMarket()
self._cooldown = cd
elif close < lower_band and strong_trend and self.Position == 0:
self.SellMarket()
self._cooldown = cd
# Exit long: price returns to middle band
if self.Position > 0 and close < middle_band:
self.SellMarket()
self._cooldown = cd
elif self.Position < 0 and close > middle_band:
self.BuyMarket()
self._cooldown = cd
def OnReseted(self):
super(bollinger_adx_strategy, self).OnReseted()
self._highs = []
self._lows = []
self._closes = []
self._cooldown = 0
def CreateClone(self):
return bollinger_adx_strategy()