Problem Description
Solution
Here is a complete, fully functional Expert Advisor (EA) for MetaTrader 4 that implements a trend-following strategy using the Parabolic SAR indicator.
Strategy Logic
- Entry Signal:
- Buy: The Parabolic SAR dot flips from above the price (bearish) to below the price (bullish) on the previous closed candle.
- Sell: The Parabolic SAR dot flips from below the price (bullish) to above the price (bearish) on the previous closed candle.
- Exit Strategy:
- Reversal: If an opposite signal occurs, the current trade is closed immediately, and the opposite trade is opened.
- Trailing Stop: The Stop Loss is automatically adjusted on every tick to match the current Parabolic SAR value, locking in profits as the trend continues.
MQL4 Code
//+------------------------------------------------------------------+
//| ParabolicSAR_EA.mq4 |
//| Copyright 2023, Quantitative Trading AI |
//| https://www.mql5.com |
//+------------------------------------------------------------------+
#property copyright "Copyright 2023, Quantitative Trading AI"
#property link "https://www.mql5.com"
#property version "1.00"
#property strict
//--- Input Parameters
input double InpLots = 0.1; // Lot Size
input double InpSarStep = 0.02; // SAR Step
input double InpSarMax = 0.2; // SAR Maximum
input int InpMagicNumber = 123456; // Magic Number
input int InpSlippage = 3; // Slippage (pips)
input bool InpUseTrailing = true; // Use SAR as Trailing Stop
//--- Global Variables
int g_ticket = 0;
double g_point;
//+------------------------------------------------------------------+
//| Expert initialization function |
//+------------------------------------------------------------------+
int OnInit()
{
// Adjust point for 3/5 digit brokers
if(Digits == 3 || Digits == 5) g_point = Point * 10;
else g_point = Point;
return(INIT_SUCCEEDED);
}
//+------------------------------------------------------------------+
//| Expert tick function |
//+------------------------------------------------------------------+
void OnTick()
{
// 1. Get SAR values for previous closed candles (Shift 1 and 2)
// We use closed candles to confirm the "flip" and avoid repainting
double sar1 = iSAR(NULL, 0, InpSarStep, InpSarMax, 1); // Previous candle
double sar2 = iSAR(NULL, 0, InpSarStep, InpSarMax, 2); // Candle before previous
double close1 = iClose(NULL, 0, 1);
double close2 = iClose(NULL, 0, 2);
// 2. Determine Signals
// Buy: SAR was above price (bearish), now below price (bullish)
bool buySignal = (sar2 > close2) && (sar1 < close1);
// Sell: SAR was below price (bullish), now above price (bearish)
bool sellSignal = (sar2 < close2) && (sar1 > close1);
// 3. Manage Open Positions
if(CountOrders() == 0)
{
if(buySignal) OpenOrder(OP_BUY);
if(sellSignal) OpenOrder(OP_SELL);
}
else
{
// Check for reversals
if(buySignal && IsOrderType(OP_SELL))
{
CloseAll();
OpenOrder(OP_BUY);
}
else if(sellSignal && IsOrderType(OP_BUY))
{
CloseAll();
OpenOrder(OP_SELL);
}
// 4. Trailing Stop Logic
if(InpUseTrailing) TrailingStopSAR();
}
}
//+------------------------------------------------------------------+
//| Helper: Open Order |
//+------------------------------------------------------------------+
void OpenOrder(int type)
{
double price, sl = 0, tp = 0;
color clr;
// Initial Stop Loss is set to the current SAR value
double currentSar = iSAR(NULL, 0, InpSarStep, InpSarMax, 0);
if(type == OP_BUY)
{
price = Ask;
clr = clrBlue;
sl = currentSar;
// Ensure SL is below price
if(sl >= price) sl = price - (100 * Point);
}
else
{
price = Bid;
clr = clrRed;
sl = currentSar;
// Ensure SL is above price
if(sl <= price) sl = price + (100 * Point);
}
int ticket = OrderSend(Symbol(), type, InpLots, price, InpSlippage, sl, tp, "SAR Trend", InpMagicNumber, 0, clr);
if(ticket < 0)
Print("OrderSend failed with error #", GetLastError());
}
//+------------------------------------------------------------------+
//| Helper: Close All Orders for this EA |
//+------------------------------------------------------------------+
void CloseAll()
{
for(int i = OrdersTotal() - 1; i >= 0; i--)
{
if(OrderSelect(i, SELECT_BY_POS, MODE_TRADES))
{
if(OrderSymbol() == Symbol() && OrderMagicNumber() == InpMagicNumber)
{
bool res = false;
if(OrderType() == OP_BUY)
res = OrderClose(OrderTicket(), OrderLots(), Bid, InpSlippage, clrGray);
if(OrderType() == OP_SELL)
res = OrderClose(OrderTicket(), OrderLots(), Ask, InpSlippage, clrGray);
if(!res) Print("OrderClose failed: ", GetLastError());
}
}
}
}
//+------------------------------------------------------------------+
//| Helper: Count Open Orders |
//+------------------------------------------------------------------+
int CountOrders()
{
int count = 0;
for(int i = 0; i < OrdersTotal(); i++)
{
if(OrderSelect(i, SELECT_BY_POS, MODE_TRADES))
{
if(OrderSymbol() == Symbol() && OrderMagicNumber() == InpMagicNumber)
count++;
}
}
return count;
}
//+------------------------------------------------------------------+
//| Helper: Check Order Type |
//+------------------------------------------------------------------+
bool IsOrderType(int type)
{
for(int i = 0; i < OrdersTotal(); i++)
{
if(OrderSelect(i, SELECT_BY_POS, MODE_TRADES))
{
if(OrderSymbol() == Symbol() && OrderMagicNumber() == InpMagicNumber)
{
if(OrderType() == type) return true;
}
}
}
return false;
}
//+------------------------------------------------------------------+
//| Logic: Trailing Stop based on SAR |
//+------------------------------------------------------------------+
void TrailingStopSAR()
{
double currentSar = iSAR(NULL, 0, InpSarStep, InpSarMax, 0);
double stoplevel = MarketInfo(Symbol(), MODE_STOPLEVEL) * Point;
for(int i = 0; i < OrdersTotal(); i++)
{
if(OrderSelect(i, SELECT_BY_POS, MODE_TRADES))
{
if(OrderSymbol() == Symbol() && OrderMagicNumber() == InpMagicNumber)
{
// Modify Buy Order
if(OrderType() == OP_BUY)
{
// Only modify if SAR is above current SL and below current price (minus stoplevel)
if(currentSar > OrderStopLoss() && currentSar < (Bid - stoplevel))
{
if(!OrderModify(OrderTicket(), OrderOpenPrice(), currentSar, OrderTakeProfit(), 0, clrGreen))
Print("Error modifying Buy SL: ", GetLastError());
}
}
// Modify Sell Order
if(OrderType() == OP_SELL)
{
// Only modify if SAR is below current SL (0 means no SL) and above current price
if((OrderStopLoss() == 0 || currentSar < OrderStopLoss()) && currentSar > (Ask + stoplevel))
{
if(!OrderModify(OrderTicket(), OrderOpenPrice(), currentSar, OrderTakeProfit(), 0, clrGreen))
Print("Error modifying Sell SL: ", GetLastError());
}
}
}
}
}
}
How to Install and Use
- Open MetaTrader 4.
- Press
F4to open the MetaEditor. - Click New -> Expert Advisor (template) -> Name it "ParabolicSAR_EA".
- Delete everything in the new file and paste the code above.
- Press
F7to Compile. Ensure there are no errors. - Go back to the MT4 terminal, find the EA in the Navigator window, and drag it onto a chart.
Key Features of This Code
- Trend Reversal Logic: It compares the SAR value of the previous candle (
shift 1) against the candle before that (shift 2). This ensures the EA only acts when the dot physically moves from one side of the price to the other. - Dynamic Trailing Stop: The
TrailingStopSARfunction updates the Stop Loss on every tick to the current SAR value. This is the classic way to trade this indicator, allowing profits to run until the trend reverses. - Risk Management: It includes checks for
MODE_STOPLEVELto prevent "Invalid Stops" errors (Error 130) if the SAR dot is too close to the current price. - Clean Execution: It closes opposite positions before opening new ones to ensure the strategy is always aligned with the current trend direction.
Q&A
Q: Why does the EA use shift 1 and shift 2 instead of shift 0?
A: Shift 0 represents the current forming candle. The Parabolic SAR value can change (repaint) while the candle is still forming. By using shift 1 (the last closed candle), we ensure the signal is confirmed and will not disappear.
Q: Can I use this on any timeframe?
A: Yes. However, Parabolic SAR works best on trending timeframes (typically H1, H4, or Daily). On lower timeframes (M1, M5), it may generate false signals during choppy (sideways) markets.
Q: How does the Trailing Stop work?
A: The Parabolic SAR indicator naturally moves closer to the price as the trend accelerates. The EA reads the value of the SAR dot on the current bar and moves the Stop Loss to that price, effectively locking in profit as the trade moves in your favor.