NEW: Memberships are live! Earn rewards, get flash discount alerts, and enjoy faster project quotes. Explore Memberships →  |  Flash Discount Alerts (coming soon)

MT4 EA Tutorial: Fix Missed Trades in Fast Markets in 9 Steps

How to Understand the Mechanics of Missed Trades

If your MT4 EA not opening trades is your problem during a high-impact news release, the Strategy Tester isn't going to tell you why. That's because the MetaTrader Strategy Tester runs in a controlled environment — modelled ticks, predictable spreads, and smoother price action than anything you'll see on a live NFP print. It's a useful baseline, but it doesn't replicate the chaos of a fast market.

Backtest vs. Live Execution — the core gap:

Environment Spread Behavior Slippage Requotes
Strategy Tester Stable, modelled Predictable Rare
Live Fast Market Spikes sharply Unpredictable Frequent

Fast markets — triggered by events like NFP, CPI, or central bank decisions — create liquidity gaps where price jumps several pips in milliseconds. Every trade your Expert Advisor attempts follows a four-stage pathway: Signal → Terminal → Server → Fill. Each stage introduces latency and potential failure points. A signal fires, the terminal packages the order, the broker's server receives it, and the fill either executes at the requested price or doesn't execute at all.

In practice, the breakdown almost always happens at the server-to-fill stage. Prices move faster than the round-trip can complete, and your Expert Advisor gets nothing — no fill, no error, just a missed opportunity logged silently.

This tutorial walks through a 9-step diagnostic and fix process to address every point in that pathway. Before getting into fixes, you need to understand exactly what terminology — slippage, requotes, spread spikes, and trade context — actually means inside MetaTrader.

How to Define Core Execution Terminology

Before you can fix expert advisor execution delays, you need a clear vocabulary. These four terms describe exactly what's happening between your EA's OrderSend() call and the broker's fill confirmation.

Slippage
The difference between the price your EA requested and the actual fill price — a natural result of low liquidity where the requested price is no longer available by the time the order reaches the server.
Requote (Error 138)
A broker’s explicit rejection of your order because the exact requested price has moved; MT4 surfaces this as error code 138, and your EA must handle it or the trade is silently dropped.
Spread Spike
A rapid, temporary widening of the Bid/Ask gap during low-liquidity windows — news releases being the most common trigger — which inflates effective entry cost and can trigger stop-loss levels instantly.
Trade Context
MT4’s single-threaded execution gate that permits only one trade command at a time; if another operation holds the context when `OnTick()` fires, `OrderSend()` returns error 146 and your trade never opens.

Understanding how these four conditions interact is what separates a stable Expert Advisor from one that misses entries under pressure. Slippage is unavoidable to a degree, but requotes and trade context conflicts are largely controllable through code — specifically through how you structure order execution logic in MQL4 and handle retry loops.

Before diving into the fix steps themselves, it's worth making sure your development environment is set up to surface these errors clearly — which is exactly what the next section covers.

How to Prepare Your Environment and Prerequisites

Before you start modifying execution logic, make sure your setup isn't the hidden cause of the problem. Traders asking "why is my EA missing trades on news?" often discover the issue isn't in their MQL4 code at all — it's in how their environment is configured. Get these foundations right before touching a single line of code.

Here's what you need in place:

  • MT4 Terminal and MetaEditor installed — You’ll be reading logs and editing code directly. MetaEditor comes bundled with MT4, so launch it via Tools → MetaEditor or press F4.
  • Working knowledge of MQL4 basics — Specifically OrderSend() and the OnTick() event handler. If your EA opens positions, every execution path runs through these two functions. Understanding how they interact with broker execution is non-negotiable. You can also review how OrderSend behaves versus Pine Script logic if you’re converting a strategy from TradingView.
  • A VPS located near your broker’s data center — Network latency is a primary driver of execution delays. Running MT4 on a co-located VPS near the trade server reduces the round-trip time on order requests, which is especially critical during fast-moving news events. Platform instability during high volatility is frequently a latency problem, not a code problem.
  • A Demo account configured to mirror your live broker settings — Test all execution changes here first. Demo accounts replicate the broker’s order handling behavior without financial risk, giving you a controlled environment to validate fixes before deployment.

Warning: Do not test execution logic changes directly on a live account. Even a single mishandled order during volatile conditions can trigger unintended trades or compound losses.

With your environment confirmed, the next step is to look at what MT4 is already telling you. The terminal logs record every order attempt and rejection — and that's exactly where the diagnostic process starts.

Step 1: Audit Your EA Logs for Error Codes

Fixing MT4 expert advisor trade rejection starts here — not in your code, but in your logs. Before you change a single line of MQL4, you need to know whether your EA is failing to trigger or getting rejected by the broker. Those are two completely different problems with two completely different fixes.

Open the MT4 terminal panel and locate the Experts and Journal tabs at the bottom of the screen. The Experts tab records every action your EA attempts, including order submissions and logic evaluations. The Journal tab captures platform-level events and broker responses. Run your EA during active market hours, then check both tabs immediately after a missed trade.

Here's what to look for:

  1. Locate the Experts tab first. This is where your EA’s Print() statements and internal logic messages appear. If you see no output at all during a price move, your entry conditions simply weren’t met — that’s a logic issue, not an execution issue.
  2. Check the Journal tab for broker rejection codes. MT4 returns Error 138 (REQUOTE) or Error 136 (OFF_QUOTES) specifically when price moves beyond your slippage parameter before the order fills. Error 130 (Invalid Stops) means your Stop Loss or Take Profit is inside the broker’s minimum stop level.
  3. Differentiate a no-trade from a failed trade. A no-trade leaves the Experts tab silent — the EA evaluated conditions and walked away. A failed trade shows an error code in the Journal tab, meaning the EA tried to place the order and the broker rejected it.
  4. Check for Error 146 (Trade Context Busy). This appears when another EA operation hasn’t finished processing. In fast markets, rapid tick arrival can stack order requests and trigger platform congestion. If you’re seeing Error 146 regularly, your EA needs a IsTradeAllowed() check before every OrderSend() call.
  5. Add custom Print() logging to your EA to capture error codes at the moment they happen:
    int ticket = OrderSend(Symbol(), OP_BUY, lots, Ask, slippage, sl, tp);
    if(ticket < 0) {
       int err = GetLastError();
       Print("OrderSend failed. Error: ", err, " | Ask: ", Ask, " | Spread: ", MarketInfo(Symbol(), MODE_SPREAD));
    }

    This logs the exact price context at the moment of rejection, making patterns much easier to spot across multiple attempts.

One practical note: a single log audit rarely tells the full story. Review logs across multiple sessions and compare MT4's execution behavior against what your strategy tester shows — discrepancies there often confirm a broker execution problem rather than a code bug.

Once you know which error codes are appearing consistently, you're ready to address the next layer: the spread conditions your EA is allowing at the moment it fires.

Step 2: Implement Dynamic Spread Filters

Once your logs are telling you where the failures are, the next common culprit is a hardcoded spread limit. A static value like 2 pips looks reasonable during a quiet London session — but during a high-impact news release, spreads on major pairs can widen to 10–20 times their typical average, instantly triggering your EA's safety filter and blocking every order. That's a legitimate rejection, but it's also a missed trade you probably wanted.

The fix is a dynamic spread check built around MarketInfo(Symbol(), MODE_SPREAD), which reads the live spread in points at the moment of execution. Pair that with a configurable MaxSpread input parameter and you give yourself full control across different sessions without recompiling.

Here's a standard MQL4 spread filter you can drop into your OnTick() block before any OrderSend() call:

// Input parameter — adjust per session or pair
input int MaxSpread = 30; // in points

// Spread check before order entry
int currentSpread = (int)MarketInfo(Symbol(), MODE_SPREAD);

if (currentSpread > MaxSpread)
{
    Print("Spread too wide: ", currentSpread, " points. Order skipped.");
    return;
}

A few things to note in practice:

  • MODE_SPREAD returns the spread in points, not pips. On a 5-digit broker, 30 points equals 3 pips — confirm your broker's digit count before setting the threshold.
  • Set MaxSpread as an input, not a constant. A value that works for EUR/USD won't hold for exotic pairs or during the Asian session.
  • Spread also directly affects your Stop Loss validity. Use MarketInfo(Symbol(), MODE_STOPLEVEL) alongside your spread check — if the spread pushes your SL inside the broker's minimum stop level, you'll hit MQL4 order send error 138 even when the spread filter passes. Both values need to clear before you call OrderSend().

This is also a good point to run an AI code review on your order logic if you're unsure whether your SL calculations account for both spread and stop level constraints together.

With your spread filter in place, the next layer to address is what happens when the order does go out but still bounces back — which is where slippage tolerance and retry logic take over.

Step 3: Optimize Slippage and Retry Logic

Understanding what is slippage in MT4 is the foundation of this step. Slippage is the difference between the price your EA requested and the price your broker actually filled. In fast markets, that gap widens — and if your OrderSend() slippage parameter is too tight, MT4 rejects the order entirely rather than filling at the available price.

The fix involves two coordinated changes: loosening your slippage tolerance during volatile conditions, and building a retry loop that handles the rejections that still slip through.

Professional MQL4 developers write custom error-handling loops that sleep for a few milliseconds and automatically retry the order — because a single OrderSend() call with no fallback is a guaranteed source of missed trades in real-market conditions.

Here's how to build a robust retry wrapper:

  1. Increase the slippage parameter. Change your static value (e.g., 2) to a dynamic figure like 1030 points during news or high-volatility sessions. Hardcoded tight slippage is one of the most common causes of Error 138.
  2. Build the retry loop. Wrap your OrderSend() in a for loop set to 3–5 attempts. Exit the loop immediately on a successful fill.
  3. Add a Sleep() call between retries. A pause of 200ms gives the trade context time to clear after Error 138 (requote) or Error 146 (trade context busy). Without it, rapid retries pile up and all fail.
  4. Refresh the price on each retry. Call RefreshRates() inside the loop before recalculating your entry price. Stale price data is a silent killer in retry logic.
  5. Check your account execution type. Instant execution accounts requote when price moves — that's Error 138. Market execution accounts fill at the best available price, so Error 138 rarely appears. If you're on a market execution account and still seeing rejections, the problem is almost always Error 146, not slippage.
int retries = 0;
int ticket = -1;

for(retries = 0; retries < 5; retries++)
{
   RefreshRates();
   double entryPrice = Ask;

   ticket = OrderSend(
      Symbol(), OP_BUY, 0.1, entryPrice,
      30,        // Slippage: 30 points
      0, 0, "RetryOrder", 0, 0, clrBlue
   );

   if(ticket > 0) break;

   int err = GetLastError();
   if(err == 138 || err == 146)
   {
      Sleep(200);
      continue;
   }
   break; // Non-retriable error — exit loop
}

When you're handling OrderSend parameters carefully from the start, this retry pattern is straightforward to drop in. The combination of a wider slippage value, a fresh price on each attempt, and a 200ms sleep covers the majority of fast-market rejection scenarios.

One caveat worth noting: don't set slippage to an arbitrarily large number thinking it eliminates rejections. In practice, excessive slippage tolerance can result in fills that are far worse than your strategy assumed — which damages backtesting accuracy and live performance alike.

With spread filtering from Step 2 and retry logic now in place, the next failure mode to address is subtler: your EA's trigger logic may never fire at all, even when price appears to hit your level. That's the tick sampling problem, and it's covered next.

Step 4: Resolve Tick Sampling and 'Phantom' Setups

Tick gaps are a silent killer for price-level triggers. Your EA doesn't run on a continuous stream — it executes only when a new tick arrives. Between those ticks, price can move significantly, and your trigger level simply gets skipped.

Here's the core problem in practice: if price jumps from 1.0995 to 1.1005 in a single tick, the EA never sees a Bid between 1.1000–1.1007 — so any condition testing for that exact level is never true. The setup appears valid on your chart, but the entry never fires. That's a phantom setup.

The fix is switching from equality logic to range logic. Follow these steps to rebuild your trigger conditions correctly:

  1. Identify equality checks — Search your code for conditions using == against a price level or indicator value.
  2. Replace with a crossed-range condition — Instead of Bid == 1.1000, use Bid >= 1.1000 && PreviousBid < 1.1000 to detect the cross regardless of tick gaps.
  3. Store the previous tick value — Declare a static double prevBid variable and update it at the end of OnTick() so your comparison always has a reference point.
  4. Apply bar-close logic for higher timeframes — If your strategy runs on H1 or above, trigger entries only when IsNewBar() returns true. This eliminates tick noise entirely and keeps execution logic consistent.
  5. Validate in the MetaTrader Strategy Tester — Run a backtest using every tick modeling to confirm entries now fire on the correct bar without phantom gaps.

Bad code:

if(Bid == 1.1000) { // Will miss price jumps
   OrderSend(...);
}

Good code:

static double prevBid = 0;
if(prevBid < 1.1000 && Bid >= 1.1000) { // Catches the cross
   OrderSend(...);
}
prevBid = Bid;

Once your trigger logic accounts for tick gaps, you've addressed the core execution failures tied to price sampling. The next layer involves the infrastructure and error-handling patterns that determine whether correct signals actually reach your broker cleanly.

How to Summarize Key Takeaways for Reliable Execution

The previous steps covered slippage tolerance, retry logic, and tick sampling gaps individually. Here's how those pieces consolidate into a repeatable execution framework you can apply to any fast-market EA.

Quick-reference checklist for reliable MT4 execution:

  • Infrastructure: Deploy your EA on a co-located VPS physically close to your broker's server. Because MT4 is a single-threaded application per the MetaQuotes MQL4 documentation, execution delays stack up fast when the platform is busy processing the previous tick — latency compounds the problem further.
  • Error Handling: Always code retry loops targeting Error 138 (Requote) and Error 146 (Trade Context Busy). Missing these two error codes is one of the most common reasons an EA silently skips entries. If you're unsure whether your current EA handles them correctly, reviewing how missing error-handling codes fail in live conditions is a practical starting point.
  • Logic: Replace exact-price triggers with range-based conditions — typically a spread-adjusted buffer — so tick gaps don't cause phantom setups that never execute.
  • Monitoring: Audit the Experts tab regularly. Distinguish between logic failures (the condition never triggered) and execution errors (the condition triggered but the order failed). They require different fixes and are frequently confused.

Applying all four consistently is what separates an EA that performs well in the MetaTrader Strategy Tester from one that holds up during live news-driven volatility. Once these are locked in, the next logical step is stress-testing the full build and deciding whether MT4's architecture still fits your strategy's demands.

How to Finalize Your Build and Next Steps

Working through these nine steps, you've transformed a fragile Expert Advisor into an execution-aware system built to handle fast market conditions. That's a meaningful shift — not just in code, but in how the EA interacts with broker execution conditions in practice.

What you built:

  • A slippage tolerance layer that filters out fills beyond acceptable price deviation
  • Retry logic that re-attempts order submission after requotes without duplicating positions
  • Tick sampling fixes that eliminate phantom setups triggered by stale price data
  • A consolidated execution framework where each layer reinforces the next

Next steps to validate and extend this work:

  1. Stress-test on a demo account during high-impact news events. NFP releases, Fed announcements, and CPI prints all generate the kind of spread spikes and thin liquidity that expose gaps in order handling logic. If the EA holds up there, it's ready for controlled live testing.
  2. Run the MetaTrader Strategy Tester with variable spread enabled. Static spread backtests won't surface the execution failures you've just fixed. Use tick data with realistic spread modeling.
  3. Audit AI-generated components before deployment. If any part of this EA originated from ChatGPT or Claude, review it against the patterns covered in this tutorial — AI-generated code often skips the error-handling and retry logic that live trading demands.
  4. Consider MT5 if MT4 limitations persist. Multi-threaded execution and improved order handling in MT5 resolve several constraints that no amount of MQL4 workarounds can fully address.
  5. Engage professional development for complex needs. High-frequency strategies, multi-symbol execution, or broker-specific compatibility issues benefit from custom MQL4/MQL5 development. With over 9,000 projects completed, MT4Programming provides the kind of hands-on EA debugging and Code Validation that turns a working prototype into a reliable, deployable system.

The framework here is solid. What determines long-term performance is disciplined testing and a willingness to iterate based on what live Broker Execution Conditions actually reveal.

ROI Calculator

See how MT4 Membership rewards can pay you back in MT4 Credits.

$
$
Enter spend to calculate ROI
Monthly rewards $0.00
Yearly rewards $0.00
Retro Rewards $0.00
? New Registration (25,000 pts) $25.00
Rewards may be applied up to 25% per project. Milestones and Flash Alerts may unlock additional rewards.
Start Earning 25% Back

Quick Quote

Send the basics. We will review your request.

Use the Full Project Specification Form →