GitHub で見る

Breakout Bars Trend Strategy

This strategy detects trend reversals using the Parabolic SAR indicator. It waits for a configurable number of negative reversals before entering in the new trend direction. Distances for stop-loss and take-profit are measured either in pips or as a percentage of the entry price.

Parameters

  • Reversal Mode – choose between pip-based or percent-based distance calculations.
  • Delta – minimum price movement required between reversals.
  • Negative Signals – how many failed reversals must occur before a trade can be opened.
  • Stop Loss – loss protection distance from the entry price.
  • Take Profit – profit target distance from the entry price.
  • Candle Type – candle series used for indicator calculations.

Logic

  1. Subscribe to candle data and calculate Parabolic SAR.
  2. When the Parabolic SAR flips direction and price moved by at least Delta, store the reversal price.
  3. Count negative reversals where price moved against the previous trend.
  4. Once the counter reaches the Negative Signals value, open a position in the new trend direction.
  5. Every candle checks stop-loss and take-profit levels using the selected Reversal Mode.
  6. Positions are closed on opposite trend change or when risk limits are hit.

The strategy is suitable for trend-following breakout systems and can be optimized by adjusting delta, stop-loss, and take-profit distances.

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;
	}
}