🚀 AI One-Click Generation of mql4 Strategy Code

How to Build a Trend-Following Expert Advisor with ADX in Mql4

mql4 | Published on: 2025/12/3
The following content is generated by EasyQuant .

Problem Description

Please help me write a trend-following trading bot using the ADX, +DI, and -DI lines.

Solution

Here is a complete, fully functional MQL4 Expert Advisor (trading bot) based on the ADX trend-following strategy.

Strategy Logic

  1. Trend Strength Filter: The main ADX line must be above a specific threshold (default 25) to confirm a trend exists.
  2. Buy Signal: The +DI line crosses above the -DI line while ADX is above the threshold.
  3. Sell Signal: The -DI line crosses above the +DI line while ADX is above the threshold.
  4. Exit Strategy: The bot uses fixed Stop Loss and Take Profit levels. Additionally, if a signal reverses (e.g., a Sell signal appears while holding a Buy), the bot will close the existing position and open the new one.

MQL4 Code

//+------------------------------------------------------------------+
//|                                                 ADX_Trend_Bot.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 string   Inp_Settings      = "--- Strategy Settings ---"; // Strategy Settings
input int      ADX_Period        = 14;          // ADX Period
input int      ADX_Threshold     = 25;          // Minimum ADX Value to Trade
input double   LotSize           = 0.1;         // Fixed Lot Size
input int      StopLoss          = 50;          // Stop Loss (in pips)
input int      TakeProfit        = 100;         // Take Profit (in pips)
input int      Slippage          = 3;           // Max Slippage (in pips)
input int      MagicNumber       = 123456;      // Magic Number ID

//--- Global Variables
double g_pipMultiplier;

//+------------------------------------------------------------------+
//| Expert initialization function                                   |
//+------------------------------------------------------------------+
int OnInit()
{
   // Adjust pip multiplier for 3 or 5 digit brokers (Jpy pairs etc)
   if(Digits == 3 || Digits == 5)
      g_pipMultiplier = 10;
   else
      g_pipMultiplier = 1;

   return(INIT_SUCCEEDED);
}

//+------------------------------------------------------------------+
//| Expert deinitialization function                                 |
//+------------------------------------------------------------------+
void OnDeinit(const int reason)
{
   // Clean up if necessary
}

//+------------------------------------------------------------------+
//| Expert tick function                                             |
//+------------------------------------------------------------------+
void OnTick()
{
   // 1. Check if we have enough bars to calculate ADX
   if(Bars < ADX_Period) return;

   // 2. Define Indicator Buffers
   // We use shift 1 (completed candle) for signals to avoid repainting
   // We use shift 2 (previous completed candle) to detect the crossover
   
   // Current Candle (Shift 1)
   double adx_main = iADX(NULL, 0, ADX_Period, PRICE_CLOSE, MODE_MAIN, 1);
   double plus_di  = iADX(NULL, 0, ADX_Period, PRICE_CLOSE, MODE_PLUSDI, 1);
   double minus_di = iADX(NULL, 0, ADX_Period, PRICE_CLOSE, MODE_MINUSDI, 1);

   // Previous Candle (Shift 2)
   double plus_di_prev  = iADX(NULL, 0, ADX_Period, PRICE_CLOSE, MODE_PLUSDI, 2);
   double minus_di_prev = iADX(NULL, 0, ADX_Period, PRICE_CLOSE, MODE_MINUSDI, 2);

   // 3. Check for Open Positions managed by this EA
   int openType = -1; // -1 means no trade, 0 = Buy, 1 = Sell
   if(OrdersTotal() > 0)
   {
      for(int i = OrdersTotal() - 1; i >= 0; i--)
      {
         if(OrderSelect(i, SELECT_BY_POS, MODE_TRADES))
         {
            if(OrderSymbol() == Symbol() && OrderMagicNumber() == MagicNumber)
            {
               openType = OrderType();
            }
         }
      }
   }

   // 4. Signal Logic
   
   // BUY SIGNAL: +DI crosses above -DI AND ADX > Threshold
   bool buySignal = (plus_di > minus_di) && (plus_di_prev <= minus_di_prev) && (adx_main > ADX_Threshold);
   
   // SELL SIGNAL: -DI crosses above +DI AND ADX > Threshold
   bool sellSignal = (minus_di > plus_di) && (minus_di_prev <= plus_di_prev) && (adx_main > ADX_Threshold);

   // 5. Execution Logic
   
   // If Buy Signal
   if(buySignal)
   {
      // If we have a Sell open, close it first (Reversal)
      if(openType == OP_SELL)
      {
         CloseAllPositions(OP_SELL);
         openType = -1; 
      }
      
      // If no trade is open, place Buy
      if(openType == -1)
      {
         OpenTrade(OP_BUY);
      }
   }
   
   // If Sell Signal
   if(sellSignal)
   {
      // If we have a Buy open, close it first (Reversal)
      if(openType == OP_BUY)
      {
         CloseAllPositions(OP_BUY);
         openType = -1;
      }
      
      // If no trade is open, place Sell
      if(openType == -1)
      {
         OpenTrade(OP_SELL);
      }
   }
}

//+------------------------------------------------------------------+
//| Helper: Open Trade                                               |
//+------------------------------------------------------------------+
void OpenTrade(int type)
{
   double price, sl, tp;
   color arrowColor;
   
   // Calculate SL and TP prices
   double point = Point;
   
   if(type == OP_BUY)
   {
      price = Ask;
      sl = (StopLoss > 0) ? price - (StopLoss * point * g_pipMultiplier) : 0;
      tp = (TakeProfit > 0) ? price + (TakeProfit * point * g_pipMultiplier) : 0;
      arrowColor = clrBlue;
   }
   else // OP_SELL
   {
      price = Bid;
      sl = (StopLoss > 0) ? price + (StopLoss * point * g_pipMultiplier) : 0;
      tp = (TakeProfit > 0) ? price - (TakeProfit * point * g_pipMultiplier) : 0;
      arrowColor = clrRed;
   }

   // Normalize prices
   price = NormalizeDouble(price, Digits);
   sl    = NormalizeDouble(sl, Digits);
   tp    = NormalizeDouble(tp, Digits);

   // Send Order
   int ticket = OrderSend(Symbol(), type, LotSize, price, Slippage * g_pipMultiplier, sl, tp, "ADX Bot", MagicNumber, 0, arrowColor);
   
   if(ticket < 0)
   {
      Print("OrderSend failed with error #", GetLastError());
   }
}

//+------------------------------------------------------------------+
//| Helper: Close All Positions of Specific Type                     |
//+------------------------------------------------------------------+
void CloseAllPositions(int typeToClose)
{
   for(int i = OrdersTotal() - 1; i >= 0; i--)
   {
      if(OrderSelect(i, SELECT_BY_POS, MODE_TRADES))
      {
         if(OrderSymbol() == Symbol() && OrderMagicNumber() == MagicNumber)
         {
            if(OrderType() == typeToClose)
            {
               double closePrice = (OrderType() == OP_BUY) ? Bid : Ask;
               bool res = OrderClose(OrderTicket(), OrderLots(), closePrice, Slippage * g_pipMultiplier, clrGray);
               if(!res)
               {
                  Print("OrderClose failed with error #", GetLastError());
               }
            }
         }
      }
   }
}
//+------------------------------------------------------------------+

How to Install and Run

  1. Open MetaTrader 4.
  2. Press F4 to open the MetaEditor.
  3. Click New -> Expert Advisor (template) -> Name it ADX_Trend_Bot.
  4. Delete everything in the new file and paste the code provided above.
  5. Press F7 or click Compile. Ensure there are no errors in the "Errors" tab at the bottom.
  6. Return to the main MT4 window.
  7. Find ADX_Trend_Bot in the Navigator panel under Expert Advisors.
  8. Drag it onto a chart (e.g., EURUSD H1).
  9. Ensure "Allow live trading" is checked in the Common tab and the AutoTrading button at the top of MT4 is green.

Q&A: ADX Trading Strategy

Q: Why does the code use shift 1 and shift 2 instead of shift 0?
A: Shift 0 refers to the current forming candle. The values of ADX and DI lines change constantly until the candle closes. Using Shift 0 causes "repainting," where a signal might appear and then disappear. Using Shift 1 (the last closed candle) ensures the signal is permanent and valid.

Q: What is the purpose of the g_pipMultiplier?
A: Standard pips are calculated differently on 4-digit brokers vs. 5-digit brokers. A 5-digit broker quotes EURUSD as 1.12345. Without the multiplier, a 50 pip Stop Loss might be interpreted as 5.0 pips. The multiplier ensures the math works correctly regardless of the broker's precision.

Q: How does the bot handle trend reversals?
A: The bot checks openType. If the bot detects a Buy signal but currently holds a Sell position, it calls CloseAllPositions(OP_SELL) to exit the short trade before opening the long trade. This ensures the bot is always aligned with the current ADX trend direction.

Q: Can I run this on multiple currency pairs simultaneously?
A: Yes. However, you should change the MagicNumber input for each chart to a unique value (e.g., 123456 for EURUSD, 123457 for GBPUSD). This prevents the bot on one chart from accidentally managing or closing the trades of another chart.