What Is an Expert Advisor? EA Meaning, How It Works, Code
A plain-language explanation of what an expert advisor is, how it runs inside MetaTrader, what it can and cannot do, and what to give an EA coder before you hire one.
What Is an Expert Advisor? EA Meaning, How It Works, Code
TLDR
An expert advisor (EA) is a program written in MQL4 or MQL5 that runs inside the MetaTrader 4 or MetaTrader 5 terminal, attached to a chart, and is allowed to send trade orders to your broker without you clicking anything. It reacts to events the terminal raises, mainly a new price tick, and decides whether to open, modify or close positions according to rules you (or your EA coder) wrote. An EA is not a strategy, a guarantee or a signal subscription; it is the automation of a rule set, and it is exactly as good or as bad as those rules plus the quality of the code.
Expert advisor meaning, in one paragraph
The term comes from MetaQuotes, the company that makes MetaTrader. Inside the terminal there are four kinds of programs: indicators, which calculate and draw; scripts, which run once and exit; services (MT5 only), which run in the background without a chart; and expert advisors, which live on a chart and are the only kind of program the terminal lets trade on a schedule of events. Everything else about the phrase is marketing. "Forex robot", "trading bot for MT4", "auto trader" and "EA" describe the same object: a compiled file with the extension .ex4 (MT4) or .ex5 (MT5) that you drop onto a chart, configure through an inputs dialog, and allow to trade by turning on the Algo Trading (MT5) or AutoTrading (MT4) button.
Because it is a program, an EA has no opinion, no fear and no fatigue. That is the point. It also has no judgment outside the rules it was given, which is the catch. If the rules do not say what to do when the spread triples during a news release, the EA will do whatever the code happens to do, which is usually "trade as normal".
How an EA actually runs inside MetaTrader
Event handlers, not a loop
People who have not written one often imagine an EA as a loop that checks the market every second. It is not. The terminal owns the loop. Your EA is a set of functions with reserved names, and the terminal calls them when the matching event happens:
- OnInit() runs once when the EA is attached, when inputs change, or when the chart symbol or timeframe changes. This is where you create indicator handles and validate inputs. Returning anything other than INIT_SUCCEEDED stops the EA from starting.
- OnTick() runs when a new tick arrives for the chart's symbol. This is where almost all trading logic lives. A tick is any change in bid or ask, so on a liquid pair this can fire many times per second.
- OnTimer() runs at an interval you set with EventSetTimer(). Useful for things that should happen even when the market is quiet, such as polling an external service or checking for a disconnect.
- OnTrade() and OnTradeTransaction() (MT5) fire when something happens to orders, deals or positions on the account. They let an EA react to fills and partial fills instead of guessing.
- OnChartEvent() fires on clicks, key presses and object changes, used for on-chart control panels.
- OnTester() and its relatives run in the Strategy Tester to return a custom optimization score.
- OnDeinit() runs once when the EA is removed or the terminal shuts down. Release handles and timers here.
Indicators, by contrast, implement OnCalculate(), which receives the whole price history as arrays and is expected to fill buffers. An indicator cannot call trade functions; the compiler refuses. That split is deliberate: calculation lives in indicators, decisions and orders live in EAs, and an EA pulls indicator values through handles (MT5) or iCustom calls (MT4).
What happens when ticks arrive faster than your code runs
In MQL5, tick events are not queued. If OnTick() is still executing when another tick arrives, the terminal records only that a new tick exists and calls OnTick() again once your function returns. You do not get one call per tick; you get "at least one call after each tick". Any EA design that assumes it sees every price change is wrong by construction. If your logic needs tick-level precision (true scalping, certain arbitrage styles), it has to read the tick history with CopyTicks() rather than trust the call count. For most strategies this is irrelevant because they act on bar closes, which brings us to the single most important habit in EA design.
Acting once per bar
Most published strategies are defined on closed candles: "when the 20 EMA crosses above the 50 EMA on the 1 hour chart, buy". The chart only knows the candle is closed when the first tick of the next candle arrives. A correct EA therefore remembers the open time of the last bar it processed, and in OnTick() does nothing until iTime(_Symbol, _Period, 0) changes. On that first tick of the new bar, it reads the indicator values of bar 1 (the one that just closed), not bar 0 (the one that just started and has one tick in it). Backtests that read bar 0 look wonderful, because in the tester the "current" bar already contains its final close, and then fall apart live. This is the EA-side cousin of what Pine Script users call repainting; we cover that side in non-repainting indicators.
Where the EA lives and what it needs to keep running
An EA executes inside the terminal process on the machine where MetaTrader is open. Close the terminal, put the laptop to sleep, or lose the internet connection and the EA stops. Pending orders and server-side stop losses already placed at the broker survive, because they live on the broker's server, but anything the EA manages itself (trailing logic, time-based exits, basket management) pauses until the terminal is back. That is why most people who run EAs seriously use a Windows VPS or the built-in MetaTrader VPS service, and why a well-written EA re-reads account state in OnInit() rather than assuming it starts from a clean slate.
Three terminal settings decide whether an EA is allowed to act at all: the global Algo Trading button, the per-EA "Allow Algo Trading" checkbox on the Common tab, and, if the EA calls out to the internet, the allow-list of URLs under Tools, Options, Expert Advisors. An EA that needs DLL imports also requires a separate permission. If an EA appears to do nothing, these are the first things to check, before anyone touches the code.
Program types inside MetaTrader compared
| Program type | Main entry point | Can send orders | Attached to a chart | Typical job |
|---|---|---|---|---|
| Expert advisor | OnTick, OnTimer, OnTrade* | Yes | Yes, one chart | Automated strategy, trade manager, copier receiver |
| Custom indicator | OnCalculate | No | Yes, draws in main or sub window | Calculations, visual signals, buffers for EAs to read |
| Script | OnStart | Yes, once | Yes, runs and exits | Close all, place a grid of pendings, export history |
| Service (MT5) | OnStart, loop inside | Yes | No | Background data feeds, custom symbols, watchdogs |
| Library / include | None | Through the caller | No | Shared code such as the standard CTrade class |
Two practical consequences of this table. First, "indicator with arrows plus an EA that trades the arrows" is a legitimate architecture, and many good EAs are built that way; the EA reads the indicator buffer through iCustom. Second, if someone sells you a "signal indicator that trades for you", it is either an EA labelled as an indicator or it does not trade. Check the file extension and the entry point.
MQL4 versus MQL5: what changes for an EA
Both languages are C-like and compiled by MetaEditor. MQL5 is the current one and the only one MetaQuotes actively develops. Differences that matter when someone asks "will my MT4 EA run on MT5?" (it will not, not without porting):
- Account model. MT4 is always hedging: every OrderSend creates a separate ticket. MT5 accounts are either netting (one aggregated position per symbol) or hedging (MT4-like). An EA has to check ACCOUNT_MARGIN_MODE and handle both, or state clearly which one it supports.
- Orders, deals, positions. MT4 has one concept, the order. MT5 separates orders (requests), deals (executions) and positions (the net result). Code that loops over OrdersTotal() to find open trades in MT4 loops over PositionsTotal() in MT5, and history questions need HistoryDealsTotal().
- Indicator access. MT4 calls iMA() and gets a number back. MT5 calls iMA() once to get a handle, then uses CopyBuffer() to read values. Handles are created in OnInit() and released in OnDeinit(). Creating handles inside OnTick() is a classic beginner leak.
- Array direction. MT4 price arrays are series (index 0 is the newest bar). In MT5 arrays you copy into are not series unless you call ArraySetAsSeries(). Half the "my MT5 port behaves differently" bugs are this.
- Trading API. MT4 uses OrderSend with eleven positional arguments. MT5 uses a MqlTradeRequest structure and OrderSend(request, result), or the standard CTrade wrapper, which is what almost every modern EA uses.
- Tester. The MT5 Strategy Tester is multi-threaded, supports real tick history from the broker, multi-currency testing, forward testing periods and genetic optimization. The MT4 tester is single-threaded and models ticks from one-minute bars unless you import tick data yourself.
If you have an MT4 EA that earns its keep, a port to MT5 is a rewrite of the trade layer and a careful re-test, not a find-and-replace. We describe that work on the MT5 expert advisor development page.
An original MQL5 example: EMA cross, once per bar, with a stop
This is a complete, compile-clean MT5 EA with the habits described above: handles created once, action only on the first tick of a new bar, values read from the last closed candle, a magic number so it only touches its own trades, and a hard stop loss on every entry. It is an example of structure, not a strategy recommendation. On a netting account the PositionClose call flattens before the new entry; on a hedging account it closes the first position on the symbol that carries this magic.
#include <Trade\Trade.mqh>input int InpFast = 20; // fast EMA period input int InpSlow = 50; // slow EMA period input double InpLots = 0.10; // fixed volume input int InpStopPts = 300; // stop loss in points input ulong InpMagic = 240610; // identifies this EA's trades
CTrade trade; int hFast = INVALID_HANDLE, hSlow = INVALID_HANDLE; datetime lastBar = 0;
int OnInit() { hFast = iMA(_Symbol, _Period, InpFast, 0, MODE_EMA, PRICE_CLOSE); hSlow = iMA(_Symbol, _Period, InpSlow, 0, MODE_EMA, PRICE_CLOSE); if(hFast == INVALID_HANDLE || hSlow == INVALID_HANDLE) return INIT_FAILED; trade.SetExpertMagicNumber(InpMagic); trade.SetDeviationInPoints(20); return INIT_SUCCEEDED; }
void OnDeinit(const int reason) { IndicatorRelease(hFast); IndicatorRelease(hSlow); }
void OnTick() { datetime bar = iTime(_Symbol, _Period, 0); if(bar == lastBar) return; // act once, on the first tick of a new bar lastBar = bar;
double fast[], slow[]; ArraySetAsSeries(fast, true); // fast[0] = last closed bar, fast[1] = the one before ArraySetAsSeries(slow, true); if(CopyBuffer(hFast, 0, 1, 2, fast) < 2 || CopyBuffer(hSlow, 0, 1, 2, slow) < 2) return;
bool crossUp = fast[1] <= slow[1] && fast[0] > slow[0]; bool crossDown = fast[1] >= slow[1] && fast[0] < slow[0]; if(!crossUp && !crossDown) return;
if(PositionSelect(_Symbol) && (ulong)PositionGetInteger(POSITION_MAGIC) == InpMagic) trade.PositionClose(_Symbol);
if(crossUp) { double ask = SymbolInfoDouble(_Symbol, SYMBOL_ASK); double sl = NormalizeDouble(ask - InpStopPts * _Point, _Digits); trade.Buy(InpLots, _Symbol, ask, sl, 0.0, "ema-cross"); } else { double bid = SymbolInfoDouble(_Symbol, SYMBOL_BID); double sl = NormalizeDouble(bid + InpStopPts * _Point, _Digits); trade.Sell(InpLots, _Symbol, bid, sl, 0.0, "ema-cross"); } }
What is missing, on purpose, is everything a production EA needs around this core: a check that the stop distance respects SYMBOL_TRADE_STOPS_LEVEL, volume normalization against SYMBOL_VOLUME_STEP and the min/max limits, a return-code check on every trade call with a retry policy for requotes, a spread filter, a session filter, position sizing from account risk instead of a fixed lot, and logging you can read a week later. Those sixty lines are the easy part; the surrounding two hundred are what you pay an EA coder for.
Worked example: turning a risk rule into a lot size
A fixed lot is the simplest input but the worst risk control, because a 300 point stop on EURUSD and a 300 point stop on USDJPY are different amounts of money. Here is the arithmetic an EA performs when it sizes from risk, written out so you can check it against your own account.
Inputs. Account equity 10,000 USD. Risk per trade 1 percent, so 100 USD. Stop distance 30 pips on EURUSD. On a standard lot of EURUSD one pip is worth 10 USD for a USD-denominated account (this is the contract size of 100,000 units multiplied by the pip size of 0.0001; check SYMBOL_TRADE_TICK_VALUE on your broker rather than assuming).
Calculation. Money at risk per lot = 30 pips x 10 USD = 300 USD. Lots = 100 USD / 300 USD = 0.333. Rounded down to the broker's volume step of 0.01, the EA sends 0.33 lots. If the broker's minimum were 0.10 and the computed size were 0.04, a correct EA skips the trade or warns, rather than silently rounding up to a position three times larger than your risk rule allows.
In code this uses SymbolInfoDouble for SYMBOL_TRADE_TICK_VALUE and SYMBOL_TRADE_TICK_SIZE, divides the stop distance by tick size to get ticks, multiplies by tick value to get money per lot, and divides the risk budget by that. It is about ten lines, and it is the difference between a bot that survives a losing streak and one that does not.
Common EA categories and what to look for in each
| Category | How it trades | Where it breaks | What to insist on from the coder |
|---|---|---|---|
| Trend / breakout | Enters on crosses, channel breaks, range breakouts; one position at a time | Chop, widened spreads at session open | Bar-close logic, spread filter, session filter, server-side stops |
| Mean reversion | Fades extremes measured by bands or oscillators | Strong trends; a stop that is "too wide to hit" until it is | Hard maximum loss per trade and per day, no averaging without a cap |
| Grid / martingale | Adds positions at fixed distances, often increasing size | Any sustained move; equity curve looks smooth until it does not | A written worst-case drawdown figure derived from the grid parameters, and a kill switch |
| News / event | Straddles or fades scheduled releases | Slippage, requotes, broker execution rules around news | Handling of return codes and partial fills; tests on real ticks |
| Trade manager | Does not enter; manages stops, partials, break-even on manual trades | Interfering with other EAs' trades | Magic-number and comment filtering, clear ownership rules |
| Copier / bridge receiver | Executes signals from another terminal or a webhook | Latency, symbol-name mismatches, duplicate signals | Idempotent signal handling, symbol mapping table, heartbeat |
Prop-firm accounts add their own rule layer on top of any of these; daily loss limits and consistency rules change what "safe" means for an EA. That is a separate topic, covered in prop firm EA rules and on the prop firm EA development page.
Backtesting an EA: what the Strategy Tester does and does not tell you
The MT5 Strategy Tester replays history through your EA and records the trades. The result depends heavily on the tick modelling mode you pick:
- Every tick based on real ticks uses the broker's stored tick history where it exists. Closest to live behavior; slowest; only as good as the broker's tick archive.
- Every tick generates synthetic ticks inside each one-minute bar. Fine for bar-close strategies; misleading for anything that depends on intrabar order.
- 1 minute OHLC gives four price points per minute. Fast, and adequate for strategies that only act on closed bars of 15 minutes or higher.
- Open prices only calls OnTick once per bar on the open. Extremely fast, and valid only if the EA genuinely does everything on the first tick of a bar, which the example above does.
- Math calculations does not use price data at all; used for pure calculations and optimizations of custom functions.
The tester cannot model your broker's real execution: requotes, variable spread outside stored history, slippage on news, or the swap changes that happen on Wednesday rollovers for some brokers. A tester report is a statement about the past under stated assumptions. Treat optimization results as hypotheses to forward-test on a demo or small live account, not as expected returns. We go deeper in the backtesting service description and in backtesting platforms compared.
What to give an EA coder before you hire one
The query "ea coder" usually comes from someone who has a strategy in their head or in a Pine Script and wants it to run unattended. The quality of what comes back is decided mostly by the brief. A usable brief contains:
- Entry rules on closed bars, stated so that two people would mark the same candles. "RSI oversold" is not a rule; "RSI(14) closes below 30 and the next candle closes above its open" is.
- Exit rules: initial stop, target, trailing behavior, time stops, and what happens to an open trade at session end or over the weekend.
- Risk model: fixed lot, percent of equity, or fixed money, plus any daily or weekly loss limit that must halt trading.
- Filters: sessions, days of the week, maximum spread, news blackout windows and how they are sourced.
- Account specifics: broker, MT4 or MT5, netting or hedging, symbols and their suffixes, and whether it must pass prop-firm rules.
- Edge cases: what to do on a restart with positions open, on a requote, on a partial fill, when the indicator returns no value.
- Acceptance criteria: which historical period it will be tested on, in which tick mode, and how you will compare the EA's trades against your manual markup.
A coder who does not ask about points 3, 6 and 7 is going to deliver the sixty lines above with your indicator swapped in. That can be fine for a quick prototype; it is not a system you should fund.
From TradingView to MetaTrader
A large share of EA requests start as Pine Script strategies. There are two ways to automate those on a MetaTrader account. The first is a conversion: reimplement the logic in MQL5 so the EA computes signals itself, which is what the Pine Script to MQL5 conversion service does. The second keeps TradingView as the brain and uses alerts and a webhook bridge to tell an EA what to do; that is explained end to end in TradingView webhooks automation. Conversion gives you backtesting inside MT5 and no dependency on alert delivery; the bridge gives you TradingView's charting and data at the cost of an extra moving part. Neither is universally better.
FAQ
Is an expert advisor the same thing as a trading bot?
Inside MetaTrader, yes. "Trading bot" is the generic term; "expert advisor" is MetaQuotes' name for a bot that runs in MT4 or MT5. Bots for crypto exchanges, Interactive Brokers or a Python stack are not EAs, even though they do a similar job; those are covered by our trading bot development and crypto trading bot development services.
Does an EA work on both MT4 and MT5?
No. An .ex4 file runs only in MT4 and an .ex5 only in MT5, and the source languages differ enough that a port is real work. Ask which platform an EA was written for before buying or commissioning it.
Does my computer need to stay on?
Yes, or a VPS does. The EA executes inside the terminal process. Server-side stop losses and pending orders already at the broker will still trigger if the terminal is offline, but anything the EA manages itself pauses.
Can an EA lose money even if the backtest was good?
Yes, and it frequently does. Backtests depend on tick modelling, spread assumptions and the period chosen; live trading adds slippage, requotes, outages and regime changes. Position sizing and hard loss limits are what keep a bad period survivable. No EA, including ones we build, comes with a return promise.
How is an EA different from an indicator with buy and sell arrows?
The indicator calculates and draws; it cannot place an order. An EA can read that indicator's buffers and trade them. Many commercial "signal indicators" repaint, meaning the arrows you see on history did not appear in real time at that place, which is why we recommend reading non-repainting indicators before automating any arrow.
How long does it take to build a custom EA?
It depends on the rule count, the filters, whether it must handle netting and hedging, and how much testing you want documented. A single-symbol bar-close strategy with standard risk controls is a short project; a multi-symbol portfolio EA with a dashboard and prop-firm guardrails is not. Our pricing page describes the tiers; the brief above is what we need to quote.
Where Viprasol fits
Viprasol builds and maintains expert advisors for MT5 and MT4: new strategies from a written spec, Pine Script conversions, prop-firm-compliant versions of existing EAs, and the optimization and maintenance work that keeps an EA aligned with a broker's changing conditions. If you have a rule set and want it coded with the habits described here (bar-close logic, risk-based sizing, return-code handling, documented tests), start with the EA development service, look at optimization and maintenance if you already have one, or send us the brief.
Trading leveraged products carries a risk of loss that can exceed your deposit; an expert advisor automates a strategy, it does not remove that risk. Nothing here is investment advice.
External Resources
About the Author
Viprasol Tech Team
Custom Software Development Specialists
The Viprasol Tech team specialises in algorithmic trading software, AI agent systems, and SaaS development. With 1000+ projects delivered across MT4/MT5 EAs, fintech platforms, and production AI systems, the team brings deep technical experience to every engagement.
Ready to Automate Your Trading?
Discuss a custom Expert Advisor with defined strategy rules, risk controls and a testing plan.
Free consultation • No commitment • Response within 24 hours
Need a custom EA or trading bot built?
We build MT4/MT5 Expert Advisors around your strategy, broker and risk requirements. Each project has an agreed scope, testing plan and individual quote.