Silent Is Not the Same as Safe: Monitoring MT5 Expert Advisors in Three Layers

25 October 2026, 12:14
Michael Thomas Harty
0
27

Most EA failures do not look like failures. The terminal is open, the chart is there, the smiley face is showing, and nothing has traded for three days because the connection dropped at 03:00 on Tuesday and the EA has been waiting for a tick that never came. A crash is easy to notice. Silence is not.

This post sets out a simple way to monitor EAs so that silence becomes visible. It uses three independent layers, because any single layer can fail in the same way as the thing it is watching.

The core idea: expected behaviour, not just "is it running"

"Is the terminal running?" is the wrong question. A better question is: "Is this EA doing what it normally does, at the rate it normally does it?"

For each EA, write down a short profile:

Field Example (illustrative)
Symbol / timeframe EURUSD M15
Trading hours Mon 01:00 – Fri 22:00 server time
Typical signals per week 4–10
Longest normal gap between trades ~3 trading days
Max open positions 3
Magic number 240115

Now "silent for 2 hours" means nothing for this EA, but "silent for 5 trading days" is a real signal. A scalper might have the opposite profile. Thresholds belong to the EA, not to the terminal.

Layer 1: in the terminal (the EA reports on itself)

The cheapest monitor is the EA itself. Add a heartbeat that records the last time it saw a tick, the last time it ran its logic, and its own state.

input int InpHeartbeatSec = 60; // illustrative
datetime g_lastTick = 0;

int OnInit()
{
   EventSetTimer(InpHeartbeatSec);
   return INIT_SUCCEEDED;
}

void OnTick()
{
   g_lastTick = TimeCurrent();
}

void OnTimer()
{
   string key = "HB_" + IntegerToString(InpMagic);
   GlobalVariableSet(key, (double)TimeLocal());            // "I am alive"
   GlobalVariableSet(key + "_tick", (double)g_lastTick);   // "I am seeing prices"

   bool connected = (bool)TerminalInfoInteger(TERMINAL_CONNECTED);
   bool algoOk = (bool)TerminalInfoInteger(TERMINAL_TRADE_ALLOWED) && (bool)MQLInfoInteger(MQL_TRADE_ALLOWED);

   if(!connected || !algoOk)
      PrintFormat("HB WARN connected=%d algo=%d", connected, algoOk);
}

Two timestamps matter because they fail differently:

  • Heartbeat stale → the EA is not running (removed from chart, terminal frozen, crashed).
  • Heartbeat fresh, tick stale during market hours → the EA is running but blind (disconnected, symbol disabled, feed issue).

Also watch the less obvious switches: AutoTrading turned off globally, "Allow Algo Trading" unticked in the EA's own properties, or the account switched to a different server after a reconnect.

The weakness of Layer 1 is obvious: if the terminal freezes, the EA cannot report that it has frozen.

Layer 2: on the host (something else on the same machine looks in)

A small scheduled script on the VPS checks things the EA cannot see:

  • Is each terminal64.exe process present, and how much memory is it using?
  • When was each terminal's log file last written?
  • Free disk space (logs and tester caches grow quietly).
  • System uptime and pending reboots.
  • Has the terminal build changed since yesterday?

Run it at a fixed time each day and write the output to a dated text file. The value comes from comparing today with yesterday: a terminal whose memory has doubled in a week, or a log folder that has grown by gigabytes, is telling you something before it breaks.

The weakness of Layer 2: if the whole VPS goes down, the host script goes with it.

Layer 3: off the host (a dead-man check)

The final layer runs somewhere else entirely, such as a different small server or your own machine, and asks a single question every few minutes: "Have I heard from each live account recently?"

It does not need to know anything about trading. It only needs the last heartbeat time for each account and a threshold. If an account that should be live has been quiet for longer than, say, 15 minutes (illustrative) during market hours, it raises an alert.

Three details that matter in practice:

  1. Alert on the checker's own failure. If the checker cannot reach whatever it reads heartbeats from, or cannot parse the reply, that is itself an alert. A monitor that fails silently is worse than none, because you trust it.
  2. De-duplicate by account, not by message. Otherwise a growing gap ("silent 20 min", "silent 25 min"…) re-alerts every cycle and you learn to ignore it.
  3. Treat "no heartbeat at all" as silent. A new or renamed account that never reports should not slip through as "no data, no problem".

Putting the layers together

Failure Layer 1 Layer 2 Layer 3
EA removed from chart ✔ (heartbeat stops) – ✔
Terminal disconnected ✔ (tick stale) partly (log) ✔ if heartbeat includes connection
Terminal frozen ✘ ✔ ✔
VPS down ✘ ✘ ✔
Disk filling slowly ✘ ✔ ✘
AutoTrading switched off ✔ – ✔ if reported

No single column is all ticks. That is the point.

A short weekly review

Once a week, spend ten minutes on each live EA:

  • Trades this week versus its normal range. Too few is as interesting as too many.
  • Longest silence this week versus its normal longest gap.
  • Any alerts, and whether each one was real. Tune thresholds that cried wolf.
  • Terminal build and EA version unchanged? If not, was the change intended?

Write the answers down. After a month you will have a baseline that makes the next anomaly obvious.

Summary

  • Define what "normal" looks like per EA, then alert on departures from it.
  • Monitor in three layers: in-terminal, on-host, off-host.
  • Make the monitor loud about its own failures.
  • Review weekly and tune thresholds so alerts stay meaningful.

Questions about specific setups are welcome in the comments.

Virtual Risk Desk

All figures, thresholds and examples are illustrative. This is educational content about operational monitoring, not trading or investment advice.