Estrategia Bollinger Winner Pro
Bollinger Winner Pro amplía la versión Lite añadiendo filtros modulares y controles de riesgo. Sigue buscando precios que cierren fuera de las Bollinger Bands, pero las operaciones se ejecutan solo cuando las confirmaciones opcionales están de acuerdo.
Los operadores pueden habilitar filtros de RSI, Aroon y media móvil para confirmar el impulso y la dirección de la tendencia. También se puede activar un stop-loss integrado para limitar el riesgo. Esta flexibilidad permite que la estrategia se adapte a diferentes mercados o necesidades de prueba.
El enfoque apunta a la reversión a la media: una vez que el precio vuelve a entrar en las bandas o toca el lado opuesto, la posición se cierra o se alcanza el stop. Dado que se pueden acumular múltiples filtros, las señales son menos frecuentes pero de mayor calidad.
Detalles
- Datos: Velas de precio.
- Criterios de entrada: La vela cierra fuera de una banda y todos los filtros habilitados coinciden.
- Criterios de salida: Retorno a la banda central/opuesta o stop-loss si
UseSLes verdadero. - Stops: Stop-loss opcional controlado por
UseSL. - Valores predeterminados:
UseRSI= TrueUseAroon= FalseUseMA= TrueUseSL= True
- Filtros:
- Categoría: Reversión a la media con confirmaciones
- Dirección: Largo/Corto
- Indicadores: Bollinger Bands, RSI, Aroon, Moving Average
- Complejidad: Avanzado
- 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>
/// Bollinger Winner Pro strategy.
/// Goes long when a candle closes below the lower band and short when it closes above the upper band, provided every
/// enabled filter agrees: RSI beyond its oversold/overbought level, Aroon up/down dominance in the trade direction and
/// the middle band on the trade side of the moving average, which confirms the trend direction. A position closes when price returns to the middle band (or reaches
/// the opposite one), and an optional percent stop-loss caps the risk.
/// </summary>
public class BollingerWinnerProStrategy : Strategy
{
private readonly StrategyParam<int> _bbLength;
private readonly StrategyParam<decimal> _bbMultiplier;
private readonly StrategyParam<bool> _useRsi;
private readonly StrategyParam<int> _rsiLength;
private readonly StrategyParam<decimal> _rsiOversold;
private readonly StrategyParam<decimal> _rsiOverbought;
private readonly StrategyParam<bool> _useAroon;
private readonly StrategyParam<int> _aroonLength;
private readonly StrategyParam<bool> _useMa;
private readonly StrategyParam<int> _maLength;
private readonly StrategyParam<bool> _useSl;
private readonly StrategyParam<decimal> _stopLossPercent;
private readonly StrategyParam<DataType> _candleType;
/// <summary>
/// Bollinger Bands period.
/// </summary>
public int BBLength
{
get => _bbLength.Value;
set => _bbLength.Value = value;
}
/// <summary>
/// Bollinger Bands standard deviation multiplier.
/// </summary>
public decimal BBMultiplier
{
get => _bbMultiplier.Value;
set => _bbMultiplier.Value = value;
}
/// <summary>
/// Enable the RSI filter.
/// </summary>
public bool UseRSI
{
get => _useRsi.Value;
set => _useRsi.Value = value;
}
/// <summary>
/// RSI period.
/// </summary>
public int RSILength
{
get => _rsiLength.Value;
set => _rsiLength.Value = value;
}
/// <summary>
/// RSI level below which longs are allowed.
/// </summary>
public decimal RSIOversold
{
get => _rsiOversold.Value;
set => _rsiOversold.Value = value;
}
/// <summary>
/// RSI level above which shorts are allowed.
/// </summary>
public decimal RSIOverbought
{
get => _rsiOverbought.Value;
set => _rsiOverbought.Value = value;
}
/// <summary>
/// Enable the Aroon filter.
/// </summary>
public bool UseAroon
{
get => _useAroon.Value;
set => _useAroon.Value = value;
}
/// <summary>
/// Aroon period.
/// </summary>
public int AroonLength
{
get => _aroonLength.Value;
set => _aroonLength.Value = value;
}
/// <summary>
/// Enable the moving average filter.
/// </summary>
public bool UseMA
{
get => _useMa.Value;
set => _useMa.Value = value;
}
/// <summary>
/// Moving average period.
/// </summary>
public int MALength
{
get => _maLength.Value;
set => _maLength.Value = value;
}
/// <summary>
/// Enable the stop-loss.
/// </summary>
public bool UseSL
{
get => _useSl.Value;
set => _useSl.Value = value;
}
/// <summary>
/// Stop-loss percentage from the entry price.
/// </summary>
public decimal StopLossPercent
{
get => _stopLossPercent.Value;
set => _stopLossPercent.Value = value;
}
/// <summary>
/// Candle type for strategy calculation.
/// </summary>
public DataType CandleType
{
get => _candleType.Value;
set => _candleType.Value = value;
}
/// <summary>
/// Constructor.
/// </summary>
public BollingerWinnerProStrategy()
{
_bbLength = Param(nameof(BBLength), 20)
.SetGreaterThanZero()
.SetDisplay("BB Period", "Bollinger Bands period", "Bollinger Bands");
_bbMultiplier = Param(nameof(BBMultiplier), 1.5m)
.SetGreaterThanZero()
.SetDisplay("BB StdDev", "Bollinger Bands standard deviation multiplier", "Bollinger Bands");
_useRsi = Param(nameof(UseRSI), true)
.SetDisplay("Use RSI", "Require RSI confirmation", "RSI Filter");
_rsiLength = Param(nameof(RSILength), 14)
.SetGreaterThanZero()
.SetDisplay("RSI Length", "RSI period", "RSI Filter");
_rsiOversold = Param(nameof(RSIOversold), 40m)
.SetDisplay("RSI Oversold", "RSI level below which longs are allowed", "RSI Filter");
_rsiOverbought = Param(nameof(RSIOverbought), 60m)
.SetDisplay("RSI Overbought", "RSI level above which shorts are allowed", "RSI Filter");
_useAroon = Param(nameof(UseAroon), false)
.SetDisplay("Use Aroon", "Require Aroon confirmation", "Aroon Filter");
_aroonLength = Param(nameof(AroonLength), 14)
.SetGreaterThanZero()
.SetDisplay("Aroon Length", "Aroon period", "Aroon Filter");
_useMa = Param(nameof(UseMA), true)
.SetDisplay("Use MA", "Require the middle band on the trade side of the moving average", "Moving Average");
_maLength = Param(nameof(MALength), 50)
.SetGreaterThanZero()
.SetDisplay("MA Length", "Moving average period", "Moving Average");
_useSl = Param(nameof(UseSL), true)
.SetDisplay("Use Stop Loss", "Enable the percent stop-loss", "Risk");
_stopLossPercent = Param(nameof(StopLossPercent), 2m)
.SetGreaterThanZero()
.SetDisplay("Stop Loss %", "Stop-loss percentage from the entry price", "Risk");
_candleType = Param(nameof(CandleType), TimeSpan.FromMinutes(30).TimeFrame())
.SetDisplay("Candle type", "Candle type for strategy calculation", "General");
}
/// <inheritdoc />
public override IEnumerable<(Security sec, DataType dt)> GetWorkingSecurities()
=> [(Security, CandleType)];
/// <inheritdoc />
protected override void OnStarted2(DateTime time)
{
base.OnStarted2(time);
var bollinger = new BollingerBands
{
Length = BBLength,
Width = BBMultiplier
};
var rsi = new RelativeStrengthIndex { Length = RSILength };
var aroon = new Aroon { Length = AroonLength };
var ma = new ExponentialMovingAverage { Length = MALength };
var subscription = SubscribeCandles(CandleType);
subscription
.BindEx(bollinger, rsi, aroon, ma, ProcessCandle)
.Start();
if (UseSL)
StartProtection(new Unit(), new Unit(StopLossPercent, UnitTypes.Percent), useMarketOrders: true);
var area = CreateChartArea();
if (area != null)
{
DrawCandles(area, subscription);
DrawIndicator(area, bollinger);
DrawIndicator(area, ma);
DrawOwnTrades(area);
}
}
private void ProcessCandle(ICandleMessage candle, IIndicatorValue bollingerValue, IIndicatorValue rsiValue, IIndicatorValue aroonValue, IIndicatorValue maValue)
{
if (candle.State != CandleStates.Finished)
return;
if (!bollingerValue.IsFormed || !rsiValue.IsFormed || !aroonValue.IsFormed || !maValue.IsFormed)
return;
var bb = (BollingerBandsValue)bollingerValue;
if (bb.UpBand is not decimal upper ||
bb.LowBand is not decimal lower ||
bb.MovingAverage is not decimal middle)
return;
var aroon = (AroonValue)aroonValue;
if (aroon.Up is not decimal aroonUp || aroon.Down is not decimal aroonDown)
return;
if (!IsFormedAndOnlineAndAllowTrading())
return;
var rsi = rsiValue.ToDecimal();
var ma = maValue.ToDecimal();
var close = candle.ClosePrice;
var longSignal = close < lower
&& (!UseRSI || rsi < RSIOversold)
&& (!UseAroon || aroonUp > aroonDown)
&& (!UseMA || middle > ma);
var shortSignal = close > upper
&& (!UseRSI || rsi > RSIOverbought)
&& (!UseAroon || aroonDown > aroonUp)
&& (!UseMA || middle < ma);
if (longSignal && Position <= 0)
BuyMarket(Volume + Math.Abs(Position));
else if (shortSignal && Position >= 0)
SellMarket(Volume + Math.Abs(Position));
else if (Position > 0 && close >= middle)
SellMarket(Position);
else if (Position < 0 && close <= middle)
BuyMarket(-Position);
}
}
import clr
clr.AddReference("StockSharp.Messages")
clr.AddReference("StockSharp.Algo")
clr.AddReference("StockSharp.Algo.Indicators")
clr.AddReference("StockSharp.Algo.Strategies")
from System import TimeSpan, Decimal
from StockSharp.Messages import DataType, CandleStates, Unit, UnitTypes
from StockSharp.Algo.Indicators import BollingerBands, RelativeStrengthIndex, ExponentialMovingAverage, Aroon
from StockSharp.Algo.Strategies import Strategy
class bollinger_winner_pro_strategy(Strategy):
"""
Bollinger Winner Pro strategy.
Goes long when a candle closes below the lower band and short when it closes above the upper band, provided every
enabled filter agrees: RSI beyond its oversold/overbought level, Aroon up/down dominance in the trade direction and
the middle band on the trade side of the moving average, which confirms the trend direction. A position closes when price returns to the middle band (or reaches
the opposite one), and an optional percent stop-loss caps the risk.
"""
def __init__(self):
super(bollinger_winner_pro_strategy, self).__init__()
self._bb_length = self.Param("BBLength", 20) \
.SetGreaterThanZero() \
.SetDisplay("BB Period", "Bollinger Bands period", "Bollinger Bands")
self._bb_multiplier = self.Param("BBMultiplier", 1.5) \
.SetGreaterThanZero() \
.SetDisplay("BB StdDev", "Bollinger Bands standard deviation multiplier", "Bollinger Bands")
self._use_rsi = self.Param("UseRSI", True) \
.SetDisplay("Use RSI", "Require RSI confirmation", "RSI Filter")
self._rsi_length = self.Param("RSILength", 14) \
.SetGreaterThanZero() \
.SetDisplay("RSI Length", "RSI period", "RSI Filter")
self._rsi_oversold = self.Param("RSIOversold", 40.0) \
.SetDisplay("RSI Oversold", "RSI level below which longs are allowed", "RSI Filter")
self._rsi_overbought = self.Param("RSIOverbought", 60.0) \
.SetDisplay("RSI Overbought", "RSI level above which shorts are allowed", "RSI Filter")
self._use_aroon = self.Param("UseAroon", False) \
.SetDisplay("Use Aroon", "Require Aroon confirmation", "Aroon Filter")
self._aroon_length = self.Param("AroonLength", 14) \
.SetGreaterThanZero() \
.SetDisplay("Aroon Length", "Aroon period", "Aroon Filter")
self._use_ma = self.Param("UseMA", True) \
.SetDisplay("Use MA", "Require the middle band on the trade side of the moving average", "Moving Average")
self._ma_length = self.Param("MALength", 50) \
.SetGreaterThanZero() \
.SetDisplay("MA Length", "Moving average period", "Moving Average")
self._use_sl = self.Param("UseSL", True) \
.SetDisplay("Use Stop Loss", "Enable the percent stop-loss", "Risk")
self._stop_loss_percent = self.Param("StopLossPercent", 2.0) \
.SetGreaterThanZero() \
.SetDisplay("Stop Loss %", "Stop-loss percentage from the entry price", "Risk")
self._candle_type = self.Param("CandleType", DataType.TimeFrame(TimeSpan.FromMinutes(30))) \
.SetDisplay("Candle type", "Candle type for strategy calculation", "General")
@property
def CandleType(self):
return self._candle_type.Value
def GetWorkingSecurities(self):
return [(self.Security, self.CandleType)]
def OnStarted2(self, time):
super(bollinger_winner_pro_strategy, self).OnStarted2(time)
bollinger = BollingerBands()
bollinger.Length = self._bb_length.Value
bollinger.Width = Decimal(self._bb_multiplier.Value)
rsi = RelativeStrengthIndex()
rsi.Length = self._rsi_length.Value
aroon = Aroon()
aroon.Length = self._aroon_length.Value
ma = ExponentialMovingAverage()
ma.Length = self._ma_length.Value
subscription = self.SubscribeCandles(self.CandleType)
subscription.BindEx(bollinger, rsi, aroon, ma, self._process_candle).Start()
if self._use_sl.Value:
self.StartProtection(Unit(), Unit(Decimal(self._stop_loss_percent.Value), UnitTypes.Percent), useMarketOrders=True)
area = self.CreateChartArea()
if area is not None:
self.DrawCandles(area, subscription)
self.DrawIndicator(area, bollinger)
self.DrawIndicator(area, ma)
self.DrawOwnTrades(area)
def _process_candle(self, candle, bollinger_value, rsi_value, aroon_value, ma_value):
if candle.State != CandleStates.Finished:
return
if not bollinger_value.IsFormed or not rsi_value.IsFormed or not aroon_value.IsFormed or not ma_value.IsFormed:
return
if bollinger_value.UpBand is None or bollinger_value.LowBand is None or bollinger_value.MovingAverage is None:
return
if aroon_value.Up is None or aroon_value.Down is None:
return
if not self.IsFormedAndOnlineAndAllowTrading():
return
upper = float(bollinger_value.UpBand)
lower = float(bollinger_value.LowBand)
middle = float(bollinger_value.MovingAverage)
aroon_up = float(aroon_value.Up)
aroon_down = float(aroon_value.Down)
rsi = float(rsi_value.GetValue[Decimal](None))
ma = float(ma_value.GetValue[Decimal](None))
close = float(candle.ClosePrice)
use_rsi = self._use_rsi.Value
use_aroon = self._use_aroon.Value
use_ma = self._use_ma.Value
long_signal = close < lower \
and (not use_rsi or rsi < float(self._rsi_oversold.Value)) \
and (not use_aroon or aroon_up > aroon_down) \
and (not use_ma or middle > ma)
short_signal = close > upper \
and (not use_rsi or rsi > float(self._rsi_overbought.Value)) \
and (not use_aroon or aroon_down > aroon_up) \
and (not use_ma or middle < ma)
if long_signal and self.Position <= 0:
self.BuyMarket(self.Volume + abs(self.Position))
elif short_signal and self.Position >= 0:
self.SellMarket(self.Volume + abs(self.Position))
elif self.Position > 0 and close >= middle:
self.SellMarket(self.Position)
elif self.Position < 0 and close <= middle:
self.BuyMarket(-self.Position)
def CreateClone(self):
return bollinger_winner_pro_strategy()