Ver en GitHub

Estrategia Bollinger Band Two MA ZigZag

Sistema híbrido de seguimiento de tendencia que combina reversiones por Bollinger Band, dos medias móviles de marcos temporales superiores y puntos de giro de un detector ZigZag. Abre dos posiciones en cada señal: una con un objetivo de take-profit calculado y una segunda "corriente" que depende de trailing y lógica de break-even.

Detalles

  • Criterios de entrada:
    • Largo: La barra anterior cerró por encima de la banda inferior anterior de Bollinger después de haber cerrado por debajo dos barras atrás, el cierre actual también está por encima de esa banda inferior, y el precio está por encima de ambas medias móviles de marcos temporales superiores.
    • Corto: La barra anterior cerró por debajo de la banda superior anterior de Bollinger después de haber cerrado por encima dos barras atrás, el cierre actual también está por debajo de esa banda superior, y el precio está por debajo de ambas medias móviles de marcos temporales superiores.
  • Gestión de posiciones:
    • Se abren dos posiciones por señal usando First Volume (con take-profit) y Second Volume (corriente).
    • Los stops están anclados al extremo de giro ZigZag más reciente menos/más Pivot Offset (pts).
    • La protección de break-even desplaza el stop a la entrada más un offset una vez que el beneficio no realizado supera Break-even Threshold (pts) + Break-even Offset (pts).
    • El trailing stop se mueve después de que el precio avance Trailing Step (pts) más allá del stop existente, manteniendo una distancia de Trailing Stop (pts).
  • Take Profit:
    • El take-profit de la primera posición se calcula como un porcentaje (Take Profit %) de la distancia entre la entrada y el stop.
    • La posición corriente no tiene objetivo fijo y sale por stop, trailing o señales opuestas.
  • Lógica adicional:
    • Las señales opuestas cierran inmediatamente las posiciones abiertas en la otra dirección antes de abrir nuevas operaciones.
    • El procesamiento de señales usa velas cerradas; los datos parciales son ignorados.
  • Valores predeterminados:
    • First Volume = 0.1
    • Second Volume = 0.1
    • Take Profit % = 50
    • Pivot Offset (pts) = 10
    • Use Break-even Move = true
    • Break-even Offset (pts) = 80
    • Break-even Threshold (pts) = 10
    • Trailing Stop (pts) = 80
    • Trailing Step (pts) = 120
    • Bollinger Period = 20
    • Bollinger Width = 2
    • Base Candle = velas de 1 hora
    • MA1 Candle = velas diarias
    • MA2 Candle = velas de 4 horas
    • MA1 Period = 20
    • MA2 Period = 20
    • ZigZag Depth = 12
    • ZigZag Deviation (pts) = 5
    • ZigZag Backstep = 3
  • Filtros:
    • Categoría: Seguimiento de tendencia
    • Dirección: Ambos
    • Indicadores: Bollinger Bands, Moving Averages, ZigZag
    • Stops: Sí (stop por giro, break-even, trailing)
    • Complejidad: Avanzado
    • Marco temporal: Multi-marco temporal (base 1h, filtros Daily + 4h)
    • Estacionalidad: No
    • Redes neuronales: No
    • Divergencia: No
    • Nivel de riesgo: Medio

Notas

  • La estrategia requiere suscripciones de velas en tres marcos temporales distintos para evaluar los filtros y gestionar las salidas.
  • La detección de giros aproxima la lógica ZigZag de MetaTrader aplicando reglas mínimas de profundidad, desviación y backstep antes de actualizar los niveles de pivote.
  • Los volúmenes se pueden ajustar de forma independiente para afinar el tamaño del tramo de take-profit frente al tramo corriente.
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 Band Two MA ZigZag strategy (simplified). Uses EMA crossover
/// with Highest/Lowest channel for swing-based entries.
/// </summary>
public class BollingerBandTwoMaZigZagStrategy : Strategy
{
	private readonly StrategyParam<DataType> _candleType;
	private readonly StrategyParam<int> _emaFastLength;
	private readonly StrategyParam<int> _emaSlowLength;
	private readonly StrategyParam<int> _channelLength;

	public DataType CandleType
	{
		get => _candleType.Value;
		set => _candleType.Value = value;
	}

	public int EmaFastLength
	{
		get => _emaFastLength.Value;
		set => _emaFastLength.Value = value;
	}

	public int EmaSlowLength
	{
		get => _emaSlowLength.Value;
		set => _emaSlowLength.Value = value;
	}

	public int ChannelLength
	{
		get => _channelLength.Value;
		set => _channelLength.Value = value;
	}

	public BollingerBandTwoMaZigZagStrategy()
	{
		_candleType = Param(nameof(CandleType), TimeSpan.FromHours(4).TimeFrame())
			.SetDisplay("Candle Type", "Candles", "General");

		_emaFastLength = Param(nameof(EmaFastLength), 10)
			.SetGreaterThanZero()
			.SetDisplay("EMA Fast", "Fast EMA period", "Indicators");

		_emaSlowLength = Param(nameof(EmaSlowLength), 30)
			.SetGreaterThanZero()
			.SetDisplay("EMA Slow", "Slow EMA period", "Indicators");

		_channelLength = Param(nameof(ChannelLength), 20)
			.SetGreaterThanZero()
			.SetDisplay("Channel Length", "Highest/Lowest lookback", "Indicators");
	}

	protected override void OnStarted2(DateTime time)
	{
		base.OnStarted2(time);

		var emaFast = new ExponentialMovingAverage { Length = EmaFastLength };
		var emaSlow = new ExponentialMovingAverage { Length = EmaSlowLength };

		decimal prevFast = 0, prevSlow = 0;
		var hasPrev = false;

		var subscription = SubscribeCandles(CandleType);
		subscription
			.Bind(emaFast, emaSlow, (ICandleMessage candle, decimal fastVal, decimal slowVal) =>
			{
				if (candle.State != CandleStates.Finished)
					return;

				if (!hasPrev)
				{
					prevFast = fastVal;
					prevSlow = slowVal;
					hasPrev = true;
					return;
				}

				if (!IsFormedAndOnlineAndAllowTrading())
				{
					prevFast = fastVal;
					prevSlow = slowVal;
					return;
				}

				var close = candle.ClosePrice;

				// EMA crossover combined with price confirmation
				var bullishCross = prevFast <= prevSlow && fastVal > slowVal;
				var bearishCross = prevFast >= prevSlow && fastVal < slowVal;

				if (bullishCross && Position <= 0 && close > fastVal)
					BuyMarket();
				else if (bearishCross && Position >= 0 && close < fastVal)
					SellMarket();

				prevFast = fastVal;
				prevSlow = slowVal;
			})
			.Start();

		var area = CreateChartArea();
		if (area != null)
		{
			DrawCandles(area, subscription);
			DrawIndicator(area, emaFast);
			DrawIndicator(area, emaSlow);
			DrawOwnTrades(area);
		}
	}
}