MQL5 Expert Advisor Development, Part 1: Build and Test a Safe Skeleton

MQL5 Expert Advisor Development, Part 1: Build and Test a Safe Skeleton

This first part is intentionally modest: create a real MQL5 Expert Advisor, compile it, and run it in MetaTrader 5 Strategy Tester without placing any orders. The goal is to confirm the toolchain and understand OnInit(), OnTick(), and OnDeinit() before money or trading logic enters the picture.

The original 2019 version of this article mixed an MQL5 title with a MetaTrader 4 download, MQL4 Wizard, and MT4 screenshots. Those screenshots are not reused here. They are historical MT4 interface captures, not an accurate guide to a current MQL5 workflow, and current MetaTrader 5 layouts can also vary by build.

This is a software-development tutorial, not a claim that an Expert Advisor will be profitable. Keep automated trading disabled for this exercise, use Strategy Tester and a demo account, and do not move later experiments to a live account until the strategy, risk limits, failure behavior, and operating process have been independently validated.

MetaTrader 5, MQL5, and the file types

Use the MetaTrader 5 toolchain throughout this series:

  • MetaTrader 5 is the terminal and contains Strategy Tester.
  • MetaEditor is the editor and compiler bundled with the terminal.
  • MQL5 is the language used by the Expert Advisor in this article.
  • The editable source file ends in .mq5; successful compilation creates an .ex5 executable in the same program folder.

MetaTrader 4 uses MQL4 and .mq4/.ex4 files. Some concepts look similar, but MT4 code and screenshots are not a drop-in MQL5 tutorial.

Download MetaTrader 5 from the official MetaQuotes download page or use an installation supplied through an organization you already trust. This article does not require a broker referral, partner link, live account, deposit, or paid service.

Create an Expert Advisor template

Open MetaEditor from MetaTrader 5 by pressing F4. You can also use Create in MetaEditor from the Expert Advisors section of the terminal’s Navigator.

In MetaEditor:

  1. Choose File > New.
  2. Select Expert Advisor (template), not Expert Advisor (generate).
  3. Name it Part1ObserverEA. Keep it under MQL5/Experts.
  4. Author and link fields are optional; do not put account details or credentials in them.
  5. The standard OnInit, OnDeinit, and OnTick handlers are already part of an EA template. No additional event handlers are needed in Part 1.

The official MQL5 Wizard documentation explains that a template is placed under MQL5/Experts and includes the three main event-handler skeletons. If MetaEditor opens an MQL4/Experts path or generates an .mq4 file, stop: the wrong platform/editor instance is open.

Replace the template with a no-trade observer

Use this complete scaffold:

#property copyright "Lachlan Chen"
#property version   "1.00"
#property description "Part 1 observer: logs lifecycle events and never trades."

input bool InpLogFirstTicks = true;
input uint InpTicksToLog    = 3;

ulong g_ticks_seen = 0;

int OnInit()
  {
   PrintFormat("Part1ObserverEA initialized: symbol=%s period=%s",
               _Symbol,
               EnumToString(_Period));
   return(INIT_SUCCEEDED);
  }

void OnTick()
  {
   g_ticks_seen++;

   if(InpLogFirstTicks && g_ticks_seen <= InpTicksToLog)
     {
      PrintFormat("tick=%I64u symbol=%s server_time=%s",
                  g_ticks_seen,
                  _Symbol,
                  TimeToString(TimeCurrent(), TIME_DATE|TIME_SECONDS));
     }
  }

void OnDeinit(const int reason)
  {
   PrintFormat("Part1ObserverEA stopped: reason=%d ticks_seen=%I64u",
               reason,
               g_ticks_seen);
  }

This program does three things only:

  • OnInit() records the test symbol and timeframe and returns INIT_SUCCEEDED.
  • OnTick() counts incoming tick events and optionally logs the first few.
  • OnDeinit() records why the program stopped and how many tick events it handled.

There is no CTrade object, OrderSend(), position operation, or trade request. Changing InpTicksToLog only changes the number of journal messages; it cannot enable trading.

MetaQuotes recommends the integer-returning form of `OnInit()`, because it can report initialization failure. `OnTick()` is called for a new tick on the chart/test symbol. Disabling automated trading blocks trade requests but does not stop an EA from receiving tick events, which is useful for a harmless observer like this one.

Compile and read the result

Save the file and press F7, or click Compile. MetaEditor should create Part1ObserverEA.ex5 alongside the .mq5 source. Read the Errors tab rather than assuming the toolbar click succeeded.

A clean compile means the language syntax and referenced functions are accepted by that MetaEditor build. It does not prove that the EA’s future trading rules are correct, safe, or profitable. Recompile after every source change; otherwise the terminal can continue running the older .ex5 file.

Run the scaffold in Strategy Tester

Return to MetaTrader 5 and open View > Strategy Tester. The current tester has several task layouts, so wording and panel placement may differ slightly between builds.

For this first run:

  1. Choose a single Expert Advisor test and select Part1ObserverEA.
  2. Select a symbol that has historical data, a timeframe, and a short past date range.
  3. Leave optimization off.
  4. Use a local testing agent. Remote/cloud agents can suppress Print() output, so they are a poor fit for this logging exercise.
  5. Start the test, then inspect the Journal.

The journal should show one initialization message, up to three tick messages with the default input, and a deinitialization message. The result should contain no deals because this exact source contains no trade request.

A flat balance line here means only “no trades were placed.” It is not a strategy result, a baseline return, or evidence of low risk. If the tester shows deals, confirm that the selected EA is Part1ObserverEA and that the compiled .ex5 corresponds to the source above.

The official Strategy Tester guide documents symbol, period, date, tick-generation, execution-delay, visual-mode, and journal settings. Record those settings whenever you later compare test results.

Before adding an order: design the validation path

Part 2 may add a signal or order path, but code that compiles is nowhere near ready for live trading. Use a staged process:

  1. Deterministic unit checks: verify calculations on small, known inputs.
  2. Historical backtest: choose an appropriate tick mode and confirm that orders follow the written rules.
  3. Unseen forward period: keep later data out of parameter selection and compare it separately.
  4. Cost and execution stress: include spread, commission, swap where relevant, and non-zero execution delay or slippage assumptions.
  5. Demo forward test: observe the EA in current market conditions without real capital.
  6. Failure tests: disconnect data, reject orders, restart the terminal, and verify that ambiguous state produces no new trade.
  7. Independent review: check position sizing, maximum exposure, stop behavior, logs, and a manual kill switch.

MetaTrader 5 supports a separate forward period during optimization specifically to reduce parameter fitting to one historical interval. It also provides execution-delay emulation and advanced commission settings. Use them; an idealized zero-cost test is not a deployable estimate.

Backtests remain sensitive to the broker’s symbol specification and history, bid/ask behavior, tick model, account type, spread, commissions, swap, latency assumptions, and the dates selected. A smooth balance curve can be produced by overfitting and is not a promise about future results.

If machine learning is added later

Machine learning adds another route for accidental look-ahead. Split time-ordered data before fitting preprocessing, feature selection, or model parameters. A scaler, threshold, feature list, or hyperparameter chosen using the future test period leaks information even if the final model never receives a column literally named “future price.”

Keep training, validation, and final forward periods in chronological order. Fit transformations only on the training portion, preserve a genuinely untouched final period, and account for when each feature would actually have become available. The scikit-learn documentation gives concise references for preventing data leakage and time-ordered cross-validation.

An ML score is not a trading result. Conversion from predictions to orders introduces turnover, spread, commissions, slippage, sizing, and execution failure. Evaluate the complete decision and execution path in Strategy Tester and on a demo account before considering any live use.

What Part 1 has accomplished

You now have a genuine MQL5 .mq5 source file, an .ex5 produced by MetaEditor, and a Strategy Tester run that exercises the EA lifecycle without trading. That is the right foundation: first prove that the environment and event flow are understood; only then add one small, testable behavior at a time.

Primary references

Checked September 1, 2026:

Leave a Reply