This strategy reproduces the MetaTrader expert advisor pSAR_alert2 inside the StockSharp framework. It monitors the Parabolic SAR indicator on the selected instrument and timeframe. Whenever the SAR value flips from above the closing price to below it (or vice versa), the strategy generates an informational alert. Optionally, it can submit market orders in the direction of the flip to transform the alert into an automated entry.
Trading Logic
Subscribe to the configured candle series and calculate the Parabolic SAR indicator with the supplied acceleration settings.
Wait for each candle to finish to emulate the original EA timing.
Compare the indicator value with the candle close:
Previous SAR above the close and current SAR below the close → bullish flip.
Previous SAR below the close and current SAR above the close → bearish flip.
Log a detailed alert for every flip. When auto trading is enabled, flatten any opposite exposure and open a new position in the direction of the signal using market orders.
Parameters
Parameter
Description
Candle Type
Timeframe used to build candles and evaluate the Parabolic SAR indicator.
SAR Step
Initial acceleration factor passed to the Parabolic SAR.
SAR Max
Maximum acceleration factor of the Parabolic SAR.
Enable Auto Trading
When true, market orders are sent on each alert; when false, only logs are generated.
Trade Volume
Order size applied when auto trading is enabled.
Conversion Notes
The original MetaTrader script relied on Sleep to throttle execution. StockSharp is event-driven, so the strategy reacts to new candles immediately without manual delays.
Alerts are produced through AddInfoLog, keeping the original behavior of pop-up notifications without requiring additional UI components.
Optional auto trading is provided to integrate the alert logic into automated workflows. Disable the Enable Auto Trading parameter to match the exact MetaTrader behavior.
Python implementation is intentionally omitted as requested.
using System;
using StockSharp.Algo.Indicators;
using StockSharp.Algo.Strategies;
using StockSharp.BusinessEntities;
using StockSharp.Messages;
namespace StockSharp.Samples.Strategies;
/// <summary>
/// Parabolic SAR Flip: EMA trend following with ATR stops.
/// </summary>
public class ParabolicSarFlipAlertStrategy : Strategy
{
private readonly StrategyParam<DataType> _candleType;
private readonly StrategyParam<int> _emaLength;
private readonly StrategyParam<int> _atrLength;
private decimal _prevClose;
private decimal _entryPrice;
public ParabolicSarFlipAlertStrategy()
{
_candleType = Param(nameof(CandleType), TimeSpan.FromMinutes(5).TimeFrame())
.SetDisplay("Candle Type", "Timeframe.", "General");
_emaLength = Param(nameof(EmaLength), 20)
.SetDisplay("EMA Length", "Trend filter.", "Indicators");
_atrLength = Param(nameof(AtrLength), 14)
.SetDisplay("ATR Length", "ATR period.", "Indicators");
}
public DataType CandleType { get => _candleType.Value; set => _candleType.Value = value; }
public int EmaLength { get => _emaLength.Value; set => _emaLength.Value = value; }
public int AtrLength { get => _atrLength.Value; set => _atrLength.Value = value; }
/// <inheritdoc />
protected override void OnReseted()
{
base.OnReseted();
_prevClose = 0; _entryPrice = 0;
}
protected override void OnStarted2(DateTime time)
{
base.OnStarted2(time);
_prevClose = 0; _entryPrice = 0;
var ema = new ExponentialMovingAverage { Length = EmaLength };
var atr = new AverageTrueRange { Length = AtrLength };
var subscription = SubscribeCandles(CandleType);
subscription.Bind(ema, atr, ProcessCandle).Start();
var area = CreateChartArea();
if (area != null) { DrawCandles(area, subscription); DrawIndicator(area, ema); DrawOwnTrades(area); }
}
private void ProcessCandle(ICandleMessage candle, decimal emaVal, decimal atrVal)
{
if (candle.State != CandleStates.Finished) return;
var close = candle.ClosePrice;
if (_prevClose == 0 || atrVal <= 0) { _prevClose = close; return; }
if (Position > 0)
{
if (close >= _entryPrice + atrVal * 2.5m || close <= _entryPrice - atrVal * 1.5m || close < emaVal) { SellMarket(); _entryPrice = 0; }
}
else if (Position < 0)
{
if (close <= _entryPrice - atrVal * 2.5m || close >= _entryPrice + atrVal * 1.5m || close > emaVal) { BuyMarket(); _entryPrice = 0; }
}
if (Position == 0)
{
if (close > emaVal && _prevClose <= emaVal) { _entryPrice = close; BuyMarket(); }
else if (close < emaVal && _prevClose >= emaVal) { _entryPrice = close; SellMarket(); }
}
_prevClose = close;
}
}
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 ExponentialMovingAverage, AverageTrueRange
from StockSharp.Algo.Strategies import Strategy
class parabolic_sar_flip_alert_strategy(Strategy):
def __init__(self):
super(parabolic_sar_flip_alert_strategy, self).__init__()
self._candle_type = self.Param("CandleType", DataType.TimeFrame(TimeSpan.FromMinutes(5))).SetDisplay("Candle Type", "Timeframe", "General")
self._ema_length = self.Param("EmaLength", 20).SetDisplay("EMA Length", "Trend filter", "Indicators")
self._atr_length = self.Param("AtrLength", 14).SetDisplay("ATR Length", "ATR period", "Indicators")
@property
def CandleType(self): return self._candle_type.Value
@CandleType.setter
def CandleType(self, value): self._candle_type.Value = value
def OnReseted(self):
super(parabolic_sar_flip_alert_strategy, self).OnReseted()
self._prev_close = 0
self._entry_price = 0
def OnStarted2(self, time):
super(parabolic_sar_flip_alert_strategy, self).OnStarted2(time)
self._prev_close = 0
self._entry_price = 0
ema = ExponentialMovingAverage()
ema.Length = self._ema_length.Value
atr = AverageTrueRange()
atr.Length = self._atr_length.Value
sub = self.SubscribeCandles(self.CandleType)
sub.Bind(ema, atr, self.OnProcess).Start()
area = self.CreateChartArea()
if area is not None:
self.DrawCandles(area, sub)
self.DrawIndicator(area, ema)
self.DrawOwnTrades(area)
def OnProcess(self, candle, ema_val, atr_val):
if candle.State != CandleStates.Finished:
return
close = candle.ClosePrice
if self._prev_close == 0 or atr_val <= 0:
self._prev_close = close
return
if self.Position > 0:
if close >= self._entry_price + atr_val * 2.5 or close <= self._entry_price - atr_val * 1.5 or close < ema_val:
self.SellMarket()
self._entry_price = 0
elif self.Position < 0:
if close <= self._entry_price - atr_val * 2.5 or close >= self._entry_price + atr_val * 1.5 or close > ema_val:
self.BuyMarket()
self._entry_price = 0
if self.Position == 0:
if close > ema_val and self._prev_close <= ema_val:
self._entry_price = close
self.BuyMarket()
elif close < ema_val and self._prev_close >= ema_val:
self._entry_price = close
self.SellMarket()
self._prev_close = close
def CreateClone(self):
return parabolic_sar_flip_alert_strategy()