Skip to content

What is an Expert Advisor?

Understand what EAs are, how they work, their advantages and risks, and how to run them safely.

💡
Before you start

A C++ compiler and a terminal. No MetaTrader, no broker, no account and no money. Most Linux machines already have g++; on macOS run xcode-select --install once; on Windows use the Windows Subsystem for Linux or MSYS2.

If you have done an earlier MQL5 page you already have the two setup files — reuse them and start at step 3.

Nothing here trades, and nothing here is trading advice. The programs below model the terminal's event sequence so you can watch it, which is far easier to follow than reading a description of it.

Expert Advisors Explained

An Expert Advisor (EA) is an automated trading program that runs on MetaTrader 5 and can analyze the market, make trading decisions, and execute orders without human intervention. EAs implement your trading strategy in code, running 24/7 as long as MT5 is open.

EAs are one of the most powerful features of the MT5 platform. They remove emotion from trading, execute with perfect discipline, and can monitor multiple instruments simultaneously — things that are difficult or impossible for manual traders.

How EAs Work

An EA is attached to a chart and responds to market events:

  • OnInit() — Runs once when the EA starts. Initializes settings, creates indicator handles, validates parameters.
  • OnTick() — Runs on every new price tick (quote). This is where the trading logic lives — analyze conditions, decide whether to open/close/modify positions.
  • OnTrade() — Runs when a trade event occurs (order filled, position modified, etc.).
  • OnTimer() — Runs at a set interval (configured with EventSetTimer()).
  • OnDeinit() — Runs when the EA is removed. Cleans up resources.

Advantages of Automated Trading

  • No emotions — An EA follows rules exactly. It does not hesitate, panic, or get greedy.
  • Speed — EAs can analyze and execute in milliseconds, capturing opportunities you would miss manually.
  • Consistency — Every trade follows the same rules. No "this time is different" exceptions.
  • 24/7 operation — The forex market runs 24 hours. An EA never sleeps.
  • Multi-instrument — One EA can monitor dozens of symbols simultaneously.
  • Backtesting — Test your strategy on years of historical data before risking real money.

Risks and Limitations

⚠️
EAs are not magic money machines

An EA is only as good as the strategy it implements. A poorly designed strategy will lose money faster as an EA because it executes consistently wrong decisions without hesitation.

  • Strategy risk — The underlying strategy may be flawed, overfitted, or unsuitable for current market conditions.
  • Technical failures — Internet disconnection, server outages, VPS crashes, and MT5 bugs can interrupt execution.
  • Slippage and spreads — Real execution differs from backtesting. Spreads widen during news, orders slip during volatility.
  • Over-optimization — Tweaking parameters to fit historical data perfectly ("curve fitting") creates strategies that fail on live markets.
  • Black swan events — No algorithm can predict sudden market crashes, flash crashes, or geopolitical shocks.

Running an EA Safely

1
Enable Algo Trading

In MT5, click the "Algo Trading" button in the toolbar (or press Ctrl + E). This is the global master switch. When it is off, no EA can trade.

2
Attach to a chart

Drag the EA from Navigator > Expert Advisors onto a chart. In the properties dialog, check "Allow Algo Trading" for this specific EA.

3
Start with a demo account

Always run new EAs on a demo account first. Monitor for at least 2-4 weeks before considering live deployment.

4
Use a VPS for production

For live trading, run MT5 on a Virtual Private Server (VPS) near your broker's server for reliable 24/7 uptime and low latency. MT5 offers built-in VPS hosting through MQL5.community.

Find Out What an Expert Advisor Actually Is, in Five Steps

An Expert Advisor is often described as “a robot that trades for you”, which is accurate and unhelpful. What it actually is, is a function the terminal calls every time the price changes, with permission to place orders — and almost everything worth knowing follows from those three facts. In the next half hour you will run all three program types side by side, watch an EA's whole lifetime from start to shutdown, and finish with an honest list of what automation does and does not give you. Every line of output below came from running these files.

1
Save the header that supplies MetaTrader's built-ins

Go: open a terminal in a folder you can write to — cd ~/Desktop on macOS or Linux, cd %USERPROFILE%\Desktop on Windows.

Do: save this as mql5.h. If you already have it from an earlier page, reuse that copy and skip to step 3.

// mql5.h - a small shim that lets a useful SUBSET of MQL5 compile and run
// with an ordinary C++ compiler, so you can practise the language without
// MetaTrader. It is NOT MetaTrader and does not trade anything.
#pragma once
#include <cstdio>
#include <cmath>
#include <string>
#include <vector>
#include <iostream>
#include <sstream>

typedef std::string string;
typedef long long datetime;
#define input static

template <typename T> int ArraySize(const std::vector<T> &a) { return (int)a.size(); }
template <typename T> void ArrayResize(std::vector<T> &a, int n) { a.resize(n); }

inline void Print() { std::cout << std::endl; }
template <typename T, typename... Rest>
void Print(const T &first, const Rest &...rest) {
    std::cout << first;
    if constexpr (sizeof...(rest) > 0) Print(rest...);
    else std::cout << std::endl;
}

inline string DoubleToString(double v, int digits = 8) {
    char buf[64]; snprintf(buf, sizeof buf, "%.*f", digits, v); return string(buf);
}
inline string IntegerToString(long long v) { return std::to_string(v); }
inline double MathAbs(double v)  { return std::fabs(v); }
inline double MathMax(double a, double b) { return a > b ? a : b; }
inline double MathMin(double a, double b) { return a < b ? a : b; }
inline int    MathRound(double v) { return (int)std::lround(v); }

You should see: nothing — this is a header file. Confirm with ls mql5.h (Windows: dir mql5.h).

If not: no compiler? On Ubuntu or Debian sudo apt install g++; on macOS xcode-select --install; on Windows use the Windows Subsystem for Linux or MSYS2.

2
Save the build script

Go: the same folder.

Do: save this as build.sh. It compiles a .mq5 file and executes the result. Make it executable with chmod +x build.sh, or invoke it as sh build.sh.

#!/bin/sh
# build.sh NAME  ->  compiles NAME.mq5 and runs it.
# MQL5 is close enough to C++ that a C++ compiler can run the subset used here,
# with mql5.h supplying the handful of built-ins MetaTrader would provide.
# Uses whichever compiler you have.
set -e
NAME="$1"
if command -v g++ >/dev/null 2>&1 && g++ -x c++ -std=c++17 -o "$NAME" "$NAME.mq5" 2>/dev/null; then
    :
elif command -v clang++ >/dev/null 2>&1; then
    clang++ -x c++ -std=c++17 -o "$NAME" "$NAME.mq5"
else
    echo "No C++ compiler found. Install g++ (Linux: apt install g++, macOS: xcode-select --install)." >&2
    exit 1
fi
"./$NAME"

You should see: nothing — this is the toolchain. ls should show both mql5.h and build.sh.

If not: Permission denied means the chmod was skipped; use sh build.sh NAME, which needs no execute bit.

3
Run all three program types and count the calls

Go: the same folder.

Do: save this as events.mq5 and run sh build.sh events.

#include "mql5.h"

//+------------------------------------------------------------------+
//| Three program types, three entry points, three lifetimes.         |
//+------------------------------------------------------------------+

int calls_indicator = 0, calls_ea = 0, calls_script = 0;

// An INDICATOR: called for calculation, may draw, may never trade.
int OnCalculate_demo(int rates_total) { calls_indicator++; return rates_total; }

// An EXPERT ADVISOR: called on every price change, may trade.
void OnTick_demo() { calls_ea++; }

// A SCRIPT: called once, then unloads.
void OnStart_demo() { calls_script++; }

void OnStart()
{
    OnStart_demo();                        // a script runs once
    for(int tick = 0; tick < 25; tick++)   // 25 price changes arrive
    {
        OnCalculate_demo(1000);
        OnTick_demo();
    }

    Print("");
    Print("PROGRAM TYPE          ENTRY POINT     CALLS   CAN TRADE?");
    Print("--------------------------------------------------------");
    Print("Script                OnStart()        ", calls_script, "      yes");
    Print("Indicator             OnCalculate()   ", calls_indicator, "      NO");
    Print("Expert Advisor        OnTick()        ", calls_ea, "      yes");

    Print("");
    Print("An indicator CANNOT place an order. That is not a limitation of");
    Print("your skill -- the terminal refuses trade calls from indicators,");
    Print("because indicators run on the interface thread and a blocking");
    Print("trade call there would freeze the chart.");
    Print("");
    Print("So the shape of almost every automated system is: an indicator");
    Print("computes, and an EA reads the indicator and decides.");
    Print("");
    Print("A script is the one to reach for when you want something done");
    Print("ONCE -- close every position, delete every order, export history.");
}

int main() { OnStart(); return 0; }

You should see: one call, twenty-five calls, and twenty-five calls:


PROGRAM TYPE          ENTRY POINT     CALLS   CAN TRADE?
--------------------------------------------------------
Script                OnStart()        1      yes
Indicator             OnCalculate()   25      NO
Expert Advisor        OnTick()        25      yes

An indicator CANNOT place an order. That is not a limitation of
your skill -- the terminal refuses trade calls from indicators,
because indicators run on the interface thread and a blocking
trade call there would freeze the chart.

So the shape of almost every automated system is: an indicator
computes, and an EA reads the indicator and decides.

A script is the one to reach for when you want something done
ONCE -- close every position, delete every order, export history.

An indicator cannot place an order, and that is enforced by the terminal. It is not a gap in your knowledge or a setting to find. Indicators run on the interface thread, where a trade call — which waits for a server — would freeze the chart, so the platform simply refuses.

That single restriction determines the shape of nearly every automated system: an indicator computes values, and an Expert Advisor reads those values and decides. If you have written an indicator you are pleased with, the automation step is a separate program, not a modification of it.

A script is the third tool and the most underused: it runs once and unloads. Closing every position, deleting every pending order, exporting your trade history — all scripts.

If not: the counts here are produced by fixed loops, so they cannot vary; if the script count is not 1, OnStart_demo was called inside the loop.

4
Watch an EA's whole life, including the part people forget

Go: the same folder.

Do: save this as lifecycle.mq5 and run sh build.sh lifecycle.

#include "mql5.h"

//+------------------------------------------------------------------+
//| An EA's life: OnInit, many OnTicks, OnDeinit -- and what survives.|
//+------------------------------------------------------------------+

int    g_ticks   = 0;          // survives the whole run
double g_ma_period = 0;        // set once, in OnInit

int OnInit(double period)
{
    if(period < 2)
    {
        Print("OnInit: period ", DoubleToString(period, 0), " invalid -> refuse to start");
        return 1;              // INIT_PARAMETERS_INCORRECT
    }
    g_ma_period = period;
    g_ticks = 0;
    Print("OnInit: validated, period ", DoubleToString(g_ma_period, 0), " -> start");
    return 0;                  // INIT_SUCCEEDED
}

void OnTick() { g_ticks++; }

void OnDeinit(int reason)
{
    string why = (reason == 1) ? "chart closed"
               : (reason == 2) ? "recompiled"
               : (reason == 3) ? "parameters changed"
                               : "removed";
    Print("OnDeinit: ", why, " after ", g_ticks, " ticks -> clean up here");
}

void OnStart()
{
    Print("attempt 1 -- a bad parameter");
    if(OnInit(0) != 0) Print("   the EA never runs. No ticks are delivered.");

    Print("");
    Print("attempt 2 -- a good parameter");
    if(OnInit(20) == 0)
    {
        for(int i = 0; i < 40; i++) OnTick();
        OnDeinit(3);
    }

    Print("");
    Print("OnInit runs ONCE. OnTick runs on every price change. OnDeinit runs");
    Print("when the EA is removed, recompiled, or its settings change -- and");
    Print("note that changing a setting is a full stop and restart, so anything");
    Print("remembered in a global variable is lost at that moment.");
    Print("");
    Print("Which is why an EA should never rely on its own memory of what it");
    Print("has open. Ask the terminal with PositionsTotal(); that survives");
    Print("restarts, reconnections and manual intervention.");
}

int main() { OnStart(); return 0; }

You should see: one EA refusing to start and another running and shutting down:

attempt 1 -- a bad parameter
OnInit: period 0 invalid -> refuse to start
   the EA never runs. No ticks are delivered.

attempt 2 -- a good parameter
OnInit: validated, period 20 -> start
OnDeinit: parameters changed after 40 ticks -> clean up here

OnInit runs ONCE. OnTick runs on every price change. OnDeinit runs
when the EA is removed, recompiled, or its settings change -- and
note that changing a setting is a full stop and restart, so anything
remembered in a global variable is lost at that moment.

Which is why an EA should never rely on its own memory of what it
has open. Ask the terminal with PositionsTotal(); that survives
restarts, reconnections and manual intervention.

OnInit() is where an EA decides whether it is safe to run at all. Returning INIT_PARAMETERS_INCORRECT means the terminal does not attach it and says why — far better than starting with a period of zero and dividing by it forty minutes later.

The detail that surprises people is in OnDeinit's reason. Changing a setting in the EA's dialog is not an adjustment — it is a full stop and restart. Every global variable resets, every counter goes back to zero, and any “I already have a position open” flag is forgotten while the position itself carries on existing.

Which is the practical rule this page is really for: never rely on an EA's memory of what it has open. Ask the terminal — PositionsTotal() and PositionSelectByTicket() — because that survives restarts, reconnections, setting changes, and you closing something by hand.

If not: if attempt 1 prints tick output, the return value of OnInit is not being checked — the guard is the whole point, and in a real EA the terminal performs that check for you.

5
Be honest about what automation buys

Go: the same folder.

Do: save this as cannot.mq5 and run sh build.sh cannot.

#include "mql5.h"

//+------------------------------------------------------------------+
//| What an EA can decide, and what it cannot know.                   |
//+------------------------------------------------------------------+
void OnStart()
{
    struct Row { string claim; bool truth; string note; };

    Row rows[9] = {
      {"react to a price change within milliseconds", true,  "faster than any human"},
      {"apply the same rule every single time",       true,  "no mood, no fatigue"},
      {"trade while you sleep",                       true,  "if the terminal stays running"},
      {"place, modify and close orders",              true,  "OnTick may trade"},
      {"know that a central bank meets tomorrow",     false, "unless you code a calendar"},
      {"notice the strategy has stopped working",     false, "it applies the rule regardless"},
      {"survive your computer sleeping",              false, "no terminal, no ticks"},
      {"guarantee a stop is filled at its price",     false, "gaps and slippage are real"},
      {"turn a losing rule into a winning one",       false, "it executes; it does not think"},
    };

    int can = 0, cannot = 0;
    for(int i = 0; i < 9; i++)
    {
        Print(rows[i].truth ? "  CAN    " : "  CANNOT ", rows[i].claim,
              "   (", rows[i].note, ")");
        if(rows[i].truth) can++; else cannot++;
    }

    Print("");
    Print("can    : ", can);
    Print("cannot : ", cannot);
    Print("");
    Print("Every item in the first group is about SPEED and CONSISTENCY.");
    Print("Every item in the second is about JUDGEMENT and CONTEXT.");
    Print("");
    Print("An EA is an execution machine. It removes hesitation, tiredness and");
    Print("the temptation to move a stop -- which is genuinely valuable, and is");
    Print("a completely different thing from having an edge.");
    Print("");
    Print("The last row is the one that costs people money: automating a rule");
    Print("that loses money makes it lose money faster and more reliably.");
}

int main() { OnStart(); return 0; }

You should see: four things an EA can do and five it cannot:

  CAN    react to a price change within milliseconds   (faster than any human)
  CAN    apply the same rule every single time   (no mood, no fatigue)
  CAN    trade while you sleep   (if the terminal stays running)
  CAN    place, modify and close orders   (OnTick may trade)
  CANNOT know that a central bank meets tomorrow   (unless you code a calendar)
  CANNOT notice the strategy has stopped working   (it applies the rule regardless)
  CANNOT survive your computer sleeping   (no terminal, no ticks)
  CANNOT guarantee a stop is filled at its price   (gaps and slippage are real)
  CANNOT turn a losing rule into a winning one   (it executes; it does not think)

can    : 4
cannot : 5

Every item in the first group is about SPEED and CONSISTENCY.
Every item in the second is about JUDGEMENT and CONTEXT.

An EA is an execution machine. It removes hesitation, tiredness and
the temptation to move a stop -- which is genuinely valuable, and is
a completely different thing from having an edge.

The last row is the one that costs people money: automating a rule
that loses money makes it lose money faster and more reliably.

Every item in the first group is about speed and consistency; every item in the second is about judgement and context. That is the whole boundary, and it is worth having clearly in mind before spending months on one.

The real value of an EA is not that it is clever — it is that it is not tempted. It will not widen a stop because the trade is nearly back to break-even, will not skip a signal because the last three lost, and will not take a position at midnight because it is bored. For most people those three habits cost more than any strategy choice.

And the last row is the one that empties accounts. Automating a rule that loses money does not improve it; it applies it faster, more often, and without the hesitation that used to limit the damage. Automation multiplies whatever the rule already was.

One practical item from the middle of that list: an EA needs the terminal running. A laptop that sleeps stops receiving ticks entirely, which is why people who run EAs seriously run them on a machine that stays awake.

If not: this prints a fixed table and cannot fail; if the counts are not 4 and 5, a row's boolean was changed.

🎉
Check yourself before moving on

Without scrolling up: someone has an indicator that reliably marks good entries and wants to “add trading to it” so it opens positions automatically. Explain what they will have to do instead, and one thing they should check about the indicator first. Answer: they cannot add trading to an indicator — step 3 showed the terminal refuses trade calls from indicators, because they run on the interface thread where a call that waits for the server would freeze the chart. What they need is a separate Expert Advisor that reads the indicator's values, using iCustom to get a handle and CopyBuffer to read the buffer, and makes the trade decision itself. The thing to check first is whether the indicator repaints: if marking a bar requires bars that come after it, the historical chart will look excellent and the live signal will arrive late or change after the fact, so the EA would be automating a signal that did not exist at the time it appeared to. Reading the indicator's buffer at index 1 rather than 0, and watching the right-hand edge in the visual tester, will settle it.

Now do it without the page: write down, in one sentence each, the three things your own trading rule needs to decide: when to enter, how much, and when to leave. Then mark which of the three you could state precisely enough for a computer to follow with no further interpretation. Most people find the first is easy and the other two are vague — and those two are where an EA's behaviour actually comes from, which is why the next pages are about order management and risk rather than about signals.

EA vs Indicator vs Script

  • Indicators — Analyze and display data. Cannot place trades. Run in the indicator thread (limited resources).
  • Expert Advisors — Full market access. Can place, modify, and close trades. Run in their own thread. One EA per chart.
  • Scripts — Run once and exit. Can place trades. Useful for batch operations like "close all positions."
💡
Custom EA development

Building a reliable, production-quality Expert Advisor requires careful strategy design, robust error handling, and extensive testing. finkatana.com offers professional EA development — we can turn your trading strategy into a fully automated system.