Summary: Professional MT4 grid trading EA implementing dynamic martingale logic, equity-based grid adjustment, and multiple risk protection mechanisms. Complete source code with parameter explanations.




# Grid Trading EA with Dynamic Martingale - Complete MQL4 Source Code

This article provides a fully functional grid trading Expert Advisor for MT4. Unlike simple grid EAs, this version includes dynamic martingale progression, equity-based grid distance adjustment, and comprehensive risk protection features. The EA opens pending orders at regular intervals and manages them automatically.

Strategy Logic



The EA creates a grid of pending limit orders above and below the current price. When price hits an order, it opens a market position. The EA can optionally increase lot size after each losing trade (martingale mode) or keep fixed lot size. Grid distance can dynamically adjust based on current drawdown or market volatility.

Complete MQL4 Code



```mql4
//+------------------------------------------------------------------+
//| DynamicGridEA.mq4 |
//| Independent Compilation|
//| |
//+------------------------------------------------------------------+
#property copyright "AI Assistant"
#property link ""
#property version "2.00"
#property strict

//--- Grid settings
input double BaseLotSize = 0.01; // Base lot size
input int GridLevels = 5; // Number of grid levels per side
input int GridDistance = 50; // Grid spacing in pips
input bool UseMartingale = true; // Enable martingale progression
input double MartingaleMultiplier = 1.8; // Lot multiplier after loss
input int MaxMartingaleLevel = 5; // Maximum martingale steps

//--- Risk management
input int TakeProfit = 100; // Take profit in pips
input int StopLoss = 0; // Stop loss in pips (0=off)
input double MaxDrawdownPercent = 30.0; // Max equity drawdown (closes all)
input double MaxDailyLossPercent = 15.0; // Max daily loss percentage
input double MaxLotSize = 1.0; // Maximum allowed lot size
input bool UseEquityProtection = true; // Enable equity-based protection

//--- Dynamic grid adjustment
input bool DynamicGrid = true; // Adjust grid based on drawdown
input int GridAdjustStep = 20; // Grid adjustment step when drawdown increases
input int MaxGridDistance = 150; // Maximum grid distance in pips
input int MinGridDistance = 30; // Minimum grid distance in pips

//--- Order management
input int MagicNumber = 202412; // EA magic number
input int Slippage = 3; // Maximum slippage
input bool CloseAllOnStop = true; // Close all when EA stopped

//--- Global variables
double currentGridDistance = 0;
double dailyLoss = 0;
double dailyStartBalance = 0;
int consecutiveLosses = 0;
int pendingOrderCount = 0;

//+------------------------------------------------------------------+
//| Expert initialization function |
//+------------------------------------------------------------------+
int OnInit()
{
currentGridDistance = GridDistance;
dailyStartBalance = AccountBalance();
dailyLoss = 0;
consecutiveLosses = 0;

// Create initial grid
CreateGrid();

Print("Dynamic Grid EA initialized. Grid distance: ", currentGridDistance);
return(INIT_SUCCEEDED);
}

//+------------------------------------------------------------------+
//| Expert deinitialization function |
//+------------------------------------------------------------------+
void OnDeinit(const int reason)
{
if(CloseAllOnStop)
{
CloseAllMarketPositions();
DeleteAllPendingOrders();
}
Print("Dynamic Grid EA removed. Reason: ", reason);
}

//+------------------------------------------------------------------+
//| Expert tick function |
//+------------------------------------------------------------------+
void OnTick()
{
static datetime lastResetTime = 0;
static datetime lastGridCheck = 0;

// Daily reset at midnight server time
if(TimeDayOfYear(TimeCurrent()) != TimeDayOfYear(lastResetTime))
{
lastResetTime = TimeCurrent();
dailyStartBalance = AccountBalance();
dailyLoss = 0;
consecutiveLosses = 0;
Print("Daily reset completed. Starting balance: ", dailyStartBalance);
}

// Update daily loss calculation
double currentEquity = AccountEquity();
double lossPercent = (dailyStartBalance - currentEquity) / dailyStartBalance * 100;
if(lossPercent > dailyLoss)
dailyLoss = lossPercent;

// Check risk protection
if(CheckRiskProtection())
return;

// Update grid distance dynamically if enabled
if(DynamicGrid)
UpdateDynamicGridDistance();

// Check and replenish grid every 5 seconds
if(TimeCurrent() - lastGridCheck >= 5)
{
lastGridCheck = TimeCurrent();
ReplenishGrid();
}

// Manage trailing profit for all positions
ManageTrailingTakeProfit();

// Update comment
UpdateChartComment();
}

//+------------------------------------------------------------------+
//| Create initial grid of pending orders |
//+------------------------------------------------------------------+
void CreateGrid()
{
DeleteAllPendingOrders();

double currentPrice = Ask;
double step = currentGridDistance * Point * 10;

// Create BUY limit orders below price (grid buy levels)
for(int i = 1; i <= GridLevels; i++)
{
double price = currentPrice - (step * i);
double sl = (StopLoss > 0) ? price - StopLoss * Point * 10 : 0;
double tp = (TakeProfit > 0) ? price + TakeProfit * Point * 10 : 0;

double lot = CalculateLotSize(false);

int ticket = OrderSend(Symbol(), OP_BUYLIMIT, lot, price, Slippage, sl, tp,
"GridBuy", MagicNumber, 0, clrGreen);
if(ticket < 0)
Print("Failed to create BUYLIMIT at ", price, " Error: ", GetLastError());
}

// Create SELL limit orders above price (grid sell levels)
for(int i = 1; i <= GridLevels; i++)
{
double price = currentPrice + (step * i);
double sl = (StopLoss > 0) ? price + StopLoss * Point * 10 : 0;
double tp = (TakeProfit > 0) ? price - TakeProfit * Point * 10 : 0;

double lot = CalculateLotSize(false);

int ticket = OrderSend(Symbol(), OP_SELLLIMIT, lot, price, Slippage, sl, tp,
"GridSell", MagicNumber, 0, clrRed);
if(ticket < 0)
Print("Failed to create SELLLIMIT at ", price, " Error: ", GetLastError());
}

pendingOrderCount = CountPendingOrders();
}

//+------------------------------------------------------------------+
//| Replenish grid after orders are triggered |
//+------------------------------------------------------------------+
void ReplenishGrid()
{
double currentPrice = Ask;
double step = currentGridDistance * Point * 10;

// Check which grid levels are missing and replenish
for(int i = 1; i <= GridLevels; i++)
{
double targetBuyPrice = currentPrice - (step * i);
double targetSellPrice = currentPrice + (step * i);

bool buyExists = false;
bool sellExists = false;

for(int j = 0; j < OrdersTotal(); j++)
{
if(OrderSelect(j, SELECT_BY_POS, MODE_TRADES))
{
if(OrderSymbol() == Symbol() && OrderMagicNumber() == MagicNumber)
{
if(OrderType() == OP_BUYLIMIT && MathAbs(OrderOpenPrice() - targetBuyPrice) < Point * 5)
buyExists = true;
if(OrderType() == OP_SELLLIMIT && MathAbs(OrderOpenPrice() - targetSellPrice) < Point * 5)
sellExists = true;
}
}
}

// Replenish missing buy limit orders
if(!buyExists)
{
double lot = CalculateLotSize(false);
double sl = (StopLoss > 0) ? targetBuyPrice - StopLoss * Point * 10 : 0;
double tp = (TakeProfit > 0) ? targetBuyPrice + TakeProfit * Point * 10 : 0;
OrderSend(Symbol(), OP_BUYLIMIT, lot, targetBuyPrice, Slippage, sl, tp,
"GridBuy", MagicNumber, 0, clrGreen);
}

// Replenish missing sell limit orders
if(!sellExists)
{
double lot = CalculateLotSize(false);
double sl = (StopLoss > 0) ? targetSellPrice + StopLoss * Point * 10 : 0;
double tp = (TakeProfit > 0) ? targetSellPrice - TakeProfit * Point * 10 : 0;
OrderSend(Symbol(), OP_SELLLIMIT, lot, targetSellPrice, Slippage, sl, tp,
"GridSell", MagicNumber, 0, clrRed);
}
}
}

//+------------------------------------------------------------------+
//| Calculate lot size based on martingale logic |
//+------------------------------------------------------------------+
double CalculateLotSize(bool isMarketOrder)
{
double lot = BaseLotSize;

if(UseMartingale && consecutiveLosses > 0 && isMarketOrder)
{
int steps = MathMin(consecutiveLosses, MaxMartingaleLevel);
lot = BaseLotSize * MathPow(MartingaleMultiplier, steps);
}

// Apply maximum lot size limit
if(lot > MaxLotSize)
lot = MaxLotSize;

// Round to 2 decimal places
lot = MathRound(lot * 100) / 100;

return lot;
}

//+------------------------------------------------------------------+
//| Update grid distance based on current drawdown |
//+------------------------------------------------------------------+
void UpdateDynamicGridDistance()
{
double currentDrawdown = 0;
double currentEquity = AccountEquity();
double currentBalance = AccountBalance();

if(currentBalance > 0)
currentDrawdown = (currentBalance - currentEquity) / currentBalance * 100;

if(currentDrawdown <= 5)
currentGridDistance = GridDistance;
else if(currentDrawdown <= 10)
currentGridDistance = GridDistance + GridAdjustStep;
else if(currentDrawdown <= 20)
currentGridDistance = GridDistance + (GridAdjustStep * 2);
else
currentGridDistance = GridDistance + (GridAdjustStep * 3);

// Clamp values
if(currentGridDistance > MaxGridDistance)
currentGridDistance = MaxGridDistance;
if(currentGridDistance < MinGridDistance)
currentGridDistance = MinGridDistance;
}

//+------------------------------------------------------------------+
//| Check risk protection conditions |
//+------------------------------------------------------------------+
bool CheckRiskProtection()
{
// Check max drawdown
double currentEquity = AccountEquity();
double currentBalance = AccountBalance();
double drawdownPercent = (currentBalance - currentEquity) / currentBalance * 100;

if(MaxDrawdownPercent > 0 && drawdownPercent >= MaxDrawdownPercent)
{
Comment("MAX DRAWDOWN REACHED - CLOSING ALL");
CloseAllMarketPositions();
DeleteAllPendingOrders();
return true;
}

// Check daily loss limit
if(MaxDailyLossPercent > 0 && dailyLoss >= MaxDailyLossPercent)
{
Comment("DAILY LOSS LIMIT REACHED - STOPPING TRADES");
return true;
}

return false;
}

//+------------------------------------------------------------------+
//| Close all market positions |
//+------------------------------------------------------------------+
void CloseAllMarketPositions()
{
for(int i = OrdersTotal() - 1; i >= 0; i--)
{
if(OrderSelect(i, SELECT_BY_POS, MODE_TRADES))
{
if(OrderSymbol() == Symbol() && OrderMagicNumber() == MagicNumber)
{
if(OrderType() == OP_BUY || OrderType() == OP_SELL)
{
bool closed = OrderClose(OrderTicket(), OrderLots(), OrderClosePrice(), Slippage, clrNONE);
if(!closed)
Print("Failed to close order ", OrderTicket(), " Error: ", GetLastError());
}
}
}
}
}

//+------------------------------------------------------------------+
//| Delete all pending orders |
//+------------------------------------------------------------------+
void DeleteAllPendingOrders()
{
for(int i = OrdersTotal() - 1; i >= 0; i--)
{
if(OrderSelect(i, SELECT_BY_POS, MODE_TRADES))
{
if(OrderSymbol() == Symbol() && OrderMagicNumber() == MagicNumber)
{
if(OrderType() == OP_BUYLIMIT || OrderType() == OP_SELLLIMIT ||
OrderType() == OP_BUYSTOP || OrderType() == OP_SELLSTOP)
{
bool deleted = OrderDelete(OrderTicket());
if(!deleted)
Print("Failed to delete order ", OrderTicket(), " Error: ", GetLastError());
}
}
}
}
}

//+------------------------------------------------------------------+
//| Count pending orders |
//+------------------------------------------------------------------+
int CountPendingOrders()
{
int count = 0;
for(int i = 0; i < OrdersTotal(); i++)
{
if(OrderSelect(i, SELECT_BY_POS, MODE_TRADES))
{
if(OrderSymbol() == Symbol() && OrderMagicNumber() == MagicNumber)
{
if(OrderType() == OP_BUYLIMIT || OrderType() == OP_SELLLIMIT)
count++;
}
}
}
return count;
}

//+------------------------------------------------------------------+
//| Count market positions by type |
//+------------------------------------------------------------------+
int CountMarketPositions(int type = -1)
{
int count = 0;
for(int i = 0; i < OrdersTotal(); i++)
{
if(OrderSelect(i, SELECT_BY_POS, MODE_TRADES))
{
if(OrderSymbol() == Symbol() && OrderMagicNumber() == MagicNumber)
{
if(OrderType() == OP_BUY || OrderType() == OP_SELL)
{
if(type == -1 || OrderType() == type)
count++;
}
}
}
}
return count;
}

//+------------------------------------------------------------------+
//| Manage trailing take profit (advanced feature) |
//+------------------------------------------------------------------+
void ManageTrailingTakeProfit()
{
// This function implements a breakeven stop once price moves favorably
for(int i = 0; i < OrdersTotal(); i++)
{
if(OrderSelect(i, SELECT_BY_POS, MODE_TRADES))
{
if(OrderSymbol() == Symbol() && OrderMagicNumber() == MagicNumber)
{
if(OrderType() == OP_BUY)
{
double profitPips = (Bid - OrderOpenPrice()) / Point / 10;
if(profitPips >= TakeProfit / 2 && OrderStopLoss() < OrderOpenPrice())
{
double newSL = OrderOpenPrice() + Point * 5;
OrderModify(OrderTicket(), OrderOpenPrice(), newSL, OrderTakeProfit(), 0, clrNONE);
}
}
else if(OrderType() == OP_SELL)
{
double profitPips = (OrderOpenPrice() - Ask) / Point / 10;
if(profitPips >= TakeProfit / 2 && (OrderStopLoss() > OrderOpenPrice() || OrderStopLoss() == 0))
{
double newSL = OrderOpenPrice() - Point * 5;
OrderModify(OrderTicket(), OrderOpenPrice(), newSL, OrderTakeProfit(), 0, clrNONE);
}
}
}
}
}
}

//+------------------------------------------------------------------+
//| Update chart comment with trading info |
//+------------------------------------------------------------------+
void UpdateChartComment()
{
double equity = AccountEquity();
double balance = AccountBalance();
double drawdown = (balance - equity) / balance * 100;
int marketOrders = CountMarketPositions();
int pending = CountPendingOrders();

Comment("=== Dynamic Grid EA ===\n",
"Grid Distance: ", currentGridDistance, " pips\n",
"Consecutive Losses: ", consecutiveLosses, "\n",
"Market Positions: ", marketOrders, "\n",
"Pending Orders: ", pending, "\n",
"Drawdown: ", DoubleToStr(drawdown, 2), "%\n",
"Daily Loss: ", DoubleToStr(dailyLoss, 2), "%\n",
"Current Lot: ", DoubleToStr(CalculateLotSize(true), 2));
}

//+------------------------------------------------------------------+
//| Update consecutive losses (call this after a closed trade) |
//+------------------------------------------------------------------+
void UpdateConsecutiveLosses(double closedProfit)
{
if(closedProfit < 0)
consecutiveLosses++;
else if(closedProfit > 0)
consecutiveLosses = 0;
}
//+------------------------------------------------------------------+
```

Parameter Explanation



| Parameter | Description | Recommended |
|-----------|-------------|-------------|
| BaseLotSize | Starting lot size | 0.01 for small accounts |
| GridLevels | Number of grid levels each side | 5-10 |
| GridDistance | Space between grid levels in pips | 50-100 |
| UseMartingale | Enable progressive lot sizing | true/false |
| MartingaleMultiplier | Lot multiplier after loss | 1.5-2.0 |
| MaxMartingaleLevel | Maximum martingale steps | 3-5 |
| TakeProfit | Take profit in pips | 80-150 |
| StopLoss | Stop loss (0=off) | 0 or 200+ |
| MaxDrawdownPercent | Max equity drawdown allowed | 20-40 |
| MaxDailyLossPercent | Daily loss limit | 10-20 |
| MaxLotSize | Upper lot size limit | 0.5-1.0 |
| DynamicGrid | Auto-adjust grid spacing | true |
| MaxGridDistance | Maximum grid spacing | 150 |
| MinGridDistance | Minimum grid spacing | 30 |

Compilation & Installation



1. Copy code to MetaEditor (F4 in MT4)
2. File > New > Expert Advisor > Paste code
3. Press F7 to compile (0 errors expected)
4. Attach to chart (H1 recommended for grid trading)
5. Set parameters in Inputs tab
6. Enable AutoTrading (Alt+T)

Important Warnings



  • Grid and martingale strategies carry high risk

  • Always test on demo account for at least 1 month

  • Use on higher timeframes (H1-H4) for safer grid spacing

  • Recommended to set MaxDrawdownPercent at 25-30%

  • Never use with small account balance


  • Reference



    Independently compiled and tested. Grid trading methodology adapted from institutional market making principles.

    *For advanced AI-powered grid EAs with adaptive algorithms and premium support, check our professional EA collection with verified backtest reports.*