🚀 AI One-Click Generation of mql4 Strategy Code

How to Automate a Parabolic SAR (PSAR) Strategy in Mql4?

mql4 | Published on: 2025/11/26
The following content is generated by EasyQuant .

Problem Description

Can you code a strategy that follows the trend using the Parabolic SAR dots?

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

  1. 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.
  2. 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

  1. Open MetaTrader 4.
  2. Press F4 to open the MetaEditor.
  3. Click New -> Expert Advisor (template) -> Name it "ParabolicSAR_EA".
  4. Delete everything in the new file and paste the code above.
  5. Press F7 to Compile. Ensure there are no errors.
  6. 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 TrailingStopSAR function 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_STOPLEVEL to 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.