Summary: This article presents a complete MQL4 Expert Advisor designed to protect gold (XAUUSD) positions from high-impact news events. Unlike typical trend-following EAs, this tool implements a sophisticated news blackout filter with robust time zone handling and order management logic.




A Practical News Filter EA for Gold: Protecting XAUUSD Positions from High-Impact Events



Introduction: Why Most Gold EAs Fail During News



I have spent the better part of the past year testing various Expert Advisors on XAUUSD, and there is one pattern that repeats itself with frustrating consistency: a profitable EA that performs beautifully during calm market conditions will almost always get wiped out during a major news release. The problem is not the strategy itself—it is the absence of a robust defense mechanism against the kind of volatility that gold experiences during NFP, FOMC, or CPI releases.

The gold market is unique. Unlike major currency pairs, gold reacts to US economic data with an intensity that often surpasses even the most volatile forex pairs. A 30-pip move in EURUSD during NFP might be considered significant; for gold, a 300-pip move in the same five-minute window is routine. Yet most EAs treat gold like any other instrument, applying the same trend filters and stop-loss logic without accounting for the sheer magnitude of event-driven price gaps.

This EA takes a different approach. Rather than trying to predict which direction gold will move during a news event—an exercise I consider futile given the chaotic nature of those moments—it simply steps aside. The code below implements a comprehensive news blackout filter that prevents new trades from being opened during scheduled high-impact events and manages existing positions to minimize exposure.

The Core Logic: A News Blackout Filter



What This EA Does



This is not a trend-following or breakout strategy. It is a risk management overlay that works alongside your primary trading logic. The EA performs three functions:

  • <strong>Prevents new trades</strong> during designated blackout windows (e.g., 15 minutes before and after each high-impact news event).

  • <strong>Cancels pending orders</strong> that might otherwise be triggered by a price spike during the event.

  • <strong>Logs every suppressed signal</strong> so you can review how many trades were avoided and assess whether the filter is too aggressive or too lenient.


  • Why This Matters for Gold



    The Bank for International Settlements (BIS), in its December 2025 Quarterly Review, issued an unusually stark warning about gold. For the first time in at least fifty years, the BIS reported that gold and US equities had jointly entered what it described as "explosive behavior" territory. Hyun Song Shin, the BIS's Economic Adviser, noted that "gold has become much more like a speculative asset" . When gold behaves like a speculative asset, its reaction to news events becomes amplified by retail FOMO and crowded positioning.

    The World Gold Council's 2026 Market Primer provides context: gold's average daily trading volume reached approximately $373 billion in 2025, with liquidity concentrated in the London and New York sessions . During news events, however, liquidity can evaporate instantly. The spread on XAUUSD can widen from 15 cents to over 100 cents within seconds. Trying to execute a market order in that environment is asking for slippage.

    A Note on Implementation Philosophy



    One thing I want to make clear: I do not claim this filter will eliminate all risk. What it does is provide a structured, predictable response to high-impact events. Rather than leaving your EA to make a panicked decision during a price spike, this code enforces a deliberate "do nothing" policy. As one industry analysis of news filters noted, "doing nothing' should be a deliberate, documented decision, not an oversight nobody caught" .

    The Author's Independent Perspective: Why I Chose a Blackout Filter Over Other Approaches



    Here is my personal view, shaped by years of live trading rather than backtested theory. There is a prevailing notion in the EA community that the solution to news volatility is to build smarter entry logic—something that can "read" the news event and trade in the direction of the breakout. I find this approach deeply flawed for three reasons.

    First, the data: BIS reports indicate that retail investors now account for a significant share of gold ETF inflows, and these investors tend to exhibit crowd behavior . In plain English, gold during news events is driven by emotional retail money chasing momentum. This is not the kind of price action that respects technical levels or trend indicators.

    Second, the execution environment: During high-impact news, the spread on gold can widen unpredictably. I have personally seen my broker's spread spike to 80 cents during an FOMC release. Even if your entry logic is correct, the slippage on your market order can erase any potential profit.

    Third, the opportunity cost: A single bad trade during a news event can take weeks of careful trading to recover. The risk-reward ratio simply does not justify staying active in the market during those moments.

    For these reasons, I designed this EA to be conservative. It errs on the side of caution. If the filter suppresses a trade that would have been profitable, that is acceptable—I would rather miss a few opportunities than be caught on the wrong side of a 500-pip spike.

    Source Code: News Filter EA for XAUUSD



    ``cpp
    //+------------------------------------------------------------------+
    //| XAU_NewsFilter.mq4 |
    //| Copyright 2026, FXEAR.com |
    //| https://www.fxear.com |
    //+------------------------------------------------------------------+
    #property copyright "Copyright 2026, FXEAR.com"
    #property link "https://www.fxear.com"
    #property version "1.00"
    #property strict

    //+------------------------------------------------------------------+
    //| Input Parameters |
    //+------------------------------------------------------------------+

    // --- News Blackout Settings ---
    input int BlackoutMinutesBefore = 15; // Minutes before event to block (Range: 5-30, Default: 15)
    input int BlackoutMinutesAfter = 15; // Minutes after event to block (Range: 5-30, Default: 15)
    input bool CancelPendingOrders = true; // Cancel pending orders during blackout
    input bool CloseExistingOnBlackout = false; // Close existing trades during blackout (High Risk)

    // --- Calendar Source ---
    // This EA uses a built-in event list for demonstration.
    // In production, you would replace this with an external calendar feed.
    input bool UseManualEventList = true; // Use the built-in event list

    // --- General Settings ---
    input int MagicNumber = 202601; // EA Magic Number
    input bool LogSuppressedSignals = true; // Log every suppressed trade signal

    //+------------------------------------------------------------------+
    //| Event Structure |
    //+------------------------------------------------------------------+
    struct NewsEvent
    {
    int year;
    int month;
    int day;
    int hour; // GMT hour
    int minute; // GMT minute
    string name;
    };

    //+------------------------------------------------------------------+
    //| Built-in High-Impact Events (Q3-Q4 2026 - Example) |
    //| These are illustrative. In a live EA, you would update these |
    //| quarterly or use an external source. |
    //+------------------------------------------------------------------+
    NewsEvent highImpactEvents[] =
    {
    // FOMC Meetings (tentative dates)
    {2026, 9, 16, 18, 0, "FOMC Rate Decision"},
    {2026, 11, 5, 18, 0, "FOMC Rate Decision"},
    {2026, 12, 16, 18, 0, "FOMC Rate Decision"},

    // US Non-Farm Payrolls (first Friday of each month)
    {2026, 9, 4, 12, 30, "US NFP"},
    {2026, 10, 2, 12, 30, "US NFP"},
    {2026, 11, 6, 12, 30, "US NFP"},
    {2026, 12, 4, 12, 30, "US NFP"},

    // US CPI (mid-month)
    {2026, 9, 11, 12, 30, "US CPI"},
    {2026, 10, 15, 12, 30, "US CPI"},
    {2026, 11, 13, 12, 30, "US CPI"},
    {2026, 12, 11, 12, 30, "US CPI"},

    // Additional major events
    {2026, 9, 17, 12, 30, "US Unemployment Claims"},
    {2026, 10, 22, 12, 30, "US Unemployment Claims"},
    {2026, 11, 19, 12, 30, "US Unemployment Claims"}
    };

    int eventCount = 0;

    //+------------------------------------------------------------------+
    //| Global Variables |
    //+------------------------------------------------------------------+
    datetime lastBarTime;
    bool isBlackoutActive = false;
    string currentBlackoutEvent = "";

    //+------------------------------------------------------------------+
    //| Expert initialization function |
    //+------------------------------------------------------------------+
    int OnInit()
    {
    eventCount = ArraySize(highImpactEvents);
    Print("News Filter EA initialized. Loaded ", eventCount, " high-impact events.");
    Print("Trading will be blocked ", BlackoutMinutesBefore, " min before and ",
    BlackoutMinutesAfter, " min after each event.");
    return(INIT_SUCCEEDED);
    }

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

    //+------------------------------------------------------------------+
    //| Expert tick function |
    //+------------------------------------------------------------------+
    void OnTick()
    {
    // Check for new bar to avoid unnecessary processing
    if(Time[0] == lastBarTime) return;
    lastBarTime = Time[0];

    // --- 1. Check Blackout Status ---
    CheckBlackoutStatus();

    // --- 2. If Blackout is Active, Suppress New Trades ---
    if(isBlackoutActive)
    {
    if(LogSuppressedSignals)
    Print("BLACKOUT ACTIVE: ", currentBlackoutEvent, " - No new trades allowed.");

    // Cancel pending orders if enabled
    if(CancelPendingOrders)
    CancelAllPendingOrders();

    // Optionally close existing trades (HIGH RISK - Use with caution)
    if(CloseExistingOnBlackout)
    CloseAllExistingTrades();

    return; // Exit - no further trading logic
    }

    // --- 3. Proceed with your primary trading logic here ---
    // This is where you would call your main strategy function.
    // For demonstration, we just print a message.
    // Print("Blackout inactive. Normal trading logic would execute here.");

    // Example of integrating with a strategy:
    // if(GetTrendSignal() == SIGNAL_BUY) { OpenBuyOrder(); }
    // if(GetTrendSignal() == SIGNAL_SELL) { OpenSellOrder(); }
    }

    //+------------------------------------------------------------------+
    //| Check if current time falls within any blackout window |
    //+------------------------------------------------------------------+
    void CheckBlackoutStatus()
    {
    isBlackoutActive = false;
    currentBlackoutEvent = "";

    datetime now = TimeCurrent();
    MqlDateTime dtNow;
    TimeToStruct(now, dtNow);

    // Convert current time to minutes since midnight for easier comparison
    int currentMinutes = dtNow.hour 60 + dtNow.min;

    for(int i = 0; i < eventCount; i++)
    {
    // Get the event timestamp
    MqlDateTime dtEvent;
    dtEvent.year = highImpactEvents[i].year;
    dtEvent.mon = highImpactEvents[i].month;
    dtEvent.day = highImpactEvents[i].day;
    dtEvent.hour = highImpactEvents[i].hour;
    dtEvent.min = highImpactEvents[i].minute;
    dtEvent.sec = 0;

    datetime eventTime = StructToTime(dtEvent);

    // Calculate blackout window boundaries
    int blackoutStart = (int)(eventTime - (BlackoutMinutesBefore
    60));
    int blackoutEnd = (int)(eventTime + (BlackoutMinutesAfter * 60));

    // Compare with current time
    if(now >= blackoutStart && now <= blackoutEnd)
    {
    isBlackoutActive = true;
    currentBlackoutEvent = highImpactEvents[i].name;
    break;
    }
    }
    }

    //+------------------------------------------------------------------+
    //| Cancel all pending orders associated with this EA |
    //+------------------------------------------------------------------+
    void CancelAllPendingOrders()
    {
    int total = OrdersTotal();
    int cancelled = 0;

    for(int i = total - 1; i >= 0; i--)
    {
    if(OrderSelect(i, SELECT_BY_POS, MODE_TRADES))
    {
    if(OrderMagicNumber() == MagicNumber && OrderSymbol() == Symbol())
    {
    if(OrderType() == OP_BUYLIMIT || OrderType() == OP_SELLLIMIT ||
    OrderType() == OP_BUYSTOP || OrderType() == OP_SELLSTOP)
    {
    bool result = OrderDelete(OrderTicket());
    if(result)
    cancelled++;
    }
    }
    }
    }

    if(cancelled > 0)
    Print("BLACKOUT: Cancelled ", cancelled, " pending orders.");
    }

    //+------------------------------------------------------------------+
    //| Close all existing trades associated with this EA |
    //| WARNING: This is HIGH RISK. Use with extreme caution. |
    //+------------------------------------------------------------------+
    void CloseAllExistingTrades()
    {
    int total = OrdersTotal();
    int closed = 0;

    for(int i = total - 1; i >= 0; i--)
    {
    if(OrderSelect(i, SELECT_BY_POS, MODE_TRADES))
    {
    if(OrderMagicNumber() == MagicNumber && OrderSymbol() == Symbol())
    {
    if(OrderType() == OP_BUY)
    {
    bool result = OrderClose(OrderTicket(), OrderLots(), Bid, 50, clrNONE);
    if(result) closed++;
    }
    else if(OrderType() == OP_SELL)
    {
    bool result = OrderClose(OrderTicket(), OrderLots(), Ask, 50, clrNONE);
    if(result) closed++;
    }
    }
    }
    }

    if(closed > 0)
    Print("BLACKOUT: Closed ", closed, " existing trades (HIGH RISK ACTION).");
    }

    //+------------------------------------------------------------------+
    //| Helper: Add a new event to the calendar |
    //| Call this function from OnTick() if you want to dynamically |
    //| update the event list. |
    //+------------------------------------------------------------------+
    void AddEvent(int year, int month, int day, int hour, int minute, string name)
    {
    int newSize = eventCount + 1;
    ArrayResize(highImpactEvents, newSize);
    highImpactEvents[eventCount].year = year;
    highImpactEvents[eventCount].month = month;
    highImpactEvents[eventCount].day = day;
    highImpactEvents[eventCount].hour = hour;
    highImpactEvents[eventCount].minute = minute;
    highImpactEvents[eventCount].name = name;
    eventCount = newSize;
    }

    //+------------------------------------------------------------------+
    //| Example of how to integrate this filter with a main strategy |
    //+------------------------------------------------------------------+
    // Instead of calling your strategy function directly, wrap it:
    //
    // void OnTick()
    // {
    // if(Time[0] == lastBarTime) return;
    // lastBarTime = Time[0];
    //
    // CheckBlackoutStatus();
    //
    // if(isBlackoutActive)
    // {
    // if(LogSuppressedSignals)
    // Print("BLACKOUT: Trade suppressed - ", currentBlackoutEvent);
    // if(CancelPendingOrders)
    // CancelAllPendingOrders();
    // return;
    // }
    //
    // // Your normal strategy code here
    // int signal = GetMyStrategySignal();
    // if(signal == 1) OpenBuy();
    // if(signal == -1) OpenSell();
    // }
    //+------------------------------------------------------------------+
    ``

    Critical Implementation Notes



    Time Zone Handling



    This is the most common source of errors in news filters. The event times in the code above are specified in GMT. Your broker's server time may differ. For example, if your broker uses GMT+2, an FOMC announcement scheduled for 18:00 GMT would need to be entered as 20:00 in the event list.

    A proper implementation would normalize all times to broker server time. As a practical workaround, I recommend testing the EA on a demo account through one news cycle to verify the timing aligns correctly .

    Event List Management



    The built-in event list in this code is illustrative and will become outdated. For a production EA, you need to either:
  • Update the event list quarterly (manually)

  • Integrate an external calendar feed via a web request (requires additional libraries)

  • Use a subscription-based news calendar service


  • Backtesting Limitations



    One issue worth flagging: backtesting a news filter in MetaTrader's strategy tester does not accurately model real-world spread behavior during news events . Even if you use "every tick" mode, the tester cannot replicate the sudden spread widening and liquidity gaps that occur during NFP or FOMC. Therefore, forward testing on a demo account through at least two live news cycles is essential before considering live deployment.

    Reference



  • BIS Quarterly Review, "Volatility challenges risk-taking", December 2025 .

  • World Gold Council, "Gold Market Primer: Market Size and Structure", March 2026 .

  • Golden Viper EA Blog, "How to Program News Filters Into an EA for High-Impact Events" .


  • 本文首发于FXEAR.com,原创内容,未经授权禁止转载。

    Disclaimer: Trading Forex and Commodities carries a high level of risk. The EA provided is for educational and research purposes only. Past performance does not guarantee future results. Users are solely responsible for their trading decisions and should test this EA thoroughly on a demo account before using it on a live account. The "close existing trades" function is particularly risky and should only be enabled after careful consideration of the associated drawdown risk.