Parabolic SAR MQL5 Indicator: Trailing Stop & Trend Reversal Detection
Implement iSAR-based dots and completed-bar flip markers in a custom MQL5 indicator. The example adds an EMA condition and an explicit ATR gate, exposes signal buffers for iCustom and keeps drawing separate from trade execution. No reproducible backtest supports a claimed performance improvement.
How the indicator signal works
Calculation and signal conditions
A completed bar produces an up marker when SAR changes from above the close to at or below it, the close is above the EMA, and ATR is at least 80% of its moving average. The reverse relation produces a down marker. All calculations use the chart timeframe and aligned chronological arrays; arrows never use the forming bar.
Interpretation and limitations
The indicator does not submit orders, move a stop, close positions or forecast an imminent flip from shrinking SAR distance. A plotted SAR level is only a reference for a separately defined EA exit rule. Neither a dot change nor the optional filter concept establishes a profitable reversal or a protected loss limit.
MQL5 Custom Indicator Code
#property strict
#property indicator_chart_window
#property indicator_buffers 4
#property indicator_plots 4
#property indicator_type1 DRAW_ARROW
#property indicator_color1 clrDodgerBlue
#property indicator_label1 "SAR"
#property indicator_type2 DRAW_ARROW
#property indicator_color2 clrLimeGreen
#property indicator_label2 "Filtered up flip"
#property indicator_type3 DRAW_ARROW
#property indicator_color3 clrTomato
#property indicator_label3 "Filtered down flip"
#property indicator_type4 DRAW_NONE
#property indicator_label4 "Completed-bar signal"
input double InpSARStep=0.02;
input double InpSARMaximum=0.2;
input int InpEMAPeriod=50;
input int InpATRPeriod=14;
input int InpATRMAPeriod=20;
double sar[],up[],down[],direction[];
int hs=INVALID_HANDLE,he=INVALID_HANDLE,ha=INVALID_HANDLE;
int OnInit()
{
if(InpSARStep<=0 || InpSARMaximum<InpSARStep || InpEMAPeriod<=0
|| InpATRPeriod<=0 || InpATRMAPeriod<=0) return INIT_PARAMETERS_INCORRECT;
SetIndexBuffer(0,sar,INDICATOR_DATA); SetIndexBuffer(1,up,INDICATOR_DATA);
SetIndexBuffer(2,down,INDICATOR_DATA); SetIndexBuffer(3,direction,INDICATOR_DATA);
ArraySetAsSeries(sar,false); ArraySetAsSeries(up,false);
ArraySetAsSeries(down,false); ArraySetAsSeries(direction,false);
PlotIndexSetInteger(0,PLOT_ARROW,159);
PlotIndexSetInteger(1,PLOT_ARROW,241); PlotIndexSetInteger(2,PLOT_ARROW,242);
int warmup=MathMax(InpEMAPeriod,InpATRPeriod+InpATRMAPeriod);
for(int p=1;p<4;p++) PlotIndexSetInteger(p,PLOT_DRAW_BEGIN,warmup);
IndicatorSetString(INDICATOR_SHORTNAME,"PSAR("+DoubleToString(InpSARStep,3)+","+DoubleToString(InpSARMaximum,3)+")");
hs=iSAR(_Symbol,_Period,InpSARStep,InpSARMaximum);
he=iMA(_Symbol,_Period,InpEMAPeriod,0,MODE_EMA,PRICE_CLOSE);
ha=iATR(_Symbol,_Period,InpATRPeriod);
return hs==INVALID_HANDLE || he==INVALID_HANDLE || ha==INVALID_HANDLE?INIT_FAILED:INIT_SUCCEEDED;
}
void OnDeinit(const int reason)
{
if(hs!=INVALID_HANDLE) IndicatorRelease(hs);
if(he!=INVALID_HANDLE) IndicatorRelease(he);
if(ha!=INVALID_HANDLE) IndicatorRelease(ha);
}
int OnCalculate(const int rates_total,const int prev_calculated,
const datetime &time[],const double &open[],const double &high[],const double &low[],
const double &close[],const long &tick_volume[],const long &volume[],const int &spread[])
{
int warmup=MathMax(InpEMAPeriod,InpATRPeriod+InpATRMAPeriod);
if(rates_total<=warmup+1) return 0;
ArraySetAsSeries(close,false); ArraySetAsSeries(high,false); ArraySetAsSeries(low,false);
double ema[],atr[]; ArraySetAsSeries(ema,false); ArraySetAsSeries(atr,false);
if(CopyBuffer(hs,0,0,rates_total,sar)!=rates_total
|| CopyBuffer(he,0,0,rates_total,ema)!=rates_total
|| CopyBuffer(ha,0,0,rates_total,atr)!=rates_total) return prev_calculated;
// Full-history calculation keeps all inputs in chronological order.
ArrayInitialize(up,EMPTY_VALUE); ArrayInitialize(down,EMPTY_VALUE);
ArrayInitialize(direction,0);
for(int i=warmup;i<rates_total-1;i++) // Exclude the forming bar.
{
double average=0;
for(int j=0;j<InpATRMAPeriod;j++) average+=atr[i-j];
average/=InpATRMAPeriod;
if(average<=0 || atr[i]<0.8*average || sar[i]<=0 || sar[i-1]<=0) continue;
bool wasAbove=sar[i-1]>close[i-1],nowAbove=sar[i]>close[i];
double offset=MathMax(high[i]-low[i],_Point)*0.3;
if(wasAbove && !nowAbove && close[i]>ema[i])
{ up[i]=low[i]-offset; direction[i]=1; }
else if(!wasAbove && nowAbove && close[i]<ema[i])
{ down[i]=high[i]+offset; direction[i]=-1; }
}
return rates_total;
}Copy this code into MetaEditor, save it in the MQL5/Indicators folder, and compile with F7.
Change the settings or add your own signal filter
Describe the inputs, buffers, alerts, visual style, or confirmation filter you want. Pineify generates editable MQL5 source code that you can inspect and compile in MetaEditor.
Frequently Asked Questions
Related MQL5 pages
Risk and testing note
These educational examples require independent compilation and testing. A compile pass or an illustrative setting does not establish execution correctness or profitability. Any reported performance must be supported by reproducible data and test settings; past results do not predict future returns. Trading involves significant risk of loss. This content is not financial advice.