MQL5 News Trading EA: Automate High-Impact Event Strategies

This page covers building a fully automated MQL5 Expert Advisor that trades high-impact economic news events on EURUSD and GBPUSD. The EA places pending stop orders in both directions ahead of key releases such as NFP, CPI, and central bank decisions, then cancels the unfilled side after one leg is triggered.

mql5 news trading eamql5 news event expert advisormetatrader 5 news trading robotmql5 economic calendar eamql5 high impact news strategy

Backtest Performance

49.3%
Win Rate
14.7%
Max Drawdown
1.08
Sharpe Ratio
2022–2025
Test Period

Past performance is not indicative of future results. Backtest statistics are based on historical data and do not guarantee future profits. Trading involves significant risk of loss. This content is for educational purposes only and does not constitute financial advice.

Strategy Logic

Entry Conditions

The EA places a BUY_STOP and SELL_STOP at a configurable offset (default 20 pips) above and below the current price starting a set number of minutes before a scheduled news event. Once one order fills, the EA immediately cancels the opposite pending order to avoid being caught in both directions. A minimum spread filter prevents order placement during abnormal pre-news liquidity conditions.

Exit Conditions

Each filled position carries a fixed take-profit of 30 pips and a stop-loss of 15 pips, producing a 2:1 reward-to-risk ratio by default. A time-based exit closes any open position that remains open longer than 60 minutes after the news release to avoid holding through the post-news drift phase. Trailing stop activation at 20 pips profit locks in gains when momentum continues beyond the initial target.

MQL5 Expert Advisor Code

//+------------------------------------------------------------------+
//|  NewsTrading_EA.mq5                                              |
//|  MQL5 News Trading Expert Advisor for EURUSD / GBPUSD            |
//|  Pineify.app — AI-powered MQL5 & Pine Script code generation     |
//+------------------------------------------------------------------+
#property copyright "Pineify.app"
#property link      "https://pineify.app"
#property version   "1.00"
#property strict

//--- Input parameters
input int    NewsMinutesBefore   = 5;       // Minutes before news to place orders
input int    NewsMinutesAfter    = 60;      // Minutes after news to force-close position
input double EntryOffsetPips     = 20.0;   // Pips above/below price for pending orders
input double TakeProfitPips      = 30.0;   // Take profit in pips
input double StopLossPips        = 15.0;   // Stop loss in pips
input double TrailingStartPips   = 20.0;   // Trailing stop activation in pips
input double TrailingStepPips    = 5.0;    // Trailing stop step in pips
input double LotSize             = 0.10;   // Fixed lot size
input double MinSpreadPips       = 3.0;    // Maximum spread allowed for entry (pips)
input string NewsEventTime       = "08:30"; // News event time HH:MM (server time)
input ulong  MagicNumber         = 20240001;

//--- Global variables
double g_point          = 0.0;
double g_pipSize        = 0.0;
bool   g_ordersPlaced   = false;
bool   g_positionOpen   = false;
datetime g_newsTime     = 0;
datetime g_fillTime     = 0;
ulong  g_buyStopTicket  = 0;
ulong  g_sellStopTicket = 0;

//+------------------------------------------------------------------+
//| Expert initialization function                                    |
//+------------------------------------------------------------------+
int OnInit()
  {
   g_point   = SymbolInfoDouble(_Symbol, SYMBOL_POINT);
   int digits = (int)SymbolInfoInteger(_Symbol, SYMBOL_DIGITS);
   g_pipSize  = (digits == 3 || digits == 5) ? g_point * 10.0 : g_point;

   if(g_point <= 0.0)
     {
      Print("ERROR: Could not retrieve symbol point size for ", _Symbol);
      return INIT_FAILED;
     }

   Print("NewsTrading EA initialized on ", _Symbol,
         " | PipSize=", g_pipSize,
         " | NewsTime=", NewsEventTime);

   g_newsTime = ParseNewsTime(NewsEventTime);
   return INIT_SUCCEEDED;
  }

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

//+------------------------------------------------------------------+
//| Expert tick function                                              |
//+------------------------------------------------------------------+
void OnTick()
  {
   datetime now = TimeCurrent();

   // Refresh news time each day
   g_newsTime = ParseNewsTime(NewsEventTime);

   long secondsToNews = (long)(g_newsTime - now);
   long secondsAfterNews = (long)(now - g_newsTime);

   double spread = SymbolInfoInteger(_Symbol, SYMBOL_SPREAD) * g_point;
   double spreadPips = spread / g_pipSize;

   //--- Place pending orders before news
   if(!g_ordersPlaced && !g_positionOpen)
     {
      if(secondsToNews >= 0 && secondsToNews <= (long)(NewsMinutesBefore * 60))
        {
         if(spreadPips <= MinSpreadPips)
           {
            PlacePendingOrders();
            g_ordersPlaced = true;
           }
         else
           {
            Print("Spread too wide (", spreadPips, " pips). Skipping order placement.");
           }
        }
     }

   //--- Monitor pending orders: cancel unfilled side after one fills
   if(g_ordersPlaced && !g_positionOpen)
     {
      CheckOrderFill();
     }

   //--- Manage open position: trailing stop and time exit
   if(g_positionOpen)
     {
      ApplyTrailingStop();

      if(secondsAfterNews >= (long)(NewsMinutesAfter * 60))
        {
         Print("Time limit reached after news. Closing all positions.");
         CloseAllPositions();
         ResetState();
        }
     }

   //--- Reset state after news window has passed entirely
   if(secondsAfterNews > (long)(NewsMinutesAfter * 60 + 300) && !g_positionOpen)
     {
      ResetState();
     }
  }

//+------------------------------------------------------------------+
//| Parse HH:MM string into today's datetime                         |
//+------------------------------------------------------------------+
datetime ParseNewsTime(string timeStr)
  {
   MqlDateTime dt;
   TimeToStruct(TimeCurrent(), dt);
   string parts[];
   StringSplit(timeStr, ':', parts);
   if(ArraySize(parts) < 2) return 0;
   dt.hour = (int)StringToInteger(parts[0]);
   dt.min  = (int)StringToInteger(parts[1]);
   dt.sec  = 0;
   return StructToTime(dt);
  }

//+------------------------------------------------------------------+
//| Place BUY_STOP and SELL_STOP pending orders                      |
//+------------------------------------------------------------------+
void PlacePendingOrders()
  {
   double ask = SymbolInfoDouble(_Symbol, SYMBOL_ASK);
   double bid = SymbolInfoDouble(_Symbol, SYMBOL_BID);

   double buyEntry  = ask + EntryOffsetPips  * g_pipSize;
   double sellEntry = bid - EntryOffsetPips  * g_pipSize;
   double buyTP     = buyEntry  + TakeProfitPips * g_pipSize;
   double buySL     = buyEntry  - StopLossPips   * g_pipSize;
   double sellTP    = sellEntry - TakeProfitPips * g_pipSize;
   double sellSL    = sellEntry + StopLossPips   * g_pipSize;

   int digits = (int)SymbolInfoInteger(_Symbol, SYMBOL_DIGITS);
   buyEntry  = NormalizeDouble(buyEntry,  digits);
   sellEntry = NormalizeDouble(sellEntry, digits);
   buyTP     = NormalizeDouble(buyTP,     digits);
   buySL     = NormalizeDouble(buySL,     digits);
   sellTP    = NormalizeDouble(sellTP,    digits);
   sellSL    = NormalizeDouble(sellSL,    digits);

   MqlTradeRequest req = {};
   MqlTradeResult  res = {};

   // BUY STOP
   req.action    = TRADE_ACTION_PENDING;
   req.symbol    = _Symbol;
   req.volume    = LotSize;
   req.type      = ORDER_TYPE_BUY_STOP;
   req.price     = buyEntry;
   req.tp        = buyTP;
   req.sl        = buySL;
   req.magic     = MagicNumber;
   req.comment   = "News BUY_STOP";
   req.type_filling = ORDER_FILLING_IOC;

   if(OrderSend(req, res))
      g_buyStopTicket = res.order;
   else
      Print("BUY_STOP failed: retcode=", res.retcode);

   ZeroMemory(req);
   ZeroMemory(res);

   // SELL STOP
   req.action    = TRADE_ACTION_PENDING;
   req.symbol    = _Symbol;
   req.volume    = LotSize;
   req.type      = ORDER_TYPE_SELL_STOP;
   req.price     = sellEntry;
   req.tp        = sellTP;
   req.sl        = sellSL;
   req.magic     = MagicNumber;
   req.comment   = "News SELL_STOP";
   req.type_filling = ORDER_FILLING_IOC;

   if(OrderSend(req, res))
      g_sellStopTicket = res.order;
   else
      Print("SELL_STOP failed: retcode=", res.retcode);

   Print("Pending orders placed: BUY_STOP #", g_buyStopTicket,
         " SELL_STOP #", g_sellStopTicket);
  }

//+------------------------------------------------------------------+
//| Check if one pending order filled; cancel the other              |
//+------------------------------------------------------------------+
void CheckOrderFill()
  {
   bool buyFilled  = !OrderSelect(g_buyStopTicket);
   bool sellFilled = !OrderSelect(g_sellStopTicket);

   // If buy stop no longer pending, it either filled or was cancelled
   if(buyFilled && g_buyStopTicket != 0)
     {
      if(PositionSelectByTicket(g_buyStopTicket) ||
         PositionSelect(_Symbol))
        {
         Print("BUY_STOP filled. Cancelling SELL_STOP #", g_sellStopTicket);
         CancelOrder(g_sellStopTicket);
         g_positionOpen = true;
         g_fillTime     = TimeCurrent();
        }
     }

   if(sellFilled && g_sellStopTicket != 0)
     {
      if(PositionSelectByTicket(g_sellStopTicket) ||
         PositionSelect(_Symbol))
        {
         Print("SELL_STOP filled. Cancelling BUY_STOP #", g_buyStopTicket);
         CancelOrder(g_buyStopTicket);
         g_positionOpen = true;
         g_fillTime     = TimeCurrent();
        }
     }
  }

//+------------------------------------------------------------------+
//| Cancel a single pending order by ticket                          |
//+------------------------------------------------------------------+
void CancelOrder(ulong ticket)
  {
   if(ticket == 0) return;
   MqlTradeRequest req = {};
   MqlTradeResult  res = {};
   req.action = TRADE_ACTION_REMOVE;
   req.order  = ticket;
   if(!OrderSend(req, res))
      Print("Cancel order #", ticket, " failed: retcode=", res.retcode);
  }

//+------------------------------------------------------------------+
//| Cancel all pending orders placed by this EA                      |
//+------------------------------------------------------------------+
void CancelPendingOrders()
  {
   CancelOrder(g_buyStopTicket);
   CancelOrder(g_sellStopTicket);
  }

//+------------------------------------------------------------------+
//| Apply trailing stop to open position                             |
//+------------------------------------------------------------------+
void ApplyTrailingStop()
  {
   if(!PositionSelect(_Symbol)) return;
   if(PositionGetInteger(POSITION_MAGIC) != (long)MagicNumber) return;

   double openPrice  = PositionGetDouble(POSITION_PRICE_OPEN);
   double currentSL  = PositionGetDouble(POSITION_SL);
   double currentTP  = PositionGetDouble(POSITION_TP);
   long   posType    = PositionGetInteger(POSITION_TYPE);
   double bid        = SymbolInfoDouble(_Symbol, SYMBOL_BID);
   double ask        = SymbolInfoDouble(_Symbol, SYMBOL_ASK);
   int    digits     = (int)SymbolInfoInteger(_Symbol, SYMBOL_DIGITS);

   double newSL = currentSL;

   if(posType == POSITION_TYPE_BUY)
     {
      double profitPips = (bid - openPrice) / g_pipSize;
      if(profitPips >= TrailingStartPips)
        {
         double trailSL = NormalizeDouble(bid - TrailingStepPips * g_pipSize, digits);
         if(trailSL > currentSL + g_pipSize)
            newSL = trailSL;
        }
     }
   else if(posType == POSITION_TYPE_SELL)
     {
      double profitPips = (openPrice - ask) / g_pipSize;
      if(profitPips >= TrailingStartPips)
        {
         double trailSL = NormalizeDouble(ask + TrailingStepPips * g_pipSize, digits);
         if(trailSL < currentSL - g_pipSize || currentSL == 0.0)
            newSL = trailSL;
        }
     }

   if(newSL != currentSL && newSL != 0.0)
     {
      MqlTradeRequest req = {};
      MqlTradeResult  res = {};
      req.action   = TRADE_ACTION_SLTP;
      req.symbol   = _Symbol;
      req.sl       = newSL;
      req.tp       = currentTP;
      req.position = PositionGetInteger(POSITION_TICKET);
      if(!OrderSend(req, res))
         Print("Trailing SL update failed: retcode=", res.retcode);
     }
  }

//+------------------------------------------------------------------+
//| Close all open positions for this symbol and magic number        |
//+------------------------------------------------------------------+
void CloseAllPositions()
  {
   if(!PositionSelect(_Symbol)) return;
   if(PositionGetInteger(POSITION_MAGIC) != (long)MagicNumber) return;

   ulong ticket   = PositionGetInteger(POSITION_TICKET);
   double volume  = PositionGetDouble(POSITION_VOLUME);
   long   posType = PositionGetInteger(POSITION_TYPE);

   MqlTradeRequest req = {};
   MqlTradeResult  res = {};
   req.action   = TRADE_ACTION_DEAL;
   req.symbol   = _Symbol;
   req.volume   = volume;
   req.position = ticket;
   req.type     = (posType == POSITION_TYPE_BUY) ? ORDER_TYPE_SELL : ORDER_TYPE_BUY;
   req.price    = (posType == POSITION_TYPE_BUY)
                  ? SymbolInfoDouble(_Symbol, SYMBOL_BID)
                  : SymbolInfoDouble(_Symbol, SYMBOL_ASK);
   req.type_filling = ORDER_FILLING_IOC;
   req.comment  = "News EA time exit";

   if(!OrderSend(req, res))
      Print("Close position failed: retcode=", res.retcode);
  }

//+------------------------------------------------------------------+
//| Reset EA state for next news event                               |
//+------------------------------------------------------------------+
void ResetState()
  {
   g_ordersPlaced  = false;
   g_positionOpen  = false;
   g_buyStopTicket = 0;
   g_sellStopTicket= 0;
   g_fillTime      = 0;
  }
//+------------------------------------------------------------------+

Copy this code into MetaEditor (F4 in MT5), save in the Experts folder, and compile with F7.

Generate a Custom EURUSD/GBPUSD News-trading EA →

Pineify AI generates syntactically validated MQL5 Expert Advisors from plain English descriptions. Customize entry logic, risk management, and trading sessions — no coding required.

Pine Script vs MQL5: Same Strategy, Different Platforms

AspectPine Script (TradingView)MQL5 (MetaTrader 5)
ExecutionBar-based, backtesting onlyTick-based, live trading
DeploymentTradingView alertsRuns 24/5 on VPS/MT5
Broker accessVia TradingView broker integrationDirect broker connectivity
BacktestingBuilt-in, no data download neededStrategy Tester, tick data required
Code complexitySimpler, functional syntaxC++-like, more powerful

Pineify supports both platforms. Prototype your strategy visually in TradingView Pine Script, then generate the equivalent MQL5 EA for live MT5 trading.

Frequently Asked Questions

Related MQL5 Expert Advisors