This strategy replicates the PZ Parabolic SAR expert advisor. It employs two Parabolic SAR indicators with different step and maximum acceleration settings. The "trade" SAR detects trend direction for entries, while the "stop" SAR follows price more closely and triggers exits when the trend reverses.
Risk control is handled through the Average True Range (ATR). An initial ATR-based stop is set when a position opens. Optionally, a trailing stop based on ATR can tighten the stop as price moves in the trade's favor. The strategy also supports partial closing: once the profit exceeds the initial stop distance, half of the position is closed and the stop is moved to break-even.
The strategy works in both long and short directions and operates on finished candles only. It uses market orders without placing actual stop orders.
Details
Entry Criteria: Price above/below trade SAR and stop SAR in the same direction.
Long/Short: Both directions.
Exit Criteria: Stop SAR crossing price or ATR trailing stop hit.
Stops: ATR-based stop with optional trailing and break-even.
Default Values:
TradeStep = 0.002
TradeMax = 0.2
StopStep = 0.004
StopMax = 0.4
AtrPeriod = 30
AtrMultiplier = 2.5
UseTrailing = false
TrailingAtrPeriod = 30
TrailingAtrMultiplier = 1.75
PartialClosing = true
PercentageToClose = 0.5
BreakEven = true
LotSize = 0.1
CandleType = TimeFrame(5m)
Filters:
Category: Trend
Direction: Both
Indicators: Parabolic SAR, ATR
Stops: ATR, Trailing
Complexity: Intermediate
Timeframe: Intraday (5m)
Seasonality: No
Neural Networks: No
Divergence: No
Risk Level: Medium
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>
/// Parabolic SAR crossover strategy.
/// </summary>
public class PzParabolicSarEaStrategy : Strategy
{
private readonly StrategyParam<decimal> _sarStep;
private readonly StrategyParam<decimal> _sarMax;
private readonly StrategyParam<DataType> _candleType;
private decimal _prevClose;
private decimal _prevSar;
private bool _hasPrev;
public decimal SarStep { get => _sarStep.Value; set => _sarStep.Value = value; }
public decimal SarMax { get => _sarMax.Value; set => _sarMax.Value = value; }
public DataType CandleType { get => _candleType.Value; set => _candleType.Value = value; }
public PzParabolicSarEaStrategy()
{
_sarStep = Param(nameof(SarStep), 0.02m)
.SetDisplay("SAR Step", "Acceleration step", "Indicators");
_sarMax = Param(nameof(SarMax), 0.2m)
.SetDisplay("SAR Max", "Maximum acceleration", "Indicators");
_candleType = Param(nameof(CandleType), TimeSpan.FromHours(4).TimeFrame())
.SetDisplay("Candle Type", "Candle type", "General");
}
public override IEnumerable<(Security sec, DataType dt)> GetWorkingSecurities()
=> [(Security, CandleType)];
protected override void OnReseted()
{
base.OnReseted();
_prevClose = 0;
_prevSar = 0;
_hasPrev = false;
}
protected override void OnStarted2(DateTime time)
{
base.OnStarted2(time);
var sar = new ParabolicSar { AccelerationStep = SarStep, AccelerationMax = SarMax };
SubscribeCandles(CandleType)
.Bind(sar, ProcessCandle)
.Start();
}
private void ProcessCandle(ICandleMessage candle, decimal sarVal)
{
if (candle.State != CandleStates.Finished) return;
var close = candle.ClosePrice;
if (!_hasPrev)
{
_prevClose = close;
_prevSar = sarVal;
_hasPrev = true;
return;
}
var crossUp = _prevClose <= _prevSar && close > sarVal;
var crossDown = _prevClose >= _prevSar && close < sarVal;
if (crossUp && Position <= 0)
{
if (Position < 0) BuyMarket();
BuyMarket();
}
else if (crossDown && Position >= 0)
{
if (Position > 0) SellMarket();
SellMarket();
}
_prevClose = close;
_prevSar = sarVal;
}
}
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
from StockSharp.Algo.Indicators import ParabolicSar
from StockSharp.Algo.Strategies import Strategy
class pz_parabolic_sar_ea_strategy(Strategy):
def __init__(self):
super(pz_parabolic_sar_ea_strategy, self).__init__()
self._sar_step = self.Param("SarStep", 0.02) .SetDisplay("SAR Step", "Acceleration step", "Indicators")
self._sar_max = self.Param("SarMax", 0.2) .SetDisplay("SAR Max", "Maximum acceleration", "Indicators")
self._candle_type = self.Param("CandleType", DataType.TimeFrame(TimeSpan.FromHours(4))) .SetDisplay("Candle Type", "Candle type", "General")
self._prev_close = 0.0
self._prev_sar = 0.0
self._has_prev = False
@property
def sar_step(self):
return self._sar_step.Value
@property
def sar_max(self):
return self._sar_max.Value
@property
def candle_type(self):
return self._candle_type.Value
def OnReseted(self):
super(pz_parabolic_sar_ea_strategy, self).OnReseted()
self._prev_close = 0.0
self._prev_sar = 0.0
self._has_prev = False
def OnStarted2(self, time):
super(pz_parabolic_sar_ea_strategy, self).OnStarted2(time)
sar = ParabolicSar()
sar.AccelerationStep = self.sar_step
sar.AccelerationMax = self.sar_max
self.SubscribeCandles(self.candle_type).Bind(sar, self.process_candle).Start()
def process_candle(self, candle, sar_val):
if candle.State != CandleStates.Finished:
return
close = float(candle.ClosePrice)
sv = float(sar_val)
if not self._has_prev:
self._prev_close = close
self._prev_sar = sv
self._has_prev = True
return
cross_up = self._prev_close <= self._prev_sar and close > sv
cross_down = self._prev_close >= self._prev_sar and close < sv
if cross_up and self.Position <= 0:
if self.Position < 0:
self.BuyMarket()
self.BuyMarket()
elif cross_down and self.Position >= 0:
if self.Position > 0:
self.SellMarket()
self.SellMarket()
self._prev_close = close
self._prev_sar = sv
def CreateClone(self):
return pz_parabolic_sar_ea_strategy()