MT5 Expert Advisor Tutorial: Complete Beginner Guide to MQL5 EAs 2026
This step-by-step MT5 Expert Advisor tutorial walks absolute beginners through every stage of building a fully functional MQL5 EA from scratch — covering project setup, indicator handles, order execution with MqlTradeRequest, and risk management. By the end you will have a working moving-average crossover EA that compiles, back-tests, and runs live on any MT5 symbol.
Strategy Logic
Entry Conditions
N/A — this is a tutorial/indicator reference page.
Exit Conditions
N/A — this is a tutorial/indicator reference page.
MQL5 Expert Advisor Code
//+------------------------------------------------------------------+
//| BeginnerEA_Tutorial.mq5 |
//| Step-by-step teaching example: MA Crossover Expert Advisor |
//| Pineify.app — AI-powered MQL5 & Pine Script code generation |
//+------------------------------------------------------------------+
#property copyright "Pineify.app"
#property link "https://pineify.app"
#property version "1.00"
#property strict
//--- Input parameters (tunable from the EA settings dialog)
input int FastMAPeriod = 10; // Fast MA period
input int SlowMAPeriod = 30; // Slow MA period
input ENUM_MA_METHOD MAMethod = MODE_EMA; // MA smoothing method
input ENUM_APPLIED_PRICE AppliedPrice = PRICE_CLOSE; // Applied price
input double LotSize = 0.10; // Trade lot size
input int MagicNumber = 123456; // Unique EA identifier
input int SlippagePoints = 10; // Maximum allowed slippage
input double StopLossPips = 50.0; // Stop-loss in pips
input double TakeProfitPips = 100.0; // Take-profit in pips
//--- Global variables
int fastMAHandle = INVALID_HANDLE;
int slowMAHandle = INVALID_HANDLE;
double fastMABuffer[];
double slowMABuffer[];
double pipValue;
//+------------------------------------------------------------------+
//| OnInit — called once when the EA is attached or reloaded |
//+------------------------------------------------------------------+
int OnInit()
{
//--- Validate inputs
if(FastMAPeriod >= SlowMAPeriod)
{
Print("ERROR: FastMAPeriod must be less than SlowMAPeriod.");
return INIT_PARAMETERS_INCORRECT;
}
if(LotSize <= 0.0)
{
Print("ERROR: LotSize must be greater than zero.");
return INIT_PARAMETERS_INCORRECT;
}
//--- Create indicator handles (MT5 handle-based API)
fastMAHandle = iMA(_Symbol, _Period, FastMAPeriod, 0, MAMethod, AppliedPrice);
slowMAHandle = iMA(_Symbol, _Period, SlowMAPeriod, 0, MAMethod, AppliedPrice);
if(fastMAHandle == INVALID_HANDLE || slowMAHandle == INVALID_HANDLE)
{
Print("ERROR: Failed to create MA indicator handles.");
return INIT_FAILED;
}
//--- Set buffer as series so index 0 = most recent bar
ArraySetAsSeries(fastMABuffer, true);
ArraySetAsSeries(slowMABuffer, true);
//--- Calculate pip value once (1 pip = 10 points for 5-digit brokers)
pipValue = _Point * 10.0;
if(Digits() == 3 || Digits() == 5)
pipValue = _Point * 10.0;
else
pipValue = _Point;
Print("BeginnerEA initialised. Symbol=", _Symbol,
" FastMA=", FastMAPeriod, " SlowMA=", SlowMAPeriod);
return INIT_SUCCEEDED;
}
//+------------------------------------------------------------------+
//| OnDeinit — called when the EA is removed or terminal closes |
//+------------------------------------------------------------------+
void OnDeinit(const int reason)
{
//--- Release indicator handles to free memory
if(fastMAHandle != INVALID_HANDLE)
IndicatorRelease(fastMAHandle);
if(slowMAHandle != INVALID_HANDLE)
IndicatorRelease(slowMAHandle);
Print("BeginnerEA removed. Reason code: ", reason);
}
//+------------------------------------------------------------------+
//| OnTick — called on every incoming price tick |
//+------------------------------------------------------------------+
void OnTick()
{
//--- Only act on the open of a new bar (avoids re-entry on same bar)
static datetime lastBarTime = 0;
datetime currentBarTime = iTime(_Symbol, _Period, 0);
if(currentBarTime == lastBarTime)
return;
lastBarTime = currentBarTime;
//--- Copy the two most recent MA values into our buffers
if(CopyBuffer(fastMAHandle, 0, 0, 3, fastMABuffer) < 3 ||
CopyBuffer(slowMAHandle, 0, 0, 3, slowMABuffer) < 3)
{
Print("WARNING: Not enough data to read MA buffers yet.");
return;
}
//--- Detect crossover signals
// Index 1 = previous closed bar, index 2 = bar before that
bool bullishCross = (fastMABuffer[2] <= slowMABuffer[2]) &&
(fastMABuffer[1] > slowMABuffer[1]);
bool bearishCross = (fastMABuffer[2] >= slowMABuffer[2]) &&
(fastMABuffer[1] < slowMABuffer[1]);
//--- Count open positions managed by this EA
int buyPositions = CountPositions(POSITION_TYPE_BUY);
int sellPositions = CountPositions(POSITION_TYPE_SELL);
//--- Act on bullish crossover: close sells, open buy
if(bullishCross)
{
CloseAllPositions(POSITION_TYPE_SELL);
if(buyPositions == 0)
OpenPosition(ORDER_TYPE_BUY);
}
//--- Act on bearish crossover: close buys, open sell
if(bearishCross)
{
CloseAllPositions(POSITION_TYPE_BUY);
if(sellPositions == 0)
OpenPosition(ORDER_TYPE_SELL);
}
}
//+------------------------------------------------------------------+
//| OpenPosition — sends a market order with SL and TP |
//+------------------------------------------------------------------+
void OpenPosition(ENUM_ORDER_TYPE orderType)
{
MqlTradeRequest request = {};
MqlTradeResult result = {};
double price, sl, tp;
double ask = SymbolInfoDouble(_Symbol, SYMBOL_ASK);
double bid = SymbolInfoDouble(_Symbol, SYMBOL_BID);
if(orderType == ORDER_TYPE_BUY)
{
price = ask;
sl = NormalizeDouble(price - StopLossPips * pipValue, _Digits);
tp = NormalizeDouble(price + TakeProfitPips * pipValue, _Digits);
}
else
{
price = bid;
sl = NormalizeDouble(price + StopLossPips * pipValue, _Digits);
tp = NormalizeDouble(price - TakeProfitPips * pipValue, _Digits);
}
request.action = TRADE_ACTION_DEAL;
request.symbol = _Symbol;
request.volume = LotSize;
request.type = orderType;
request.price = price;
request.sl = sl;
request.tp = tp;
request.deviation = SlippagePoints;
request.magic = MagicNumber;
request.comment = "BeginnerEA_Tutorial";
request.type_filling = ORDER_FILLING_FOK;
if(!OrderSend(request, result))
Print("OrderSend failed. RetCode=", result.retcode,
" Comment=", result.comment);
else
Print("Order opened. Ticket=", result.order,
" Type=", EnumToString(orderType), " Price=", price);
}
//+------------------------------------------------------------------+
//| CloseAllPositions — closes every position of the given type |
//+------------------------------------------------------------------+
void CloseAllPositions(ENUM_POSITION_TYPE posType)
{
for(int i = PositionsTotal() - 1; i >= 0; i--)
{
ulong ticket = PositionGetTicket(i);
if(!PositionSelectByTicket(ticket))
continue;
if(PositionGetString(POSITION_SYMBOL) != _Symbol) continue;
if(PositionGetInteger(POSITION_MAGIC) != MagicNumber) continue;
if((ENUM_POSITION_TYPE)PositionGetInteger(POSITION_TYPE) != posType) continue;
MqlTradeRequest req = {};
MqlTradeResult res = {};
req.action = TRADE_ACTION_DEAL;
req.symbol = _Symbol;
req.volume = PositionGetDouble(POSITION_VOLUME);
req.position = ticket;
req.deviation = SlippagePoints;
req.magic = MagicNumber;
req.comment = "BeginnerEA_Close";
req.type_filling = ORDER_FILLING_FOK;
if(posType == POSITION_TYPE_BUY)
{
req.type = ORDER_TYPE_SELL;
req.price = SymbolInfoDouble(_Symbol, SYMBOL_BID);
}
else
{
req.type = ORDER_TYPE_BUY;
req.price = SymbolInfoDouble(_Symbol, SYMBOL_ASK);
}
if(!OrderSend(req, res))
Print("Close failed. Ticket=", ticket, " RetCode=", res.retcode);
}
}
//+------------------------------------------------------------------+
//| CountPositions — returns open position count for given type |
//+------------------------------------------------------------------+
int CountPositions(ENUM_POSITION_TYPE posType)
{
int count = 0;
for(int i = PositionsTotal() - 1; i >= 0; i--)
{
ulong ticket = PositionGetTicket(i);
if(!PositionSelectByTicket(ticket))
continue;
if(PositionGetString(POSITION_SYMBOL) != _Symbol) continue;
if(PositionGetInteger(POSITION_MAGIC) != MagicNumber) continue;
if((ENUM_POSITION_TYPE)PositionGetInteger(POSITION_TYPE) == posType)
count++;
}
return count;
}
//+------------------------------------------------------------------+Copy this code into MetaEditor (F4 in MT5), save in the Experts folder, and compile with F7.
Generate a Custom Multi-pair Educational EA →
Pineify AI generates syntactically validated MQL5 Expert Advisors from plain English descriptions. Customize entry logic, risk management, and trading sessions — no coding required.
Pine Script vs MQL5: Same Strategy, Different Platforms
| Aspect | Pine Script (TradingView) | MQL5 (MetaTrader 5) |
|---|---|---|
| Execution | Bar-based, backtesting only | Tick-based, live trading |
| Deployment | TradingView alerts | Runs 24/5 on VPS/MT5 |
| Broker access | Via TradingView broker integration | Direct broker connectivity |
| Backtesting | Built-in, no data download needed | Strategy Tester, tick data required |
| Code complexity | Simpler, functional syntax | C++-like, more powerful |
Pineify supports both platforms. Prototype your strategy visually in TradingView Pine Script, then generate the equivalent MQL5 EA for live MT5 trading.