All Articles
MetaTrader MT4 MT5 MQL Expert Advisor Guide

Complete Guide to MetaTrader 4 & 5 (MQL): Build Expert Advisors & Custom Indicators

Brokerlytic TeamApril 10, 2026
Key Takeaways:Master MQL4 and MQL5 programming β€” learn to build Expert Advisors (EAs), custom indicators, and automated trading systems on MetaTrader platforms.

What is MQL (MetaQuotes Language)?

MQL is the programming language behind MetaTrader 4 (MQL4) and MetaTrader 5 (MQL5). It allows traders to create:

  • Expert Advisors (EAs) β€” fully automated trading robots
  • Custom Indicators β€” technical analysis tools beyond built-in ones
  • Scripts β€” one-time execution programs for batch operations
  • Libraries β€” reusable code modules

MT4 vs MT5: Which to Choose?

FeatureMT4 (MQL4)MT5 (MQL5)
LanguageC-like proceduralC++ object-oriented
BacktestingSingle-threadedMulti-threaded (faster)
Order SystemSimple (OrderSend)Position-based (CTrade)
Timeframes9 timeframes21 timeframes
Market TypesForex focusMulti-asset (Stocks, Futures)
HedgingDefaultOptional (Hedging + Netting)
CommunityMassive legacy codebaseGrowing, modern

Recommendation: Start with MT5/MQL5 for new projects. Use MT4 only if your broker doesn't support MT5.


Chapter 1: Setting Up Your Development Environment

MetaEditor

  1. Open MetaTrader 4 or 5
  2. Press F4 or click "MetaEditor" icon
  3. File β†’ New β†’ Expert Advisor (or Indicator)
  4. Name your project and click "Finish"

File Structure

MQL5/
β”œβ”€β”€ Experts/       ← Expert Advisors (.ex5)
β”œβ”€β”€ Indicators/    ← Custom Indicators (.ex5)
β”œβ”€β”€ Scripts/       ← One-shot Scripts (.ex5)
β”œβ”€β”€ Include/       ← Header files (.mqh)
└── Libraries/     ← Shared libraries (.ex5)

Chapter 2: Your First Expert Advisor (MQL5)

Simple Moving Average Crossover EA

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

#include <Trade/Trade.mqh>

input int FastPeriod = 10;    // Fast MA Period
input int SlowPeriod = 30;    // Slow MA Period
input double LotSize = 0.1;   // Trade Volume
input int StopLoss = 100;     // Stop Loss in points
input int TakeProfit = 200;   // Take Profit in points

CTrade trade;
int fastHandle, slowHandle;

int OnInit()
{
   fastHandle = iMA(_Symbol, PERIOD_CURRENT, FastPeriod, 0, MODE_SMA, PRICE_CLOSE);
   slowHandle = iMA(_Symbol, PERIOD_CURRENT, SlowPeriod, 0, MODE_SMA, PRICE_CLOSE);
   
   if(fastHandle == INVALID_HANDLE || slowHandle == INVALID_HANDLE)
   {
      Print("Failed to create indicator handles");
      return(INIT_FAILED);
   }
   return(INIT_SUCCEEDED);
}

void OnDeinit(const int reason)
{
   IndicatorRelease(fastHandle);
   IndicatorRelease(slowHandle);
}

void OnTick()
{
   // Only trade on new bar
   static datetime lastBar = 0;
   datetime currentBar = iTime(_Symbol, PERIOD_CURRENT, 0);
   if(currentBar == lastBar) return;
   lastBar = currentBar;
   
   // Get MA values
   double fastMA[2], slowMA[2];
   CopyBuffer(fastHandle, 0, 1, 2, fastMA);
   CopyBuffer(slowHandle, 0, 1, 2, slowMA);
   
   // Check for crossover
   bool bullishCross = fastMA[1] > slowMA[1] && fastMA[0] <= slowMA[0];
   bool bearishCross = fastMA[1] < slowMA[1] && fastMA[0] >= slowMA[0];
   
   // Check if we have open positions
   bool hasPosition = PositionSelect(_Symbol);
   
   double ask = SymbolInfoDouble(_Symbol, SYMBOL_ASK);
   double bid = SymbolInfoDouble(_Symbol, SYMBOL_BID);
   double point = SymbolInfoDouble(_Symbol, SYMBOL_POINT);
   
   if(bullishCross && !hasPosition)
   {
      double sl = ask - StopLoss * point;
      double tp = ask + TakeProfit * point;
      trade.Buy(LotSize, _Symbol, ask, sl, tp, "SMA Cross Buy");
   }
   
   if(bearishCross && hasPosition)
   {
      trade.PositionClose(_Symbol);
   }
}

Chapter 3: Building Custom Indicators

RSI with Signal Arrows

//+------------------------------------------------------------------+
//|                                            RSI_Signals.mq5       |
//+------------------------------------------------------------------+
#property indicator_separate_window
#property indicator_buffers 3
#property indicator_plots   3

#property indicator_label1  "RSI"
#property indicator_type1   DRAW_LINE
#property indicator_color1  clrDodgerBlue
#property indicator_width1  2

#property indicator_label2  "Buy Signal"
#property indicator_type2   DRAW_ARROW
#property indicator_color2  clrLime

#property indicator_label3  "Sell Signal"
#property indicator_type3   DRAW_ARROW
#property indicator_color3  clrRed

input int RSI_Period = 14;
input int Oversold = 30;
input int Overbought = 70;

double rsiBuffer[], buyBuffer[], sellBuffer[];
int rsiHandle;

int OnInit()
{
   SetIndexBuffer(0, rsiBuffer, INDICATOR_DATA);
   SetIndexBuffer(1, buyBuffer, INDICATOR_DATA);
   SetIndexBuffer(2, sellBuffer, INDICATOR_DATA);
   
   PlotIndexSetInteger(1, PLOT_ARROW, 233);  // Up arrow
   PlotIndexSetInteger(2, PLOT_ARROW, 234);  // Down arrow
   
   rsiHandle = iRSI(_Symbol, PERIOD_CURRENT, RSI_Period, PRICE_CLOSE);
   
   IndicatorSetDouble(INDICATOR_LEVELVALUE, 0, Overbought);
   IndicatorSetDouble(INDICATOR_LEVELVALUE, 1, Oversold);
   
   return(INIT_SUCCEEDED);
}

int OnCalculate(const int rates_total, const int prev_calculated,
                const datetime &time[], const double &open[],
                const double &high[], const double &low[],
                const double &close[], const long &tick_volume[],
                const long &volume[], const int &spread[])
{
   int start = prev_calculated > 0 ? prev_calculated - 1 : 0;
   
   double rsi[];
   CopyBuffer(rsiHandle, 0, 0, rates_total, rsi);
   
   for(int i = start; i < rates_total; i++)
   {
      rsiBuffer[i] = rsi[i];
      buyBuffer[i] = EMPTY_VALUE;
      sellBuffer[i] = EMPTY_VALUE;
      
      if(i > 0)
      {
         if(rsi[i-1] < Oversold && rsi[i] >= Oversold)
            buyBuffer[i] = rsi[i];
         if(rsi[i-1] > Overbought && rsi[i] <= Overbought)
            sellBuffer[i] = rsi[i];
      }
   }
   return(rates_total);
}

Chapter 4: Risk Management in EAs

Position Sizing Based on Account Risk

double CalculateLotSize(double riskPercent, double stopLossPoints)
{
   double accountBalance = AccountInfoDouble(ACCOUNT_BALANCE);
   double riskAmount = accountBalance * riskPercent / 100.0;
   
   double tickValue = SymbolInfoDouble(_Symbol, SYMBOL_TRADE_TICK_VALUE);
   double tickSize = SymbolInfoDouble(_Symbol, SYMBOL_TRADE_TICK_SIZE);
   double point = SymbolInfoDouble(_Symbol, SYMBOL_POINT);
   
   double pipValue = tickValue * (point / tickSize);
   double lotSize = riskAmount / (stopLossPoints * pipValue);
   
   // Normalize to broker's lot step
   double lotStep = SymbolInfoDouble(_Symbol, SYMBOL_VOLUME_STEP);
   double minLot = SymbolInfoDouble(_Symbol, SYMBOL_VOLUME_MIN);
   double maxLot = SymbolInfoDouble(_Symbol, SYMBOL_VOLUME_MAX);
   
   lotSize = MathFloor(lotSize / lotStep) * lotStep;
   lotSize = MathMax(minLot, MathMin(maxLot, lotSize));
   
   return lotSize;
}

Trailing Stop Implementation

void TrailingStop(int trailPoints)
{
   for(int i = PositionsTotal() - 1; i >= 0; i--)
   {
      if(PositionGetSymbol(i) != _Symbol) continue;
      
      double openPrice = PositionGetDouble(POSITION_PRICE_OPEN);
      double currentSL = PositionGetDouble(POSITION_SL);
      double point = SymbolInfoDouble(_Symbol, SYMBOL_POINT);
      long ticket = PositionGetInteger(POSITION_TICKET);
      
      if(PositionGetInteger(POSITION_TYPE) == POSITION_TYPE_BUY)
      {
         double bid = SymbolInfoDouble(_Symbol, SYMBOL_BID);
         double newSL = bid - trailPoints * point;
         if(newSL > currentSL && newSL > openPrice)
            trade.PositionModify(ticket, newSL, PositionGetDouble(POSITION_TP));
      }
      else
      {
         double ask = SymbolInfoDouble(_Symbol, SYMBOL_ASK);
         double newSL = ask + trailPoints * point;
         if(newSL < currentSL && newSL < openPrice)
            trade.PositionModify(ticket, newSL, PositionGetDouble(POSITION_TP));
      }
   }
}

Chapter 5: Backtesting & Optimization

Strategy Tester Setup

  1. Open Strategy Tester (Ctrl+R in MT5)
  2. Select your EA from the dropdown
  3. Choose symbol and timeframe
  4. Set date range for testing
  5. Choose modeling mode:
    • Every tick β€” most accurate, slowest
    • 1 Minute OHLC β€” good balance
    • Open prices only β€” fastest, least accurate

Optimization Tips

  • Use Genetic Algorithm for faster optimization
  • Avoid over-fitting: test on out-of-sample data
  • Forward-test on demo accounts before going live
  • Monitor key metrics: Profit Factor > 1.5, Max Drawdown < 20%, Win Rate > 40%

Summary

MetaTrader with MQL is the industry standard for automated Forex trading. Key takeaways:

  1. Start with MT5/MQL5 for modern features
  2. Use the CTrade class for cleaner order management
  3. Always implement risk management (position sizing, stops)
  4. Backtest extensively before live trading
  5. Join the MQL5 community for support and shared code

Ready to start? Download MetaTrader 5 and begin building your first EA today.

Frequently Asked Questions

What is the main concept of to MetaTrader 4 & 5 (MQL): Build Expert Advisors & Custom Indicators?

Master MQL4 and MQL5 programming β€” learn to build Expert Advisors (EAs), custom indicators, and automated trading systems on MetaTrader platforms.

Who should read this guide?

This guide is perfect for both beginners looking to understand the basics and experienced traders wanting to refine their strategies in MetaTrader.