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
Suscribirse a los datos de velas y calcular el Parabolic SAR.
Cuando el Parabolic SAR cambia de dirección y el precio se mueve al menos Delta, almacenar el precio de reversión.
Contar las reversiones negativas donde el precio se movió contra la tendencia anterior.
Una vez que el contador alcanza el valor de Negative Signals, abrir una posición en la nueva dirección de la tendencia.
Cada vela comprueba los niveles de stop-loss y take-profit usando el Reversal Mode seleccionado.
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;
}
}
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()