You do not need to be a developer to write a working Expert Advisor. You do need to understand a few core MQL5 functions and the order of operations MT5 expects. Here is the minimum viable EA, explained line by line.
An Expert Advisor (EA) is a compiled .ex5 program that MT5 runs on a chart. The EA gets called on every incoming tick and can read market data, place orders, modify orders, and close positions. It runs server-side from the broker's perspective: orders go directly from your MT5 terminal to the broker.
An EA is not an indicator. Indicators only draw on the chart; they cannot place trades. EAs can trade. Scripts are a third type: they run once on demand and then exit. EAs run continuously while attached to a chart.
Every EA has up to four special functions that MT5 calls automatically:
| Function | When it runs | Typical use |
|---|---|---|
OnInit() | Once, when the EA is attached or the chart restarts | Initialise variables, check inputs, set up indicators |
OnDeinit() | Once, when the EA is removed or the chart closes | Cleanup, release indicator handles |
OnTick() | Every incoming price update for the chart symbol | Strategy logic, order management |
OnTimer() | Every N seconds, if you set up a timer | Periodic checks independent of ticks (less common) |
For a minimal EA you only need OnInit and OnTick.
MetaEditor is the IDE for writing MQL5. Open it from MT5 with F4 or from the toolbar. Then:
SimpleMA_EA. The file will be saved as SimpleMA_EA.mq5 in your MQL5/Experts folder.The strategy is intentionally simple. We will use two moving averages: a fast (10-period) and a slow (30-period). When fast crosses above slow we buy. When fast crosses below slow we sell. One position at a time. Fixed lot size for now.
Below is the complete source. We will go through it section by section.
//+------------------------------------------------------------------+
//| SimpleMA_EA.mq5 |
//+------------------------------------------------------------------+
#property copyright "Educational example"
#property version "1.00"
#property strict
#include <Trade/Trade.mqh>
// Inputs
input int FastMA_Period = 10;
input int SlowMA_Period = 30;
input double LotSize = 0.01;
input int SL_Points = 200; // stop loss distance in points
input int TP_Points = 400; // take profit distance in points
input ulong MagicNumber = 20260524;
// Globals
CTrade trade;
int fastHandle, slowHandle;
//+------------------------------------------------------------------+
int OnInit()
{
trade.SetExpertMagicNumber(MagicNumber);
fastHandle = iMA(_Symbol, _Period, FastMA_Period, 0, MODE_EMA, PRICE_CLOSE);
slowHandle = iMA(_Symbol, _Period, SlowMA_Period, 0, MODE_EMA, PRICE_CLOSE);
if(fastHandle == INVALID_HANDLE || slowHandle == INVALID_HANDLE)
{
Print("Failed to create MA indicators");
return(INIT_FAILED);
}
return(INIT_SUCCEEDED);
}
//+------------------------------------------------------------------+
void OnDeinit(const int reason)
{
IndicatorRelease(fastHandle);
IndicatorRelease(slowHandle);
}
//+------------------------------------------------------------------+
void OnTick()
{
// Only act on a new bar
static datetime lastBar = 0;
datetime currentBar = iTime(_Symbol, _Period, 0);
if(currentBar == lastBar) return;
lastBar = currentBar;
// Get last two values of each MA
double fast[2], slow[2];
if(CopyBuffer(fastHandle, 0, 1, 2, fast) < 2) return;
if(CopyBuffer(slowHandle, 0, 1, 2, slow) < 2) return;
// fast[0] = bar that just closed, fast[1] = bar before that
bool crossUp = (fast[1] <= slow[1]) && (fast[0] > slow[0]);
bool crossDown = (fast[1] >= slow[1]) && (fast[0] < slow[0]);
// Check for existing position
bool hasPosition = PositionSelect(_Symbol);
if(crossUp && !hasPosition)
{
double ask = SymbolInfoDouble(_Symbol, SYMBOL_ASK);
double sl = ask - SL_Points * _Point;
double tp = ask + TP_Points * _Point;
trade.Buy(LotSize, _Symbol, ask, sl, tp, "MA cross long");
}
else if(crossDown && !hasPosition)
{
double bid = SymbolInfoDouble(_Symbol, SYMBOL_BID);
double sl = bid + SL_Points * _Point;
double tp = bid - TP_Points * _Point;
trade.Sell(LotSize, _Symbol, bid, sl, tp, "MA cross short");
}
}
#property strict turns on stricter compile checking. Always include it.
#include <Trade/Trade.mqh> imports the standard trading library. This gives us the CTrade class which wraps the low-level order send functions into clean methods like trade.Buy() and trade.Sell().
The input keyword exposes a variable in the EA settings dialog when the user attaches the EA. This means you can change parameters without recompiling. Use inputs for anything you might want to optimise or tweak.
We set the magic number on the trade object. The magic number is a tag attached to every order our EA sends. It lets us identify which positions belong to this EA versus other EAs or manual trades. Use a unique value per EA per chart.
iMA() creates an indicator handle. Note that in MQL5 we do not get values directly from iMA; we get a handle that we use later with CopyBuffer to read values. If iMA returns INVALID_HANDLE, something is wrong (out of memory or invalid parameters) and we abort initialisation.
The first block ensures we only run our logic once per closed bar. Without this guard, OnTick() would fire every tick and we would trade many times within the same bar. The static lastBar variable persists between ticks.
CopyBuffer(handle, buffer, start, count, array) reads indicator values. We pass start=1 to skip the still-forming current bar and count=2 to get the last two closed bars.
The crossover detection compares two consecutive bars. If the fast MA was below or equal to the slow MA on bar [1] and is now above on bar [0], that is a cross up. The opposite for cross down.
trade.Buy() and trade.Sell() send market orders. The CTrade class handles the order request structure, the deal_id and order_id tracking, and the return code checking. If you tried to do this manually with OrderSend() you would write 20 more lines.
In MetaEditor press F7 or click Compile. If there are errors they appear in the Errors tab at the bottom. Common first-time errors:
'iMA' - undeclared identifier. You forgot to specify the right MQL5 imports or used an MQL4 function name.cannot convert. Type mismatch. Common when mixing int and double.not all control paths return a value. Your function says it returns something but a code path falls through without returning.Once compiled cleanly, switch to MT5. Open the Navigator (Ctrl+N), expand Expert Advisors, find your EA, drag it onto a chart. The settings dialog appears. Enable the "Allow Algo Trading" checkbox. Click OK. Your EA is now running.
Before running an EA on a real account, backtest it. View → Strategy Tester (Ctrl+R). Select your EA, the symbol, the timeframe, and a date range. Choose modelling quality (Every tick based on real ticks is the gold standard for FX). Click Start.
The tester runs the EA against historical data and produces a report: net profit, drawdown, profit factor, number of trades. Common first-EA outcomes:
The example EA uses fixed stop loss and take profit distances in points. This is the simplest possible risk model. For anything beyond educational testing you want:
| Mistake | What goes wrong |
|---|---|
| Trading on every tick instead of every closed bar | Hundreds of duplicate orders, signals firing then reversing within seconds |
| Forgetting magic numbers | EA closes positions opened by other EAs or by you manually |
| Hardcoding lot size, then running on a small account | Margin call on first trade |
Not checking SymbolInfoInteger(_Symbol, SYMBOL_TRADE_MODE) | EA tries to trade a symbol that is in close-only mode (weekends, holidays, restricted hours) |
| Ignoring slippage and partial fills | Real-account performance diverges from backtest |
| Optimising on the same data you tested on | In-sample overfitting. Performance collapses on new data. |
Once your first EA compiles and runs in the tester, the natural progression is:
AccountInfoDouble(ACCOUNT_EQUITY) and SymbolInfoDouble(_Symbol, SYMBOL_TRADE_TICK_VALUE).trade.PositionModify().The MQL5 documentation at the official MetaQuotes site is dense but complete. The CodeBase section has thousands of open-source EAs you can read to learn idioms and patterns.
MQL5 syntax is C++-like but you can write functional EAs knowing only the basics: variables, if/else, loops, functions. Object-oriented features (classes, inheritance) are available but not required for simple EAs.
Yes, as long as each EA uses a different magic number and they are not contradicting each other on the same symbol. Each EA must be attached to its own chart.
Writing one takes hours. Making it profitable takes months or years and most attempts fail. The hard part is not coding; it is finding an edge that survives real market conditions.
Yes, MetaQuotes runs a marketplace where you can sell EAs to other MT5 users. There is a review process and revenue share. Most sellers earn very little; a few sellers earn substantial income from well-marketed products with consistent performance.
Mostly yes, but expect small differences. Symbol naming varies (XAUUSD vs GOLD vs XAU/USD). Tick size and minimum lot may differ. Execution speed and slippage characteristics depend on the broker server.
We post real trade ideas to test your EAs against. Useful as an independent reference when evaluating whether your EA is performing rationally. Free for partner-broker clients.
See the signals →