Home / MQL5 Programming
Programming

MQL5 Programming Basics: Build Your First Expert Advisor

By Alex Mercer Updated Mar 31, 2026 18 min read
Table of Contents
  1. What Is MQL5?
  2. The MetaEditor IDE
  3. Program Types: EA, Indicator, Script
  4. Syntax and Data Types
  5. Event Handlers
  6. Accessing Indicator Values
  7. Order Management
  8. Building Your First EA
  9. Backtesting Your EA
  10. Next Steps

What Is MQL5?

MQL5 (MetaQuotes Language 5) is the programming language used to create trading robots (Expert Advisors), custom indicators, scripts, and libraries for MetaTrader 5. It is a high-level, C++-like language that provides direct access to market data, trading functions, and the MetaTrader platform API.

With MQL5, you can:

MQL5 is an object-oriented language with support for classes, interfaces, inheritance, and polymorphism. If you have experience with C++, Java, or C#, the transition will be smooth.

The MetaEditor IDE

MetaEditor is the built-in IDE (Integrated Development Environment) for writing MQL5 code. Access it from MetaTrader by pressing F4 or clicking the MetaEditor icon in the toolbar.

Key features:

// Your first MQL5 program — a script that prints to the Experts log
//+------------------------------------------------------------------+
//|                                              HelloWorld.mq5       |
//+------------------------------------------------------------------+
void OnStart()
{
   Print("Hello, MetaTrader 5!");
   Print("Account Balance: ", AccountInfoDouble(ACCOUNT_BALANCE));
   Print("Server: ", AccountInfoString(ACCOUNT_SERVER));
}

Program Types: EA, Indicator, Script

MQL5 supports four types of programs, each serving a different purpose:

Type File Extension Purpose Runs on Chart
Expert Advisor .mq5 / .ex5 Automated trading strategy. Runs continuously. Yes (one per chart)
Custom Indicator .mq5 / .ex5 Custom calculations and chart overlays. Yes (multiple allowed)
Script .mq5 / .ex5 One-time execution tasks. Yes (runs once, then stops)
Service .mq5 / .ex5 Background processes (no chart needed). No

Syntax and Data Types

MQL5 syntax is very similar to C++. Here are the essential building blocks:

// --- Variables and Data Types ---
int    orderCount    = 0;        // Integer
double lotSize       = 0.01;     // Double (floating point)
string symbol        = "EURUSD"; // String
bool   isTrading     = true;     // Boolean
datetime tradeTime   = TimeCurrent(); // Date/Time

// --- Arrays ---
double prices[];          // Dynamic array
ArrayResize(prices, 100); // Resize to 100 elements

// --- Conditional Statements ---
if (lotSize > 0.1) {
   Print("Large position");
} else if (lotSize > 0.01) {
   Print("Standard position");
} else {
   Print("Micro position");
}

// --- Loops ---
for (int i = 0; i < 10; i++) {
   Print("Iteration: ", i);
}

// --- Functions ---
double CalculateRisk(double balance, double riskPercent)
{
   return balance * riskPercent / 100.0;
}

Event Handlers

MQL5 uses an event-driven architecture. Your code responds to events triggered by the platform:

// --- Key Event Handlers for Expert Advisors ---

// Called once when EA is attached to chart
int OnInit()
{
   Print("EA initialized on ", _Symbol);
   return INIT_SUCCEEDED;
}

// Called on every new tick (price update)
void OnTick()
{
   // This is where your main trading logic goes
   double bid = SymbolInfoDouble(_Symbol, SYMBOL_BID);
   double ask = SymbolInfoDouble(_Symbol, SYMBOL_ASK);
   Print("Bid: ", bid, " | Ask: ", ask);
}

// Called when EA is removed from chart
void OnDeinit(const int reason)
{
   Print("EA removed. Reason: ", reason);
}

// Called on timer events (if set with EventSetTimer)
void OnTimer()
{
   // Periodic tasks (e.g., check news, update dashboard)
}

// Called on chart events (clicks, key presses)
void OnChartEvent(const int id, const long &lparam,
                  const double &dparam, const string &sparam)
{
   // Handle user interaction
}

Code and Test in Real-Time

Open a free MT5 demo account to use MetaEditor, compile your EAs, and backtest against real market data.

Open Free MT5 Demo →

Accessing Indicator Values in MQL5

To use indicator values in your EA, you create an indicator handle and then copy its buffer data into an array:

// Step 1: Create indicator handle (do this in OnInit)
int maHandle;

int OnInit()
{
   // Create a 50-period SMA handle
   maHandle = iMA(_Symbol, PERIOD_H1, 50, 0, MODE_SMA, PRICE_CLOSE);

   if (maHandle == INVALID_HANDLE) {
      Print("Failed to create MA indicator");
      return INIT_FAILED;
   }

   return INIT_SUCCEEDED;
}

// Step 2: Read indicator values (do this in OnTick)
void OnTick()
{
   double maValues[];
   ArraySetAsSeries(maValues, true);  // Most recent value first

   // Copy last 3 values from the indicator buffer
   if (CopyBuffer(maHandle, 0, 0, 3, maValues) < 3) {
      Print("Not enough data");
      return;
   }

   double currentMA  = maValues[0];  // Current bar
   double previousMA = maValues[1];  // Previous bar
   double currentBid = SymbolInfoDouble(_Symbol, SYMBOL_BID);

   if (currentBid > currentMA && previousMA > maValues[2]) {
      Print("Price above rising MA - bullish signal");
   }
}

// Step 3: Release handle (do this in OnDeinit)
void OnDeinit(const int reason)
{
   IndicatorRelease(maHandle);
}

Order Management

MQL5 uses a structured approach to order management through the MqlTradeRequest and MqlTradeResult structures:

// --- Opening a Buy Position ---
void OpenBuy(double lots, double slPoints, double tpPoints)
{
   MqlTradeRequest request = {};
   MqlTradeResult  result  = {};

   double ask  = SymbolInfoDouble(_Symbol, SYMBOL_ASK);
   double point = SymbolInfoDouble(_Symbol, SYMBOL_POINT);

   request.action   = TRADE_ACTION_DEAL;
   request.symbol   = _Symbol;
   request.volume   = lots;
   request.type     = ORDER_TYPE_BUY;
   request.price    = ask;
   request.sl       = ask - slPoints * point;  // Stop Loss
   request.tp       = ask + tpPoints * point;  // Take Profit
   request.deviation = 10;  // Max slippage in points
   request.magic    = 123456;  // Magic number to identify EA orders
   request.comment  = "My First EA";

   if (!OrderSend(request, result)) {
      Print("OrderSend failed: ", GetLastError());
   } else {
      Print("Order placed. Ticket: ", result.order);
   }
}

// --- Closing a Position ---
void ClosePosition(ulong ticket)
{
   MqlTradeRequest request = {};
   MqlTradeResult  result  = {};

   if (!PositionSelectByTicket(ticket)) return;

   request.action   = TRADE_ACTION_DEAL;
   request.position = ticket;
   request.symbol   = PositionGetString(POSITION_SYMBOL);
   request.volume   = PositionGetDouble(POSITION_VOLUME);

   // Reverse the direction to close
   if (PositionGetInteger(POSITION_TYPE) == POSITION_TYPE_BUY) {
      request.type  = ORDER_TYPE_SELL;
      request.price = SymbolInfoDouble(request.symbol, SYMBOL_BID);
   } else {
      request.type  = ORDER_TYPE_BUY;
      request.price = SymbolInfoDouble(request.symbol, SYMBOL_ASK);
   }

   request.deviation = 10;

   if (!OrderSend(request, result))
      Print("Close failed: ", GetLastError());
}

Building Your First Expert Advisor: SMA Crossover

Let's combine everything into a complete, working Expert Advisor. This EA trades the classic Moving Average crossover strategy:

//+------------------------------------------------------------------+
//|                                         SMA_Crossover_EA.mq5      |
//|                              MetaTraderGuide.com                   |
//+------------------------------------------------------------------+
#property copyright "MetaTraderGuide.com"
#property version   "1.00"

// --- Input Parameters (adjustable in EA settings) ---
input int    FastMA_Period = 10;       // Fast MA Period
input int    SlowMA_Period = 50;       // Slow MA Period
input double LotSize       = 0.01;     // Position Size
input int    StopLoss      = 500;      // Stop Loss (points)
input int    TakeProfit    = 1000;     // Take Profit (points)
input int    MagicNumber   = 100001;   // EA Magic Number

// --- Global Variables ---
int fastHandle, slowHandle;

//+------------------------------------------------------------------+
int OnInit()
{
   fastHandle = iMA(_Symbol, PERIOD_CURRENT, FastMA_Period,
                    0, MODE_SMA, PRICE_CLOSE);
   slowHandle = iMA(_Symbol, PERIOD_CURRENT, SlowMA_Period,
                    0, MODE_SMA, PRICE_CLOSE);

   if (fastHandle == INVALID_HANDLE ||
       slowHandle == INVALID_HANDLE) {
      Print("Failed to create indicators");
      return INIT_FAILED;
   }

   Print("SMA Crossover EA initialized");
   return INIT_SUCCEEDED;
}

//+------------------------------------------------------------------+
void OnTick()
{
   // Only trade on new bar open (avoid multiple signals per bar)
   static datetime lastBar = 0;
   datetime currentBar = iTime(_Symbol, PERIOD_CURRENT, 0);
   if (currentBar == lastBar) return;
   lastBar = currentBar;

   // Get indicator values
   double fast[], slow[];
   ArraySetAsSeries(fast, true);
   ArraySetAsSeries(slow, true);

   if (CopyBuffer(fastHandle, 0, 0, 3, fast) < 3) return;
   if (CopyBuffer(slowHandle, 0, 0, 3, slow) < 3) return;

   // Check for crossover (using previous completed bars)
   bool bullishCross = fast[1] > slow[1] && fast[2] <= slow[2];
   bool bearishCross = fast[1] < slow[1] && fast[2] >= slow[2];

   // Check if we already have a position
   bool hasPosition = false;
   for (int i = 0; i < PositionsTotal(); i++) {
      if (PositionGetSymbol(i) == _Symbol &&
          PositionGetInteger(POSITION_MAGIC) == MagicNumber) {
         hasPosition = true;
         break;
      }
   }

   // Trading logic
   if (bullishCross && !hasPosition) {
      OpenBuy();
   } else if (bearishCross && !hasPosition) {
      OpenSell();
   }
}

//+------------------------------------------------------------------+
void OpenBuy()
{
   MqlTradeRequest req = {};
   MqlTradeResult  res = {};
   double ask   = SymbolInfoDouble(_Symbol, SYMBOL_ASK);
   double point = SymbolInfoDouble(_Symbol, SYMBOL_POINT);

   req.action    = TRADE_ACTION_DEAL;
   req.symbol    = _Symbol;
   req.volume    = LotSize;
   req.type      = ORDER_TYPE_BUY;
   req.price     = ask;
   req.sl        = ask - StopLoss * point;
   req.tp        = ask + TakeProfit * point;
   req.deviation = 10;
   req.magic     = MagicNumber;
   req.comment   = "SMA Cross BUY";

   if (!OrderSend(req, res))
      Print("Buy failed: ", GetLastError());
   else
      Print("Buy opened at ", ask);
}

//+------------------------------------------------------------------+
void OpenSell()
{
   MqlTradeRequest req = {};
   MqlTradeResult  res = {};
   double bid   = SymbolInfoDouble(_Symbol, SYMBOL_BID);
   double point = SymbolInfoDouble(_Symbol, SYMBOL_POINT);

   req.action    = TRADE_ACTION_DEAL;
   req.symbol    = _Symbol;
   req.volume    = LotSize;
   req.type      = ORDER_TYPE_SELL;
   req.price     = bid;
   req.sl        = bid + StopLoss * point;
   req.tp        = bid - TakeProfit * point;
   req.deviation = 10;
   req.magic     = MagicNumber;
   req.comment   = "SMA Cross SELL";

   if (!OrderSend(req, res))
      Print("Sell failed: ", GetLastError());
   else
      Print("Sell opened at ", bid);
}

//+------------------------------------------------------------------+
void OnDeinit(const int reason)
{
   IndicatorRelease(fastHandle);
   IndicatorRelease(slowHandle);
   Print("SMA Crossover EA removed");
}

Backtesting Your EA

After compiling your EA in MetaEditor (press F7), test it using the Strategy Tester:

  1. Open Strategy Tester: In MetaTrader, press Ctrl+R or go to View > Strategy Tester.
  2. Select your EA: Choose "SMA_Crossover_EA" from the dropdown.
  3. Configure settings: Choose the symbol (e.g., EURUSD), timeframe (e.g., H1), date range, and modeling type.
  4. Modeling type: Use "Every tick based on real ticks" for the most accurate results. "Open prices only" is fastest but less precise.
  5. Run the test: Click Start and wait for results.
  6. Analyze results: Check the Results, Graph, and Statistics tabs. Look at net profit, drawdown, profit factor, and number of trades.

Key metrics to evaluate:

A profitable backtest does not guarantee future results. Always forward-test on a demo account for at least 3 months before deploying any EA with real money. Market conditions change, and past performance is not predictive.

Next Steps

Once you are comfortable with the basics covered here, explore these advanced topics:

Start Coding on MetaTrader 5

Open a free demo account to access MetaEditor, compile your EAs, and backtest with real historical data.

Open Free MT5 Account →

Frequently Asked Questions

Do I need programming experience to learn MQL5?
Prior programming experience helps but is not required. MQL5 syntax is similar to C++ and Java. Start with the basic concepts in this guide and practice with simple scripts. The MetaEditor IDE includes auto-complete and debugging tools that help beginners learn faster.
What is the difference between MQL4 and MQL5?
MQL5 is fully object-oriented with classes and interfaces, uses an event-driven model, supports multi-currency backtesting, compiles to faster native code, and has a larger standard library. MQL4 is simpler and procedural, making it easier for beginners but less powerful overall.
Can I sell my Expert Advisors?
Yes, you can sell EAs on the MQL5 Market, which is built into MetaTrader. You set the price and receive 80% of sales revenue. Your EA goes through a review process to ensure quality. You can also offer free demo versions and subscription-based rentals.
Risk Disclaimer

Trading forex and CFDs carries a high level of risk and may not be suitable for all investors. Automated trading systems (Expert Advisors) do not guarantee profits. Past backtest performance is not indicative of future results. The information on this website is for educational purposes only and does not constitute financial advice.