Master MetaEditor Debugger: Step Through MQL4/MQL5 Code

Learn to use MetaEditor's debugger to step through MQL4/MQL5 code, set breakpoints, inspect variables, and fix logic errors in EAs and indicators.

master-metaeditor-debugger-step-through-mql4-mql5

Introduction

Every MQL developer hits that moment where a compiled EA or indicator just doesn't behave as expected. Maybe your trailing stop logic fires late, or your custom indicator draws lines in the wrong place. You stare at the code, re-read it twice, and still can't spot the bug. That's where the MetaEditor debugger saves your afternoon.

This guide walks you through using the MetaEditor debugger to step through MQL4 and MQL5 code line by line. You'll learn to set breakpoints, inspect variables in real time, step into functions, and identify logic errors that print statements alone won't catch. By the end, you'll have a repeatable workflow for debugging any EA, custom indicator, or script directly inside MetaTrader's development environment.

I've been writing MQL code for years, and I still lean on the debugger weekly. It's one of those tools that looks intimidating at first but becomes second nature after a few sessions. Let's get you there.

Prerequisites

Before you start debugging, make sure you have these basics in place:

  • MetaTrader 4 or MetaTrader 5 installed and running. The debugger works in both, though I'll note differences where they matter. MT4's debugger is slightly older and doesn't handle multi-threaded debugging as well as MT5's.
  • MetaEditor — it ships with the platform. Open it from MT4/MT5: Tools → MetaQuotes Language Editor, or press F4. In MT5, you can also click the MetaEditor icon on the standard toolbar.
  • An MQL4 or MQL5 project — an EA, indicator, or script you want to debug. It must compile without syntax errors (warnings are okay). The debugger doesn't fix compilation failures; it finds runtime logic bugs.
  • A demo account connected to the platform. The debugger runs against live market data or history, so you need a connection. A demo account is fine, but make sure it's active — if your broker logs you out after inactivity, the debugger may hang.
  • AutoTrading enabled in the terminal. Go to Tools → Options → Expert Advisors and check "Allow Automated Trading". In MT4, also ensure the "AutoTrading" button on the toolbar is green (not gray). Without this, the debugger can't execute your EA's trades.
  • Basic familiarity with the MQL language — variables, functions, loops, conditions. You don't need to be an expert, but you should know what your code intends to do.

One important note for MT5 users: The debugger in MetaEditor 5 supports multi-threaded debugging better than MT4's version. If you're using custom indicators that run on multiple timeframes, MT5's debugger handles that more gracefully. I've had MT4's debugger crash on complex indicators with multiple buffers — MT5 is more stable there.

Step-by-Step: Debugging Your First MQL Program

Let me walk you through a real debugging session. I'll use a simple MQL5 EA that's supposed to place a buy order when the fast moving average crosses above the slow one. But it has a logic bug — it places orders on every tick instead of waiting for new bars. We'll catch that with the debugger.

Step 1: Open Your Code in MetaEditor

Press F4 in MetaTrader to launch MetaEditor. Open your MQL file from the Navigator panel on the left. If you don't see it, go to View → Navigator or press Ctrl+N. Double-click the file to open it in the editor. The file must be the active tab — the debugger always starts the file you're currently viewing.

For this tutorial, create a quick test EA: File → New → Expert Advisor, name it "DebugDemo", and paste this intentionally buggy code:

//+------------------------------------------------------------------+
//|                                          DebugDemo.mq5          |
//+------------------------------------------------------------------+
input int FastMA = 10;
input int SlowMA = 20;
input double LotSize = 0.1;

int fastHandle, slowHandle;
double fastBuffer[], slowBuffer[];

int OnInit()
  {
   fastHandle = iMA(_Symbol, PERIOD_CURRENT, FastMA, 0, MODE_SMA, PRICE_CLOSE);
   slowHandle = iMA(_Symbol, PERIOD_CURRENT, SlowMA, 0, MODE_SMA, PRICE_CLOSE);
   if(fastHandle == INVALID_HANDLE || slowHandle == INVALID_HANDLE)
      return(INIT_FAILED);
   ArraySetAsSeries(fastBuffer, true);
   ArraySetAsSeries(slowBuffer, true);
   return(INIT_SUCCEEDED);
  }

void OnTick()
  {
   CopyBuffer(fastHandle, 0, 0, 3, fastBuffer);
   CopyBuffer(slowHandle, 0, 0, 3, slowBuffer);
   if(fastBuffer[1] > slowBuffer[1] && fastBuffer[2] <= slowBuffer[2])
     {
      MqlTradeRequest req = {};
      MqlTradeResult res = {};
      req.action = TRADE_ACTION_DEAL;
      req.symbol = _Symbol;
      req.volume = LotSize;
      req.type = ORDER_TYPE_BUY;
      req.price = SymbolInfoDouble(_Symbol, SYMBOL_ASK);
      req.deviation = 10;
      OrderSend(req, res);
     }
  }

Compile it (F7). It should pass without errors. If not, fix obvious typos first — missing semicolons or mismatched brackets are the usual suspects.

Step 2: Set Breakpoints

Breakpoints tell the debugger where to pause execution. Click in the left margin next to a line number — a red dot appears. For our demo, set a breakpoint on the if(fastBuffer[1] > slowBuffer[1] ...) line (line 28 in my editor). You can also set multiple breakpoints; the debugger stops at the first one it hits.

Pro tip: Set breakpoints on condition checks, function calls, or variable assignments you suspect are wrong. Don't set them inside loops that run thousands of times unless you plan to step through each iteration — you'll be clicking "Continue" forever. Instead, use conditional breakpoints (see the Tips section) to pause only when a specific condition is met.

In MT5, you can also set breakpoints on function entries by clicking in the margin next to the function declaration line. This is useful for catching the first call to OnTick() or OnCalculate().

Step 3: Start the Debugger

In MetaEditor, go to Debug → Start Debugging or press F5. A dialog appears asking for debug parameters:

ParameterRequiredTypical ValueNotes
SymbolYesEURUSDPick any symbol from your Market Watch. Use a liquid pair for realistic tick flow.
PeriodYesM15Use the timeframe your EA expects. M1 generates many ticks fast — good for stress testing.
Start dateOptionalLeave blank to start from current bar. Set a past date if you want to replay history.
Visual modeNoUncheckedCheck only if you want to see the chart update step by step. Slows things down but helps visual bugs.

Click OK. The debugger attaches to a hidden MetaTrader instance and starts running your code. It pauses at the first breakpoint it encounters. You'll see a yellow arrow in the left margin pointing to the current line. The debugger also opens a hidden chart window — you can't interact with it directly, but it receives live ticks.

Step 4: Step Through the Code

Once paused, you control execution with these keys:

  • F10 (Step Over) — Executes the current line and moves to the next one. If the line calls a function, it runs that function fully and returns — you don't see inside it. Use this for code you trust, like standard library calls.
  • F11 (Step Into) — Jumps into the function called on the current line. Use this to inspect what happens inside CopyBuffer, OrderSend, or your own helper functions. Note: you can't step into system DLLs or built-in MQL functions — the debugger will just step over them.
  • Shift+F11 (Step Out) — Runs the rest of the current function and returns to the caller. Handy when you've stepped into a function and realize it's fine — you want to get back quickly without stepping through every line.
  • F5 (Continue) — Resumes normal execution until the next breakpoint. If no more breakpoints are hit, the EA runs until you stop debugging.

In our demo, when the debugger stops at the if condition, hover over fastBuffer[1] and slowBuffer[1]. You'll see their current values in a tooltip. That's the quickest way to check if your moving averages are computed correctly. If the values look like garbage (e.g., 0.0 or huge numbers), your CopyBuffer call might be failing — check the handle values.

Step 5: Inspect Variables and Watch Expressions

The Watch window (View → Watch or Ctrl+W) is your best friend. Add variables you want to monitor: right-click in the Watch window, select "Add Watch", and type the variable name. For our demo, add fastBuffer[1], slowBuffer[1], and a flag like NewBar() if you had one. You can also add expressions like fastBuffer[1] - slowBuffer[1] to see the difference directly.

The Locals window (View → Locals) automatically shows all local variables in the current function. In OnTick(), you'll see fastBuffer, slowBuffer, req, and res. Expand arrays to see individual elements. The Locals window updates as you step — you can watch values change in real time.

Real scenario: In the demo, step through a few ticks. You'll notice that every time OnTick() runs, the condition fastBuffer[1] > slowBuffer[1] && fastBuffer[2] <= slowBuffer[2] evaluates to true on multiple consecutive ticks because the cross just happened. That's the bug — no check for a new bar. The fix would be adding a NewBar() function and only trading once per bar. The debugger makes this obvious in 30 seconds.

Advanced Debugging Features

Beyond basic stepping, MetaEditor offers several advanced features that experienced developers rely on.

Conditional Breakpoints

Right-click an existing breakpoint and select "Breakpoint Properties". In the dialog that appears, you can set a condition — a boolean expression that must be true for the breakpoint to trigger. For example, to pause only when the crossover condition is met, use:

fastBuffer[1] > slowBuffer[1] && fastBuffer[2] <= slowBuffer[2]

This is a lifesaver when debugging EAs on M1 charts where OnTick() fires dozens of times per second. Without conditional breakpoints, you'd be pressing F5 constantly.

Breakpoint Hit Count

In the same Breakpoint Properties dialog, you can set a hit count. For instance, "Hit count = 10" means the breakpoint only activates after the 10th time execution reaches that line. This is useful for catching bugs that manifest after a specific number of iterations — like an array index error that only appears on the 50th bar.

Data Breakpoints (MQL5 Only)

MT5's debugger supports data breakpoints — they pause execution when the value of a specific variable changes. To set one, right-click a variable in the Locals or Watch window and select "Break when value changes". This is incredibly powerful for tracking down why a variable gets corrupted. For example, if

Community

Clap for the article and open comments only when you want to read them.

0 claps0 comments

Related articles