🚀 AI One-Click Generation of mql4 Strategy Code

How to Code an ATR-Based Trailing Stop and Take Profit in Mql4

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

Problem Description

Please write an Expert Advisor that uses the ATR indicator to set the stop loss and take profit for new trades.

Solution

Here is a complete, fully functional Expert Advisor (EA) for MetaTrader 4.

This EA uses a simple Moving Average Crossover strategy to determine entry points, but its primary focus is using the Average True Range (ATR) to calculate dynamic Stop Loss and Take Profit levels based on market volatility.

MQL4 Code

//+------------------------------------------------------------------+
//|                                                 ATR_Risk_EA.mq4  |
//|                                     Copyright 2023, MetaQuotes   |
//|                                             https://www.mql5.com |
//+------------------------------------------------------------------+
#property copyright "Copyright 2023, Quantitative Assistant"
#property link      "https://www.mql5.com"
#property version   "1.00"
#property strict

//--- Input Parameters
input string   RiskSettings   = "--- Risk Settings ---"; // Section Title
input double   InpLotSize     = 0.1;        // Trade Volume
input int      InpATRPeriod   = 14;         // ATR Period
input double   InpSLMultiplier= 1.5;        // Stop Loss Multiplier (x ATR)
input double   InpTPMultiplier= 2.0;        // Take Profit Multiplier (x ATR)

input string   StrategySettings = "--- Strategy Settings ---"; // Section Title
input int      InpFastMA      = 10;         // Fast Moving Average Period
input int      InpSlowMA      = 20;         // Slow Moving Average Period
input int      InpMagicNum    = 123456;     // Magic Number
input int      InpSlippage    = 3;          // Max Slippage (pips)

//+------------------------------------------------------------------+
//| Expert initialization function                                   |
//+------------------------------------------------------------------+
int OnInit()
{
   // Basic validation
   if(InpATRPeriod <= 0 || InpFastMA <= 0 || InpSlowMA <= 0)
   {
      Print("Error: Periods must be greater than 0.");
      return(INIT_FAILED);
   }
   return(INIT_SUCCEEDED);
}

//+------------------------------------------------------------------+
//| Expert tick function                                             |
//+------------------------------------------------------------------+
void OnTick()
{
   // 1. Check if we already have an open trade for this symbol/magic number
   if(CountOpenTrades() > 0) return;

   // 2. Define Indicators
   // ATR for volatility calculation
   double atrValue = iATR(NULL, 0, InpATRPeriod, 0);
   
   // Moving Averages for Entry Signal (Shift 1 = closed bar, Shift 2 = previous closed bar)
   double fastMA_curr = iMA(NULL, 0, InpFastMA, 0, MODE_EMA, PRICE_CLOSE, 1);
   double slowMA_curr = iMA(NULL, 0, InpSlowMA, 0, MODE_EMA, PRICE_CLOSE, 1);
   double fastMA_prev = iMA(NULL, 0, InpFastMA, 0, MODE_EMA, PRICE_CLOSE, 2);
   double slowMA_prev = iMA(NULL, 0, InpSlowMA, 0, MODE_EMA, PRICE_CLOSE, 2);

   // 3. Calculate Dynamic SL and TP distances based on ATR
   double slDistance = atrValue * InpSLMultiplier;
   double tpDistance = atrValue * InpTPMultiplier;

   // 4. Trading Logic (Crossover)
   
   // Buy Signal: Fast MA crosses above Slow MA
   if(fastMA_curr > slowMA_curr && fastMA_prev <= slowMA_prev)
   {
      // Calculate Price Levels
      double entryPrice = Ask;
      double stopLoss   = NormalizeDouble(entryPrice - slDistance, Digits);
      double takeProfit = NormalizeDouble(entryPrice + tpDistance, Digits);
      
      // Send Order
      int ticket = OrderSend(Symbol(), OP_BUY, InpLotSize, entryPrice, GetSlippage(), stopLoss, takeProfit, "ATR Buy", InpMagicNum, 0, clrBlue);
      
      if(ticket < 0) Print("OrderSend failed with error #", GetLastError());
   }

   // Sell Signal: Fast MA crosses below Slow MA
   if(fastMA_curr < slowMA_curr && fastMA_prev >= slowMA_prev)
   {
      // Calculate Price Levels
      double entryPrice = Bid;
      double stopLoss   = NormalizeDouble(entryPrice + slDistance, Digits);
      double takeProfit = NormalizeDouble(entryPrice - tpDistance, Digits);
      
      // Send Order
      int ticket = OrderSend(Symbol(), OP_SELL, InpLotSize, entryPrice, GetSlippage(), stopLoss, takeProfit, "ATR Sell", InpMagicNum, 0, clrRed);
      
      if(ticket < 0) Print("OrderSend failed with error #", GetLastError());
   }
}

//+------------------------------------------------------------------+
//| Helper: Count open trades for this EA                            |
//+------------------------------------------------------------------+
int CountOpenTrades()
{
   int count = 0;
   for(int i = OrdersTotal() - 1; i >= 0; i--)
   {
      if(OrderSelect(i, SELECT_BY_POS, MODE_TRADES))
      {
         if(OrderSymbol() == Symbol() && OrderMagicNumber() == InpMagicNum)
         {
            if(OrderType() == OP_BUY || OrderType() == OP_SELL)
               count++;
         }
      }
   }
   return(count);
}

//+------------------------------------------------------------------+
//| Helper: Adjust Slippage for 4/5 digit brokers                    |
//+------------------------------------------------------------------+
int GetSlippage()
{
   if(Digits == 3 || Digits == 5) return(InpSlippage * 10);
   return(InpSlippage);
}
//+------------------------------------------------------------------+

How It Works

  1. Inputs:

    • InpATRPeriod: Determines how many candles back the ATR calculates volatility (Standard is 14).
    • InpSLMultiplier: Multiplies the ATR value to set the Stop Loss distance. (e.g., if ATR is 0.0020 and multiplier is 1.5, SL is 30 pips away).
    • InpTPMultiplier: Multiplies the ATR value to set the Take Profit distance.
  2. Entry Logic:

    • The EA uses a standard Moving Average Crossover (Fast EMA crossing Slow EMA) to decide when to enter. This ensures the EA actually trades so you can test the ATR logic.
  3. ATR Calculation:

    • iATR(NULL, 0, InpATRPeriod, 0) retrieves the ATR value for the current symbol and timeframe.
    • The Stop Loss distance is calculated as ATR * SL_Multiplier.
    • The Take Profit distance is calculated as ATR * TP_Multiplier.
  4. Price Normalization:

    • The code uses NormalizeDouble(..., Digits) on the calculated SL and TP prices. This is crucial in MQL4; without it, the server may reject the order because the calculated price has too many decimal places.
  5. Execution:

    • The OrderSend function places the trade with the calculated SL and TP immediately attached to the order.

How to Use

  1. Open MetaEditor in MT4 (Press F4).
  2. Create a new Expert Advisor file.
  3. Paste the code above into the file.
  4. Compile the file (Press F7).
  5. Go back to the MT4 Terminal, find ATR_Risk_EA in the Navigator, and drag it onto a chart.

Q&A

Q: Why use ATR for Stop Loss?
A: ATR (Average True Range) measures market volatility. Using a fixed pip stop loss (e.g., 20 pips) might be too tight in a volatile market (getting stopped out by noise) or too wide in a quiet market (risking too much). ATR adjusts the stop distance dynamically based on how much the price is currently moving.

Q: Can I use this on any timeframe?
A: Yes. Since the code uses Period() (represented by 0 in the iATR function), it automatically adapts to M15, H1, D1, etc. Note that ATR values on D1 are much larger than on M1, so your stops will naturally be wider on higher timeframes.

Q: What happens if the broker is ECN?
A: This code places SL/TP directly within the OrderSend function. Most modern MT4 bridges handle this fine. However, strictly speaking, some old ECN bridges require you to open the trade with 0 SL/TP first, and then use OrderModify to add them. This code uses the standard method for simplicity and compatibility with standard accounts.