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:
  • Grid Setup: Places buy stop and sell stop orders at grid distance intervals from current price.

  • Dynamic Distance: Grid distance = BaseDistance <em> (ATR / ReferenceATR), adapting to volatility.

  • Position Management: Maximum grid levels (pairs) limited to MaxGridLevels per side.

  • 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.