Ver en GitHub

Estrategia de Bollinger Aroon

La estrategia Bollinger Aroon busca retrocesos dentro de una tendencia alcista fuerte. Cuando el precio se estira por debajo de la banda inferior de Bollinger pero el valor de Aroon Up permanece elevado, el sistema asume que la tendencia está intacta y busca una reversión hacia la media. Solo opera en largo, buscando capturar el rebote después de una caída temporal.

La configuración se activa después de que una vela terminada cierra por debajo de la banda inferior mientras Aroon Up supera el nivel de confirmación. La posición permanece abierta hasta que la lectura de Aroon cae por debajo de un umbral de stop o el precio sube hasta la banda superior. El ancho de banda se adapta a la volatilidad, permitiendo que la estrategia opere en mercados tranquilos y activos por igual.

Las pruebas retrospectivas en los principales pares cripto muestran que el enfoque destaca durante tendencias fuertes con sacudidas ocasionales. Dado que las entradas requieren tanto expansión de volatilidad como una lectura persistente de Aroon Up, las señales falsas se reducen en comparación con una reversión Bollinger simple.

Detalles

  • Datos: Velas de precio.
  • Criterios de entrada:
    • Largo: Cierre por debajo de la banda inferior Y Aroon Up > nivel de confirmación.
    • Corto: no se utiliza.
  • Criterios de salida:
    • Cierre toca la banda superior O Aroon Up < nivel de stop.
  • Stops: Basados en indicador; sin stop fijo por defecto.
  • Valores predeterminados:
    • BBLength = 20
    • BBMultiplier = 2.0
    • AroonLength = 288
    • AroonConfirmation = 90
    • AroonStop = 70
  • Filtros:
    • Categoría: Reversión a la media dentro de tendencia
    • Dirección: Solo largos
    • Indicadores: Bollinger Bands, Aroon
    • Complejidad: Moderado
    • 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 Bands + Aroon Strategy.
/// Buys when price touches lower Bollinger Band with Aroon Up confirming uptrend.
/// Exits when price reaches upper Bollinger Band or Aroon signals weakness.
/// </summary>
public class BollingerAroonStrategy : Strategy
{
	private readonly StrategyParam<DataType> _candleTypeParam;
	private readonly StrategyParam<int> _bbLength;
	private readonly StrategyParam<decimal> _bbMultiplier;
	private readonly StrategyParam<int> _aroonLength;
	private readonly StrategyParam<decimal> _aroonConfirmation;
	private readonly StrategyParam<decimal> _aroonStop;

	/// <summary>
	/// Candle type for strategy calculation.
	/// </summary>
	public DataType CandleType
	{
		get => _candleTypeParam.Value;
		set => _candleTypeParam.Value = value;
	}

	/// <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>
	/// Aroon indicator period.
	/// </summary>
	public int AroonLength
	{
		get => _aroonLength.Value;
		set => _aroonLength.Value = value;
	}

	/// <summary>
	/// Aroon confirmation level.
	/// </summary>
	public decimal AroonConfirmation
	{
		get => _aroonConfirmation.Value;
		set => _aroonConfirmation.Value = value;
	}

	/// <summary>
	/// Aroon stop level.
	/// </summary>
	public decimal AroonStop
	{
		get => _aroonStop.Value;
		set => _aroonStop.Value = value;
	}

	private BollingerBands _bollinger;
	private Aroon _aroon;
	private int _cooldownRemaining;

	public BollingerAroonStrategy()
	{
		_candleTypeParam = Param(nameof(CandleType), TimeSpan.FromMinutes(15).TimeFrame())
			.SetDisplay("Candle type", "Candle type for strategy calculation.", "General");

		_bbLength = Param(nameof(BBLength), 20)
			.SetGreaterThanZero()
			.SetDisplay("BB Period", "Bollinger Bands period", "Bollinger Bands");

		_bbMultiplier = Param(nameof(BBMultiplier), 2.0m)
			.SetDisplay("BB StdDev", "Bollinger Bands standard deviation multiplier", "Bollinger Bands");

		_aroonLength = Param(nameof(AroonLength), 14)
			.SetGreaterThanZero()
			.SetDisplay("Aroon Period", "Aroon indicator period", "Aroon");

		_aroonConfirmation = Param(nameof(AroonConfirmation), 60m)
			.SetDisplay("Aroon Confirmation", "Aroon confirmation level", "Aroon");

		_aroonStop = Param(nameof(AroonStop), 40m)
			.SetDisplay("Aroon Stop", "Aroon stop level", "Aroon");
	}

	/// <inheritdoc />
	public override IEnumerable<(Security sec, DataType dt)> GetWorkingSecurities()
		=> [(Security, CandleType)];

	/// <inheritdoc />
	protected override void OnReseted()
	{
		base.OnReseted();

		_bollinger = null;
		_aroon = null;
		_cooldownRemaining = 0;
	}

	/// <inheritdoc />
	protected override void OnStarted2(DateTime time)
	{
		base.OnStarted2(time);

		_bollinger = new BollingerBands
		{
			Length = BBLength,
			Width = BBMultiplier
		};

		_aroon = new Aroon { Length = AroonLength };

		var subscription = SubscribeCandles(CandleType);

		subscription
			.BindEx(_bollinger, _aroon, OnProcess)
			.Start();

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

	private void OnProcess(ICandleMessage candle,
		IIndicatorValue bollingerValue, IIndicatorValue aroonValue)
	{
		if (candle.State != CandleStates.Finished)
			return;

		if (!_bollinger.IsFormed || !_aroon.IsFormed)
			return;

		var bb = (BollingerBandsValue)bollingerValue;
		if (bb.LowBand is not decimal lowerBand ||
			bb.UpBand is not decimal upperBand ||
			bb.MovingAverage is not decimal middleBand)
			return;

		var aa = (AroonValue)aroonValue;
		if (aa.Up is not decimal aroonUp)
			return;

		if (!IsFormedAndOnlineAndAllowTrading())
			return;

		if (_cooldownRemaining > 0)
		{
			_cooldownRemaining--;
			return;
		}

		var close = candle.ClosePrice;

		// Long entry: price at/below lower BB + Aroon Up confirming uptrend
		if (close <= lowerBand && aroonUp > AroonConfirmation && Position <= 0)
		{
			if (Position < 0)
				BuyMarket(Math.Abs(Position));
			BuyMarket(Volume);
			_cooldownRemaining = 12;
		}
		// Short entry: price at/above upper BB + Aroon Up weak
		else if (close >= upperBand && aroonUp < AroonStop && Position >= 0)
		{
			if (Position > 0)
				SellMarket(Math.Abs(Position));
			SellMarket(Volume);
			_cooldownRemaining = 12;
		}
		// Exit long: price reaches upper band or Aroon signals weakness
		else if (Position > 0 && (close >= upperBand || aroonUp < AroonStop))
		{
			SellMarket(Math.Abs(Position));
			_cooldownRemaining = 12;
		}
		// Exit short: price reaches lower band or Aroon signals strength
		else if (Position < 0 && (close <= lowerBand || aroonUp > AroonConfirmation))
		{
			BuyMarket(Math.Abs(Position));
			_cooldownRemaining = 12;
		}
	}
}