Ver en GitHub

Estrategia Bbsr Extreme

La estrategia Bbsr Extreme combina rupturas de Bollinger Bands con un filtro de tendencia basado en una media móvil. Una posición larga aparece cuando el precio rebota desde la banda inferior mientras la media está subiendo. Una posición corta se abre en un retroceso desde la banda superior cuando la media desciende. Las salidas se basan en stop-loss y take profit calculados mediante ATR.

Detalles

  • Criterios de entrada: El precio cruza las bandas con confirmación de tendencia.
  • Largo/Corto: Ambas direcciones.
  • Criterios de salida: Stop ATR o take profit.
  • Stops: Sí, basados en ATR.
  • Valores predeterminados:
    • BollingerPeriod = 20
    • BollingerMultiplier = 2
    • MaLength = 7
    • AtrLength = 14
    • AtrStopMultiplier = 2
    • AtrProfitMultiplier = 3
    • CandleType = TimeSpan.FromMinutes(5).TimeFrame()
  • Filtros:
    • Categoría: Seguimiento de tendencia
    • Dirección: Ambos
    • Indicadores: Bollinger Bands, EMA, ATR
    • Stops: Sí
    • Complejidad: Básico
    • Marco temporal: Intradía (5m)
    • Estacionalidad: No
    • Redes neuronales: No
    • Divergencia: No
    • Nivel de riesgo: Medio
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>
/// Strategy uses Bollinger Bands breakout with moving average trend filter and ATR-based exits.
/// </summary>
public class BbsrExtremeStrategy : Strategy
{
	private readonly StrategyParam<int> _bollingerPeriod;
	private readonly StrategyParam<decimal> _bollingerMultiplier;
	private readonly StrategyParam<int> _maLength;
	private readonly StrategyParam<int> _atrLength;
	private readonly StrategyParam<decimal> _atrStopMultiplier;
	private readonly StrategyParam<decimal> _atrProfitMultiplier;
	private readonly StrategyParam<DataType> _candleType;

	private decimal _prevLower;
	private decimal _prevUpper;
	private decimal _prevClose;
	private decimal _prevMa;
	private bool _isInitialized;
	private decimal _entryPrice;
	private int _cooldown;

	/// <summary>
	/// Period for Bollinger Bands calculation.
	/// </summary>
	public int BollingerPeriod
	{
		get => _bollingerPeriod.Value;
		set => _bollingerPeriod.Value = value;
	}

	/// <summary>
	/// Standard deviation multiplier for Bollinger Bands.
	/// </summary>
	public decimal BollingerMultiplier
	{
		get => _bollingerMultiplier.Value;
		set => _bollingerMultiplier.Value = value;
	}

	/// <summary>
	/// Moving average length.
	/// </summary>
	public int MaLength
	{
		get => _maLength.Value;
		set => _maLength.Value = value;
	}

	/// <summary>
	/// ATR period.
	/// </summary>
	public int AtrLength
	{
		get => _atrLength.Value;
		set => _atrLength.Value = value;
	}

	/// <summary>
	/// ATR multiplier for stop calculation.
	/// </summary>
	public decimal AtrStopMultiplier
	{
		get => _atrStopMultiplier.Value;
		set => _atrStopMultiplier.Value = value;
	}

	/// <summary>
	/// ATR multiplier for take profit.
	/// </summary>
	public decimal AtrProfitMultiplier
	{
		get => _atrProfitMultiplier.Value;
		set => _atrProfitMultiplier.Value = value;
	}

	/// <summary>
	/// Candle type.
	/// </summary>
	public DataType CandleType
	{
		get => _candleType.Value;
		set => _candleType.Value = value;
	}

	/// <summary>
	/// Initializes the strategy.
	/// </summary>
	public BbsrExtremeStrategy()
	{
		_bollingerPeriod = Param(nameof(BollingerPeriod), 30)
			.SetGreaterThanZero()
			.SetDisplay("Bollinger Period", "Bollinger Bands length", "Indicators");

		_bollingerMultiplier = Param(nameof(BollingerMultiplier), 2.5m)
			.SetGreaterThanZero()
			.SetDisplay("Bollinger Multiplier", "Standard deviation multiplier", "Indicators");

		_maLength = Param(nameof(MaLength), 7)
			.SetGreaterThanZero()
			.SetDisplay("MA Length", "Length for moving average", "Indicators");

		_atrLength = Param(nameof(AtrLength), 14)
			.SetGreaterThanZero()
			.SetDisplay("ATR Length", "ATR indicator period", "Risk Management");

		_atrStopMultiplier = Param(nameof(AtrStopMultiplier), 2m)
			.SetGreaterThanZero()
			.SetDisplay("ATR Stop Multiplier", "ATR multiplier for stop", "Risk Management");

		_atrProfitMultiplier = Param(nameof(AtrProfitMultiplier), 3m)
			.SetGreaterThanZero()
			.SetDisplay("ATR Profit Multiplier", "ATR multiplier for take profit", "Risk Management");

		_candleType = Param(nameof(CandleType), TimeSpan.FromHours(1).TimeFrame())
			.SetDisplay("Candle Type", "Type of candles to use", "General");
	}

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

	/// <inheritdoc />
	protected override void OnReseted()
	{
		base.OnReseted();
		_prevLower = default;
		_prevUpper = default;
		_prevClose = default;
		_prevMa = default;
		_isInitialized = false;
		_entryPrice = 0m;
		_cooldown = 0;
	}

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

		var bollinger = new BollingerBands
		{
			Length = BollingerPeriod,
			Width = BollingerMultiplier
		};

		var ma = new EMA
		{
			Length = MaLength
		};

		var atr = new AverageTrueRange
		{
			Length = AtrLength
		};

		var subscription = SubscribeCandles(CandleType);
		subscription
			.BindEx([bollinger, ma, atr], ProcessCandle)
			.Start();

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

	}

	private void ProcessCandle(ICandleMessage candle, IIndicatorValue[] values)
	{
		if (candle.State != CandleStates.Finished)
			return;

		if (values[0] is not BollingerBandsValue bbValue ||
			bbValue.UpBand is not decimal upper ||
			bbValue.LowBand is not decimal lower ||
			!values[1].IsFormed || !values[2].IsFormed)
			return;

		var maValue = values[1].GetValue<decimal>();
		var atrValue = values[2].GetValue<decimal>();

		if (!_isInitialized)
		{
			_prevLower = lower;
			_prevUpper = upper;
			_prevClose = candle.ClosePrice;
			_prevMa = maValue;
			_isInitialized = true;
			return;
		}

		if (_cooldown > 0)
			_cooldown--;

		var bull = _prevClose < _prevLower && candle.ClosePrice > lower && maValue > _prevMa;
		var bear = _prevClose > _prevUpper && candle.ClosePrice < upper && maValue < _prevMa;

		if (bull && Position <= 0 && _cooldown == 0)
		{
			BuyMarket();
			_entryPrice = candle.ClosePrice;
			_cooldown = 20;
		}
		else if (bear && Position >= 0 && _cooldown == 0)
		{
			SellMarket();
			_entryPrice = candle.ClosePrice;
			_cooldown = 20;
		}

		var stop = AtrStopMultiplier * atrValue;
		var profit = AtrProfitMultiplier * atrValue;

		if (Position > 0)
		{
			var stopPrice = _entryPrice - stop;
			var profitPrice = _entryPrice + profit;

			if (candle.LowPrice <= stopPrice || candle.HighPrice >= profitPrice)
			{
				SellMarket();
				_entryPrice = 0m;
				_cooldown = 20;
			}
		}
		else if (Position < 0)
		{
			var stopPrice = _entryPrice + stop;
			var profitPrice = _entryPrice - profit;

			if (candle.HighPrice >= stopPrice || candle.LowPrice <= profitPrice)
			{
				BuyMarket();
				_entryPrice = 0m;
				_cooldown = 20;
			}
		}

		_prevLower = lower;
		_prevUpper = upper;
		_prevClose = candle.ClosePrice;
		_prevMa = maValue;
	}
}