Esta estrategia es una adaptación de C# del asesor experto MetaTrader BollingerBandsEA (ver. 3.0). Negocia configuraciones de reversión a la media que ocurren después de que el precio se extiende más allá de las bandas Bollinger durante la sesión de negociación activa.
Lógica comercial
Suscríbase a la serie principal de velas intradiarias (velas de 15 minutos de forma predeterminada) y a una serie de velas diarias utilizadas para crear el filtro de tendencias.
Calcule Bollinger Bandas (largo 20, ancho 2.0) en la serie intradiaria y un SMA de 100 períodos en cierres diarios.
Realice un seguimiento de los máximos y mínimos del día actual y anterior, y mantenga los valores de banda Bollinger anteriores para la evaluación de la señal.
Solo permita entradas dentro de la ventana de la sesión de negociación: desde SessionStartOffsetMinutes después de la apertura del día de negociación hasta SessionEndOffsetMinutes antes del final del día de negociación.
Omita las operaciones una vez que el PnL acumulado del día actual se vuelva positivo, imitando la parada diaria de EA.
Entre en corto cuando la vela anterior sea bajista, cierre por encima de la banda superior, el cierre actual permanezca por encima de esa banda, el ancho de la banda sea lo suficientemente ancho, el precio esté por debajo del SMA diario y el precio se negocie por encima del máximo diario actual o anterior.
Entre en largo cuando la vela anterior sea alcista, cierre por debajo de la banda inferior, el cierre actual permanezca por debajo de esa banda, el ancho de la banda sea lo suficientemente ancho, el precio esté por encima del SMA diario y el precio se negocie por debajo del mínimo diario actual o anterior.
El tamaño de la posición está determinado por el volumen fijo configurado o por el tamaño basado en el riesgo que utiliza la distancia hasta el stop-loss en puntos.
Las salidas se realizan comprobando el stop-loss, el take-profit, el cierre opcional en la banda media, un trailing stop opcional y la lógica de equilibrio opcional. Las operaciones perdedoras también se pueden liquidar después de un tiempo de espera configurable.
Parámetros
Parámetro
Descripción
CandleType
Serie de velas intradía utilizadas para el comercio.
BollingerLength
Periodo de la media móvil de las Bollinger Bandas.
BollingerWidth
Multiplicador de ancho de las Bollinger Bandas.
DailyMaLength
Duración del filtro diario SMA.
StopLossPoints
Distancia de stop-loss expresada en puntos del instrumento.
UseRiskVolume
Permite dimensionar las posiciones en función del riesgo.
RiskPercent
Porcentaje de cuenta utilizado para el dimensionamiento basado en riesgos.
FixedVolume
Volumen fijo alternativo cuando el tamaño del riesgo está deshabilitado o no es posible.
SessionStartOffsetMinutes
Minutos después del inicio de la sesión antes de que se permitan las entradas.
SessionEndOffsetMinutes
Minutos antes de finalizar la sesión cuando se bloquean las entradas.
CloseOnMiddleBand
Salga de la posición cuando el precio cruce la banda media Bollinger.
EnableTrailing
Permite realizar ajustes de trailing stop.
TrailingFactor
Se requiere un multiplicador de distancia antes de seguir la parada.
EnableBreakEven
Permite mover el stop al precio de entrada.
BreakEvenFactor
Múltiplo de beneficio necesario para mover el stop al punto de equilibrio.
CloseLosingAfterMinutes
Cierra las operaciones perdedoras después de mantenerlas durante los minutos especificados.
Notas
Las órdenes protectoras de stop-loss y take-profit se simulan comprobando los extremos de las velas en cada actualización. Ajuste esta sección si se requieren órdenes de protección del lado cambiario.
El tamaño basado en el riesgo depende de Security.Step y Security.StepPrice. Si faltan estos valores, la estrategia volverá al volumen fijo.
El stop de ganancias diario utiliza la estrategia PnL, por lo tanto, el PnL realizado y el flotante deben estar en la misma moneda que la cartera.
using System;
using System.Linq;
using System.Collections.Generic;
using Ecng.Common;
using Ecng.Collections;
using Ecng.Serialization;
using StockSharp.Algo.Indicators;
using StockSharp.Algo.Strategies;
using StockSharp.BusinessEntities;
using StockSharp.Messages;
namespace StockSharp.Samples.Strategies;
/// <summary>
/// Trades reversals from Bollinger Bands.
/// Buys when price closes below lower band and sells when price closes above upper band.
/// </summary>
public class BollingerBandsSessionReversalStrategy : Strategy
{
private readonly StrategyParam<DataType> _candleType;
private readonly StrategyParam<int> _bollingerLength;
private readonly StrategyParam<decimal> _bollingerWidth;
/// <summary>
/// Candle type.
/// </summary>
public DataType CandleType
{
get => _candleType.Value;
set => _candleType.Value = value;
}
/// <summary>
/// Length of the Bollinger Bands moving average.
/// </summary>
public int BollingerLength
{
get => _bollingerLength.Value;
set => _bollingerLength.Value = value;
}
/// <summary>
/// Width multiplier for Bollinger Bands.
/// </summary>
public decimal BollingerWidth
{
get => _bollingerWidth.Value;
set => _bollingerWidth.Value = value;
}
/// <summary>
/// Initializes strategy parameters.
/// </summary>
public BollingerBandsSessionReversalStrategy()
{
_candleType = Param(nameof(CandleType), TimeSpan.FromHours(4).TimeFrame())
.SetDisplay("Candle Type", "Primary candle series", "General");
_bollingerLength = Param(nameof(BollingerLength), 20)
.SetGreaterThanZero()
.SetDisplay("Bollinger Length", "MA period for Bollinger Bands", "Indicators");
_bollingerWidth = Param(nameof(BollingerWidth), 2.0m)
.SetGreaterThanZero()
.SetDisplay("Bollinger Width", "Band width multiplier", "Indicators");
}
/// <inheritdoc />
public override IEnumerable<(Security sec, DataType dt)> GetWorkingSecurities()
{
return [(Security, CandleType)];
}
/// <inheritdoc />
protected override void OnStarted2(DateTime time)
{
base.OnStarted2(time);
var bollinger = new BollingerBands
{
Length = BollingerLength,
Width = BollingerWidth
};
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 bbValue)
{
if (candle.State != CandleStates.Finished)
return;
if (bbValue is not IBollingerBandsValue bb)
return;
var middle = bb.MovingAverage ?? 0m;
var upper = bb.UpBand ?? 0m;
var lower = bb.LowBand ?? 0m;
if (middle == 0m || upper == 0m || lower == 0m)
return;
var price = candle.ClosePrice;
// Reversal: buy when price falls below lower band
if (price < lower && Position <= 0)
{
if (Position < 0)
BuyMarket();
BuyMarket();
}
// Reversal: sell when price rises above upper band
else if (price > upper && Position >= 0)
{
if (Position > 0)
SellMarket();
SellMarket();
}
// Exit at middle band
else if (Position > 0 && price >= middle)
{
SellMarket();
}
else if (Position < 0 && price <= middle)
{
BuyMarket();
}
}
}
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_bands_session_reversal_strategy(Strategy):
def __init__(self):
super(bollinger_bands_session_reversal_strategy, self).__init__()
self._candle_type = self.Param("CandleType", DataType.TimeFrame(TimeSpan.FromHours(4))) \
.SetDisplay("Candle Type", "Primary candle series", "General")
self._bollinger_length = self.Param("BollingerLength", 20) \
.SetGreaterThanZero() \
.SetDisplay("Bollinger Length", "MA period for Bollinger Bands", "Indicators")
self._bollinger_width = self.Param("BollingerWidth", 2.0) \
.SetGreaterThanZero() \
.SetDisplay("Bollinger Width", "Band width multiplier", "Indicators")
@property
def CandleType(self):
return self._candle_type.Value
@CandleType.setter
def CandleType(self, value):
self._candle_type.Value = value
@property
def BollingerLength(self):
return self._bollinger_length.Value
@BollingerLength.setter
def BollingerLength(self, value):
self._bollinger_length.Value = value
@property
def BollingerWidth(self):
return self._bollinger_width.Value
@BollingerWidth.setter
def BollingerWidth(self, value):
self._bollinger_width.Value = value
def OnStarted2(self, time):
super(bollinger_bands_session_reversal_strategy, self).OnStarted2(time)
bollinger = BollingerBands()
bollinger.Length = self.BollingerLength
bollinger.Width = self.BollingerWidth
subscription = self.SubscribeCandles(self.CandleType)
subscription.BindEx(bollinger, self._process_candle).Start()
def _process_candle(self, candle, bb_value):
if candle.State != CandleStates.Finished:
return
upper = bb_value.UpBand
lower = bb_value.LowBand
middle = bb_value.MovingAverage
if upper is None or lower is None or middle is None:
return
up = float(upper)
lo = float(lower)
mid = float(middle)
if up == 0 or lo == 0 or mid == 0:
return
price = float(candle.ClosePrice)
if price < lo and self.Position <= 0:
if self.Position < 0:
self.BuyMarket()
self.BuyMarket()
elif price > up and self.Position >= 0:
if self.Position > 0:
self.SellMarket()
self.SellMarket()
elif self.Position > 0 and price >= mid:
self.SellMarket()
elif self.Position < 0 and price <= mid:
self.BuyMarket()
def CreateClone(self):
return bollinger_bands_session_reversal_strategy()