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
Subscribe to candle data and calculate Parabolic SAR.
When the Parabolic SAR flips direction and price moved by at least Delta, store the reversal price.
Count negative reversals where price moved against the previous trend.
Once the counter reaches the Negative Signals value, open a position in the new trend direction.
Every candle checks stop-loss and take-profit levels using the selected Reversal Mode.
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;
}
}
import clr
clr.AddReference("StockSharp.Messages")
clr.AddReference("StockSharp.Algo")
clr.AddReference("StockSharp.Algo.Indicators")
clr.AddReference("StockSharp.Algo.Strategies")
from System import TimeSpan
from StockSharp.Messages import DataType, CandleStates, Unit, UnitTypes
from StockSharp.Algo.Indicators import ParabolicSar, CandleIndicatorValue
from StockSharp.Algo.Strategies import Strategy
class breakout_bars_trend_strategy(Strategy):
def __init__(self):
super(breakout_bars_trend_strategy, self).__init__()
self._negatives = self.Param("Negatives", 1) \
.SetDisplay("Negative Signals", "Negative reversals before entry", "General")
self._stop_loss_pct = self.Param("StopLossPct", 2.0) \
.SetDisplay("Stop Loss %", "Stop loss percentage", "Risk")
self._take_profit_pct = self.Param("TakeProfitPct", 4.0) \
.SetDisplay("Take Profit %", "Take profit percentage", "Risk")
self._candle_type = self.Param("CandleType", DataType.TimeFrame(TimeSpan.FromHours(4))) \
.SetDisplay("Candle Type", "Type of candles", "General")
self._parabolic = None
self._last_trend = 0
self._negative_counter = 0
@property
def negatives(self):
return self._negatives.Value
@property
def stop_loss_pct(self):
return self._stop_loss_pct.Value
@property
def take_profit_pct(self):
return self._take_profit_pct.Value
@property
def candle_type(self):
return self._candle_type.Value
def OnReseted(self):
super(breakout_bars_trend_strategy, self).OnReseted()
self._parabolic = None
self._last_trend = 0
self._negative_counter = 0
def OnStarted2(self, time):
super(breakout_bars_trend_strategy, self).OnStarted2(time)
self._parabolic = ParabolicSar()
self.Indicators.Add(self._parabolic)
subscription = self.SubscribeCandles(self.candle_type)
subscription.Bind(self.process_candle).Start()
self.StartProtection(
takeProfit=Unit(self.take_profit_pct, UnitTypes.Percent),
stopLoss=Unit(self.stop_loss_pct, UnitTypes.Percent),
useMarketOrders=True)
area = self.CreateChartArea()
if area is not None:
self.DrawCandles(area, subscription)
self.DrawOwnTrades(area)
def process_candle(self, candle):
if candle.State != CandleStates.Finished:
return
cv = CandleIndicatorValue(self._parabolic, candle)
sar_result = self._parabolic.Process(cv)
if not sar_result.IsFormed:
return
if not self.IsFormedAndOnlineAndAllowTrading():
return
sar_value = float(sar_result)
trend = 1 if sar_value < float(candle.ClosePrice) else -1
if self._last_trend != 0 and self._last_trend != trend:
if trend == 1 and self.Position < 0:
self.BuyMarket()
elif trend == -1 and self.Position > 0:
self.SellMarket()
self._negative_counter += 1
if self._negative_counter > int(self.negatives):
if trend == 1 and self.Position <= 0:
self.BuyMarket()
elif trend == -1 and self.Position >= 0:
self.SellMarket()
self._negative_counter = 0
self._last_trend = trend
def CreateClone(self):
return breakout_bars_trend_strategy()