Summary: EURUSD Grid Stabilizer EA is an MQL4 expert advisor for EURUSD using a controlled grid strategy with dynamic distance, max position limits, and daily loss protection.
EURUSD Grid Stabilizer EA implements a classic grid trading strategy optimized for the stable, range-bound behavior of EURUSD. The EA places buy and sell limit orders at fixed intervals around the current price. Unlike aggressive martingale systems, this EA uses fixed lot sizes and limits the maximum number of grid levels to control drawdown. A dynamic grid distance based on ATR volatility adapts to market conditions. The EA includes equity-based drawdown protection and a take-profit target that closes the entire grid when overall profit reaches a set threshold.
Recommended Timeframe: M30
Trading Logic:
1. Grid Setup: Places buy stop and sell stop orders at grid distance intervals from current price.
2. Dynamic Distance: Grid distance = BaseDistance * (ATR / ReferenceATR), adapting to volatility.
3. Position Management: Maximum grid levels (pairs) limited to MaxGridLevels per side.
4. Exit Strategy: Close entire grid when total floating profit reaches TakeProfitTarget (in money or pips).
```mql4
//+------------------------------------------------------------------+
//| EURUSDGridStabilizerEA.mq4 |
//+------------------------------------------------------------------+
#property copyright ""
#property link ""
#property version "1.00"
#property strict
//--- input parameters with comments
input double LotSize = 0.01; // Fixed lot size for each grid order
input int BaseGridDistance = 25; // Base grid distance in points (25 points = 2.5 pips)
input int MaxGridLevels = 5; // Maximum grid levels per side (buy/sell)
input double TakeProfitTarget = 15.0; // Take profit target in currency units ($15)
input bool UseDynamicDistance = true; // Enable ATR-based dynamic grid distance
input int ATRPeriod = 14; // ATR period for dynamic distance
input double ATRReference = 20.0; // Reference ATR value in points (baseline)
input int MaxSpread = 20; // Maximum allowed spread in points
input double DailyLossLimit = 8.0; // Daily loss limit as percentage of balance
input int MagicNumber = 202416; // Unique EA identifier
input bool UseFridayClose = true; // Close all orders before Friday 21:00 GMT
//--- global variables
double dailyStartBalance = 0;
datetime lastBarTime = 0;
bool fridayCloseExecuted = false;
double currentGridDistance = 0;
double referenceATRValue = 0;
//+------------------------------------------------------------------+
//| Expert initialization function |
//+------------------------------------------------------------------+
int OnInit()
{
dailyStartBalance = AccountBalance();
lastBarTime = 0;
fridayCloseExecuted = false;
referenceATRValue = iATR(Symbol(), PERIOD_M30, ATRPeriod, 1);
if(referenceATRValue <= 0) referenceATRValue = ATRReference * Point;
currentGridDistance = BaseGridDistance;
return(INIT_SUCCEEDED);
}
//+------------------------------------------------------------------+
//| Expert deinitialization function |
//+------------------------------------------------------------------+
void OnDeinit(const int reason)
{
Comment("");
}
//+------------------------------------------------------------------+
//| Expert tick function |
//+------------------------------------------------------------------+
void OnTick()
{
// Daily equity protection
double currentEquity = AccountEquity();
double lossPercent = (dailyStartBalance - currentEquity) / dailyStartBalance * 100;
if(lossPercent >= DailyLossLimit)
{
Comment("Daily loss limit reached. No new grid orders.");
return;
}
// Friday close before weekend
if(UseFridayClose && !fridayCloseExecuted)
{
datetime currentTime = TimeCurrent();
if(TimeDayOfWeek(currentTime) == 5 && TimeHour(currentTime) >= 21)
{
CloseAllOrders();
fridayCloseExecuted = true;
return;
}
if(TimeDayOfWeek(currentTime) != 5)
fridayCloseExecuted = false;
}
// Spread filter
if(MarketInfo(Symbol(), MODE_SPREAD) > MaxSpread)
{
Comment("Spread too high: ", MarketInfo(Symbol(), MODE_SPREAD));
return;
}
// Check total profit and close grid if target reached
double totalProfit = GetTotalProfit();
if(totalProfit >= TakeProfitTarget)
{
CloseAllOrders();
Comment("Take profit target reached. Grid closed.");
return;
}
// Update grid distance dynamically if enabled
if(UseDynamicDistance)
{
double currentATR = iATR(Symbol(), PERIOD_M30, ATRPeriod, 1);
if(currentATR > 0 && referenceATRValue > 0)
{
double atrRatio = currentATR / referenceATRValue;
atrRatio = MathMax(0.5, MathMin(2.0, atrRatio));
currentGridDistance = BaseGridDistance * atrRatio;
}
else
{
currentGridDistance = BaseGridDistance;
}
}
else
{
currentGridDistance = BaseGridDistance;
}
// Manage grid orders: delete old and place new
ManageGridOrders();
}
//+------------------------------------------------------------------+
//| Manage grid orders: remove old, place new based on current price|
//+------------------------------------------------------------------+
void ManageGridOrders()
{
double currentPrice = Bid;
double point = Point;
double distance = currentGridDistance * point;
// Count current buy and sell orders
int buyCount = 0, sellCount = 0;
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_BUYLIMIT || OrderType() == OP_BUYSTOP)
buyCount++;
if(OrderType() == OP_SELL || OrderType() == OP_SELLLIMIT || OrderType() == OP_SELLSTOP)
sellCount++;
}
}
}
// Remove existing pending orders if they don't match current grid structure
for(int i = OrdersTotal()-1; i >= 0; i--)
{
if(OrderSelect(i, SELECT_BY_POS, MODE_TRADES))
{
if(OrderSymbol() == Symbol() && OrderMagicNumber() == MagicNumber && OrderType() > 1)
{
bool shouldRemove = false;
if(OrderType() == OP_BUYSTOP || OrderType() == OP_BUYLIMIT)
{
double expectedPrice = currentPrice + (buyCount + 1) * distance;
if(MathAbs(OrderOpenPrice() - expectedPrice) > point * 5)
shouldRemove = true;
}
if(OrderType() == OP_SELLSTOP || OrderType() == OP_SELLLIMIT)
{
double expectedPrice = currentPrice - (sellCount + 1) * distance;
if(MathAbs(OrderOpenPrice() - expectedPrice) > point * 5)
shouldRemove = true;
}
if(shouldRemove)
OrderDelete(OrderTicket());
}
}
}
// Place new buy stop orders (above price)
for(int level = 1; level <= MaxGridLevels; level++)
{
double buyPrice = currentPrice + (level * distance);
bool exists = false;
for(int i = OrdersTotal()-1; i >= 0; i--)
{
if(OrderSelect(i, SELECT_BY_POS, MODE_TRADES))
{
if(OrderSymbol() == Symbol() && OrderMagicNumber() == MagicNumber)
{
if((OrderType() == OP_BUYSTOP || OrderType() == OP_BUY) && MathAbs(OrderOpenPrice() - buyPrice) < point * 10)
{
exists = true;
break;
}
}
}
}
if(!exists)
{
double sl = buyPrice - (distance * 1.5);
double tp = buyPrice + (distance * 3.0);
int ticket = OrderSend(Symbol(), OP_BUYSTOP, LotSize, buyPrice, 3, sl, tp, "Grid Buy", MagicNumber, 0, clrNONE);
if(ticket < 0)
Print("Buy stop order failed at ", buyPrice, " Error: ", GetLastError());
}
}
// Place new sell stop orders (below price)
for(int level = 1; level <= MaxGridLevels; level++)
{
double sellPrice = currentPrice - (level * distance);
bool exists = false;
for(int i = OrdersTotal()-1; i >= 0; i--)
{
if(OrderSelect(i, SELECT_BY_POS, MODE_TRADES))
{
if(OrderSymbol() == Symbol() && OrderMagicNumber() == MagicNumber)
{
if((OrderType() == OP_SELLSTOP || OrderType() == OP_SELL) && MathAbs(OrderOpenPrice() - sellPrice) < point * 10)
{
exists = true;
break;
}
}
}
}
if(!exists)
{
double sl = sellPrice + (distance * 1.5);
double tp = sellPrice - (distance * 3.0);
int ticket = OrderSend(Symbol(), OP_SELLSTOP, LotSize, sellPrice, 3, sl, tp, "Grid Sell", MagicNumber, 0, clrNONE);
if(ticket < 0)
Print("Sell stop order failed at ", sellPrice, " Error: ", GetLastError());
}
}
}
//+------------------------------------------------------------------+
//| Calculate total profit of all open positions |
//+------------------------------------------------------------------+
double GetTotalProfit()
{
double profit = 0;
for(int i = OrdersTotal()-1; i >= 0; i--)
{
if(OrderSelect(i, SELECT_BY_POS, MODE_TRADES))
{
if(OrderSymbol() == Symbol() && OrderMagicNumber() == MagicNumber)
{
profit += OrderProfit() + OrderSwap() + OrderCommission();
}
}
}
return profit;
}
//+------------------------------------------------------------------+
//| Close all orders (market and pending) for this EA |
//+------------------------------------------------------------------+
void CloseAllOrders()
{
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)
OrderClose(OrderTicket(), OrderLots(), Bid, 3, clrNONE);
else if(OrderType() == OP_SELL)
OrderClose(OrderTicket(), OrderLots(), Ask, 3, clrNONE);
else if(OrderType() > 1)
OrderDelete(OrderTicket());
}
}
}
}
//+------------------------------------------------------------------+
```
Reference: Original MQL4 code for educational purposes.
Disclaimer: Grid trading can generate frequent small profits but may lead to significant drawdown during strong trends. This EA is provided as-is without any guarantee of profit. Test thoroughly on demo before live trading. Past performance does not guarantee future results.