Ver en GitHub

Estrategia de Ruptura de Barras de Tendencia

Esta estrategia detecta reversiones de tendencia utilizando el indicador Parabolic SAR. Espera un número configurable de reversiones negativas antes de entrar en la nueva dirección de la tendencia. Las distancias para el stop-loss y el take-profit se miden en pips o como un porcentaje del precio de entrada.

Parámetros

  • Reversal Mode – elegir entre cálculos de distancia basados en pips o en porcentaje.
  • Delta – movimiento mínimo de precio requerido entre reversiones.
  • Negative Signals – cuántas reversiones fallidas deben ocurrir antes de abrir una operación.
  • Stop Loss – distancia de protección de pérdidas desde el precio de entrada.
  • Take Profit – distancia del objetivo de beneficio desde el precio de entrada.
  • Candle Type – serie de velas utilizada para los cálculos del indicador.

Lógica

  1. Suscribirse a los datos de velas y calcular el Parabolic SAR.
  2. Cuando el Parabolic SAR cambia de dirección y el precio se mueve al menos Delta, almacenar el precio de reversión.
  3. Contar las reversiones negativas donde el precio se movió contra la tendencia anterior.
  4. Una vez que el contador alcanza el valor de Negative Signals, abrir una posición en la nueva dirección de la tendencia.
  5. Cada vela comprueba los niveles de stop-loss y take-profit usando el Reversal Mode seleccionado.
  6. Las posiciones se cierran ante un cambio de tendencia opuesto o cuando se alcanzan los límites de riesgo.

La estrategia es adecuada para sistemas de ruptura de seguimiento de tendencia y puede optimizarse ajustando las distancias de delta, stop-loss y take-profit.

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>
/// Trend-following breakout strategy based on Parabolic SAR reversals.
/// Opens trades after negative reversals and uses percentage-based SL/TP.
/// </summary>
public class BreakoutBarsTrendStrategy : Strategy
{
	private readonly StrategyParam<int> _negatives;
	private readonly StrategyParam<decimal> _stopLossPct;
	private readonly StrategyParam<decimal> _takeProfitPct;
	private readonly StrategyParam<DataType> _candleType;

	private ParabolicSar _parabolic;
	private int _lastTrend; // -1, 0, 1
	private int _negativeCounter;

	public int Negatives
	{
		get => _negatives.Value;
		set => _negatives.Value = value;
	}

	public decimal StopLossPct
	{
		get => _stopLossPct.Value;
		set => _stopLossPct.Value = value;
	}

	public decimal TakeProfitPct
	{
		get => _takeProfitPct.Value;
		set => _takeProfitPct.Value = value;
	}

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

	public BreakoutBarsTrendStrategy()
	{
		_negatives = Param(nameof(Negatives), 1)
			.SetNotNegative()
			.SetDisplay("Negative Signals", "Negative reversals before entry", "General");

		_stopLossPct = Param(nameof(StopLossPct), 2m)
			.SetGreaterThanZero()
			.SetDisplay("Stop Loss %", "Stop loss percentage", "Risk");

		_takeProfitPct = Param(nameof(TakeProfitPct), 4m)
			.SetGreaterThanZero()
			.SetDisplay("Take Profit %", "Take profit percentage", "Risk");

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

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

	/// <inheritdoc />
	protected override void OnReseted()
	{
		base.OnReseted();
		_parabolic = default;
		_lastTrend = 0;
		_negativeCounter = 0;
	}

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

		_parabolic = new ParabolicSar();

		Indicators.Add(_parabolic);

		var subscription = SubscribeCandles(CandleType);

		subscription
			.Bind(ProcessCandle)
			.Start();

		StartProtection(
			takeProfit: new Unit(TakeProfitPct, UnitTypes.Percent),
			stopLoss: new Unit(StopLossPct, UnitTypes.Percent),
			useMarketOrders: true);

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

	private void ProcessCandle(ICandleMessage candle)
	{
		if (candle.State != CandleStates.Finished)
			return;

		var sarResult = _parabolic.Process(candle);
		if (!sarResult.IsFormed)
			return;

		if (!IsFormedAndOnlineAndAllowTrading())
			return;

		var sarValue = sarResult.ToDecimal();
		var trend = sarValue < candle.ClosePrice ? 1 : -1;

		if (_lastTrend != 0 && _lastTrend != trend)
		{
			// Reversal detected
			if (trend == 1 && Position < 0)
				BuyMarket();
			else if (trend == -1 && Position > 0)
				SellMarket();

			_negativeCounter++;

			if (_negativeCounter > Negatives)
			{
				if (trend == 1 && Position <= 0)
				{
					BuyMarket();
				}
				else if (trend == -1 && Position >= 0)
				{
					SellMarket();
				}
				_negativeCounter = 0;
			}
		}

		_lastTrend = trend;
	}
}