Summary: Gold Breakout Engine EA is an MQL4 expert advisor for XAUUSD that identifies volatility compression breakouts with Order Block trend confirmation and ATR-based dynamic grid recovery. Suitable for M15 timeframe.




Gold Breakout Engine EA is designed specifically for XAUUSD, combining three core modules: volatility breakout detection, Order Block trend filtering, and ATR-based grid recovery. The EA first identifies volatility compression patterns (narrowing ranges), then confirms the breakout direction using a simplified Order Block concept (last swing high/low structure), and finally manages trade series with an ATR-adaptive grid that closes all positions when profit targets are met. This multi-layer filtering reduces false breakouts while the controlled grid structure helps recover drawdowns without infinite risk.

Recommended Timeframe: M15
Trading Logic:
  • Volatility Compression: Detect when current ATR(14) drops below 0.65x average ATR of last 20 bars.

  • Breakout Direction: When price closes above the highest high of compression period → potential buy; below lowest low → potential sell.

  • Order Block Filter: Confirm that the last swing structure supports the breakout direction (price has broken a previous key level).

  • Grid Execution: First trade with fixed stop loss (ATR × 1.2), then add grid levels at ATR-distance intervals with 1.5x lot multiplier (max 3 levels).

  • Exit: Close all positions when total floating profit reaches 1% of account balance or individual TP hits.


  • ``mql4
    //+------------------------------------------------------------------+
    //| GoldBreakoutEngineEA.mq4 |
    //+------------------------------------------------------------------+
    #property copyright ""
    #property link ""
    #property version "1.00"
    #property strict

    //--- input parameters with comments
    input double BaseLotSize = 0.01; // Base lot size for first trade
    input int ATRPeriod = 14; // ATR period for volatility detection
    input double CompressionRatio = 0.65; // Volatility compression ratio (current ATR / avg ATR)
    input int LookbackBars = 20; // Lookback bars for compression detection
    input int OBLookback = 10; // Order Block lookback (swing detection)
    input double GridDistanceATR = 1.2; // Grid distance as multiple of ATR
    input double GridLotMultiplier = 1.5; // Lot multiplier for each grid level
    input int MaxGridLevels = 3; // Maximum grid levels (0 = disable grid)
    input double TPPercent = 1.0; // Take profit percentage of balance (close all)
    input double StopLossATR = 1.2; // Stop loss as multiple of ATR
    input int MagicNumber = 202601; // Unique EA identifier
    input int MaxSpread = 35; // Maximum allowed spread in points
    input double DailyLossLimit = 5.0; // Daily loss limit as percentage
    input bool UseFridayClose = true; // Close trades before Friday 20:00 GMT

    //--- global variables
    double dailyStartBalance = 0;
    datetime lastBarTime = 0;
    bool fridayCloseExecuted = false;
    double compressionHigh = 0;
    double compressionLow = 0;
    bool compressionActive = false;
    datetime compressionStartTime = 0;

    //+------------------------------------------------------------------+
    //| Expert initialization function |
    //+------------------------------------------------------------------+
    int OnInit()
    {
    dailyStartBalance = AccountBalance();
    lastBarTime = 0;
    fridayCloseExecuted = false;
    compressionHigh = 0;
    compressionLow = 0;
    compressionActive = false;
    return(INIT_SUCCEEDED);
    }

    //+------------------------------------------------------------------+
    //| Expert deinitialization function |
    //+------------------------------------------------------------------+
    void OnDeinit(const int reason)
    {
    Comment("");
    }

    //+------------------------------------------------------------------+
    //| Calculate average ATR over lookback period |
    //+------------------------------------------------------------------+
    double GetAvgATR()
    {
    double sum = 0;
    for(int i = 1; i <= LookbackBars; i++)
    sum += iATR(Symbol(), PERIOD_M15, ATRPeriod, i);
    return sum / LookbackBars;
    }

    //+------------------------------------------------------------------+
    //| Detect volatility compression pattern |
    //+------------------------------------------------------------------+
    bool DetectCompression()
    {
    double currentATR = iATR(Symbol(), PERIOD_M15, ATRPeriod, 1);
    double avgATR = GetAvgATR();

    if(avgATR <= 0) return false;

    bool isCompressed = (currentATR < avgATR CompressionRatio);

    if(isCompressed && !compressionActive)
    {
    // Start new compression tracking
    compressionActive = true;
    compressionStartTime = Time[0];

    // Find highest high and lowest low during compression
    compressionHigh = iHigh(Symbol(), PERIOD_M15, iHighest(Symbol(), PERIOD_M15, MODE_HIGH, 5, 1));
    compressionLow = iLow(Symbol(), PERIOD_M15, iLowest(Symbol(), PERIOD_M15, MODE_LOW, 5, 1));
    }
    else if(!isCompressed && compressionActive)
    {
    // Compression ended, check breakout
    compressionActive = false;

    double close1 = iClose(Symbol(), PERIOD_M15, 1);
    double high1 = iHigh(Symbol(), PERIOD_M15, 1);
    double low1 = iLow(Symbol(), PERIOD_M15, 1);

    // Bullish breakout: close above compression high
    if(close1 > compressionHigh)
    return true;
    // Bearish breakout: close below compression low
    else if(close1 < compressionLow)
    return true;
    }

    return false;
    }

    //+------------------------------------------------------------------+
    //| Simplified Order Block trend check (swing structure) |
    //+------------------------------------------------------------------+
    int CheckOrderBlockTrend()
    {
    double highs[];
    double lows[];
    ArrayResize(highs, OBLookback);
    ArrayResize(lows, OBLookback);

    for(int i = 0; i < OBLookback; i++)
    {
    highs[i] = iHigh(Symbol(), PERIOD_M15, i);
    lows[i] = iLow(Symbol(), PERIOD_M15, i);
    }

    // Find recent swing high and low
    double recentHigh = highs[ArrayMaximum(highs, 0, OBLookback/2)];
    double recentLow = lows[ArrayMinimum(lows, 0, OBLookback/2)];
    double currentPrice = iClose(Symbol(), PERIOD_M15, 1);

    // Bullish: price broke above recent swing high
    if(currentPrice > recentHigh)
    return 1;
    // Bearish: price broke below recent swing low
    else if(currentPrice < recentLow)
    return -1;

    return 0;
    }

    //+------------------------------------------------------------------+
    //| Check if current position series exists |
    //+------------------------------------------------------------------+
    bool HasActiveSeries()
    {
    for(int i = OrdersTotal()-1; i >= 0; i--)
    {
    if(OrderSelect(i, SELECT_BY_POS, MODE_TRADES))
    {
    if(OrderSymbol() == Symbol() && OrderMagicNumber() == MagicNumber)
    return true;
    }
    }
    return false;
    }

    //+------------------------------------------------------------------+
    //| Count current grid levels (positions in series) |
    //+------------------------------------------------------------------+
    int CountGridLevels()
    {
    int count = 0;
    for(int i = OrdersTotal()-1; i >= 0; i--)
    {
    if(OrderSelect(i, SELECT_BY_POS, MODE_TRADES))
    {
    if(OrderSymbol() == Symbol() && OrderMagicNumber() == MagicNumber)
    count++;
    }
    }
    return count;
    }

    //+------------------------------------------------------------------+
    //| Get current direction of the position series (1=buy, -1=sell) |
    //+------------------------------------------------------------------+
    int GetSeriesDirection()
    {
    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) return 1;
    if(OrderType() == OP_SELL) return -1;
    }
    }
    }
    return 0;
    }

    //+------------------------------------------------------------------+
    //| Get last order price in the series |
    //+------------------------------------------------------------------+
    double GetLastOrderPrice()
    {
    datetime latestTime = 0;
    double latestPrice = 0;
    for(int i = OrdersTotal()-1; i >= 0; i--)
    {
    if(OrderSelect(i, SELECT_BY_POS, MODE_TRADES))
    {
    if(OrderSymbol() == Symbol() && OrderMagicNumber() == MagicNumber)
    {
    if(OrderOpenTime() > latestTime)
    {
    latestTime = OrderOpenTime();
    latestPrice = OrderOpenPrice();
    }
    }
    }
    }
    return latestPrice;
    }

    //+------------------------------------------------------------------+
    //| Calculate total floating profit in dollars |
    //+------------------------------------------------------------------+
    double GetTotalFloatingProfit()
    {
    double total = 0;
    for(int i = OrdersTotal()-1; i >= 0; i--)
    {
    if(OrderSelect(i, SELECT_BY_POS, MODE_TRADES))
    {
    if(OrderSymbol() == Symbol() && OrderMagicNumber() == MagicNumber)
    total += OrderProfit() + OrderSwap() + OrderCommission();
    }
    }
    return total;
    }

    //+------------------------------------------------------------------+
    //| Close all positions in the series |
    //+------------------------------------------------------------------+
    void CloseAllSeriesOrders()
    {
    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, 5, clrNONE);
    else if(OrderType() == OP_SELL)
    OrderClose(OrderTicket(), OrderLots(), Ask, 5, clrNONE);
    }
    }
    }
    }

    //+------------------------------------------------------------------+
    //| Add next grid level |
    //+------------------------------------------------------------------+
    void AddGridLevel(int direction, double distance)
    {
    int currentLevels = CountGridLevels();
    if(currentLevels >= MaxGridLevels) return;

    double lot = BaseLotSize
    MathPow(GridLotMultiplier, currentLevels);
    if(lot > 1.0) lot = 1.0;

    double atr = iATR(Symbol(), PERIOD_M15, ATRPeriod, 1);
    double slDistance = atr StopLossATR;

    int cmd = -1;
    double price = 0, sl = 0, tp = 0;

    if(direction == 1) // Buy series
    {
    cmd = OP_BUY;
    price = Ask;
    sl = price - slDistance;
    }
    else if(direction == -1) // Sell series
    {
    cmd = OP_SELL;
    price = Bid;
    sl = price + slDistance;
    }

    if(cmd != -1)
    {
    int ticket = OrderSend(Symbol(), cmd, lot, price, 5, sl, 0, "Grid Level", MagicNumber, 0, clrNONE);
    if(ticket < 0)
    Print("Grid level add failed: ", GetLastError());
    }
    }

    //+------------------------------------------------------------------+
    //| 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 trades.");
    return;
    }

    // Friday close before weekend
    if(UseFridayClose && !fridayCloseExecuted)
    {
    datetime currentTime = TimeCurrent();
    if(TimeDayOfWeek(currentTime) == 5 && TimeHour(currentTime) >= 20)
    {
    CloseAllSeriesOrders();
    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;
    }

    // New bar logic (M15)
    if(Time[0] == lastBarTime)
    return;
    lastBarTime = Time[0];

    // Check if we have an active position series
    bool hasSeries = HasActiveSeries();

    if(hasSeries)
    {
    // Manage existing series: check profit target
    double totalProfit = GetTotalFloatingProfit();
    double targetProfit = AccountBalance() TPPercent / 100;

    if(totalProfit >= targetProfit)
    {
    CloseAllSeriesOrders();
    Print("Profit target reached: ", totalProfit);
    return;
    }

    // Check if we need to add next grid level
    int currentLevels = CountGridLevels();
    if(currentLevels > 0 && currentLevels < MaxGridLevels)
    {
    int direction = GetSeriesDirection();
    double lastPrice = GetLastOrderPrice();
    double atr = iATR(Symbol(), PERIOD_M15, ATRPeriod, 1);
    double gridDistance = atr
    GridDistanceATR;
    double currentPrice = (direction == 1) ? Bid : Ask;
    double priceDiff = MathAbs(currentPrice - lastPrice);

    if(priceDiff >= gridDistance)
    {
    AddGridLevel(direction, gridDistance);
    }
    }
    }
    else
    {
    // No active series: look for new setup

    // Step 1: Detect breakout from volatility compression
    bool breakoutDetected = DetectCompression();
    if(!breakoutDetected)
    {
    Comment("No breakout detected");
    return;
    }

    // Step 2: Confirm Order Block trend direction
    int obTrend = CheckOrderBlockTrend();
    if(obTrend == 0)
    {
    Comment("No clear Order Block direction");
    return;
    }

    // Step 3: Determine breakout direction from compression
    double close1 = iClose(Symbol(), PERIOD_M15, 1);
    int breakoutDir = 0;
    if(close1 > compressionHigh) breakoutDir = 1;
    else if(close1 < compressionLow) breakoutDir = -1;

    // Step 4: Align breakout with OB trend
    if(breakoutDir != obTrend)
    {
    Comment("Breakout and OB trend misaligned");
    return;
    }

    // Step 5: Open first trade
    double atr = iATR(Symbol(), PERIOD_M15, ATRPeriod, 1);
    double slDistance = atr * StopLossATR;
    double lot = BaseLotSize;

    int cmd = -1;
    double price = 0, sl = 0;

    if(breakoutDir == 1) // Long
    {
    cmd = OP_BUY;
    price = Ask;
    sl = price - slDistance;
    }
    else if(breakoutDir == -1) // Short
    {
    cmd = OP_SELL;
    price = Bid;
    sl = price + slDistance;
    }

    if(cmd != -1)
    {
    int ticket = OrderSend(Symbol(), cmd, lot, price, 5, sl, 0, "Breakout Entry", MagicNumber, 0, clrNONE);
    if(ticket < 0)
    Print("First order failed: ", GetLastError());
    else
    Print("Breakout trade opened. Direction: ", cmd==OP_BUY?"BUY":"SELL");
    }
    }
    }
    //+------------------------------------------------------------------+
    ``
    Reference: Original MQL4 code inspired by institutional SMC concepts and volatility breakout strategies .
    Disclaimer: Gold trading involves substantial risk due to high volatility. This EA is provided as-is without any guarantee of profit. Test thoroughly on a demo account for at least 2 months before live deployment. Past performance does not guarantee future results.