Complete .mq5 source code

MQL5 News Trading EA: Audit a Manual Event Straddle

Audit a single manually scheduled MT5 news straddle: two pending stop orders, actual-position checks, cancellation retries and a terminal-managed time exit. The example does not fetch an economic calendar and has no verified performance record.

Strategy logic

Entry conditions

Set one future broker-server datetime. During the configured pre-event window, the EA attempts one BUY_STOP and SELL_STOP pair after checking the current spread. A failed pair starts cleanup. On later chart ticks, a matching position triggers cancellation of remaining orders; cancellation is not atomic with execution, so both legs can fill.

Exit conditions

Each requested pending order has fixed SL and TP distances. At the post-event deadline, or if both legs are observed open, cleanup remains latched and retries cancellation and position closure on subsequent ticks. It does not re-enter, auto-schedule another event, or trail stops. Execution can fail or slip; a disconnect can leave GTC orders active.

MQL5 Expert Advisor Code

#property strict
// Educational single-event straddle. Set a future broker-server datetime.
input datetime NewsEventTime = D'2026.01.01 00:00';
input int MinutesBefore = 5;
input int MinutesAfter = 60;
input double EntryOffsetPips = 20;
input double StopLossPips = 15;
input double TakeProfitPips = 30;
input double MaxSpreadPips = 3;
input double LotSize = 0.10;
input ulong MagicNumber = 20240001;
double pip = 0, lots = 0;
bool attempted = false, closing = false, sawPosition = false;

bool MinePosition(const int i)
{
   return PositionGetTicket(i) != 0 && PositionGetString(POSITION_SYMBOL) == _Symbol
      && PositionGetInteger(POSITION_MAGIC) == (long)MagicNumber;
}
bool MineOrder(const int i)
{
   return OrderGetTicket(i) != 0 && OrderGetString(ORDER_SYMBOL) == _Symbol
      && OrderGetInteger(ORDER_MAGIC) == (long)MagicNumber;
}
int PositionCount()
{
   int n=0;
   for(int i=PositionsTotal()-1;i>=0;i--) if(MinePosition(i)) n++;
   return n;
}
int OrderCount()
{
   int n=0;
   for(int i=OrdersTotal()-1;i>=0;i--) if(MineOrder(i)) n++;
   return n;
}
void CancelOrders()
{
   for(int i=OrdersTotal()-1;i>=0;i--)
   {
      if(!MineOrder(i)) continue;
      MqlTradeRequest q={}; MqlTradeResult r={};
      q.action=TRADE_ACTION_REMOVE; q.order=(ulong)OrderGetInteger(ORDER_TICKET);
      if(!OrderSend(q,r) || r.retcode!=TRADE_RETCODE_DONE)
         Print("Cancel unresolved: ",q.order," retcode=",r.retcode);
   }
}
void ClosePositions()
{
   for(int i=PositionsTotal()-1;i>=0;i--)
   {
      if(!MinePosition(i)) continue;
      MqlTick tick; if(!SymbolInfoTick(_Symbol,tick)) continue;
      MqlTradeRequest q={}; MqlTradeResult r={};
      q.action=TRADE_ACTION_DEAL; q.symbol=_Symbol; q.magic=MagicNumber;
      q.position=(ulong)PositionGetInteger(POSITION_TICKET);
      q.volume=PositionGetDouble(POSITION_VOLUME);
      bool buy=PositionGetInteger(POSITION_TYPE)==POSITION_TYPE_BUY;
      q.type=buy?ORDER_TYPE_SELL:ORDER_TYPE_BUY;
      q.price=buy?tick.bid:tick.ask;
      q.type_filling=ORDER_FILLING_FOK; q.deviation=20;
      if(!OrderSend(q,r) || r.retcode!=TRADE_RETCODE_DONE)
         Print("Close unresolved: ",q.position," retcode=",r.retcode);
   }
}
bool PlaceStop(const bool buy,const MqlTick &tick)
{
   MqlTradeRequest q={}; MqlTradeResult r={};
   q.action=TRADE_ACTION_PENDING; q.symbol=_Symbol; q.magic=MagicNumber;
   q.volume=lots; q.type=buy?ORDER_TYPE_BUY_STOP:ORDER_TYPE_SELL_STOP;
   q.price=NormalizeDouble((buy?tick.ask:tick.bid)+(buy?1:-1)*EntryOffsetPips*pip,_Digits);
   q.sl=NormalizeDouble(q.price+(buy?-1:1)*StopLossPips*pip,_Digits);
   q.tp=NormalizeDouble(q.price+(buy?1:-1)*TakeProfitPips*pip,_Digits);
   q.type_filling=ORDER_FILLING_RETURN;
   // Terminal-managed cleanup: no server-expiry guarantee if disconnected.
   q.type_time=ORDER_TIME_GTC;
   bool sent=OrderSend(q,r);
   bool accepted=sent && (r.retcode==TRADE_RETCODE_PLACED || r.retcode==TRADE_RETCODE_DONE);
   if(!accepted) Print("Pending request failed: ",r.retcode," ",r.comment);
   return accepted;
}
int OnInit()
{
   if(AccountInfoInteger(ACCOUNT_MARGIN_MODE)!=ACCOUNT_MARGIN_MODE_RETAIL_HEDGING)
      return INIT_PARAMETERS_INCORRECT;
   if(NewsEventTime<=TimeCurrent() || MinutesBefore<=0 || MinutesAfter<=0
      || EntryOffsetPips<=0 || StopLossPips<=0 || TakeProfitPips<=0
      || MaxSpreadPips<=0 || LotSize<=0 || MagicNumber==0)
      return INIT_PARAMETERS_INCORRECT;
   double step=SymbolInfoDouble(_Symbol,SYMBOL_VOLUME_STEP);
   double minimum=SymbolInfoDouble(_Symbol,SYMBOL_VOLUME_MIN);
   double maximum=SymbolInfoDouble(_Symbol,SYMBOL_VOLUME_MAX);
   if(_Point<=0 || step<=0 || minimum<=0 || maximum<=0) return INIT_FAILED;
   pip=(_Digits==3 || _Digits==5)?10*_Point:_Point;
   lots=NormalizeDouble(MathFloor(LotSize/step)*step,8);
   if(lots<minimum || lots>maximum || PositionCount()>0 || OrderCount()>0)
      return INIT_PARAMETERS_INCORRECT;
   // This sample's market-close requests require FOK support.
   if((SymbolInfoInteger(_Symbol,SYMBOL_FILLING_MODE)&SYMBOL_FILLING_FOK)==0)
      return INIT_PARAMETERS_INCORRECT;
   return INIT_SUCCEEDED;
}
void OnTick()
{
   datetime now=TimeCurrent();
   int positions=PositionCount();
   if(positions>0) sawPosition=true;
   // Both legs can fill before the next tick: OCO is not atomic.
   if(positions>1 || now>=NewsEventTime+(long)MinutesAfter*60) closing=true;
   if(closing)
   {
      CancelOrders(); ClosePositions();
      return; // Remain latched; failed cleanup is retried on future ticks.
   }
   if(sawPosition)
   {
      CancelOrders();
      return; // No re-entry, including after the position has already closed.
   }
   if(attempted || now>=NewsEventTime || now<NewsEventTime-(long)MinutesBefore*60) return;
   MqlTick tick; if(!SymbolInfoTick(_Symbol,tick) || tick.ask<=tick.bid) return;
   if((tick.ask-tick.bid)/pip>MaxSpreadPips) return;
   attempted=true;
   bool buy=PlaceStop(true,tick);
   bool sell=false;
   if(buy) sell=PlaceStop(false,tick);
   if(!buy || !sell)
   {
      closing=true;
      CancelOrders(); ClosePositions();
   }
}
void OnTradeTransaction(const MqlTradeTransaction &trans,
                        const MqlTradeRequest &request,
                        const MqlTradeResult &result)
{
   // A position may hit SL/TP before OnTick observes it. Retain the fill event.
   if(trans.type==TRADE_TRANSACTION_DEAL_ADD && HistoryDealSelect(trans.deal)
      && HistoryDealGetString(trans.deal,DEAL_SYMBOL)==_Symbol
      && HistoryDealGetInteger(trans.deal,DEAL_MAGIC)==(long)MagicNumber
      && HistoryDealGetInteger(trans.deal,DEAL_ENTRY)==DEAL_ENTRY_IN)
      sawPosition=true;
}
void OnDeinit(const int reason)
{
   CancelOrders(); // One attempt only; inspect terminal/broker state after unloading.
}

Copy this code into MetaEditor, save it in the MQL5/Experts folder, and compile with F7.

Generate a custom EURUSD/GBPUSD news-trading EA

Describe the entry, exit, position, order, and risk rules you want. Pineify generates editable MQL5 source code that you can inspect and compile in MetaEditor.

Pine Script vs MQL5: Same Strategy, Different Platforms

AspectPine Script (TradingView)MQL5 (MetaTrader 5)
ExecutionSeries evaluated on chart updatesEvent handlers with explicit buffers
DeploymentRuns in TradingView with alertsRuns in the MT5 terminal or on a VPS
Broker accessStrategy orders are simulated; live automation needs a separate integrationDirect broker connectivity
BacktestingStrategy Tester for strategy scriptsStrategy Tester for Expert Advisors
Code complexitySimpler, functional syntaxC++-like, more powerful

The calculation can be implemented on either platform, but the runtime model and buffer APIs differ.

Frequently Asked Questions

Related MQL5 pages

Risk and testing note

These educational examples require independent compilation and testing. A compile pass or an illustrative setting does not establish execution correctness or profitability. Any reported performance must be supported by reproducible data and test settings; past results do not predict future returns. Trading involves significant risk of loss. This content is not financial advice.