The flagship product moved to fxroboteasy.com
Forex Robot Easy
informationalMetaTrader 5 Expert Advisors
By William Harris · Reviewed by William Harris · Published May 21, 2026

MQL5 is the programming language used to build Expert Advisors (EAs), indicators, and scripts in MetaTrader 5. If you want to create automated trading strategies on MT5, MQL5 is where you start. This guide covers the language fundamentals, development environment setup, and your first working EA — written from the perspective of a programmer who knows at least one other language.

Note: Programming EAs in MQL5 requires testing before deployment on real capital. All code examples in this guide are for educational purposes. See our risk disclosure.

What MQL5 Is and How It Relates to C++

MQL5 is a proprietary language developed by MetaQuotes, syntactically similar to C++. If you've written C, C++, Java, or C# before, MQL5 will feel familiar. If Python is your background, the syntax is more verbose but the concepts translate well.

Key MQL5 characteristics:

  • Statically typed (you declare variable types explicitly)
  • Compiled language (errors caught at compile time)
  • Runs inside MetaTrader 5 — direct access to chart data, account info, and trading functions
  • Object-oriented support (classes, inheritance, polymorphism)
  • Single-threaded per EA instance

MQL5 vs MQL4: MQL4 was the previous version for MT4. MQL5 is significantly different — more powerful, better OOP support, more data access functions. MQL4 code does not run in MT5 without modification.

Setting Up MetaEditor

MetaEditor is the IDE bundled with MetaTrader 5. It's free.

Opening MetaEditor:

  1. Open MetaTrader 5
  2. Press F4 or go to Tools → MetaQuotes Language Editor
  3. MetaEditor opens as a separate window

Creating a new EA:

  1. In MetaEditor, press Ctrl+N
  2. Select "Expert Advisor (template)"
  3. Name your EA and fill in the author and description fields
  4. Click Finish — a template file is created in the Experts folder

The default file location: [MT5 data folder]/MQL5/Experts/

To find your MT5 data folder: In MT5, go to File → Open Data Folder.

Core MQL5 Structure

Every EA has three required functions. Understanding these is foundational:

//+------------------------------------------------------------------+
//| Expert initialization function — runs ONCE when EA loads        |
//+------------------------------------------------------------------+
int OnInit()
{
   // Called when EA is first attached to chart
   // Set up indicators, validate inputs, initialize variables
   Print("EA initialized");
   return(INIT_SUCCEEDED);  // Return INIT_FAILED to abort
}

//+------------------------------------------------------------------+
//| Expert deinitialization function — runs ONCE when EA removes     |
//+------------------------------------------------------------------+
void OnDeinit(const int reason)
{
   // Called when EA is removed from chart or MT5 closes
   // Clean up resources (delete indicator handles, etc.)
   Print("EA removed, reason: ", reason);
}

//+------------------------------------------------------------------+
//| Expert tick function — runs on EVERY new price tick             |
//+------------------------------------------------------------------+
void OnTick()
{
   // Core trading logic goes here
   // Called every time the price updates
   // Keep this function efficient — it runs thousands of times per day
}

Variables and Data Types

MQL5 uses familiar types with some forex-specific additions:

// Basic types
int    counter = 0;        // Integer
double price = 1.08542;   // Double — use for prices, lot sizes
bool   isBullish = true;   // Boolean
string message = "Hello"; // String
datetime lastBar = 0;      // MQL5-specific: timestamp type

// Forex-specific
// Prices are doubles — always use proper decimal precision
// Lots: 0.01 = micro lot, 0.10 = mini lot, 1.0 = standard lot

// Constants
ENUM_TIMEFRAMES period = PERIOD_H1;  // H1 chart
ENUM_ORDER_TYPE orderType = ORDER_TYPE_BUY;

Input Parameters (EA Settings)

Input parameters are the configurable values users see in the EA's settings dialog:

// Input parameters appear in EA settings dialog
input double LotSize = 0.10;           // Lot Size
input int    StopLossPips = 20;        // Stop Loss (pips)
input int    TakeProfitPips = 40;      // Take Profit (pips)
input int    MAPeriod = 14;            // MA Period
input ENUM_TIMEFRAMES Timeframe = PERIOD_CURRENT;  // Timeframe

// Sinput = same as input but hidden from optimization
sinput string EA_Name = "MyFirstEA";  // EA identifier

Use input for parameters you want traders to customize. Use sinput for labels and identifiers that shouldn't be optimized.

Getting Price Data

// Current price data
double bid = SymbolInfoDouble(_Symbol, SYMBOL_BID);  // Current bid
double ask = SymbolInfoDouble(_Symbol, SYMBOL_ASK);  // Current ask
double spread = ask - bid;

// Historical bar data
// CopyClose returns array of close prices
double closes[];
ArraySetAsSeries(closes, true);  // Index 0 = most recent bar
int copied = CopyClose(_Symbol, PERIOD_H1, 0, 100, closes);
// closes[0] = current (possibly unclosed) bar
// closes[1] = last completed bar (use this for signals)
// closes[2] = bar before that, etc.

// Full bar data using MqlRates structure
MqlRates bars[];
ArraySetAsSeries(bars, true);
CopyRates(_Symbol, PERIOD_H1, 0, 100, bars);
// bars[1].open, bars[1].high, bars[1].low, bars[1].close
// bars[1].time — timestamp of the bar

// Critical rule: always use bars[1] or closes[1] for signal calculations
// Never use [0] (current unclosed bar) — causes look-ahead issues in backtest

Working with Indicators

// Example: Moving Average
// Create handle on initialization (OnInit)
int maHandle;

int OnInit()
{
   maHandle = iMA(_Symbol, PERIOD_H1, 14, 0, MODE_EMA, PRICE_CLOSE);
   if(maHandle == INVALID_HANDLE)
   {
      Print("Failed to create MA indicator");
      return(INIT_FAILED);
   }
   return(INIT_SUCCEEDED);
}

// Get MA values in OnTick
double maValues[];
ArraySetAsSeries(maValues, true);
CopyBuffer(maHandle, 0, 0, 3, maValues);
// maValues[1] = last completed bar's MA value (use for signals)
// maValues[0] = current bar's MA (don't use for signals)

// Example condition: price above MA
double close1 = iClose(_Symbol, PERIOD_H1, 1);  // Last bar close
bool priceAboveMA = (close1 > maValues[1]);

Placing and Managing Orders

// Open a buy trade
void OpenBuy(double lots, int slPips, int tpPips)
{
   MqlTradeRequest request;
   MqlTradeResult result;
   ZeroMemory(request);
   
   double ask = SymbolInfoDouble(_Symbol, SYMBOL_ASK);
   double pipValue = SymbolInfoDouble(_Symbol, SYMBOL_TRADE_TICK_SIZE) * 10;
   
   request.action   = TRADE_ACTION_DEAL;
   request.symbol   = _Symbol;
   request.volume   = lots;
   request.type     = ORDER_TYPE_BUY;
   request.price    = ask;
   request.sl       = ask - slPips * pipValue;    // Stop loss
   request.tp       = ask + tpPips * pipValue;    // Take profit
   request.deviation = 10;  // Max price deviation in points
   request.magic    = 123456;  // Unique EA identifier
   request.comment  = "MyEA Buy";
   
   if(!OrderSend(request, result))
      Print("OrderSend error: ", GetLastError());
   else
      Print("Order placed: ticket=", result.order);
}

// Check if we have an open position
bool HasOpenPosition()
{
   for(int i = 0; i < PositionsTotal(); i++)
   {
      if(PositionGetSymbol(i) == _Symbol && 
         PositionGetInteger(POSITION_MAGIC) == 123456)
         return true;
   }
   return false;
}

// Close all positions
void CloseAll()
{
   for(int i = PositionsTotal() - 1; i >= 0; i--)
   {
      if(PositionGetSymbol(i) == _Symbol &&
         PositionGetInteger(POSITION_MAGIC) == 123456)
      {
         MqlTradeRequest request;
         MqlTradeResult result;
         ZeroMemory(request);
         request.action = TRADE_ACTION_DEAL;
         request.symbol = _Symbol;
         request.volume = PositionGetDouble(POSITION_VOLUME);
         request.type   = ORDER_TYPE_SELL;  // Close buy with sell
         request.price  = SymbolInfoDouble(_Symbol, SYMBOL_BID);
         request.deviation = 10;
         request.position = PositionGetInteger(POSITION_TICKET);
         OrderSend(request, result);
      }
   }
}

A Complete Simple EA: MA Crossover

Putting it together — a moving average crossover EA:

input double Lots = 0.10;
input int FastMAPeriod = 10;
input int SlowMAPeriod = 20;
input int StopLossPips = 30;
input int TakeProfitPips = 60;

int fastMAHandle, slowMAHandle;
const int MAGIC = 99001;

int OnInit()
{
   fastMAHandle = iMA(_Symbol, PERIOD_CURRENT, FastMAPeriod, 0, MODE_EMA, PRICE_CLOSE);
   slowMAHandle = iMA(_Symbol, PERIOD_CURRENT, SlowMAPeriod, 0, MODE_EMA, PRICE_CLOSE);
   
   if(fastMAHandle == INVALID_HANDLE || slowMAHandle == INVALID_HANDLE)
      return INIT_FAILED;
      
   return INIT_SUCCEEDED;
}

void OnDeinit(const int reason)
{
   IndicatorRelease(fastMAHandle);
   IndicatorRelease(slowMAHandle);
}

void OnTick()
{
   // Only run logic on new bar (not every tick)
   static datetime lastBar = 0;
   datetime currentBar = iTime(_Symbol, PERIOD_CURRENT, 0);
   if(currentBar == lastBar) return;
   lastBar = currentBar;
   
   // Get indicator values (use bar[1] — last completed)
   double fastMA[], slowMA[];
   ArraySetAsSeries(fastMA, true);
   ArraySetAsSeries(slowMA, true);
   CopyBuffer(fastMAHandle, 0, 0, 3, fastMA);
   CopyBuffer(slowMAHandle, 0, 0, 3, slowMA);
   
   bool bullCross = (fastMA[1] > slowMA[1]) && (fastMA[2] <= slowMA[2]);
   bool bearCross = (fastMA[1] < slowMA[1]) && (fastMA[2] >= slowMA[2]);
   
   if(!HasOpenPosition(MAGIC))
   {
      double pipSize = SymbolInfoDouble(_Symbol, SYMBOL_TRADE_TICK_SIZE) * 10;
      
      if(bullCross)
      {
         double ask = SymbolInfoDouble(_Symbol, SYMBOL_ASK);
         PlaceTrade(ORDER_TYPE_BUY, Lots, ask,
                    ask - StopLossPips * pipSize,
                    ask + TakeProfitPips * pipSize, MAGIC);
      }
      else if(bearCross)
      {
         double bid = SymbolInfoDouble(_Symbol, SYMBOL_BID);
         PlaceTrade(ORDER_TYPE_SELL, Lots, bid,
                    bid + StopLossPips * pipSize,
                    bid - TakeProfitPips * pipSize, MAGIC);
      }
   }
}

bool HasOpenPosition(int magic)
{
   for(int i = 0; i < PositionsTotal(); i++)
      if(PositionGetSymbol(i) == _Symbol && 
         PositionGetInteger(POSITION_MAGIC) == magic) return true;
   return false;
}

void PlaceTrade(ENUM_ORDER_TYPE type, double lots, double price, 
                double sl, double tp, int magic)
{
   MqlTradeRequest req; MqlTradeResult res;
   ZeroMemory(req);
   req.action = TRADE_ACTION_DEAL;
   req.symbol = _Symbol; req.volume = lots;
   req.type = type; req.price = price;
   req.sl = sl; req.tp = tp;
   req.deviation = 10; req.magic = magic;
   if(!OrderSend(req, res)) Print("Error: ", GetLastError());
}

What to Learn Next

After mastering the basics:

  1. Error handling: GetLastError(), retry logic for failed orders
  2. Position sizing: Calculate lot size from % risk and stop distance
  3. Multiple timeframe analysis: Use data from H4 for signals, M15 for entries
  4. Indicator creation: Building custom indicators with separate indicator buffers
  5. Object-Oriented MQL5: Classes for cleaner, reusable EA architecture
  6. Strategy Tester optimization: Using the built-in optimizer on your EA

The MT5 Strategy Tester guide covers backtesting your MQL5 EA once you have working code. The MT5 EA Position Sizing guide covers calculating proper lot sizes for risk-based entry.

Frequently Asked Questions

How long does it take to learn MQL5?

With programming experience, writing basic EAs: 1–2 weeks. Writing sophisticated, production-ready EAs: 3–6 months of regular practice. Without programming experience, add 2–3 months to learn programming fundamentals alongside MQL5.

Where can I find MQL5 help?

The official MQL5 reference (mql5.com/en/docs) is comprehensive. The MQL5 community forum has millions of posts and code examples. Stack Overflow has limited MQL5 content but good general programming answers that apply.

Can AI help me write MQL5 code?

LLMs like Claude and GPT-4 can write MQL5 code and find bugs in existing code. The output requires testing — generated code has errors that need debugging, particularly around proper bar indexing and order management patterns. Treat AI-generated code as a starting point, not production-ready output.


Code examples are educational. Test all EA code thoroughly in the Strategy Tester on demo before any live deployment. Trading always involves risk of capital loss.

About William Harris

William Harris is the founding editor of Forex Robot Easy. He has spent over a decade building and reviewing algorithmic trading systems on MetaTrader 4 and 5, with a focus on machine learning, walk-forward validation, and execution mechanics.