MQL5 Grid Trading EA: Full Code, Risk Management & Optimization
This page provides a complete, production-ready MQL5 Expert Advisor that implements a multi-pair grid trading strategy in MetaTrader 5. It covers the full source code, configurable grid spacing, dynamic lot sizing, risk management parameters, and practical optimization tips for live deployment.
Backtest Performance
Past performance is not indicative of future results. Backtest statistics are based on historical data and do not guarantee future profits. Trading involves significant risk of loss. This content is for educational purposes only and does not constitute financial advice.
Strategy Logic
Entry Conditions
The EA opens a buy order at the initial price and then places additional buy orders at fixed grid intervals below the current price, and sell orders at fixed intervals above. Each new grid level is activated when the market price crosses the predefined step distance, ensuring a new order is placed only when the prior level has been reached. Position sizing scales with account equity using a configurable risk-per-grid parameter to keep total exposure within acceptable drawdown limits.
Exit Conditions
Each grid order carries an individual take-profit level equal to one grid step above the entry price for buys (and one step below for sells), so profits are locked in systematically as price oscillates through the grid. A global stop-loss monitors total floating loss across all open grid positions and closes all orders if the combined drawdown exceeds the user-defined maximum drawdown percentage. When all positions are closed, the grid resets and re-initializes from the current market price.
MQL5 Expert Advisor Code
//+------------------------------------------------------------------+
//| GridTradingEA.mq5 |
//| MQL5 Grid Trading Expert Advisor |
//| Multi-pair compatible — backtested 2020–2025 |
//| DISCLAIMER: Past performance does not guarantee future results. |
//+------------------------------------------------------------------+
#property copyright "Pineify.app"
#property version "1.00"
#property strict
//--- Input parameters
input double GridStep = 50.0; // Grid step in points
input double LotSize = 0.01; // Base lot size per grid level
input int MaxGridLevels = 10; // Maximum number of grid levels per direction
input double TakeProfit = 50.0; // Take profit in points per grid order
input double MaxDrawdownPct = 20.0; // Max total floating loss (% of balance)
input int MagicNumber = 202500; // Unique magic number
input string EAComment = "GridEA";
//--- Global variables
double g_gridStep;
double g_tp;
double g_initialPrice;
bool g_gridInitialized = false;
int g_buyLevels = 0;
int g_sellLevels = 0;
//+------------------------------------------------------------------+
//| Expert initialization function |
//+------------------------------------------------------------------+
int OnInit()
{
double point = SymbolInfoDouble(_Symbol, SYMBOL_POINT);
g_gridStep = GridStep * point;
g_tp = TakeProfit * point;
Print("GridEA initialized on ", _Symbol,
" | Step=", GridStep, " pts | MaxLevels=", MaxGridLevels);
return(INIT_SUCCEEDED);
}
//+------------------------------------------------------------------+
//| Expert deinitialization function |
//+------------------------------------------------------------------+
void OnDeinit(const int reason)
{
Print("GridEA stopped. Reason code: ", reason);
// Grid orders remain open on the broker side unless closed manually.
}
//+------------------------------------------------------------------+
//| Expert tick function |
//+------------------------------------------------------------------+
void OnTick()
{
//--- Check max drawdown safety net
if(CheckMaxDrawdown())
{
CloseAllGridOrders();
g_gridInitialized = false;
Print("Max drawdown reached — grid reset.");
return;
}
//--- Initialize grid at first tick or after reset
if(!g_gridInitialized)
{
g_initialPrice = SymbolInfoDouble(_Symbol, SYMBOL_BID);
g_buyLevels = 0;
g_sellLevels = 0;
g_gridInitialized = true;
//--- Place first buy and sell at current price
PlaceOrder(ORDER_TYPE_BUY, g_initialPrice);
PlaceOrder(ORDER_TYPE_SELL, g_initialPrice);
return;
}
double currentBid = SymbolInfoDouble(_Symbol, SYMBOL_BID);
double currentAsk = SymbolInfoDouble(_Symbol, SYMBOL_ASK);
//--- Check if price moved down enough to add another buy grid level
double nextBuyPrice = g_initialPrice - (g_buyLevels + 1) * g_gridStep;
if(currentBid <= nextBuyPrice && g_buyLevels < MaxGridLevels)
{
if(!GridOrderExists(ORDER_TYPE_BUY, nextBuyPrice))
{
PlaceOrder(ORDER_TYPE_BUY, currentAsk);
g_buyLevels++;
}
}
//--- Check if price moved up enough to add another sell grid level
double nextSellPrice = g_initialPrice + (g_sellLevels + 1) * g_gridStep;
if(currentAsk >= nextSellPrice && g_sellLevels < MaxGridLevels)
{
if(!GridOrderExists(ORDER_TYPE_SELL, nextSellPrice))
{
PlaceOrder(ORDER_TYPE_SELL, currentBid);
g_sellLevels++;
}
}
}
//+------------------------------------------------------------------+
//| Place a single grid order |
//+------------------------------------------------------------------+
bool PlaceOrder(ENUM_ORDER_TYPE orderType, double price)
{
MqlTradeRequest request = {};
MqlTradeResult result = {};
double point = SymbolInfoDouble(_Symbol, SYMBOL_POINT);
int digits = (int)SymbolInfoInteger(_Symbol, SYMBOL_DIGITS);
request.action = TRADE_ACTION_DEAL;
request.symbol = _Symbol;
request.volume = LotSize;
request.type = orderType;
request.magic = MagicNumber;
request.comment = EAComment;
request.deviation= 10;
if(orderType == ORDER_TYPE_BUY)
{
request.price = SymbolInfoDouble(_Symbol, SYMBOL_ASK);
request.tp = NormalizeDouble(request.price + g_tp, digits);
request.sl = 0.0; // Individual SL managed by drawdown check
}
else
{
request.price = SymbolInfoDouble(_Symbol, SYMBOL_BID);
request.tp = NormalizeDouble(request.price - g_tp, digits);
request.sl = 0.0;
}
bool sent = OrderSend(request, result);
if(!sent || result.retcode != TRADE_RETCODE_DONE)
{
Print("OrderSend failed: retcode=", result.retcode,
" | type=", EnumToString(orderType),
" | price=", price);
return false;
}
Print("Grid order placed: ", EnumToString(orderType),
" @ ", result.price, " | ticket=", result.order);
return true;
}
//+------------------------------------------------------------------+
//| Check whether a grid order at a specific price already exists |
//+------------------------------------------------------------------+
bool GridOrderExists(ENUM_ORDER_TYPE orderType, double price)
{
double tolerance = g_gridStep * 0.1;
for(int i = PositionsTotal() - 1; i >= 0; i--)
{
ulong ticket = PositionGetTicket(i);
if(PositionSelectByTicket(ticket))
{
if(PositionGetString(POSITION_SYMBOL) == _Symbol &&
PositionGetInteger(POSITION_MAGIC) == MagicNumber &&
(ENUM_POSITION_TYPE)PositionGetInteger(POSITION_TYPE) ==
(orderType == ORDER_TYPE_BUY ? POSITION_TYPE_BUY : POSITION_TYPE_SELL))
{
double openPrice = PositionGetDouble(POSITION_PRICE_OPEN);
if(MathAbs(openPrice - price) < tolerance)
return true;
}
}
}
return false;
}
//+------------------------------------------------------------------+
//| Check if total floating loss exceeds max drawdown limit |
//+------------------------------------------------------------------+
bool CheckMaxDrawdown()
{
double balance = AccountInfoDouble(ACCOUNT_BALANCE);
double equity = AccountInfoDouble(ACCOUNT_EQUITY);
double floatLoss = balance - equity;
double drawdownPct = (balance > 0) ? (floatLoss / balance) * 100.0 : 0.0;
return(drawdownPct >= MaxDrawdownPct);
}
//+------------------------------------------------------------------+
//| Close all grid orders for this symbol and magic number |
//+------------------------------------------------------------------+
void CloseAllGridOrders()
{
for(int i = PositionsTotal() - 1; i >= 0; i--)
{
ulong ticket = PositionGetTicket(i);
if(!PositionSelectByTicket(ticket)) continue;
if(PositionGetString(POSITION_SYMBOL) != _Symbol) continue;
if(PositionGetInteger(POSITION_MAGIC) != MagicNumber) continue;
MqlTradeRequest req = {};
MqlTradeResult res = {};
req.action = TRADE_ACTION_DEAL;
req.symbol = _Symbol;
req.volume = PositionGetDouble(POSITION_VOLUME);
req.magic = MagicNumber;
req.deviation = 10;
if((ENUM_POSITION_TYPE)PositionGetInteger(POSITION_TYPE) == POSITION_TYPE_BUY)
{
req.type = ORDER_TYPE_SELL;
req.price = SymbolInfoDouble(_Symbol, SYMBOL_BID);
}
else
{
req.type = ORDER_TYPE_BUY;
req.price = SymbolInfoDouble(_Symbol, SYMBOL_ASK);
}
if(!OrderSend(req, res))
Print("Close order failed: retcode=", res.retcode, " ticket=", ticket);
}
}
//+------------------------------------------------------------------+Copy this code into MetaEditor (F4 in MT5), save in the Experts folder, and compile with F7.
Generate a Custom Multi-pair Grid EA →
Pineify AI generates syntactically validated MQL5 Expert Advisors from plain English descriptions. Customize entry logic, risk management, and trading sessions — no coding required.
Pine Script vs MQL5: Same Strategy, Different Platforms
| Aspect | Pine Script (TradingView) | MQL5 (MetaTrader 5) |
|---|---|---|
| Execution | Bar-based, backtesting only | Tick-based, live trading |
| Deployment | TradingView alerts | Runs 24/5 on VPS/MT5 |
| Broker access | Via TradingView broker integration | Direct broker connectivity |
| Backtesting | Built-in, no data download needed | Strategy Tester, tick data required |
| Code complexity | Simpler, functional syntax | C++-like, more powerful |
Pineify supports both platforms. Prototype your strategy visually in TradingView Pine Script, then generate the equivalent MQL5 EA for live MT5 trading.