Problem Description
Solution
Here is a complete, fully functional MQL4 Expert Advisor (trading bot) based on the ADX trend-following strategy.
Strategy Logic
- Trend Strength Filter: The main ADX line must be above a specific threshold (default 25) to confirm a trend exists.
- Buy Signal: The +DI line crosses above the -DI line while ADX is above the threshold.
- Sell Signal: The -DI line crosses above the +DI line while ADX is above the threshold.
- 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
- Open MetaTrader 4.
- Press
F4to open the MetaEditor. - Click New -> Expert Advisor (template) -> Name it
ADX_Trend_Bot. - Delete everything in the new file and paste the code provided above.
- Press
F7or click Compile. Ensure there are no errors in the "Errors" tab at the bottom. - Return to the main MT4 window.
- Find
ADX_Trend_Botin the Navigator panel under Expert Advisors. - Drag it onto a chart (e.g., EURUSD H1).
- 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.